1 // SPDX-License-Identifier: GPL-2.0
3 * System Control and Power Interface (SCMI) Protocol based clock driver
5 * Copyright (C) 2018 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/scmi_protocol.h>
14 #include <asm/div64.h>
19 const struct scmi_clock_info
*info
;
20 const struct scmi_handle
*handle
;
23 #define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
25 static unsigned long scmi_clk_recalc_rate(struct clk_hw
*hw
,
26 unsigned long parent_rate
)
30 struct scmi_clk
*clk
= to_scmi_clk(hw
);
32 ret
= clk
->handle
->clk_ops
->rate_get(clk
->handle
, clk
->id
, &rate
);
38 static long scmi_clk_round_rate(struct clk_hw
*hw
, unsigned long rate
,
39 unsigned long *parent_rate
)
43 struct scmi_clk
*clk
= to_scmi_clk(hw
);
46 * We can't figure out what rate it will be, so just return the
47 * rate back to the caller. scmi_clk_recalc_rate() will be called
48 * after the rate is set and we'll know what rate the clock is
51 if (clk
->info
->rate_discrete
)
54 fmin
= clk
->info
->range
.min_rate
;
55 fmax
= clk
->info
->range
.max_rate
;
58 else if (rate
>= fmax
)
62 ftmp
+= clk
->info
->range
.step_size
- 1; /* to round up */
63 step
= do_div(ftmp
, clk
->info
->range
.step_size
);
65 return step
* clk
->info
->range
.step_size
+ fmin
;
68 static int scmi_clk_set_rate(struct clk_hw
*hw
, unsigned long rate
,
69 unsigned long parent_rate
)
71 struct scmi_clk
*clk
= to_scmi_clk(hw
);
73 return clk
->handle
->clk_ops
->rate_set(clk
->handle
, clk
->id
, 0, rate
);
76 static int scmi_clk_enable(struct clk_hw
*hw
)
78 struct scmi_clk
*clk
= to_scmi_clk(hw
);
80 return clk
->handle
->clk_ops
->enable(clk
->handle
, clk
->id
);
83 static void scmi_clk_disable(struct clk_hw
*hw
)
85 struct scmi_clk
*clk
= to_scmi_clk(hw
);
87 clk
->handle
->clk_ops
->disable(clk
->handle
, clk
->id
);
90 static const struct clk_ops scmi_clk_ops
= {
91 .recalc_rate
= scmi_clk_recalc_rate
,
92 .round_rate
= scmi_clk_round_rate
,
93 .set_rate
= scmi_clk_set_rate
,
95 * We can't provide enable/disable callback as we can't perform the same
96 * in atomic context. Since the clock framework provides standard API
97 * clk_prepare_enable that helps cases using clk_enable in non-atomic
98 * context, it should be fine providing prepare/unprepare.
100 .prepare
= scmi_clk_enable
,
101 .unprepare
= scmi_clk_disable
,
104 static int scmi_clk_ops_init(struct device
*dev
, struct scmi_clk
*sclk
)
107 struct clk_init_data init
= {
108 .flags
= CLK_GET_RATE_NOCACHE
,
110 .ops
= &scmi_clk_ops
,
111 .name
= sclk
->info
->name
,
114 sclk
->hw
.init
= &init
;
115 ret
= devm_clk_hw_register(dev
, &sclk
->hw
);
117 clk_hw_set_rate_range(&sclk
->hw
, sclk
->info
->range
.min_rate
,
118 sclk
->info
->range
.max_rate
);
122 static int scmi_clocks_probe(struct scmi_device
*sdev
)
126 struct clk_hw_onecell_data
*clk_data
;
127 struct device
*dev
= &sdev
->dev
;
128 struct device_node
*np
= dev
->of_node
;
129 const struct scmi_handle
*handle
= sdev
->handle
;
131 if (!handle
|| !handle
->clk_ops
)
134 count
= handle
->clk_ops
->count_get(handle
);
136 dev_err(dev
, "%s: invalid clock output count\n", np
->name
);
140 clk_data
= devm_kzalloc(dev
, struct_size(clk_data
, hws
, count
),
145 clk_data
->num
= count
;
148 for (idx
= 0; idx
< count
; idx
++) {
149 struct scmi_clk
*sclk
;
151 sclk
= devm_kzalloc(dev
, sizeof(*sclk
), GFP_KERNEL
);
155 sclk
->info
= handle
->clk_ops
->info_get(handle
, idx
);
157 dev_dbg(dev
, "invalid clock info for idx %d\n", idx
);
162 sclk
->handle
= handle
;
164 err
= scmi_clk_ops_init(dev
, sclk
);
166 dev_err(dev
, "failed to register clock %d\n", idx
);
167 devm_kfree(dev
, sclk
);
170 dev_dbg(dev
, "Registered clock:%s\n", sclk
->info
->name
);
171 hws
[idx
] = &sclk
->hw
;
175 return devm_of_clk_add_hw_provider(dev
, of_clk_hw_onecell_get
,
179 static const struct scmi_device_id scmi_id_table
[] = {
180 { SCMI_PROTOCOL_CLOCK
},
183 MODULE_DEVICE_TABLE(scmi
, scmi_id_table
);
185 static struct scmi_driver scmi_clocks_driver
= {
186 .name
= "scmi-clocks",
187 .probe
= scmi_clocks_probe
,
188 .id_table
= scmi_id_table
,
190 module_scmi_driver(scmi_clocks_driver
);
192 MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
193 MODULE_DESCRIPTION("ARM SCMI clock driver");
194 MODULE_LICENSE("GPL v2");