xref: /openbmc/linux/drivers/clk/clk-scpi.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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