blob: 51a5e0b1a53d1c4894be9423d90e4de025e6c137 [file] [log] [blame]
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -08001/*
Deepak Katragaddaeeccf5a2017-03-21 13:47:56 -07002 * Copyright (c) 2013, 2016-2017, The Linux Foundation. All rights reserved.
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -08003 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/kernel.h>
15#include <linux/bitops.h>
16#include <linux/err.h>
17#include <linux/delay.h>
18#include <linux/export.h>
Taniya Das4b006ad2016-11-07 10:01:38 +053019#include <linux/clk.h>
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -080020#include <linux/clk-provider.h>
21#include <linux/regmap.h>
Taniya Das77598782016-10-28 12:37:12 +053022#include <linux/clk/qcom.h>
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -080023
24#include "clk-branch.h"
Taniya Das4b006ad2016-11-07 10:01:38 +053025#include "clk-regmap.h"
Taniya Das8436bd72016-11-21 17:50:13 +053026#include "clk-debug.h"
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -080027
28static bool clk_branch_in_hwcg_mode(const struct clk_branch *br)
29{
30 u32 val;
31
32 if (!br->hwcg_reg)
33 return 0;
34
35 regmap_read(br->clkr.regmap, br->hwcg_reg, &val);
36
37 return !!(val & BIT(br->hwcg_bit));
38}
39
40static bool clk_branch_check_halt(const struct clk_branch *br, bool enabling)
41{
42 bool invert = (br->halt_check == BRANCH_HALT_ENABLE);
43 u32 val;
44
45 regmap_read(br->clkr.regmap, br->halt_reg, &val);
46
47 val &= BIT(br->halt_bit);
48 if (invert)
49 val = !val;
50
51 return !!val == !enabling;
52}
53
54#define BRANCH_CLK_OFF BIT(31)
55#define BRANCH_NOC_FSM_STATUS_SHIFT 28
56#define BRANCH_NOC_FSM_STATUS_MASK 0x7
57#define BRANCH_NOC_FSM_STATUS_ON (0x2 << BRANCH_NOC_FSM_STATUS_SHIFT)
58
59static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling)
60{
61 u32 val;
62 u32 mask;
63
64 mask = BRANCH_NOC_FSM_STATUS_MASK << BRANCH_NOC_FSM_STATUS_SHIFT;
65 mask |= BRANCH_CLK_OFF;
66
67 regmap_read(br->clkr.regmap, br->halt_reg, &val);
68
69 if (enabling) {
70 val &= mask;
71 return (val & BRANCH_CLK_OFF) == 0 ||
72 val == BRANCH_NOC_FSM_STATUS_ON;
73 } else {
74 return val & BRANCH_CLK_OFF;
75 }
76}
77
78static int clk_branch_wait(const struct clk_branch *br, bool enabling,
79 bool (check_halt)(const struct clk_branch *, bool))
80{
81 bool voted = br->halt_check & BRANCH_VOTED;
Stephen Boyd836ee0f2015-08-12 11:42:23 -070082 const char *name = clk_hw_get_name(&br->clkr.hw);
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -080083
84 /* Skip checking halt bit if the clock is in hardware gated mode */
85 if (clk_branch_in_hwcg_mode(br))
86 return 0;
87
88 if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) {
89 udelay(10);
90 } else if (br->halt_check == BRANCH_HALT_ENABLE ||
91 br->halt_check == BRANCH_HALT ||
92 (enabling && voted)) {
93 int count = 200;
94
95 while (count-- > 0) {
96 if (check_halt(br, enabling))
97 return 0;
98 udelay(1);
99 }
100 WARN(1, "%s status stuck at 'o%s'", name,
101 enabling ? "ff" : "n");
102 return -EBUSY;
103 }
104 return 0;
105}
106
107static int clk_branch_toggle(struct clk_hw *hw, bool en,
108 bool (check_halt)(const struct clk_branch *, bool))
109{
110 struct clk_branch *br = to_clk_branch(hw);
111 int ret;
112
113 if (en) {
114 ret = clk_enable_regmap(hw);
115 if (ret)
116 return ret;
117 } else {
118 clk_disable_regmap(hw);
119 }
120
121 return clk_branch_wait(br, en, check_halt);
122}
123
124static int clk_branch_enable(struct clk_hw *hw)
125{
126 return clk_branch_toggle(hw, true, clk_branch_check_halt);
127}
128
Taniya Das0192f782016-07-14 11:57:53 +0530129static int clk_cbcr_set_flags(struct regmap *regmap, unsigned int reg,
130 unsigned long flags)
131{
132 u32 cbcr_val;
133
134 regmap_read(regmap, reg, &cbcr_val);
135
136 switch (flags) {
137 case CLKFLAG_PERIPH_OFF_SET:
138 cbcr_val |= BIT(12);
139 break;
140 case CLKFLAG_PERIPH_OFF_CLEAR:
141 cbcr_val &= ~BIT(12);
142 break;
143 case CLKFLAG_RETAIN_PERIPH:
144 cbcr_val |= BIT(13);
145 break;
146 case CLKFLAG_NORETAIN_PERIPH:
147 cbcr_val &= ~BIT(13);
148 break;
149 case CLKFLAG_RETAIN_MEM:
150 cbcr_val |= BIT(14);
151 break;
152 case CLKFLAG_NORETAIN_MEM:
153 cbcr_val &= ~BIT(14);
154 break;
155 default:
156 return -EINVAL;
157 }
158
159 regmap_write(regmap, reg, cbcr_val);
160
161 /* Make sure power is enabled/disabled before returning. */
162 mb();
163 udelay(1);
164
165 return 0;
166}
167
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800168static void clk_branch_disable(struct clk_hw *hw)
169{
170 clk_branch_toggle(hw, false, clk_branch_check_halt);
171}
172
Taniya Das0192f782016-07-14 11:57:53 +0530173static int clk_branch_set_flags(struct clk_hw *hw, unsigned int flags)
174{
175 struct clk_branch *br = to_clk_branch(hw);
176
177 return clk_cbcr_set_flags(br->clkr.regmap, br->halt_reg, flags);
178}
179
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800180const struct clk_ops clk_branch_ops = {
181 .enable = clk_branch_enable,
182 .disable = clk_branch_disable,
183 .is_enabled = clk_is_enabled_regmap,
Taniya Das0192f782016-07-14 11:57:53 +0530184 .set_flags = clk_branch_set_flags,
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800185};
186EXPORT_SYMBOL_GPL(clk_branch_ops);
187
Deepak Katragadda9caf8992016-12-21 11:12:55 -0800188static int clk_branch2_set_rate(struct clk_hw *hw, unsigned long rate,
189 unsigned long parent_rate)
190{
191 struct clk_branch *branch = to_clk_branch(hw);
192 struct clk_hw *parent = clk_hw_get_parent(hw);
193 unsigned long curr_rate, new_rate, other_rate = 0;
194 int ret = 0;
195
196 if (!parent)
197 return -EPERM;
198
199 if (!branch->aggr_sibling_rates || !clk_hw_is_prepared(hw)) {
200 branch->rate = rate;
201 return 0;
202 }
203
204 other_rate = clk_aggregate_rate(hw, parent->core);
205 curr_rate = max(other_rate, branch->rate);
206 new_rate = max(other_rate, rate);
207
208 if (new_rate != curr_rate) {
209 ret = clk_set_rate(parent->clk, new_rate);
210 if (ret)
211 goto err;
212 }
213 branch->rate = rate;
214err:
215 return ret;
216}
217
218static long clk_branch2_round_rate(struct clk_hw *hw, unsigned long rate,
219 unsigned long *parent_rate)
220{
221 struct clk_hw *parent = clk_hw_get_parent(hw);
Deepak Katragaddaeeccf5a2017-03-21 13:47:56 -0700222 unsigned long rrate = 0;
Deepak Katragadda9caf8992016-12-21 11:12:55 -0800223
224 if (!parent)
225 return -EPERM;
226
Deepak Katragaddaeeccf5a2017-03-21 13:47:56 -0700227 rrate = clk_hw_round_rate(parent, rate);
228 /*
229 * If the rounded rate that's returned is valid, update the parent_rate
230 * field so that the set_rate() call can be propagated to the parent.
231 */
232 if (rrate > 0)
233 *parent_rate = rrate;
234
235 return rrate;
Deepak Katragadda9caf8992016-12-21 11:12:55 -0800236}
237
238static unsigned long clk_branch2_recalc_rate(struct clk_hw *hw,
239 unsigned long parent_rate)
240{
241 return to_clk_branch(hw)->rate;
242}
243
Taniya Das4b006ad2016-11-07 10:01:38 +0530244static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw)
245{
246 struct clk_branch *br = to_clk_branch(hw);
247 struct clk_regmap *rclk = to_clk_regmap(hw);
248 int size, i, val;
249
250 static struct clk_register_data data[] = {
251 {"CBCR", 0x0},
252 };
253
254 static struct clk_register_data data1[] = {
255 {"APSS_VOTE", 0x0},
256 {"APSS_SLEEP_VOTE", 0x4},
257 };
258
259 size = ARRAY_SIZE(data);
260
261 for (i = 0; i < size; i++) {
262 regmap_read(br->clkr.regmap, br->halt_reg + data[i].offset,
263 &val);
264 seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val);
265 }
266
267 if ((br->halt_check & BRANCH_HALT_VOTED) &&
268 !(br->halt_check & BRANCH_VOTED)) {
269 if (rclk->enable_reg) {
270 size = ARRAY_SIZE(data1);
271 for (i = 0; i < size; i++) {
272 regmap_read(br->clkr.regmap, rclk->enable_reg +
273 data1[i].offset, &val);
274 seq_printf(f, "%20s: 0x%.8x\n",
275 data1[i].name, val);
276 }
277 }
278 }
279}
280
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800281static int clk_branch2_enable(struct clk_hw *hw)
282{
283 return clk_branch_toggle(hw, true, clk_branch2_check_halt);
284}
285
Deepak Katragadda9caf8992016-12-21 11:12:55 -0800286static int clk_branch2_prepare(struct clk_hw *hw)
287{
288 struct clk_branch *branch = to_clk_branch(hw);
289 struct clk_hw *parent = clk_hw_get_parent(hw);
290 unsigned long curr_rate, branch_rate = branch->rate;
291 int ret = 0;
292
293 /*
294 * Do the rate aggregation and scaling of the RCG in the prepare/
295 * unprepare functions to avoid potential RPM(/h) communication due to
296 * votes on the voltage rails.
297 */
298 if (branch->aggr_sibling_rates) {
299 curr_rate = clk_aggregate_rate(hw, parent->core);
300 if (branch_rate > curr_rate) {
301 ret = clk_set_rate(parent->clk, branch_rate);
302 if (ret)
303 goto exit;
304 }
305 }
306exit:
307 return ret;
308}
309
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800310static void clk_branch2_disable(struct clk_hw *hw)
311{
312 clk_branch_toggle(hw, false, clk_branch2_check_halt);
313}
314
Deepak Katragadda9caf8992016-12-21 11:12:55 -0800315static void clk_branch2_unprepare(struct clk_hw *hw)
316{
317 struct clk_branch *branch = to_clk_branch(hw);
318 struct clk_hw *parent = clk_hw_get_parent(hw);
319 unsigned long curr_rate, new_rate, branch_rate = branch->rate;
320
321 if (branch->aggr_sibling_rates) {
322 new_rate = clk_aggregate_rate(hw, parent->core);
323 curr_rate = max(new_rate, branch_rate);
324 if (new_rate < curr_rate)
325 if (clk_set_rate(parent->clk, new_rate))
326 pr_err("Failed to scale %s to %lu\n",
327 clk_hw_get_name(parent), new_rate);
328 }
329}
330
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800331const struct clk_ops clk_branch2_ops = {
Deepak Katragadda9caf8992016-12-21 11:12:55 -0800332 .prepare = clk_branch2_prepare,
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800333 .enable = clk_branch2_enable,
Deepak Katragadda9caf8992016-12-21 11:12:55 -0800334 .unprepare = clk_branch2_unprepare,
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800335 .disable = clk_branch2_disable,
336 .is_enabled = clk_is_enabled_regmap,
Deepak Katragadda9caf8992016-12-21 11:12:55 -0800337 .set_rate = clk_branch2_set_rate,
338 .round_rate = clk_branch2_round_rate,
339 .recalc_rate = clk_branch2_recalc_rate,
Taniya Das0192f782016-07-14 11:57:53 +0530340 .set_flags = clk_branch_set_flags,
Taniya Das4b006ad2016-11-07 10:01:38 +0530341 .list_registers = clk_branch2_list_registers,
Taniya Das8436bd72016-11-21 17:50:13 +0530342 .debug_init = clk_debug_measure_add,
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800343};
344EXPORT_SYMBOL_GPL(clk_branch2_ops);
345
Odelu Kukatla0d941532016-06-06 22:19:53 +0530346static int clk_gate_toggle(struct clk_hw *hw, bool en)
347{
348 struct clk_gate2 *gt = to_clk_gate2(hw);
349 int ret = 0;
350
351 if (en) {
352 ret = clk_enable_regmap(hw);
353 if (ret)
354 return ret;
355 } else {
356 clk_disable_regmap(hw);
357 }
358
359 if (gt->udelay)
360 udelay(gt->udelay);
361
362 return ret;
363}
364
365static int clk_gate2_enable(struct clk_hw *hw)
366{
367 return clk_gate_toggle(hw, true);
368}
369
370static void clk_gate2_disable(struct clk_hw *hw)
371{
372 clk_gate_toggle(hw, false);
373}
374
Taniya Das4b006ad2016-11-07 10:01:38 +0530375static void clk_gate2_list_registers(struct seq_file *f, struct clk_hw *hw)
376{
377 struct clk_gate2 *gt = to_clk_gate2(hw);
378 int size, i, val;
379
380 static struct clk_register_data data[] = {
381 {"EN_REG", 0x0},
382 };
383
384 size = ARRAY_SIZE(data);
385
386 for (i = 0; i < size; i++) {
387 regmap_read(gt->clkr.regmap, gt->clkr.enable_reg +
388 data[i].offset, &val);
389 seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val);
390 }
391}
392
Odelu Kukatla0d941532016-06-06 22:19:53 +0530393const struct clk_ops clk_gate2_ops = {
394 .enable = clk_gate2_enable,
395 .disable = clk_gate2_disable,
396 .is_enabled = clk_is_enabled_regmap,
Taniya Das4b006ad2016-11-07 10:01:38 +0530397 .list_registers = clk_gate2_list_registers,
Taniya Das8436bd72016-11-21 17:50:13 +0530398 .debug_init = clk_debug_measure_add,
Odelu Kukatla0d941532016-06-06 22:19:53 +0530399};
400EXPORT_SYMBOL_GPL(clk_gate2_ops);
401
Stephen Boyd6e0ad1b2014-01-15 10:47:26 -0800402const struct clk_ops clk_branch_simple_ops = {
403 .enable = clk_enable_regmap,
404 .disable = clk_disable_regmap,
405 .is_enabled = clk_is_enabled_regmap,
406};
407EXPORT_SYMBOL_GPL(clk_branch_simple_ops);