1 // SPDX-License-Identifier: GPL-2.0-only
3 * System Control and Power Interface (SCPI) Protocol based clock driver
5 * Copyright (C) 2015 ARM Ltd.
8 #include <linux/clk-provider.h>
9 #include <linux/device.h>
10 #include <linux/err.h>
12 #include <linux/module.h>
13 #include <linux/of_platform.h>
14 #include <linux/platform_device.h>
15 #include <linux/scpi_protocol.h>
20 struct scpi_dvfs_info
*info
;
21 struct scpi_ops
*scpi_ops
;
24 #define to_scpi_clk(clk) container_of(clk, struct scpi_clk, hw)
26 static struct platform_device
*cpufreq_dev
;
28 static unsigned long scpi_clk_recalc_rate(struct clk_hw
*hw
,
29 unsigned long parent_rate
)
31 struct scpi_clk
*clk
= to_scpi_clk(hw
);
33 return clk
->scpi_ops
->clk_get_val(clk
->id
);
36 static long scpi_clk_round_rate(struct clk_hw
*hw
, unsigned long rate
,
37 unsigned long *parent_rate
)
40 * We can't figure out what rate it will be, so just return the
41 * rate back to the caller. scpi_clk_recalc_rate() will be called
42 * after the rate is set and we'll know what rate the clock is
48 static int scpi_clk_set_rate(struct clk_hw
*hw
, unsigned long rate
,
49 unsigned long parent_rate
)
51 struct scpi_clk
*clk
= to_scpi_clk(hw
);
53 return clk
->scpi_ops
->clk_set_val(clk
->id
, rate
);
56 static const struct clk_ops scpi_clk_ops
= {
57 .recalc_rate
= scpi_clk_recalc_rate
,
58 .round_rate
= scpi_clk_round_rate
,
59 .set_rate
= scpi_clk_set_rate
,
62 /* find closest match to given frequency in OPP table */
63 static long __scpi_dvfs_round_rate(struct scpi_clk
*clk
, unsigned long rate
)
66 unsigned long fmin
= 0, fmax
= ~0, ftmp
;
67 const struct scpi_opp
*opp
= clk
->info
->opps
;
69 for (idx
= 0; idx
< clk
->info
->count
; idx
++, opp
++) {
75 } else if (ftmp
>= fmin
) {
79 return fmax
!= ~0 ? fmax
: fmin
;
82 static unsigned long scpi_dvfs_recalc_rate(struct clk_hw
*hw
,
83 unsigned long parent_rate
)
85 struct scpi_clk
*clk
= to_scpi_clk(hw
);
86 int idx
= clk
->scpi_ops
->dvfs_get_idx(clk
->id
);
87 const struct scpi_opp
*opp
;
92 opp
= clk
->info
->opps
+ idx
;
96 static long scpi_dvfs_round_rate(struct clk_hw
*hw
, unsigned long rate
,
97 unsigned long *parent_rate
)
99 struct scpi_clk
*clk
= to_scpi_clk(hw
);
101 return __scpi_dvfs_round_rate(clk
, rate
);
104 static int __scpi_find_dvfs_index(struct scpi_clk
*clk
, unsigned long rate
)
106 int idx
, max_opp
= clk
->info
->count
;
107 const struct scpi_opp
*opp
= clk
->info
->opps
;
109 for (idx
= 0; idx
< max_opp
; idx
++, opp
++)
110 if (opp
->freq
== rate
)
115 static int scpi_dvfs_set_rate(struct clk_hw
*hw
, unsigned long rate
,
116 unsigned long parent_rate
)
118 struct scpi_clk
*clk
= to_scpi_clk(hw
);
119 int ret
= __scpi_find_dvfs_index(clk
, rate
);
123 return clk
->scpi_ops
->dvfs_set_idx(clk
->id
, (u8
)ret
);
126 static const struct clk_ops scpi_dvfs_ops
= {
127 .recalc_rate
= scpi_dvfs_recalc_rate
,
128 .round_rate
= scpi_dvfs_round_rate
,
129 .set_rate
= scpi_dvfs_set_rate
,
132 static const struct of_device_id scpi_clk_match
[] = {
133 { .compatible
= "arm,scpi-dvfs-clocks", .data
= &scpi_dvfs_ops
, },
134 { .compatible
= "arm,scpi-variable-clocks", .data
= &scpi_clk_ops
, },
139 scpi_clk_ops_init(struct device
*dev
, const struct of_device_id
*match
,
140 struct scpi_clk
*sclk
, const char *name
)
142 struct clk_init_data init
;
143 unsigned long min
= 0, max
= 0;
148 init
.num_parents
= 0;
149 init
.ops
= match
->data
;
150 sclk
->hw
.init
= &init
;
151 sclk
->scpi_ops
= get_scpi_ops();
153 if (init
.ops
== &scpi_dvfs_ops
) {
154 sclk
->info
= sclk
->scpi_ops
->dvfs_get_info(sclk
->id
);
155 if (IS_ERR(sclk
->info
))
156 return PTR_ERR(sclk
->info
);
157 } else if (init
.ops
== &scpi_clk_ops
) {
158 if (sclk
->scpi_ops
->clk_get_range(sclk
->id
, &min
, &max
) || !max
)
164 ret
= devm_clk_hw_register(dev
, &sclk
->hw
);
166 clk_hw_set_rate_range(&sclk
->hw
, min
, max
);
170 struct scpi_clk_data
{
171 struct scpi_clk
**clk
;
172 unsigned int clk_num
;
175 static struct clk_hw
*
176 scpi_of_clk_src_get(struct of_phandle_args
*clkspec
, void *data
)
178 struct scpi_clk
*sclk
;
179 struct scpi_clk_data
*clk_data
= data
;
180 unsigned int idx
= clkspec
->args
[0], count
;
182 for (count
= 0; count
< clk_data
->clk_num
; count
++) {
183 sclk
= clk_data
->clk
[count
];
188 return ERR_PTR(-EINVAL
);
191 static int scpi_clk_add(struct device
*dev
, struct device_node
*np
,
192 const struct of_device_id
*match
)
195 struct scpi_clk_data
*clk_data
;
197 count
= of_property_count_strings(np
, "clock-output-names");
199 dev_err(dev
, "%pOFn: invalid clock output count\n", np
);
203 clk_data
= devm_kmalloc(dev
, sizeof(*clk_data
), GFP_KERNEL
);
207 clk_data
->clk_num
= count
;
208 clk_data
->clk
= devm_kcalloc(dev
, count
, sizeof(*clk_data
->clk
),
213 for (idx
= 0; idx
< count
; idx
++) {
214 struct scpi_clk
*sclk
;
218 sclk
= devm_kzalloc(dev
, sizeof(*sclk
), GFP_KERNEL
);
222 if (of_property_read_string_index(np
, "clock-output-names",
224 dev_err(dev
, "invalid clock name @ %pOFn\n", np
);
228 if (of_property_read_u32_index(np
, "clock-indices",
230 dev_err(dev
, "invalid clock index @ %pOFn\n", np
);
236 err
= scpi_clk_ops_init(dev
, match
, sclk
, name
);
238 dev_err(dev
, "failed to register clock '%s'\n", name
);
242 dev_dbg(dev
, "Registered clock '%s'\n", name
);
243 clk_data
->clk
[idx
] = sclk
;
246 return of_clk_add_hw_provider(np
, scpi_of_clk_src_get
, clk_data
);
249 static int scpi_clocks_remove(struct platform_device
*pdev
)
251 struct device
*dev
= &pdev
->dev
;
252 struct device_node
*child
, *np
= dev
->of_node
;
255 platform_device_unregister(cpufreq_dev
);
259 for_each_available_child_of_node(np
, child
)
260 of_clk_del_provider(np
);
264 static int scpi_clocks_probe(struct platform_device
*pdev
)
267 struct device
*dev
= &pdev
->dev
;
268 struct device_node
*child
, *np
= dev
->of_node
;
269 const struct of_device_id
*match
;
274 for_each_available_child_of_node(np
, child
) {
275 match
= of_match_node(scpi_clk_match
, child
);
278 ret
= scpi_clk_add(dev
, child
, match
);
280 scpi_clocks_remove(pdev
);
285 if (match
->data
!= &scpi_dvfs_ops
)
287 /* Add the virtual cpufreq device if it's DVFS clock provider */
288 cpufreq_dev
= platform_device_register_simple("scpi-cpufreq",
290 if (IS_ERR(cpufreq_dev
))
291 pr_warn("unable to register cpufreq device");
296 static const struct of_device_id scpi_clocks_ids
[] = {
297 { .compatible
= "arm,scpi-clocks", },
300 MODULE_DEVICE_TABLE(of
, scpi_clocks_ids
);
302 static struct platform_driver scpi_clocks_driver
= {
304 .name
= "scpi_clocks",
305 .of_match_table
= scpi_clocks_ids
,
307 .probe
= scpi_clocks_probe
,
308 .remove
= scpi_clocks_remove
,
310 module_platform_driver(scpi_clocks_driver
);
312 MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
313 MODULE_DESCRIPTION("ARM SCPI clock driver");
314 MODULE_LICENSE("GPL v2");