19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cd52c2a4SSudeep Holla /*
3cd52c2a4SSudeep Holla * System Control and Power Interface (SCPI) Protocol based clock driver
4cd52c2a4SSudeep Holla *
5cd52c2a4SSudeep Holla * Copyright (C) 2015 ARM Ltd.
6cd52c2a4SSudeep Holla */
7cd52c2a4SSudeep Holla
8cd52c2a4SSudeep Holla #include <linux/clk-provider.h>
9cd52c2a4SSudeep Holla #include <linux/device.h>
10cd52c2a4SSudeep Holla #include <linux/err.h>
11cd52c2a4SSudeep Holla #include <linux/of.h>
12cd52c2a4SSudeep Holla #include <linux/module.h>
13cd52c2a4SSudeep Holla #include <linux/platform_device.h>
14cd52c2a4SSudeep Holla #include <linux/scpi_protocol.h>
15cd52c2a4SSudeep Holla
16cd52c2a4SSudeep Holla struct scpi_clk {
17cd52c2a4SSudeep Holla u32 id;
18cd52c2a4SSudeep Holla struct clk_hw hw;
19cd52c2a4SSudeep Holla struct scpi_dvfs_info *info;
20cd52c2a4SSudeep Holla struct scpi_ops *scpi_ops;
21cd52c2a4SSudeep Holla };
22cd52c2a4SSudeep Holla
23cd52c2a4SSudeep Holla #define to_scpi_clk(clk) container_of(clk, struct scpi_clk, hw)
24cd52c2a4SSudeep Holla
259490f01eSSudeep Holla static struct platform_device *cpufreq_dev;
269490f01eSSudeep Holla
scpi_clk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)27cd52c2a4SSudeep Holla static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw,
28cd52c2a4SSudeep Holla unsigned long parent_rate)
29cd52c2a4SSudeep Holla {
30cd52c2a4SSudeep Holla struct scpi_clk *clk = to_scpi_clk(hw);
31cd52c2a4SSudeep Holla
32cd52c2a4SSudeep Holla return clk->scpi_ops->clk_get_val(clk->id);
33cd52c2a4SSudeep Holla }
34cd52c2a4SSudeep Holla
scpi_clk_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)35cd52c2a4SSudeep Holla static long scpi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
36cd52c2a4SSudeep Holla unsigned long *parent_rate)
37cd52c2a4SSudeep Holla {
38cd52c2a4SSudeep Holla /*
39cd52c2a4SSudeep Holla * We can't figure out what rate it will be, so just return the
40cd52c2a4SSudeep Holla * rate back to the caller. scpi_clk_recalc_rate() will be called
41cd52c2a4SSudeep Holla * after the rate is set and we'll know what rate the clock is
42cd52c2a4SSudeep Holla * running at then.
43cd52c2a4SSudeep Holla */
44cd52c2a4SSudeep Holla return rate;
45cd52c2a4SSudeep Holla }
46cd52c2a4SSudeep Holla
scpi_clk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)47cd52c2a4SSudeep Holla static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
48cd52c2a4SSudeep Holla unsigned long parent_rate)
49cd52c2a4SSudeep Holla {
50cd52c2a4SSudeep Holla struct scpi_clk *clk = to_scpi_clk(hw);
51cd52c2a4SSudeep Holla
52cd52c2a4SSudeep Holla return clk->scpi_ops->clk_set_val(clk->id, rate);
53cd52c2a4SSudeep Holla }
54cd52c2a4SSudeep Holla
55cd52c2a4SSudeep Holla static const struct clk_ops scpi_clk_ops = {
56cd52c2a4SSudeep Holla .recalc_rate = scpi_clk_recalc_rate,
57cd52c2a4SSudeep Holla .round_rate = scpi_clk_round_rate,
58cd52c2a4SSudeep Holla .set_rate = scpi_clk_set_rate,
59cd52c2a4SSudeep Holla };
60cd52c2a4SSudeep Holla
61cd52c2a4SSudeep Holla /* find closest match to given frequency in OPP table */
__scpi_dvfs_round_rate(struct scpi_clk * clk,unsigned long rate)627374aec9SSudeep Holla static long __scpi_dvfs_round_rate(struct scpi_clk *clk, unsigned long rate)
63cd52c2a4SSudeep Holla {
64cd52c2a4SSudeep Holla int idx;
657374aec9SSudeep Holla unsigned long fmin = 0, fmax = ~0, ftmp;
66cd52c2a4SSudeep Holla const struct scpi_opp *opp = clk->info->opps;
67cd52c2a4SSudeep Holla
68cd52c2a4SSudeep Holla for (idx = 0; idx < clk->info->count; idx++, opp++) {
69cd52c2a4SSudeep Holla ftmp = opp->freq;
707374aec9SSudeep Holla if (ftmp >= rate) {
71cd52c2a4SSudeep Holla if (ftmp <= fmax)
72cd52c2a4SSudeep Holla fmax = ftmp;
73cd52c2a4SSudeep Holla break;
74cd52c2a4SSudeep Holla } else if (ftmp >= fmin) {
75cd52c2a4SSudeep Holla fmin = ftmp;
76cd52c2a4SSudeep Holla }
77cd52c2a4SSudeep Holla }
78cd52c2a4SSudeep Holla return fmax != ~0 ? fmax : fmin;
79cd52c2a4SSudeep Holla }
80cd52c2a4SSudeep Holla
scpi_dvfs_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)81cd52c2a4SSudeep Holla static unsigned long scpi_dvfs_recalc_rate(struct clk_hw *hw,
82cd52c2a4SSudeep Holla unsigned long parent_rate)
83cd52c2a4SSudeep Holla {
84cd52c2a4SSudeep Holla struct scpi_clk *clk = to_scpi_clk(hw);
85cd52c2a4SSudeep Holla int idx = clk->scpi_ops->dvfs_get_idx(clk->id);
86cd52c2a4SSudeep Holla const struct scpi_opp *opp;
87cd52c2a4SSudeep Holla
88cd52c2a4SSudeep Holla if (idx < 0)
89cd52c2a4SSudeep Holla return 0;
90cd52c2a4SSudeep Holla
91cd52c2a4SSudeep Holla opp = clk->info->opps + idx;
92cd52c2a4SSudeep Holla return opp->freq;
93cd52c2a4SSudeep Holla }
94cd52c2a4SSudeep Holla
scpi_dvfs_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)95cd52c2a4SSudeep Holla static long scpi_dvfs_round_rate(struct clk_hw *hw, unsigned long rate,
96cd52c2a4SSudeep Holla unsigned long *parent_rate)
97cd52c2a4SSudeep Holla {
98cd52c2a4SSudeep Holla struct scpi_clk *clk = to_scpi_clk(hw);
99cd52c2a4SSudeep Holla
100cd52c2a4SSudeep Holla return __scpi_dvfs_round_rate(clk, rate);
101cd52c2a4SSudeep Holla }
102cd52c2a4SSudeep Holla
__scpi_find_dvfs_index(struct scpi_clk * clk,unsigned long rate)103cd52c2a4SSudeep Holla static int __scpi_find_dvfs_index(struct scpi_clk *clk, unsigned long rate)
104cd52c2a4SSudeep Holla {
105cd52c2a4SSudeep Holla int idx, max_opp = clk->info->count;
106cd52c2a4SSudeep Holla const struct scpi_opp *opp = clk->info->opps;
107cd52c2a4SSudeep Holla
108cd52c2a4SSudeep Holla for (idx = 0; idx < max_opp; idx++, opp++)
109cd52c2a4SSudeep Holla if (opp->freq == rate)
110cd52c2a4SSudeep Holla return idx;
111cd52c2a4SSudeep Holla return -EINVAL;
112cd52c2a4SSudeep Holla }
113cd52c2a4SSudeep Holla
scpi_dvfs_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)114cd52c2a4SSudeep Holla static int scpi_dvfs_set_rate(struct clk_hw *hw, unsigned long rate,
115cd52c2a4SSudeep Holla unsigned long parent_rate)
116cd52c2a4SSudeep Holla {
117cd52c2a4SSudeep Holla struct scpi_clk *clk = to_scpi_clk(hw);
118cd52c2a4SSudeep Holla int ret = __scpi_find_dvfs_index(clk, rate);
119cd52c2a4SSudeep Holla
120cd52c2a4SSudeep Holla if (ret < 0)
121cd52c2a4SSudeep Holla return ret;
122cd52c2a4SSudeep Holla return clk->scpi_ops->dvfs_set_idx(clk->id, (u8)ret);
123cd52c2a4SSudeep Holla }
124cd52c2a4SSudeep Holla
125cd52c2a4SSudeep Holla static const struct clk_ops scpi_dvfs_ops = {
126cd52c2a4SSudeep Holla .recalc_rate = scpi_dvfs_recalc_rate,
127cd52c2a4SSudeep Holla .round_rate = scpi_dvfs_round_rate,
128cd52c2a4SSudeep Holla .set_rate = scpi_dvfs_set_rate,
129cd52c2a4SSudeep Holla };
130cd52c2a4SSudeep Holla
131975d25cbSKrzysztof Kozlowski static const struct of_device_id scpi_clk_match[] __maybe_unused = {
132cd52c2a4SSudeep Holla { .compatible = "arm,scpi-dvfs-clocks", .data = &scpi_dvfs_ops, },
133cd52c2a4SSudeep Holla { .compatible = "arm,scpi-variable-clocks", .data = &scpi_clk_ops, },
134cd52c2a4SSudeep Holla {}
135cd52c2a4SSudeep Holla };
136cd52c2a4SSudeep Holla
13793ae00beSStephen Boyd static int
scpi_clk_ops_init(struct device * dev,const struct of_device_id * match,struct scpi_clk * sclk,const char * name)138cd52c2a4SSudeep Holla scpi_clk_ops_init(struct device *dev, const struct of_device_id *match,
139cd52c2a4SSudeep Holla struct scpi_clk *sclk, const char *name)
140cd52c2a4SSudeep Holla {
141cd52c2a4SSudeep Holla struct clk_init_data init;
142cd52c2a4SSudeep Holla unsigned long min = 0, max = 0;
14393ae00beSStephen Boyd int ret;
144cd52c2a4SSudeep Holla
145cd52c2a4SSudeep Holla init.name = name;
14690593682SStephen Boyd init.flags = 0;
147cd52c2a4SSudeep Holla init.num_parents = 0;
148cd52c2a4SSudeep Holla init.ops = match->data;
149cd52c2a4SSudeep Holla sclk->hw.init = &init;
150cd52c2a4SSudeep Holla sclk->scpi_ops = get_scpi_ops();
151cd52c2a4SSudeep Holla
152cd52c2a4SSudeep Holla if (init.ops == &scpi_dvfs_ops) {
153cd52c2a4SSudeep Holla sclk->info = sclk->scpi_ops->dvfs_get_info(sclk->id);
154cd52c2a4SSudeep Holla if (IS_ERR(sclk->info))
15593ae00beSStephen Boyd return PTR_ERR(sclk->info);
156cd52c2a4SSudeep Holla } else if (init.ops == &scpi_clk_ops) {
157cd52c2a4SSudeep Holla if (sclk->scpi_ops->clk_get_range(sclk->id, &min, &max) || !max)
15893ae00beSStephen Boyd return -EINVAL;
159cd52c2a4SSudeep Holla } else {
16093ae00beSStephen Boyd return -EINVAL;
161cd52c2a4SSudeep Holla }
162cd52c2a4SSudeep Holla
16393ae00beSStephen Boyd ret = devm_clk_hw_register(dev, &sclk->hw);
16493ae00beSStephen Boyd if (!ret && max)
165cd52c2a4SSudeep Holla clk_hw_set_rate_range(&sclk->hw, min, max);
16693ae00beSStephen Boyd return ret;
167cd52c2a4SSudeep Holla }
168cd52c2a4SSudeep Holla
169cd52c2a4SSudeep Holla struct scpi_clk_data {
170cd52c2a4SSudeep Holla struct scpi_clk **clk;
171cd52c2a4SSudeep Holla unsigned int clk_num;
172cd52c2a4SSudeep Holla };
173cd52c2a4SSudeep Holla
17493ae00beSStephen Boyd static struct clk_hw *
scpi_of_clk_src_get(struct of_phandle_args * clkspec,void * data)175cd52c2a4SSudeep Holla scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data)
176cd52c2a4SSudeep Holla {
177cd52c2a4SSudeep Holla struct scpi_clk *sclk;
178cd52c2a4SSudeep Holla struct scpi_clk_data *clk_data = data;
179cd52c2a4SSudeep Holla unsigned int idx = clkspec->args[0], count;
180cd52c2a4SSudeep Holla
181cd52c2a4SSudeep Holla for (count = 0; count < clk_data->clk_num; count++) {
182cd52c2a4SSudeep Holla sclk = clk_data->clk[count];
183cd52c2a4SSudeep Holla if (idx == sclk->id)
18493ae00beSStephen Boyd return &sclk->hw;
185cd52c2a4SSudeep Holla }
186cd52c2a4SSudeep Holla
187cd52c2a4SSudeep Holla return ERR_PTR(-EINVAL);
188cd52c2a4SSudeep Holla }
189cd52c2a4SSudeep Holla
scpi_clk_add(struct device * dev,struct device_node * np,const struct of_device_id * match)190cd52c2a4SSudeep Holla static int scpi_clk_add(struct device *dev, struct device_node *np,
191cd52c2a4SSudeep Holla const struct of_device_id *match)
192cd52c2a4SSudeep Holla {
19393ae00beSStephen Boyd int idx, count, err;
194cd52c2a4SSudeep Holla struct scpi_clk_data *clk_data;
195cd52c2a4SSudeep Holla
196cd52c2a4SSudeep Holla count = of_property_count_strings(np, "clock-output-names");
197cd52c2a4SSudeep Holla if (count < 0) {
198e665f029SRob Herring dev_err(dev, "%pOFn: invalid clock output count\n", np);
199cd52c2a4SSudeep Holla return -EINVAL;
200cd52c2a4SSudeep Holla }
201cd52c2a4SSudeep Holla
202cd52c2a4SSudeep Holla clk_data = devm_kmalloc(dev, sizeof(*clk_data), GFP_KERNEL);
203cd52c2a4SSudeep Holla if (!clk_data)
204cd52c2a4SSudeep Holla return -ENOMEM;
205cd52c2a4SSudeep Holla
206cd52c2a4SSudeep Holla clk_data->clk_num = count;
207cd52c2a4SSudeep Holla clk_data->clk = devm_kcalloc(dev, count, sizeof(*clk_data->clk),
208cd52c2a4SSudeep Holla GFP_KERNEL);
209cd52c2a4SSudeep Holla if (!clk_data->clk)
210cd52c2a4SSudeep Holla return -ENOMEM;
211cd52c2a4SSudeep Holla
212cd52c2a4SSudeep Holla for (idx = 0; idx < count; idx++) {
213cd52c2a4SSudeep Holla struct scpi_clk *sclk;
214cd52c2a4SSudeep Holla const char *name;
215cd52c2a4SSudeep Holla u32 val;
216cd52c2a4SSudeep Holla
217cd52c2a4SSudeep Holla sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
218cd52c2a4SSudeep Holla if (!sclk)
219cd52c2a4SSudeep Holla return -ENOMEM;
220cd52c2a4SSudeep Holla
221cd52c2a4SSudeep Holla if (of_property_read_string_index(np, "clock-output-names",
222cd52c2a4SSudeep Holla idx, &name)) {
223e665f029SRob Herring dev_err(dev, "invalid clock name @ %pOFn\n", np);
224cd52c2a4SSudeep Holla return -EINVAL;
225cd52c2a4SSudeep Holla }
226cd52c2a4SSudeep Holla
227cd52c2a4SSudeep Holla if (of_property_read_u32_index(np, "clock-indices",
228cd52c2a4SSudeep Holla idx, &val)) {
229e665f029SRob Herring dev_err(dev, "invalid clock index @ %pOFn\n", np);
230cd52c2a4SSudeep Holla return -EINVAL;
231cd52c2a4SSudeep Holla }
232cd52c2a4SSudeep Holla
233cd52c2a4SSudeep Holla sclk->id = val;
234cd52c2a4SSudeep Holla
23593ae00beSStephen Boyd err = scpi_clk_ops_init(dev, match, sclk, name);
2362b286b09SJerome Brunet if (err) {
237cd52c2a4SSudeep Holla dev_err(dev, "failed to register clock '%s'\n", name);
2382b286b09SJerome Brunet return err;
2392b286b09SJerome Brunet }
2402b286b09SJerome Brunet
241cd52c2a4SSudeep Holla dev_dbg(dev, "Registered clock '%s'\n", name);
242cd52c2a4SSudeep Holla clk_data->clk[idx] = sclk;
243cd52c2a4SSudeep Holla }
244cd52c2a4SSudeep Holla
24593ae00beSStephen Boyd return of_clk_add_hw_provider(np, scpi_of_clk_src_get, clk_data);
246cd52c2a4SSudeep Holla }
247cd52c2a4SSudeep Holla
scpi_clocks_remove(struct platform_device * pdev)248*dd904848SUwe Kleine-König static void scpi_clocks_remove(struct platform_device *pdev)
249cd52c2a4SSudeep Holla {
250cd52c2a4SSudeep Holla struct device *dev = &pdev->dev;
251cd52c2a4SSudeep Holla struct device_node *child, *np = dev->of_node;
252cd52c2a4SSudeep Holla
2539490f01eSSudeep Holla if (cpufreq_dev) {
2549490f01eSSudeep Holla platform_device_unregister(cpufreq_dev);
2559490f01eSSudeep Holla cpufreq_dev = NULL;
2569490f01eSSudeep Holla }
2579490f01eSSudeep Holla
258cd52c2a4SSudeep Holla for_each_available_child_of_node(np, child)
259cd52c2a4SSudeep Holla of_clk_del_provider(np);
260cd52c2a4SSudeep Holla }
261cd52c2a4SSudeep Holla
scpi_clocks_probe(struct platform_device * pdev)262cd52c2a4SSudeep Holla static int scpi_clocks_probe(struct platform_device *pdev)
263cd52c2a4SSudeep Holla {
264cd52c2a4SSudeep Holla int ret;
265cd52c2a4SSudeep Holla struct device *dev = &pdev->dev;
266cd52c2a4SSudeep Holla struct device_node *child, *np = dev->of_node;
267cd52c2a4SSudeep Holla const struct of_device_id *match;
268cd52c2a4SSudeep Holla
269cd52c2a4SSudeep Holla if (!get_scpi_ops())
270cd52c2a4SSudeep Holla return -ENXIO;
271cd52c2a4SSudeep Holla
272cd52c2a4SSudeep Holla for_each_available_child_of_node(np, child) {
273cd52c2a4SSudeep Holla match = of_match_node(scpi_clk_match, child);
274cd52c2a4SSudeep Holla if (!match)
275cd52c2a4SSudeep Holla continue;
276cd52c2a4SSudeep Holla ret = scpi_clk_add(dev, child, match);
277cd52c2a4SSudeep Holla if (ret) {
278cd52c2a4SSudeep Holla scpi_clocks_remove(pdev);
279e80cf2e5SJulia Lawall of_node_put(child);
280cd52c2a4SSudeep Holla return ret;
281cd52c2a4SSudeep Holla }
28267bcc2c5SSudeep Holla
28367bcc2c5SSudeep Holla if (match->data != &scpi_dvfs_ops)
28467bcc2c5SSudeep Holla continue;
28567bcc2c5SSudeep Holla /* Add the virtual cpufreq device if it's DVFS clock provider */
2869490f01eSSudeep Holla cpufreq_dev = platform_device_register_simple("scpi-cpufreq",
2879490f01eSSudeep Holla -1, NULL, 0);
288d22eb66bSAxel Lin if (IS_ERR(cpufreq_dev))
2899490f01eSSudeep Holla pr_warn("unable to register cpufreq device");
29067bcc2c5SSudeep Holla }
291cd52c2a4SSudeep Holla return 0;
292cd52c2a4SSudeep Holla }
293cd52c2a4SSudeep Holla
294cd52c2a4SSudeep Holla static const struct of_device_id scpi_clocks_ids[] = {
295cd52c2a4SSudeep Holla { .compatible = "arm,scpi-clocks", },
296cd52c2a4SSudeep Holla {}
297cd52c2a4SSudeep Holla };
298cd52c2a4SSudeep Holla MODULE_DEVICE_TABLE(of, scpi_clocks_ids);
299cd52c2a4SSudeep Holla
300cd52c2a4SSudeep Holla static struct platform_driver scpi_clocks_driver = {
301cd52c2a4SSudeep Holla .driver = {
302cd52c2a4SSudeep Holla .name = "scpi_clocks",
303cd52c2a4SSudeep Holla .of_match_table = scpi_clocks_ids,
304cd52c2a4SSudeep Holla },
305cd52c2a4SSudeep Holla .probe = scpi_clocks_probe,
306*dd904848SUwe Kleine-König .remove_new = scpi_clocks_remove,
307cd52c2a4SSudeep Holla };
308cd52c2a4SSudeep Holla module_platform_driver(scpi_clocks_driver);
309cd52c2a4SSudeep Holla
310cd52c2a4SSudeep Holla MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
311cd52c2a4SSudeep Holla MODULE_DESCRIPTION("ARM SCPI clock driver");
312cd52c2a4SSudeep Holla MODULE_LICENSE("GPL v2");
313