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