1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
4 */
5
6 #include <linux/export.h>
7 #include <linux/module.h>
8 #include <linux/regmap.h>
9 #include <linux/platform_device.h>
10 #include <linux/clk-provider.h>
11 #include <linux/interconnect-clk.h>
12 #include <linux/pm_runtime.h>
13 #include <linux/reset-controller.h>
14 #include <linux/of.h>
15
16 #include "common.h"
17 #include "clk-alpha-pll.h"
18 #include "clk-branch.h"
19 #include "clk-rcg.h"
20 #include "clk-regmap.h"
21 #include "reset.h"
22 #include "gdsc.h"
23
24 struct qcom_cc {
25 struct qcom_reset_controller reset;
26 struct clk_regmap **rclks;
27 size_t num_rclks;
28 struct dev_pm_domain_list *pd_list;
29 };
30
31 const
qcom_find_freq(const struct freq_tbl * f,unsigned long rate)32 struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
33 {
34 if (!f)
35 return NULL;
36
37 if (!f->freq)
38 return f;
39
40 for (; f->freq; f++)
41 if (rate <= f->freq)
42 return f;
43
44 /* Default to our fastest rate */
45 return f - 1;
46 }
47 EXPORT_SYMBOL_GPL(qcom_find_freq);
48
qcom_find_freq_multi(const struct freq_multi_tbl * f,unsigned long rate)49 const struct freq_multi_tbl *qcom_find_freq_multi(const struct freq_multi_tbl *f,
50 unsigned long rate)
51 {
52 if (!f)
53 return NULL;
54
55 if (!f->freq)
56 return f;
57
58 for (; f->freq; f++)
59 if (rate <= f->freq)
60 return f;
61
62 /* Default to our fastest rate */
63 return f - 1;
64 }
65 EXPORT_SYMBOL_GPL(qcom_find_freq_multi);
66
qcom_find_freq_floor(const struct freq_tbl * f,unsigned long rate)67 const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
68 unsigned long rate)
69 {
70 const struct freq_tbl *best = NULL;
71
72 for ( ; f->freq; f++) {
73 if (rate >= f->freq)
74 best = f;
75 else
76 break;
77 }
78
79 return best;
80 }
81 EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
82
qcom_find_src_index(struct clk_hw * hw,const struct parent_map * map,u8 src)83 int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
84 {
85 int i, num_parents = clk_hw_get_num_parents(hw);
86
87 for (i = 0; i < num_parents; i++)
88 if (src == map[i].src)
89 return i;
90
91 return -ENOENT;
92 }
93 EXPORT_SYMBOL_GPL(qcom_find_src_index);
94
qcom_find_cfg_index(struct clk_hw * hw,const struct parent_map * map,u8 cfg)95 int qcom_find_cfg_index(struct clk_hw *hw, const struct parent_map *map, u8 cfg)
96 {
97 int i, num_parents = clk_hw_get_num_parents(hw);
98
99 for (i = 0; i < num_parents; i++)
100 if (cfg == map[i].cfg)
101 return i;
102
103 return -ENOENT;
104 }
105 EXPORT_SYMBOL_GPL(qcom_find_cfg_index);
106
107 struct regmap *
qcom_cc_map(struct platform_device * pdev,const struct qcom_cc_desc * desc)108 qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
109 {
110 void __iomem *base;
111 struct device *dev = &pdev->dev;
112
113 base = devm_platform_ioremap_resource(pdev, 0);
114 if (IS_ERR(base))
115 return ERR_CAST(base);
116
117 return devm_regmap_init_mmio(dev, base, desc->config);
118 }
119 EXPORT_SYMBOL_GPL(qcom_cc_map);
120
121 void
qcom_pll_set_fsm_mode(struct regmap * map,u32 reg,u8 bias_count,u8 lock_count)122 qcom_pll_set_fsm_mode(struct regmap *map, u32 reg, u8 bias_count, u8 lock_count)
123 {
124 u32 val;
125 u32 mask;
126
127 /* De-assert reset to FSM */
128 regmap_update_bits(map, reg, PLL_VOTE_FSM_RESET, 0);
129
130 /* Program bias count and lock count */
131 val = bias_count << PLL_BIAS_COUNT_SHIFT |
132 lock_count << PLL_LOCK_COUNT_SHIFT;
133 mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
134 mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
135 regmap_update_bits(map, reg, mask, val);
136
137 /* Enable PLL FSM voting */
138 regmap_update_bits(map, reg, PLL_VOTE_FSM_ENA, PLL_VOTE_FSM_ENA);
139 }
140 EXPORT_SYMBOL_GPL(qcom_pll_set_fsm_mode);
141
qcom_cc_gdsc_unregister(void * data)142 static void qcom_cc_gdsc_unregister(void *data)
143 {
144 gdsc_unregister(data);
145 }
146
147 /*
148 * Backwards compatibility with old DTs. Register a pass-through factor 1/1
149 * clock to translate 'path' clk into 'name' clk and register the 'path'
150 * clk as a fixed rate clock if it isn't present.
151 */
_qcom_cc_register_board_clk(struct device * dev,const char * path,const char * name,unsigned long rate,bool add_factor)152 static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
153 const char *name, unsigned long rate,
154 bool add_factor)
155 {
156 struct device_node *node = NULL;
157 struct device_node *clocks_node;
158 struct clk_fixed_factor *factor;
159 struct clk_fixed_rate *fixed;
160 struct clk_init_data init_data = { };
161 int ret;
162
163 clocks_node = of_find_node_by_path("/clocks");
164 if (clocks_node) {
165 node = of_get_child_by_name(clocks_node, path);
166 of_node_put(clocks_node);
167 }
168
169 if (!node) {
170 fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL);
171 if (!fixed)
172 return -EINVAL;
173
174 fixed->fixed_rate = rate;
175 fixed->hw.init = &init_data;
176
177 init_data.name = path;
178 init_data.ops = &clk_fixed_rate_ops;
179
180 ret = devm_clk_hw_register(dev, &fixed->hw);
181 if (ret)
182 return ret;
183 }
184 of_node_put(node);
185
186 if (add_factor) {
187 factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL);
188 if (!factor)
189 return -EINVAL;
190
191 factor->mult = factor->div = 1;
192 factor->hw.init = &init_data;
193
194 init_data.name = name;
195 init_data.parent_names = &path;
196 init_data.num_parents = 1;
197 init_data.flags = 0;
198 init_data.ops = &clk_fixed_factor_ops;
199
200 ret = devm_clk_hw_register(dev, &factor->hw);
201 if (ret)
202 return ret;
203 }
204
205 return 0;
206 }
207
qcom_cc_register_board_clk(struct device * dev,const char * path,const char * name,unsigned long rate)208 int qcom_cc_register_board_clk(struct device *dev, const char *path,
209 const char *name, unsigned long rate)
210 {
211 bool add_factor = true;
212
213 /*
214 * TODO: The RPM clock driver currently does not support the xo clock.
215 * When xo is added to the RPM clock driver, we should change this
216 * function to skip registration of xo factor clocks.
217 */
218
219 return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
220 }
221 EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk);
222
qcom_cc_register_sleep_clk(struct device * dev)223 int qcom_cc_register_sleep_clk(struct device *dev)
224 {
225 return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src",
226 32768, true);
227 }
228 EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
229
230 /* Drop 'protected-clocks' from the list of clocks to register */
qcom_cc_drop_protected(struct device * dev,struct qcom_cc * cc)231 static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc)
232 {
233 struct device_node *np = dev->of_node;
234 u32 i;
235
236 of_property_for_each_u32(np, "protected-clocks", i) {
237 if (i >= cc->num_rclks)
238 continue;
239
240 cc->rclks[i] = NULL;
241 }
242 }
243
qcom_cc_clk_hw_get(struct of_phandle_args * clkspec,void * data)244 static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
245 void *data)
246 {
247 struct qcom_cc *cc = data;
248 unsigned int idx = clkspec->args[0];
249
250 if (idx >= cc->num_rclks) {
251 pr_err("%s: invalid index %u\n", __func__, idx);
252 return ERR_PTR(-EINVAL);
253 }
254
255 return cc->rclks[idx] ? &cc->rclks[idx]->hw : NULL;
256 }
257
qcom_cc_icc_register(struct device * dev,const struct qcom_cc_desc * desc)258 static int qcom_cc_icc_register(struct device *dev,
259 const struct qcom_cc_desc *desc)
260 {
261 struct icc_clk_data *icd;
262 struct clk_hw *hws;
263 int i;
264
265 if (!IS_ENABLED(CONFIG_INTERCONNECT_CLK))
266 return 0;
267
268 if (!desc->icc_hws)
269 return 0;
270
271 icd = devm_kcalloc(dev, desc->num_icc_hws, sizeof(*icd), GFP_KERNEL);
272 if (!icd)
273 return -ENOMEM;
274
275 for (i = 0; i < desc->num_icc_hws; i++) {
276 icd[i].master_id = desc->icc_hws[i].master_id;
277 icd[i].slave_id = desc->icc_hws[i].slave_id;
278 hws = &desc->clks[desc->icc_hws[i].clk_id]->hw;
279 icd[i].clk = devm_clk_hw_get_clk(dev, hws, "icc");
280 if (IS_ERR(icd[i].clk))
281 return dev_err_probe(dev, PTR_ERR(icd[i].clk),
282 "(%d) clock entry is null\n", i);
283 icd[i].name = clk_hw_get_name(hws);
284 }
285
286 return devm_icc_clk_register(dev, desc->icc_first_node_id,
287 desc->num_icc_hws, icd);
288 }
289
qcom_cc_clk_pll_configure(const struct qcom_cc_driver_data * data,struct regmap * regmap)290 static int qcom_cc_clk_pll_configure(const struct qcom_cc_driver_data *data,
291 struct regmap *regmap)
292 {
293 const struct clk_init_data *init;
294 struct clk_alpha_pll *pll;
295 int i;
296
297 for (i = 0; i < data->num_alpha_plls; i++) {
298 pll = data->alpha_plls[i];
299 init = pll->clkr.hw.init;
300
301 if (!pll->config || !pll->regs) {
302 pr_err("%s: missing pll config or regs\n", init->name);
303 return -EINVAL;
304 }
305
306 qcom_clk_alpha_pll_configure(pll, regmap);
307 }
308
309 return 0;
310 }
311
qcom_cc_clk_regs_configure(struct device * dev,const struct qcom_cc_driver_data * data,struct regmap * regmap)312 static void qcom_cc_clk_regs_configure(struct device *dev, const struct qcom_cc_driver_data *data,
313 struct regmap *regmap)
314 {
315 int i;
316
317 for (i = 0; i < data->num_clk_cbcrs; i++)
318 qcom_branch_set_clk_en(regmap, data->clk_cbcrs[i]);
319
320 if (data->clk_regs_configure)
321 data->clk_regs_configure(dev, regmap);
322 }
323
qcom_cc_really_probe(struct device * dev,const struct qcom_cc_desc * desc,struct regmap * regmap)324 int qcom_cc_really_probe(struct device *dev,
325 const struct qcom_cc_desc *desc, struct regmap *regmap)
326 {
327 int i, ret;
328 struct qcom_reset_controller *reset;
329 struct qcom_cc *cc;
330 struct gdsc_desc *scd;
331 size_t num_clks = desc->num_clks;
332 struct clk_regmap **rclks = desc->clks;
333 size_t num_clk_hws = desc->num_clk_hws;
334 struct clk_hw **clk_hws = desc->clk_hws;
335
336 cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
337 if (!cc)
338 return -ENOMEM;
339
340 ret = devm_pm_domain_attach_list(dev, NULL, &cc->pd_list);
341 if (ret < 0 && ret != -EEXIST)
342 return ret;
343
344 if (desc->use_rpm) {
345 ret = devm_pm_runtime_enable(dev);
346 if (ret)
347 return ret;
348
349 ret = pm_runtime_resume_and_get(dev);
350 if (ret)
351 return ret;
352 }
353
354 if (desc->driver_data) {
355 ret = qcom_cc_clk_pll_configure(desc->driver_data, regmap);
356 if (ret)
357 goto put_rpm;
358
359 qcom_cc_clk_regs_configure(dev, desc->driver_data, regmap);
360 }
361
362 reset = &cc->reset;
363 reset->rcdev.of_node = dev->of_node;
364 reset->rcdev.ops = &qcom_reset_ops;
365 reset->rcdev.owner = dev->driver->owner;
366 reset->rcdev.nr_resets = desc->num_resets;
367 reset->regmap = regmap;
368 reset->reset_map = desc->resets;
369
370 ret = devm_reset_controller_register(dev, &reset->rcdev);
371 if (ret)
372 goto put_rpm;
373
374 if (desc->gdscs && desc->num_gdscs) {
375 scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL);
376 if (!scd) {
377 ret = -ENOMEM;
378 goto put_rpm;
379 }
380 scd->dev = dev;
381 scd->scs = desc->gdscs;
382 scd->num = desc->num_gdscs;
383 scd->pd_list = cc->pd_list;
384 ret = gdsc_register(scd, &reset->rcdev, regmap);
385 if (ret)
386 goto put_rpm;
387 ret = devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister,
388 scd);
389 if (ret)
390 goto put_rpm;
391 }
392
393 if (desc->driver_data &&
394 desc->driver_data->dfs_rcgs &&
395 desc->driver_data->num_dfs_rcgs) {
396 ret = qcom_cc_register_rcg_dfs(regmap,
397 desc->driver_data->dfs_rcgs,
398 desc->driver_data->num_dfs_rcgs);
399 if (ret)
400 goto put_rpm;
401 }
402
403 cc->rclks = rclks;
404 cc->num_rclks = num_clks;
405
406 qcom_cc_drop_protected(dev, cc);
407
408 for (i = 0; i < num_clk_hws; i++) {
409 ret = devm_clk_hw_register(dev, clk_hws[i]);
410 if (ret)
411 goto put_rpm;
412 }
413
414 for (i = 0; i < num_clks; i++) {
415 if (!rclks[i])
416 continue;
417
418 ret = devm_clk_register_regmap(dev, rclks[i]);
419 if (ret)
420 goto put_rpm;
421 }
422
423 ret = devm_of_clk_add_hw_provider(dev, qcom_cc_clk_hw_get, cc);
424 if (ret)
425 goto put_rpm;
426
427 ret = qcom_cc_icc_register(dev, desc);
428
429 put_rpm:
430 if (desc->use_rpm)
431 pm_runtime_put(dev);
432
433 return ret;
434 }
435 EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
436
qcom_cc_probe(struct platform_device * pdev,const struct qcom_cc_desc * desc)437 int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
438 {
439 struct regmap *regmap;
440
441 regmap = qcom_cc_map(pdev, desc);
442 if (IS_ERR(regmap))
443 return PTR_ERR(regmap);
444
445 return qcom_cc_really_probe(&pdev->dev, desc, regmap);
446 }
447 EXPORT_SYMBOL_GPL(qcom_cc_probe);
448
qcom_cc_probe_by_index(struct platform_device * pdev,int index,const struct qcom_cc_desc * desc)449 int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
450 const struct qcom_cc_desc *desc)
451 {
452 struct regmap *regmap;
453 void __iomem *base;
454
455 base = devm_platform_ioremap_resource(pdev, index);
456 if (IS_ERR(base))
457 return -ENOMEM;
458
459 regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config);
460 if (IS_ERR(regmap))
461 return PTR_ERR(regmap);
462
463 return qcom_cc_really_probe(&pdev->dev, desc, regmap);
464 }
465 EXPORT_SYMBOL_GPL(qcom_cc_probe_by_index);
466
467 MODULE_LICENSE("GPL v2");
468 MODULE_DESCRIPTION("QTI Common Clock module");
469