OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From patchwork Fri Dec 8 09:42:21 2017 |
2 | Content-Type: text/plain; charset="utf-8" |
||
3 | MIME-Version: 1.0 |
||
4 | Content-Transfer-Encoding: 7bit |
||
5 | Subject: [v4,03/12] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) |
||
6 | From: Sricharan R <sricharan@codeaurora.org> |
||
7 | X-Patchwork-Id: 10102083 |
||
8 | Message-Id: <1512726150-7204-4-git-send-email-sricharan@codeaurora.org> |
||
9 | To: mturquette@baylibre.com, sboyd@codeaurora.org, |
||
10 | devicetree@vger.kernel.org, linux-pm@vger.kernel.org, |
||
11 | linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, |
||
12 | viresh.kumar@linaro.org, linux-arm-kernel@lists.infradead.org |
||
13 | Cc: sricharan@codeaurora.org |
||
14 | Date: Fri, 8 Dec 2017 15:12:21 +0530 |
||
15 | |||
16 | From: Stephen Boyd <sboyd@codeaurora.org> |
||
17 | |||
18 | HFPLLs are the main frequency source for Krait CPU clocks. Add |
||
19 | support for changing the rate of these PLLs. |
||
20 | |||
21 | Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> |
||
22 | --- |
||
23 | drivers/clk/qcom/Makefile | 1 + |
||
24 | drivers/clk/qcom/clk-hfpll.c | 253 +++++++++++++++++++++++++++++++++++++++++++ |
||
25 | drivers/clk/qcom/clk-hfpll.h | 54 +++++++++ |
||
26 | 3 files changed, 308 insertions(+) |
||
27 | create mode 100644 drivers/clk/qcom/clk-hfpll.c |
||
28 | create mode 100644 drivers/clk/qcom/clk-hfpll.h |
||
29 | |||
30 | --- a/drivers/clk/qcom/Makefile |
||
31 | +++ b/drivers/clk/qcom/Makefile |
||
32 | @@ -10,6 +10,7 @@ clk-qcom-y += clk-rcg2.o |
||
33 | clk-qcom-y += clk-branch.o |
||
34 | clk-qcom-y += clk-regmap-divider.o |
||
35 | clk-qcom-y += clk-regmap-mux.o |
||
36 | +clk-qcom-y += clk-hfpll.o |
||
37 | clk-qcom-y += reset.o |
||
38 | clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o |
||
39 | |||
40 | --- /dev/null |
||
41 | +++ b/drivers/clk/qcom/clk-hfpll.c |
||
42 | @@ -0,0 +1,253 @@ |
||
43 | +/* |
||
44 | + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. |
||
45 | + * |
||
46 | + * This program is free software; you can redistribute it and/or modify |
||
47 | + * it under the terms of the GNU General Public License version 2 and |
||
48 | + * only version 2 as published by the Free Software Foundation. |
||
49 | + * |
||
50 | + * This program is distributed in the hope that it will be useful, |
||
51 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
52 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
53 | + * GNU General Public License for more details. |
||
54 | + */ |
||
55 | +#include <linux/kernel.h> |
||
56 | +#include <linux/export.h> |
||
57 | +#include <linux/regmap.h> |
||
58 | +#include <linux/delay.h> |
||
59 | +#include <linux/err.h> |
||
60 | +#include <linux/clk-provider.h> |
||
61 | +#include <linux/spinlock.h> |
||
62 | + |
||
63 | +#include "clk-regmap.h" |
||
64 | +#include "clk-hfpll.h" |
||
65 | + |
||
66 | +#define PLL_OUTCTRL BIT(0) |
||
67 | +#define PLL_BYPASSNL BIT(1) |
||
68 | +#define PLL_RESET_N BIT(2) |
||
69 | + |
||
70 | +/* Initialize a HFPLL at a given rate and enable it. */ |
||
71 | +static void __clk_hfpll_init_once(struct clk_hw *hw) |
||
72 | +{ |
||
73 | + struct clk_hfpll *h = to_clk_hfpll(hw); |
||
74 | + struct hfpll_data const *hd = h->d; |
||
75 | + struct regmap *regmap = h->clkr.regmap; |
||
76 | + |
||
77 | + if (likely(h->init_done)) |
||
78 | + return; |
||
79 | + |
||
80 | + /* Configure PLL parameters for integer mode. */ |
||
81 | + if (hd->config_val) |
||
82 | + regmap_write(regmap, hd->config_reg, hd->config_val); |
||
83 | + regmap_write(regmap, hd->m_reg, 0); |
||
84 | + regmap_write(regmap, hd->n_reg, 1); |
||
85 | + |
||
86 | + if (hd->user_reg) { |
||
87 | + u32 regval = hd->user_val; |
||
88 | + unsigned long rate; |
||
89 | + |
||
90 | + rate = clk_hw_get_rate(hw); |
||
91 | + |
||
92 | + /* Pick the right VCO. */ |
||
93 | + if (hd->user_vco_mask && rate > hd->low_vco_max_rate) |
||
94 | + regval |= hd->user_vco_mask; |
||
95 | + regmap_write(regmap, hd->user_reg, regval); |
||
96 | + } |
||
97 | + |
||
98 | + if (hd->droop_reg) |
||
99 | + regmap_write(regmap, hd->droop_reg, hd->droop_val); |
||
100 | + |
||
101 | + h->init_done = true; |
||
102 | +} |
||
103 | + |
||
104 | +static void __clk_hfpll_enable(struct clk_hw *hw) |
||
105 | +{ |
||
106 | + struct clk_hfpll *h = to_clk_hfpll(hw); |
||
107 | + struct hfpll_data const *hd = h->d; |
||
108 | + struct regmap *regmap = h->clkr.regmap; |
||
109 | + u32 val; |
||
110 | + |
||
111 | + __clk_hfpll_init_once(hw); |
||
112 | + |
||
113 | + /* Disable PLL bypass mode. */ |
||
114 | + regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); |
||
115 | + |
||
116 | + /* |
||
117 | + * H/W requires a 5us delay between disabling the bypass and |
||
118 | + * de-asserting the reset. Delay 10us just to be safe. |
||
119 | + */ |
||
120 | + udelay(10); |
||
121 | + |
||
122 | + /* De-assert active-low PLL reset. */ |
||
123 | + regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); |
||
124 | + |
||
125 | + /* Wait for PLL to lock. */ |
||
126 | + if (hd->status_reg) { |
||
127 | + do { |
||
128 | + regmap_read(regmap, hd->status_reg, &val); |
||
129 | + } while (!(val & BIT(hd->lock_bit))); |
||
130 | + } else { |
||
131 | + udelay(60); |
||
132 | + } |
||
133 | + |
||
134 | + /* Enable PLL output. */ |
||
135 | + regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); |
||
136 | +} |
||
137 | + |
||
138 | +/* Enable an already-configured HFPLL. */ |
||
139 | +static int clk_hfpll_enable(struct clk_hw *hw) |
||
140 | +{ |
||
141 | + unsigned long flags; |
||
142 | + struct clk_hfpll *h = to_clk_hfpll(hw); |
||
143 | + struct hfpll_data const *hd = h->d; |
||
144 | + struct regmap *regmap = h->clkr.regmap; |
||
145 | + u32 mode; |
||
146 | + |
||
147 | + spin_lock_irqsave(&h->lock, flags); |
||
148 | + regmap_read(regmap, hd->mode_reg, &mode); |
||
149 | + if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) |
||
150 | + __clk_hfpll_enable(hw); |
||
151 | + spin_unlock_irqrestore(&h->lock, flags); |
||
152 | + |
||
153 | + return 0; |
||
154 | +} |
||
155 | + |
||
156 | +static void __clk_hfpll_disable(struct clk_hfpll *h) |
||
157 | +{ |
||
158 | + struct hfpll_data const *hd = h->d; |
||
159 | + struct regmap *regmap = h->clkr.regmap; |
||
160 | + |
||
161 | + /* |
||
162 | + * Disable the PLL output, disable test mode, enable the bypass mode, |
||
163 | + * and assert the reset. |
||
164 | + */ |
||
165 | + regmap_update_bits(regmap, hd->mode_reg, |
||
166 | + PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); |
||
167 | +} |
||
168 | + |
||
169 | +static void clk_hfpll_disable(struct clk_hw *hw) |
||
170 | +{ |
||
171 | + struct clk_hfpll *h = to_clk_hfpll(hw); |
||
172 | + unsigned long flags; |
||
173 | + |
||
174 | + spin_lock_irqsave(&h->lock, flags); |
||
175 | + __clk_hfpll_disable(h); |
||
176 | + spin_unlock_irqrestore(&h->lock, flags); |
||
177 | +} |
||
178 | + |
||
179 | +static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, |
||
180 | + unsigned long *parent_rate) |
||
181 | +{ |
||
182 | + struct clk_hfpll *h = to_clk_hfpll(hw); |
||
183 | + struct hfpll_data const *hd = h->d; |
||
184 | + unsigned long rrate; |
||
185 | + |
||
186 | + rate = clamp(rate, hd->min_rate, hd->max_rate); |
||
187 | + |
||
188 | + rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; |
||
189 | + if (rrate > hd->max_rate) |
||
190 | + rrate -= *parent_rate; |
||
191 | + |
||
192 | + return rrate; |
||
193 | +} |
||
194 | + |
||
195 | +/* |
||
196 | + * For optimization reasons, assumes no downstream clocks are actively using |
||
197 | + * it. |
||
198 | + */ |
||
199 | +static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, |
||
200 | + unsigned long parent_rate) |
||
201 | +{ |
||
202 | + struct clk_hfpll *h = to_clk_hfpll(hw); |
||
203 | + struct hfpll_data const *hd = h->d; |
||
204 | + struct regmap *regmap = h->clkr.regmap; |
||
205 | + unsigned long flags; |
||
206 | + u32 l_val, val; |
||
207 | + bool enabled; |
||
208 | + |
||
209 | + l_val = rate / parent_rate; |
||
210 | + |
||
211 | + spin_lock_irqsave(&h->lock, flags); |
||
212 | + |
||
213 | + enabled = __clk_is_enabled(hw->clk); |
||
214 | + if (enabled) |
||
215 | + __clk_hfpll_disable(h); |
||
216 | + |
||
217 | + /* Pick the right VCO. */ |
||
218 | + if (hd->user_reg && hd->user_vco_mask) { |
||
219 | + regmap_read(regmap, hd->user_reg, &val); |
||
220 | + if (rate <= hd->low_vco_max_rate) |
||
221 | + val &= ~hd->user_vco_mask; |
||
222 | + else |
||
223 | + val |= hd->user_vco_mask; |
||
224 | + regmap_write(regmap, hd->user_reg, val); |
||
225 | + } |
||
226 | + |
||
227 | + regmap_write(regmap, hd->l_reg, l_val); |
||
228 | + |
||
229 | + if (enabled) |
||
230 | + __clk_hfpll_enable(hw); |
||
231 | + |
||
232 | + spin_unlock_irqrestore(&h->lock, flags); |
||
233 | + |
||
234 | + return 0; |
||
235 | +} |
||
236 | + |
||
237 | +static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, |
||
238 | + unsigned long parent_rate) |
||
239 | +{ |
||
240 | + struct clk_hfpll *h = to_clk_hfpll(hw); |
||
241 | + struct hfpll_data const *hd = h->d; |
||
242 | + struct regmap *regmap = h->clkr.regmap; |
||
243 | + u32 l_val; |
||
244 | + |
||
245 | + regmap_read(regmap, hd->l_reg, &l_val); |
||
246 | + |
||
247 | + return l_val * parent_rate; |
||
248 | +} |
||
249 | + |
||
250 | +static void clk_hfpll_init(struct clk_hw *hw) |
||
251 | +{ |
||
252 | + struct clk_hfpll *h = to_clk_hfpll(hw); |
||
253 | + struct hfpll_data const *hd = h->d; |
||
254 | + struct regmap *regmap = h->clkr.regmap; |
||
255 | + u32 mode, status; |
||
256 | + |
||
257 | + regmap_read(regmap, hd->mode_reg, &mode); |
||
258 | + if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { |
||
259 | + __clk_hfpll_init_once(hw); |
||
260 | + return; |
||
261 | + } |
||
262 | + |
||
263 | + if (hd->status_reg) { |
||
264 | + regmap_read(regmap, hd->status_reg, &status); |
||
265 | + if (!(status & BIT(hd->lock_bit))) { |
||
266 | + WARN(1, "HFPLL %s is ON, but not locked!\n", |
||
267 | + __clk_get_name(hw->clk)); |
||
268 | + clk_hfpll_disable(hw); |
||
269 | + __clk_hfpll_init_once(hw); |
||
270 | + } |
||
271 | + } |
||
272 | +} |
||
273 | + |
||
274 | +static int hfpll_is_enabled(struct clk_hw *hw) |
||
275 | +{ |
||
276 | + struct clk_hfpll *h = to_clk_hfpll(hw); |
||
277 | + struct hfpll_data const *hd = h->d; |
||
278 | + struct regmap *regmap = h->clkr.regmap; |
||
279 | + u32 mode; |
||
280 | + |
||
281 | + regmap_read(regmap, hd->mode_reg, &mode); |
||
282 | + mode &= 0x7; |
||
283 | + return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); |
||
284 | +} |
||
285 | + |
||
286 | +const struct clk_ops clk_ops_hfpll = { |
||
287 | + .enable = clk_hfpll_enable, |
||
288 | + .disable = clk_hfpll_disable, |
||
289 | + .is_enabled = hfpll_is_enabled, |
||
290 | + .round_rate = clk_hfpll_round_rate, |
||
291 | + .set_rate = clk_hfpll_set_rate, |
||
292 | + .recalc_rate = clk_hfpll_recalc_rate, |
||
293 | + .init = clk_hfpll_init, |
||
294 | +}; |
||
295 | +EXPORT_SYMBOL_GPL(clk_ops_hfpll); |
||
296 | --- /dev/null |
||
297 | +++ b/drivers/clk/qcom/clk-hfpll.h |
||
298 | @@ -0,0 +1,54 @@ |
||
299 | +/* |
||
300 | + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. |
||
301 | + * |
||
302 | + * This program is free software; you can redistribute it and/or modify |
||
303 | + * it under the terms of the GNU General Public License version 2 and |
||
304 | + * only version 2 as published by the Free Software Foundation. |
||
305 | + * |
||
306 | + * This program is distributed in the hope that it will be useful, |
||
307 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
308 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
309 | + * GNU General Public License for more details. |
||
310 | + */ |
||
311 | +#ifndef __QCOM_CLK_HFPLL_H__ |
||
312 | +#define __QCOM_CLK_HFPLL_H__ |
||
313 | + |
||
314 | +#include <linux/clk-provider.h> |
||
315 | +#include <linux/spinlock.h> |
||
316 | +#include "clk-regmap.h" |
||
317 | + |
||
318 | +struct hfpll_data { |
||
319 | + u32 mode_reg; |
||
320 | + u32 l_reg; |
||
321 | + u32 m_reg; |
||
322 | + u32 n_reg; |
||
323 | + u32 user_reg; |
||
324 | + u32 droop_reg; |
||
325 | + u32 config_reg; |
||
326 | + u32 status_reg; |
||
327 | + u8 lock_bit; |
||
328 | + |
||
329 | + u32 droop_val; |
||
330 | + u32 config_val; |
||
331 | + u32 user_val; |
||
332 | + u32 user_vco_mask; |
||
333 | + unsigned long low_vco_max_rate; |
||
334 | + |
||
335 | + unsigned long min_rate; |
||
336 | + unsigned long max_rate; |
||
337 | +}; |
||
338 | + |
||
339 | +struct clk_hfpll { |
||
340 | + struct hfpll_data const *d; |
||
341 | + int init_done; |
||
342 | + |
||
343 | + struct clk_regmap clkr; |
||
344 | + spinlock_t lock; |
||
345 | +}; |
||
346 | + |
||
347 | +#define to_clk_hfpll(_hw) \ |
||
348 | + container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr) |
||
349 | + |
||
350 | +extern const struct clk_ops clk_ops_hfpll; |
||
351 | + |
||
352 | +#endif |