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
)
42 struct scmi_clk
*clk
= to_scmi_clk(hw
);
45 * We can't figure out what rate it will be, so just return the
46 * rate back to the caller. scmi_clk_recalc_rate() will be called
47 * after the rate is set and we'll know what rate the clock is
50 if (clk
->info
->rate_discrete
)
53 fmin
= clk
->info
->range
.min_rate
;
54 fmax
= clk
->info
->range
.max_rate
;
57 else if (rate
>= fmax
)
61 ftmp
+= clk
->info
->range
.step_size
- 1; /* to round up */
62 do_div(ftmp
, clk
->info
->range
.step_size
);
64 return ftmp
* clk
->info
->range
.step_size
+ fmin
;
67 static int scmi_clk_set_rate(struct clk_hw
*hw
, unsigned long rate
,
68 unsigned long parent_rate
)
70 struct scmi_clk
*clk
= to_scmi_clk(hw
);
72 return clk
->handle
->clk_ops
->rate_set(clk
->handle
, clk
->id
, 0, rate
);
75 static int scmi_clk_enable(struct clk_hw
*hw
)
77 struct scmi_clk
*clk
= to_scmi_clk(hw
);
79 return clk
->handle
->clk_ops
->enable(clk
->handle
, clk
->id
);
82 static void scmi_clk_disable(struct clk_hw
*hw
)
84 struct scmi_clk
*clk
= to_scmi_clk(hw
);
86 clk
->handle
->clk_ops
->disable(clk
->handle
, clk
->id
);
89 static const struct clk_ops scmi_clk_ops
= {
90 .recalc_rate
= scmi_clk_recalc_rate
,
91 .round_rate
= scmi_clk_round_rate
,
92 .set_rate
= scmi_clk_set_rate
,
94 * We can't provide enable/disable callback as we can't perform the same
95 * in atomic context. Since the clock framework provides standard API
96 * clk_prepare_enable that helps cases using clk_enable in non-atomic
97 * context, it should be fine providing prepare/unprepare.
99 .prepare
= scmi_clk_enable
,
100 .unprepare
= scmi_clk_disable
,
103 static int scmi_clk_ops_init(struct device
*dev
, struct scmi_clk
*sclk
)
106 struct clk_init_data init
= {
107 .flags
= CLK_GET_RATE_NOCACHE
,
109 .ops
= &scmi_clk_ops
,
110 .name
= sclk
->info
->name
,
113 sclk
->hw
.init
= &init
;
114 ret
= devm_clk_hw_register(dev
, &sclk
->hw
);
116 clk_hw_set_rate_range(&sclk
->hw
, sclk
->info
->range
.min_rate
,
117 sclk
->info
->range
.max_rate
);
121 static int scmi_clocks_probe(struct scmi_device
*sdev
)
125 struct clk_hw_onecell_data
*clk_data
;
126 struct device
*dev
= &sdev
->dev
;
127 struct device_node
*np
= dev
->of_node
;
128 const struct scmi_handle
*handle
= sdev
->handle
;
130 if (!handle
|| !handle
->clk_ops
)
133 count
= handle
->clk_ops
->count_get(handle
);
135 dev_err(dev
, "%s: invalid clock output count\n", np
->name
);
139 clk_data
= devm_kzalloc(dev
, struct_size(clk_data
, hws
, count
),
144 clk_data
->num
= count
;
147 for (idx
= 0; idx
< count
; idx
++) {
148 struct scmi_clk
*sclk
;
150 sclk
= devm_kzalloc(dev
, sizeof(*sclk
), GFP_KERNEL
);
154 sclk
->info
= handle
->clk_ops
->info_get(handle
, idx
);
156 dev_dbg(dev
, "invalid clock info for idx %d\n", idx
);
161 sclk
->handle
= handle
;
163 err
= scmi_clk_ops_init(dev
, sclk
);
165 dev_err(dev
, "failed to register clock %d\n", idx
);
166 devm_kfree(dev
, sclk
);
169 dev_dbg(dev
, "Registered clock:%s\n", sclk
->info
->name
);
170 hws
[idx
] = &sclk
->hw
;
174 return devm_of_clk_add_hw_provider(dev
, of_clk_hw_onecell_get
,
178 static const struct scmi_device_id scmi_id_table
[] = {
179 { SCMI_PROTOCOL_CLOCK
},
182 MODULE_DEVICE_TABLE(scmi
, scmi_id_table
);
184 static struct scmi_driver scmi_clocks_driver
= {
185 .name
= "scmi-clocks",
186 .probe
= scmi_clocks_probe
,
187 .id_table
= scmi_id_table
,
189 module_scmi_driver(scmi_clocks_driver
);
191 MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
192 MODULE_DESCRIPTION("ARM SCMI clock driver");
193 MODULE_LICENSE("GPL v2");