1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/export.h>
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/regmap.h>
9*4882a593Smuzhiyun #include <linux/platform_device.h>
10*4882a593Smuzhiyun #include <linux/clk-provider.h>
11*4882a593Smuzhiyun #include <linux/reset-controller.h>
12*4882a593Smuzhiyun #include <linux/of.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include "common.h"
15*4882a593Smuzhiyun #include "clk-rcg.h"
16*4882a593Smuzhiyun #include "clk-regmap.h"
17*4882a593Smuzhiyun #include "reset.h"
18*4882a593Smuzhiyun #include "gdsc.h"
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun struct qcom_cc {
21*4882a593Smuzhiyun struct qcom_reset_controller reset;
22*4882a593Smuzhiyun struct clk_regmap **rclks;
23*4882a593Smuzhiyun size_t num_rclks;
24*4882a593Smuzhiyun };
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun const
qcom_find_freq(const struct freq_tbl * f,unsigned long rate)27*4882a593Smuzhiyun struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun if (!f)
30*4882a593Smuzhiyun return NULL;
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun if (!f->freq)
33*4882a593Smuzhiyun return f;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun for (; f->freq; f++)
36*4882a593Smuzhiyun if (rate <= f->freq)
37*4882a593Smuzhiyun return f;
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /* Default to our fastest rate */
40*4882a593Smuzhiyun return f - 1;
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_find_freq);
43*4882a593Smuzhiyun
qcom_find_freq_floor(const struct freq_tbl * f,unsigned long rate)44*4882a593Smuzhiyun const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
45*4882a593Smuzhiyun unsigned long rate)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun const struct freq_tbl *best = NULL;
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun for ( ; f->freq; f++) {
50*4882a593Smuzhiyun if (rate >= f->freq)
51*4882a593Smuzhiyun best = f;
52*4882a593Smuzhiyun else
53*4882a593Smuzhiyun break;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun return best;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
59*4882a593Smuzhiyun
qcom_find_src_index(struct clk_hw * hw,const struct parent_map * map,u8 src)60*4882a593Smuzhiyun int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun int i, num_parents = clk_hw_get_num_parents(hw);
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun for (i = 0; i < num_parents; i++)
65*4882a593Smuzhiyun if (src == map[i].src)
66*4882a593Smuzhiyun return i;
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun return -ENOENT;
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_find_src_index);
71*4882a593Smuzhiyun
qcom_find_cfg_index(struct clk_hw * hw,const struct parent_map * map,u8 cfg)72*4882a593Smuzhiyun int qcom_find_cfg_index(struct clk_hw *hw, const struct parent_map *map, u8 cfg)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun int i, num_parents = clk_hw_get_num_parents(hw);
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun for (i = 0; i < num_parents; i++)
77*4882a593Smuzhiyun if (cfg == map[i].cfg)
78*4882a593Smuzhiyun return i;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun return -ENOENT;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_find_cfg_index);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun struct regmap *
qcom_cc_map(struct platform_device * pdev,const struct qcom_cc_desc * desc)85*4882a593Smuzhiyun qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun void __iomem *base;
88*4882a593Smuzhiyun struct resource *res;
89*4882a593Smuzhiyun struct device *dev = &pdev->dev;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
92*4882a593Smuzhiyun base = devm_ioremap_resource(dev, res);
93*4882a593Smuzhiyun if (IS_ERR(base))
94*4882a593Smuzhiyun return ERR_CAST(base);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return devm_regmap_init_mmio(dev, base, desc->config);
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_cc_map);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun void
qcom_pll_set_fsm_mode(struct regmap * map,u32 reg,u8 bias_count,u8 lock_count)101*4882a593Smuzhiyun qcom_pll_set_fsm_mode(struct regmap *map, u32 reg, u8 bias_count, u8 lock_count)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun u32 val;
104*4882a593Smuzhiyun u32 mask;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun /* De-assert reset to FSM */
107*4882a593Smuzhiyun regmap_update_bits(map, reg, PLL_VOTE_FSM_RESET, 0);
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun /* Program bias count and lock count */
110*4882a593Smuzhiyun val = bias_count << PLL_BIAS_COUNT_SHIFT |
111*4882a593Smuzhiyun lock_count << PLL_LOCK_COUNT_SHIFT;
112*4882a593Smuzhiyun mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
113*4882a593Smuzhiyun mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
114*4882a593Smuzhiyun regmap_update_bits(map, reg, mask, val);
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun /* Enable PLL FSM voting */
117*4882a593Smuzhiyun regmap_update_bits(map, reg, PLL_VOTE_FSM_ENA, PLL_VOTE_FSM_ENA);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_pll_set_fsm_mode);
120*4882a593Smuzhiyun
qcom_cc_gdsc_unregister(void * data)121*4882a593Smuzhiyun static void qcom_cc_gdsc_unregister(void *data)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun gdsc_unregister(data);
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun /*
127*4882a593Smuzhiyun * Backwards compatibility with old DTs. Register a pass-through factor 1/1
128*4882a593Smuzhiyun * clock to translate 'path' clk into 'name' clk and register the 'path'
129*4882a593Smuzhiyun * clk as a fixed rate clock if it isn't present.
130*4882a593Smuzhiyun */
_qcom_cc_register_board_clk(struct device * dev,const char * path,const char * name,unsigned long rate,bool add_factor)131*4882a593Smuzhiyun static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
132*4882a593Smuzhiyun const char *name, unsigned long rate,
133*4882a593Smuzhiyun bool add_factor)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun struct device_node *node = NULL;
136*4882a593Smuzhiyun struct device_node *clocks_node;
137*4882a593Smuzhiyun struct clk_fixed_factor *factor;
138*4882a593Smuzhiyun struct clk_fixed_rate *fixed;
139*4882a593Smuzhiyun struct clk_init_data init_data = { };
140*4882a593Smuzhiyun int ret;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun clocks_node = of_find_node_by_path("/clocks");
143*4882a593Smuzhiyun if (clocks_node) {
144*4882a593Smuzhiyun node = of_get_child_by_name(clocks_node, path);
145*4882a593Smuzhiyun of_node_put(clocks_node);
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (!node) {
149*4882a593Smuzhiyun fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL);
150*4882a593Smuzhiyun if (!fixed)
151*4882a593Smuzhiyun return -EINVAL;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun fixed->fixed_rate = rate;
154*4882a593Smuzhiyun fixed->hw.init = &init_data;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun init_data.name = path;
157*4882a593Smuzhiyun init_data.ops = &clk_fixed_rate_ops;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun ret = devm_clk_hw_register(dev, &fixed->hw);
160*4882a593Smuzhiyun if (ret)
161*4882a593Smuzhiyun return ret;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun of_node_put(node);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun if (add_factor) {
166*4882a593Smuzhiyun factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL);
167*4882a593Smuzhiyun if (!factor)
168*4882a593Smuzhiyun return -EINVAL;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun factor->mult = factor->div = 1;
171*4882a593Smuzhiyun factor->hw.init = &init_data;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun init_data.name = name;
174*4882a593Smuzhiyun init_data.parent_names = &path;
175*4882a593Smuzhiyun init_data.num_parents = 1;
176*4882a593Smuzhiyun init_data.flags = 0;
177*4882a593Smuzhiyun init_data.ops = &clk_fixed_factor_ops;
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun ret = devm_clk_hw_register(dev, &factor->hw);
180*4882a593Smuzhiyun if (ret)
181*4882a593Smuzhiyun return ret;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun return 0;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
qcom_cc_register_board_clk(struct device * dev,const char * path,const char * name,unsigned long rate)187*4882a593Smuzhiyun int qcom_cc_register_board_clk(struct device *dev, const char *path,
188*4882a593Smuzhiyun const char *name, unsigned long rate)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun bool add_factor = true;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun /*
193*4882a593Smuzhiyun * TODO: The RPM clock driver currently does not support the xo clock.
194*4882a593Smuzhiyun * When xo is added to the RPM clock driver, we should change this
195*4882a593Smuzhiyun * function to skip registration of xo factor clocks.
196*4882a593Smuzhiyun */
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk);
201*4882a593Smuzhiyun
qcom_cc_register_sleep_clk(struct device * dev)202*4882a593Smuzhiyun int qcom_cc_register_sleep_clk(struct device *dev)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src",
205*4882a593Smuzhiyun 32768, true);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun /* Drop 'protected-clocks' from the list of clocks to register */
qcom_cc_drop_protected(struct device * dev,struct qcom_cc * cc)210*4882a593Smuzhiyun static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun struct device_node *np = dev->of_node;
213*4882a593Smuzhiyun struct property *prop;
214*4882a593Smuzhiyun const __be32 *p;
215*4882a593Smuzhiyun u32 i;
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun of_property_for_each_u32(np, "protected-clocks", prop, p, i) {
218*4882a593Smuzhiyun if (i >= cc->num_rclks)
219*4882a593Smuzhiyun continue;
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun cc->rclks[i] = NULL;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
qcom_cc_clk_hw_get(struct of_phandle_args * clkspec,void * data)225*4882a593Smuzhiyun static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
226*4882a593Smuzhiyun void *data)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun struct qcom_cc *cc = data;
229*4882a593Smuzhiyun unsigned int idx = clkspec->args[0];
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun if (idx >= cc->num_rclks) {
232*4882a593Smuzhiyun pr_err("%s: invalid index %u\n", __func__, idx);
233*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun return cc->rclks[idx] ? &cc->rclks[idx]->hw : NULL;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun
qcom_cc_really_probe(struct platform_device * pdev,const struct qcom_cc_desc * desc,struct regmap * regmap)239*4882a593Smuzhiyun int qcom_cc_really_probe(struct platform_device *pdev,
240*4882a593Smuzhiyun const struct qcom_cc_desc *desc, struct regmap *regmap)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun int i, ret;
243*4882a593Smuzhiyun struct device *dev = &pdev->dev;
244*4882a593Smuzhiyun struct qcom_reset_controller *reset;
245*4882a593Smuzhiyun struct qcom_cc *cc;
246*4882a593Smuzhiyun struct gdsc_desc *scd;
247*4882a593Smuzhiyun size_t num_clks = desc->num_clks;
248*4882a593Smuzhiyun struct clk_regmap **rclks = desc->clks;
249*4882a593Smuzhiyun size_t num_clk_hws = desc->num_clk_hws;
250*4882a593Smuzhiyun struct clk_hw **clk_hws = desc->clk_hws;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
253*4882a593Smuzhiyun if (!cc)
254*4882a593Smuzhiyun return -ENOMEM;
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun reset = &cc->reset;
257*4882a593Smuzhiyun reset->rcdev.of_node = dev->of_node;
258*4882a593Smuzhiyun reset->rcdev.ops = &qcom_reset_ops;
259*4882a593Smuzhiyun reset->rcdev.owner = dev->driver->owner;
260*4882a593Smuzhiyun reset->rcdev.nr_resets = desc->num_resets;
261*4882a593Smuzhiyun reset->regmap = regmap;
262*4882a593Smuzhiyun reset->reset_map = desc->resets;
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun ret = devm_reset_controller_register(dev, &reset->rcdev);
265*4882a593Smuzhiyun if (ret)
266*4882a593Smuzhiyun return ret;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun if (desc->gdscs && desc->num_gdscs) {
269*4882a593Smuzhiyun scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL);
270*4882a593Smuzhiyun if (!scd)
271*4882a593Smuzhiyun return -ENOMEM;
272*4882a593Smuzhiyun scd->dev = dev;
273*4882a593Smuzhiyun scd->scs = desc->gdscs;
274*4882a593Smuzhiyun scd->num = desc->num_gdscs;
275*4882a593Smuzhiyun ret = gdsc_register(scd, &reset->rcdev, regmap);
276*4882a593Smuzhiyun if (ret)
277*4882a593Smuzhiyun return ret;
278*4882a593Smuzhiyun ret = devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister,
279*4882a593Smuzhiyun scd);
280*4882a593Smuzhiyun if (ret)
281*4882a593Smuzhiyun return ret;
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun cc->rclks = rclks;
285*4882a593Smuzhiyun cc->num_rclks = num_clks;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun qcom_cc_drop_protected(dev, cc);
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun for (i = 0; i < num_clk_hws; i++) {
290*4882a593Smuzhiyun ret = devm_clk_hw_register(dev, clk_hws[i]);
291*4882a593Smuzhiyun if (ret)
292*4882a593Smuzhiyun return ret;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun for (i = 0; i < num_clks; i++) {
296*4882a593Smuzhiyun if (!rclks[i])
297*4882a593Smuzhiyun continue;
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun ret = devm_clk_register_regmap(dev, rclks[i]);
300*4882a593Smuzhiyun if (ret)
301*4882a593Smuzhiyun return ret;
302*4882a593Smuzhiyun }
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun ret = devm_of_clk_add_hw_provider(dev, qcom_cc_clk_hw_get, cc);
305*4882a593Smuzhiyun if (ret)
306*4882a593Smuzhiyun return ret;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun return 0;
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
311*4882a593Smuzhiyun
qcom_cc_probe(struct platform_device * pdev,const struct qcom_cc_desc * desc)312*4882a593Smuzhiyun int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
313*4882a593Smuzhiyun {
314*4882a593Smuzhiyun struct regmap *regmap;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun regmap = qcom_cc_map(pdev, desc);
317*4882a593Smuzhiyun if (IS_ERR(regmap))
318*4882a593Smuzhiyun return PTR_ERR(regmap);
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun return qcom_cc_really_probe(pdev, desc, regmap);
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_cc_probe);
323*4882a593Smuzhiyun
qcom_cc_probe_by_index(struct platform_device * pdev,int index,const struct qcom_cc_desc * desc)324*4882a593Smuzhiyun int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
325*4882a593Smuzhiyun const struct qcom_cc_desc *desc)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun struct regmap *regmap;
328*4882a593Smuzhiyun struct resource *res;
329*4882a593Smuzhiyun void __iomem *base;
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, index);
332*4882a593Smuzhiyun base = devm_ioremap_resource(&pdev->dev, res);
333*4882a593Smuzhiyun if (IS_ERR(base))
334*4882a593Smuzhiyun return -ENOMEM;
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config);
337*4882a593Smuzhiyun if (IS_ERR(regmap))
338*4882a593Smuzhiyun return PTR_ERR(regmap);
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun return qcom_cc_really_probe(pdev, desc, regmap);
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_cc_probe_by_index);
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
345