OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From patchwork Fri Dec 8 09:42:27 2017 |
2 | Content-Type: text/plain; charset="utf-8" |
||
3 | MIME-Version: 1.0 |
||
4 | Content-Transfer-Encoding: 7bit |
||
5 | Subject: [v4,09/12] clk: qcom: Add Krait clock controller driver |
||
6 | From: Sricharan R <sricharan@codeaurora.org> |
||
7 | X-Patchwork-Id: 10102061 |
||
8 | Message-Id: <1512726150-7204-10-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:27 +0530 |
||
15 | |||
16 | From: Stephen Boyd <sboyd@codeaurora.org> |
||
17 | |||
18 | The Krait CPU clocks are made up of a primary mux and secondary |
||
19 | mux for each CPU and the L2, controlled via cp15 accessors. For |
||
20 | Kraits within KPSSv1 each secondary mux accepts a different aux |
||
21 | source, but on KPSSv2 each secondary mux accepts the same aux |
||
22 | source. |
||
23 | |||
24 | Cc: <devicetree@vger.kernel.org> |
||
25 | Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> |
||
26 | --- |
||
27 | .../devicetree/bindings/clock/qcom,krait-cc.txt | 22 ++ |
||
28 | drivers/clk/qcom/Kconfig | 8 + |
||
29 | drivers/clk/qcom/Makefile | 1 + |
||
30 | drivers/clk/qcom/krait-cc.c | 350 +++++++++++++++++++++ |
||
31 | 4 files changed, 381 insertions(+) |
||
32 | create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt |
||
33 | create mode 100644 drivers/clk/qcom/krait-cc.c |
||
34 | |||
35 | --- /dev/null |
||
36 | +++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt |
||
37 | @@ -0,0 +1,22 @@ |
||
38 | +Krait Clock Controller |
||
39 | + |
||
40 | +PROPERTIES |
||
41 | + |
||
42 | +- compatible: |
||
43 | + Usage: required |
||
44 | + Value type: <string> |
||
45 | + Definition: must be one of: |
||
46 | + "qcom,krait-cc-v1" |
||
47 | + "qcom,krait-cc-v2" |
||
48 | + |
||
49 | +- #clock-cells: |
||
50 | + Usage: required |
||
51 | + Value type: <u32> |
||
52 | + Definition: must be 1 |
||
53 | + |
||
54 | +Example: |
||
55 | + |
||
56 | + kraitcc: clock-controller { |
||
57 | + compatible = "qcom,krait-cc-v1"; |
||
58 | + #clock-cells = <1>; |
||
59 | + }; |
||
60 | --- a/drivers/clk/qcom/Kconfig |
||
61 | +++ b/drivers/clk/qcom/Kconfig |
||
62 | @@ -213,6 +213,14 @@ config KPSS_XCC |
||
63 | if you want to support CPU frequency scaling on devices such |
||
64 | as MSM8960, APQ8064, etc. |
||
65 | |||
66 | +config KRAITCC |
||
67 | + tristate "Krait Clock Controller" |
||
68 | + depends on COMMON_CLK_QCOM && ARM |
||
69 | + select KRAIT_CLOCKS |
||
70 | + help |
||
71 | + Support for the Krait CPU clocks on Qualcomm devices. |
||
72 | + Say Y if you want to support CPU frequency scaling. |
||
73 | + |
||
74 | config KRAIT_CLOCKS |
||
75 | bool |
||
76 | select KRAIT_L2_ACCESSORS |
||
77 | --- a/drivers/clk/qcom/Makefile |
||
78 | +++ b/drivers/clk/qcom/Makefile |
||
79 | @@ -38,3 +38,4 @@ obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o |
||
80 | obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o |
||
81 | obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o |
||
82 | obj-$(CONFIG_QCOM_HFPLL) += hfpll.o |
||
83 | +obj-$(CONFIG_KRAITCC) += krait-cc.o |
||
84 | --- /dev/null |
||
85 | +++ b/drivers/clk/qcom/krait-cc.c |
||
86 | @@ -0,0 +1,350 @@ |
||
87 | +/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. |
||
88 | + * |
||
89 | + * This program is free software; you can redistribute it and/or modify |
||
90 | + * it under the terms of the GNU General Public License version 2 and |
||
91 | + * only version 2 as published by the Free Software Foundation. |
||
92 | + * |
||
93 | + * This program is distributed in the hope that it will be useful, |
||
94 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
95 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
96 | + * GNU General Public License for more details. |
||
97 | + */ |
||
98 | + |
||
99 | +#include <linux/kernel.h> |
||
100 | +#include <linux/init.h> |
||
101 | +#include <linux/module.h> |
||
102 | +#include <linux/platform_device.h> |
||
103 | +#include <linux/err.h> |
||
104 | +#include <linux/io.h> |
||
105 | +#include <linux/of.h> |
||
106 | +#include <linux/of_device.h> |
||
107 | +#include <linux/clk.h> |
||
108 | +#include <linux/clk-provider.h> |
||
109 | +#include <linux/slab.h> |
||
110 | + |
||
111 | +#include "clk-krait.h" |
||
112 | + |
||
113 | +static unsigned int sec_mux_map[] = { |
||
114 | + 2, |
||
115 | + 0, |
||
116 | +}; |
||
117 | + |
||
118 | +static unsigned int pri_mux_map[] = { |
||
119 | + 1, |
||
120 | + 2, |
||
121 | + 0, |
||
122 | +}; |
||
123 | + |
||
124 | +static int |
||
125 | +krait_add_div(struct device *dev, int id, const char *s, unsigned int offset) |
||
126 | +{ |
||
127 | + struct krait_div2_clk *div; |
||
128 | + struct clk_init_data init = { |
||
129 | + .num_parents = 1, |
||
130 | + .ops = &krait_div2_clk_ops, |
||
131 | + .flags = CLK_SET_RATE_PARENT, |
||
132 | + }; |
||
133 | + const char *p_names[1]; |
||
134 | + struct clk *clk; |
||
135 | + |
||
136 | + div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); |
||
137 | + if (!div) |
||
138 | + return -ENOMEM; |
||
139 | + |
||
140 | + div->width = 2; |
||
141 | + div->shift = 6; |
||
142 | + div->lpl = id >= 0; |
||
143 | + div->offset = offset; |
||
144 | + div->hw.init = &init; |
||
145 | + |
||
146 | + init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s); |
||
147 | + if (!init.name) |
||
148 | + return -ENOMEM; |
||
149 | + |
||
150 | + init.parent_names = p_names; |
||
151 | + p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); |
||
152 | + if (!p_names[0]) { |
||
153 | + kfree(init.name); |
||
154 | + return -ENOMEM; |
||
155 | + } |
||
156 | + |
||
157 | + clk = devm_clk_register(dev, &div->hw); |
||
158 | + kfree(p_names[0]); |
||
159 | + kfree(init.name); |
||
160 | + |
||
161 | + return PTR_ERR_OR_ZERO(clk); |
||
162 | +} |
||
163 | + |
||
164 | +static int |
||
165 | +krait_add_sec_mux(struct device *dev, int id, const char *s, |
||
166 | + unsigned int offset, bool unique_aux) |
||
167 | +{ |
||
168 | + struct krait_mux_clk *mux; |
||
169 | + static const char *sec_mux_list[] = { |
||
170 | + "acpu_aux", |
||
171 | + "qsb", |
||
172 | + }; |
||
173 | + struct clk_init_data init = { |
||
174 | + .parent_names = sec_mux_list, |
||
175 | + .num_parents = ARRAY_SIZE(sec_mux_list), |
||
176 | + .ops = &krait_mux_clk_ops, |
||
177 | + .flags = CLK_SET_RATE_PARENT, |
||
178 | + }; |
||
179 | + struct clk *clk; |
||
180 | + |
||
181 | + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); |
||
182 | + if (!mux) |
||
183 | + return -ENOMEM; |
||
184 | + |
||
185 | + mux->offset = offset; |
||
186 | + mux->lpl = id >= 0; |
||
187 | + mux->mask = 0x3; |
||
188 | + mux->shift = 2; |
||
189 | + mux->parent_map = sec_mux_map; |
||
190 | + mux->hw.init = &init; |
||
191 | + |
||
192 | + init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); |
||
193 | + if (!init.name) |
||
194 | + return -ENOMEM; |
||
195 | + |
||
196 | + if (unique_aux) { |
||
197 | + sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s); |
||
198 | + if (!sec_mux_list[0]) { |
||
199 | + clk = ERR_PTR(-ENOMEM); |
||
200 | + goto err_aux; |
||
201 | + } |
||
202 | + } |
||
203 | + |
||
204 | + clk = devm_clk_register(dev, &mux->hw); |
||
205 | + |
||
206 | + if (unique_aux) |
||
207 | + kfree(sec_mux_list[0]); |
||
208 | +err_aux: |
||
209 | + kfree(init.name); |
||
210 | + return PTR_ERR_OR_ZERO(clk); |
||
211 | +} |
||
212 | + |
||
213 | +static struct clk * |
||
214 | +krait_add_pri_mux(struct device *dev, int id, const char *s, |
||
215 | + unsigned int offset) |
||
216 | +{ |
||
217 | + struct krait_mux_clk *mux; |
||
218 | + const char *p_names[3]; |
||
219 | + struct clk_init_data init = { |
||
220 | + .parent_names = p_names, |
||
221 | + .num_parents = ARRAY_SIZE(p_names), |
||
222 | + .ops = &krait_mux_clk_ops, |
||
223 | + .flags = CLK_SET_RATE_PARENT, |
||
224 | + }; |
||
225 | + struct clk *clk; |
||
226 | + |
||
227 | + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); |
||
228 | + if (!mux) |
||
229 | + return ERR_PTR(-ENOMEM); |
||
230 | + |
||
231 | + mux->mask = 0x3; |
||
232 | + mux->shift = 0; |
||
233 | + mux->offset = offset; |
||
234 | + mux->lpl = id >= 0; |
||
235 | + mux->parent_map = pri_mux_map; |
||
236 | + mux->hw.init = &init; |
||
237 | + |
||
238 | + init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); |
||
239 | + if (!init.name) |
||
240 | + return ERR_PTR(-ENOMEM); |
||
241 | + |
||
242 | + p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); |
||
243 | + if (!p_names[0]) { |
||
244 | + clk = ERR_PTR(-ENOMEM); |
||
245 | + goto err_p0; |
||
246 | + } |
||
247 | + |
||
248 | + p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s); |
||
249 | + if (!p_names[1]) { |
||
250 | + clk = ERR_PTR(-ENOMEM); |
||
251 | + goto err_p1; |
||
252 | + } |
||
253 | + |
||
254 | + p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); |
||
255 | + if (!p_names[2]) { |
||
256 | + clk = ERR_PTR(-ENOMEM); |
||
257 | + goto err_p2; |
||
258 | + } |
||
259 | + |
||
260 | + clk = devm_clk_register(dev, &mux->hw); |
||
261 | + |
||
262 | + kfree(p_names[2]); |
||
263 | +err_p2: |
||
264 | + kfree(p_names[1]); |
||
265 | +err_p1: |
||
266 | + kfree(p_names[0]); |
||
267 | +err_p0: |
||
268 | + kfree(init.name); |
||
269 | + return clk; |
||
270 | +} |
||
271 | + |
||
272 | +/* id < 0 for L2, otherwise id == physical CPU number */ |
||
273 | +static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux) |
||
274 | +{ |
||
275 | + int ret; |
||
276 | + unsigned int offset; |
||
277 | + void *p = NULL; |
||
278 | + const char *s; |
||
279 | + struct clk *clk; |
||
280 | + |
||
281 | + if (id >= 0) { |
||
282 | + offset = 0x4501 + (0x1000 * id); |
||
283 | + s = p = kasprintf(GFP_KERNEL, "%d", id); |
||
284 | + if (!s) |
||
285 | + return ERR_PTR(-ENOMEM); |
||
286 | + } else { |
||
287 | + offset = 0x500; |
||
288 | + s = "_l2"; |
||
289 | + } |
||
290 | + |
||
291 | + ret = krait_add_div(dev, id, s, offset); |
||
292 | + if (ret) { |
||
293 | + clk = ERR_PTR(ret); |
||
294 | + goto err; |
||
295 | + } |
||
296 | + |
||
297 | + ret = krait_add_sec_mux(dev, id, s, offset, unique_aux); |
||
298 | + if (ret) { |
||
299 | + clk = ERR_PTR(ret); |
||
300 | + goto err; |
||
301 | + } |
||
302 | + |
||
303 | + clk = krait_add_pri_mux(dev, id, s, offset); |
||
304 | +err: |
||
305 | + kfree(p); |
||
306 | + return clk; |
||
307 | +} |
||
308 | + |
||
309 | +static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data) |
||
310 | +{ |
||
311 | + unsigned int idx = clkspec->args[0]; |
||
312 | + struct clk **clks = data; |
||
313 | + |
||
314 | + if (idx >= 5) { |
||
315 | + pr_err("%s: invalid clock index %d\n", __func__, idx); |
||
316 | + return ERR_PTR(-EINVAL); |
||
317 | + } |
||
318 | + |
||
319 | + return clks[idx] ? : ERR_PTR(-ENODEV); |
||
320 | +} |
||
321 | + |
||
322 | +static const struct of_device_id krait_cc_match_table[] = { |
||
323 | + { .compatible = "qcom,krait-cc-v1", (void *)1UL }, |
||
324 | + { .compatible = "qcom,krait-cc-v2" }, |
||
325 | + {} |
||
326 | +}; |
||
327 | +MODULE_DEVICE_TABLE(of, krait_cc_match_table); |
||
328 | + |
||
329 | +static int krait_cc_probe(struct platform_device *pdev) |
||
330 | +{ |
||
331 | + struct device *dev = &pdev->dev; |
||
332 | + const struct of_device_id *id; |
||
333 | + unsigned long cur_rate, aux_rate; |
||
334 | + int cpu; |
||
335 | + struct clk *clk; |
||
336 | + struct clk **clks; |
||
337 | + struct clk *l2_pri_mux_clk; |
||
338 | + |
||
339 | + id = of_match_device(krait_cc_match_table, dev); |
||
340 | + if (!id) |
||
341 | + return -ENODEV; |
||
342 | + |
||
343 | + /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */ |
||
344 | + clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1); |
||
345 | + if (IS_ERR(clk)) |
||
346 | + return PTR_ERR(clk); |
||
347 | + |
||
348 | + if (!id->data) { |
||
349 | + clk = clk_register_fixed_factor(dev, "acpu_aux", |
||
350 | + "gpll0_vote", 0, 1, 2); |
||
351 | + if (IS_ERR(clk)) |
||
352 | + return PTR_ERR(clk); |
||
353 | + } |
||
354 | + |
||
355 | + /* Krait configurations have at most 4 CPUs and one L2 */ |
||
356 | + clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL); |
||
357 | + if (!clks) |
||
358 | + return -ENOMEM; |
||
359 | + |
||
360 | + for_each_possible_cpu(cpu) { |
||
361 | + clk = krait_add_clks(dev, cpu, id->data); |
||
362 | + if (IS_ERR(clk)) |
||
363 | + return PTR_ERR(clk); |
||
364 | + clks[cpu] = clk; |
||
365 | + } |
||
366 | + |
||
367 | + l2_pri_mux_clk = krait_add_clks(dev, -1, id->data); |
||
368 | + if (IS_ERR(l2_pri_mux_clk)) |
||
369 | + return PTR_ERR(l2_pri_mux_clk); |
||
370 | + clks[4] = l2_pri_mux_clk; |
||
371 | + |
||
372 | + /* |
||
373 | + * We don't want the CPU or L2 clocks to be turned off at late init |
||
374 | + * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the |
||
375 | + * refcount of these clocks. Any cpufreq/hotplug manager can assume |
||
376 | + * that the clocks have already been prepared and enabled by the time |
||
377 | + * they take over. |
||
378 | + */ |
||
379 | + for_each_online_cpu(cpu) { |
||
380 | + clk_prepare_enable(l2_pri_mux_clk); |
||
381 | + WARN(clk_prepare_enable(clks[cpu]), |
||
382 | + "Unable to turn on CPU%d clock", cpu); |
||
383 | + } |
||
384 | + |
||
385 | + /* |
||
386 | + * Force reinit of HFPLLs and muxes to overwrite any potential |
||
387 | + * incorrect configuration of HFPLLs and muxes by the bootloader. |
||
388 | + * While at it, also make sure the cores are running at known rates |
||
389 | + * and print the current rate. |
||
390 | + * |
||
391 | + * The clocks are set to aux clock rate first to make sure the |
||
392 | + * secondary mux is not sourcing off of QSB. The rate is then set to |
||
393 | + * two different rates to force a HFPLL reinit under all |
||
394 | + * circumstances. |
||
395 | + */ |
||
396 | + cur_rate = clk_get_rate(l2_pri_mux_clk); |
||
397 | + aux_rate = 384000000; |
||
398 | + if (cur_rate == 1) { |
||
399 | + pr_info("L2 @ QSB rate. Forcing new rate.\n"); |
||
400 | + cur_rate = aux_rate; |
||
401 | + } |
||
402 | + clk_set_rate(l2_pri_mux_clk, aux_rate); |
||
403 | + clk_set_rate(l2_pri_mux_clk, 2); |
||
404 | + clk_set_rate(l2_pri_mux_clk, cur_rate); |
||
405 | + pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000); |
||
406 | + for_each_possible_cpu(cpu) { |
||
407 | + clk = clks[cpu]; |
||
408 | + cur_rate = clk_get_rate(clk); |
||
409 | + if (cur_rate == 1) { |
||
410 | + pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu); |
||
411 | + cur_rate = aux_rate; |
||
412 | + } |
||
413 | + |
||
414 | + clk_set_rate(clk, aux_rate); |
||
415 | + clk_set_rate(clk, 2); |
||
416 | + clk_set_rate(clk, cur_rate); |
||
417 | + pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000); |
||
418 | + } |
||
419 | + |
||
420 | + of_clk_add_provider(dev->of_node, krait_of_get, clks); |
||
421 | + |
||
422 | + return 0; |
||
423 | +} |
||
424 | + |
||
425 | +static struct platform_driver krait_cc_driver = { |
||
426 | + .probe = krait_cc_probe, |
||
427 | + .driver = { |
||
428 | + .name = "krait-cc", |
||
429 | + .of_match_table = krait_cc_match_table, |
||
430 | + }, |
||
431 | +}; |
||
432 | +module_platform_driver(krait_cc_driver); |
||
433 | + |
||
434 | +MODULE_DESCRIPTION("Krait CPU Clock Driver"); |
||
435 | +MODULE_LICENSE("GPL v2"); |
||
436 | +MODULE_ALIAS("platform:krait-cc"); |