blob: af69850cdd81b835019f69eb37364886330bfd8c [file] [log] [blame]
Etienne Carriere78928e12020-09-09 18:44:04 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
Etienne Carriere528f9882022-05-31 18:09:25 +02003 * Copyright (C) 2019-2022 Linaro Limited
Etienne Carriere78928e12020-09-09 18:44:04 +02004 */
Patrick Delaunay1285f8e2021-10-28 19:13:13 +02005
6#define LOG_CATEGORY UCLASS_CLK
7
Etienne Carriere78928e12020-09-09 18:44:04 +02008#include <clk-uclass.h>
9#include <dm.h>
10#include <scmi_agent.h>
Alice Guof71894b2025-04-28 18:37:25 +080011#include <scmi_agent-uclass.h>
Etienne Carriere78928e12020-09-09 18:44:04 +020012#include <scmi_protocols.h>
13#include <asm/types.h>
Etienne Carriere4c4ec902022-02-21 09:22:42 +010014#include <linux/clk-provider.h>
15
Alice Guo26d0ed02025-04-28 18:37:31 +080016struct clk_scmi {
17 struct clk clk;
18 u32 ctrl_flags;
19};
20
21struct scmi_clock_priv {
22 u32 version;
23};
24
25static int scmi_clk_get_permissions(struct udevice *dev, int clkid, u32 *perm)
26{
27 struct scmi_clock_priv *priv = dev_get_priv(dev);
28 int ret;
29
30 struct scmi_clk_get_permissions_in in = {
31 .clock_id = clkid,
32 };
33 struct scmi_clk_get_permissions_out out;
34 struct scmi_msg msg = {
35 .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
36 .message_id = SCMI_CLOCK_GET_PERMISSIONS,
37 .in_msg = (u8 *)&in,
38 .in_msg_sz = sizeof(in),
39 .out_msg = (u8 *)&out,
40 .out_msg_sz = sizeof(out),
41 };
42
43 if (priv->version < CLOCK_PROTOCOL_VERSION_3_0) {
44 log_debug("%s: SCMI clock management protocol version is less than 3.0.\n", __func__);
45 return -EINVAL;
46 }
47
48 ret = devm_scmi_process_msg(dev, &msg);
49 if (ret) {
50 log_debug("%s: get SCMI clock management protocol permissions failed\n", __func__);
51 return ret;
52 }
53
54 ret = scmi_to_linux_errno(out.status);
55 if (ret < 0) {
56 log_debug("%s: the status code of getting permissions: %d\n", __func__, ret);
57 return ret;
58 }
59
60 *perm = out.permissions;
61 return 0;
62}
63
Etienne Carriere4c4ec902022-02-21 09:22:42 +010064static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
65{
66 struct scmi_clk_protocol_attr_out out;
67 struct scmi_msg msg = {
68 .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
69 .message_id = SCMI_PROTOCOL_ATTRIBUTES,
70 .out_msg = (u8 *)&out,
71 .out_msg_sz = sizeof(out),
72 };
73 int ret;
74
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +090075 ret = devm_scmi_process_msg(dev, &msg);
Etienne Carriere4c4ec902022-02-21 09:22:42 +010076 if (ret)
77 return ret;
78
79 *num_clocks = out.attributes & SCMI_CLK_PROTO_ATTR_COUNT_MASK;
80
81 return 0;
82}
83
Alice Guo26d0ed02025-04-28 18:37:31 +080084static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name,
85 u32 *attr)
Etienne Carriere4c4ec902022-02-21 09:22:42 +010086{
87 struct scmi_clk_attribute_in in = {
88 .clock_id = clkid,
89 };
90 struct scmi_clk_attribute_out out;
91 struct scmi_msg msg = {
92 .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
93 .message_id = SCMI_CLOCK_ATTRIBUTES,
94 .in_msg = (u8 *)&in,
95 .in_msg_sz = sizeof(in),
96 .out_msg = (u8 *)&out,
97 .out_msg_sz = sizeof(out),
98 };
99 int ret;
100
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900101 ret = devm_scmi_process_msg(dev, &msg);
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100102 if (ret)
103 return ret;
104
Heinrich Schuchardtba9c8922022-04-26 23:26:31 +0200105 *name = strdup(out.clock_name);
Alice Guo26d0ed02025-04-28 18:37:31 +0800106 *attr = out.attributes;
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100107
108 return 0;
109}
Etienne Carriere78928e12020-09-09 18:44:04 +0200110
111static int scmi_clk_gate(struct clk *clk, int enable)
112{
113 struct scmi_clk_state_in in = {
114 .clock_id = clk->id,
115 .attributes = enable,
116 };
117 struct scmi_clk_state_out out;
118 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
119 SCMI_CLOCK_CONFIG_SET,
120 in, out);
121 int ret;
122
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900123 ret = devm_scmi_process_msg(clk->dev, &msg);
Etienne Carriere78928e12020-09-09 18:44:04 +0200124 if (ret)
125 return ret;
126
127 return scmi_to_linux_errno(out.status);
128}
129
130static int scmi_clk_enable(struct clk *clk)
131{
Alice Guo26d0ed02025-04-28 18:37:31 +0800132 struct clk_scmi *clkscmi;
133 struct clk *c;
134 int ret;
135
136 if (!CONFIG_IS_ENABLED(CLK_CCF))
137 return scmi_clk_gate(clk, 1);
138
139 ret = clk_get_by_id(clk->id, &c);
140 if (ret)
141 return ret;
142
143 clkscmi = container_of(c, struct clk_scmi, clk);
144
145 if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
146 return scmi_clk_gate(clk, 1);
147
148 /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
149 log_debug("%s: SCMI CLOCK: the clock cannot be enabled by the agent.\n", __func__);
150 return 0;
Etienne Carriere78928e12020-09-09 18:44:04 +0200151}
152
153static int scmi_clk_disable(struct clk *clk)
154{
Alice Guo26d0ed02025-04-28 18:37:31 +0800155 struct clk_scmi *clkscmi;
156 struct clk *c;
157 int ret;
158
159 if (!CONFIG_IS_ENABLED(CLK_CCF))
160 return scmi_clk_gate(clk, 0);
161
162 ret = clk_get_by_id(clk->id, &c);
163 if (ret)
164 return ret;
165
166 clkscmi = container_of(c, struct clk_scmi, clk);
167
168 if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
169 return scmi_clk_gate(clk, 0);
170
171 /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
172 log_debug("%s: SCMI CLOCK: the clock cannot be disabled by the agent.\n", __func__);
173 return 0;
Etienne Carriere78928e12020-09-09 18:44:04 +0200174}
175
176static ulong scmi_clk_get_rate(struct clk *clk)
177{
178 struct scmi_clk_rate_get_in in = {
179 .clock_id = clk->id,
180 };
181 struct scmi_clk_rate_get_out out;
182 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
183 SCMI_CLOCK_RATE_GET,
184 in, out);
185 int ret;
186
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900187 ret = devm_scmi_process_msg(clk->dev, &msg);
Etienne Carriere78928e12020-09-09 18:44:04 +0200188 if (ret < 0)
189 return ret;
190
191 ret = scmi_to_linux_errno(out.status);
192 if (ret < 0)
193 return ret;
194
195 return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
196}
197
Alice Guo26d0ed02025-04-28 18:37:31 +0800198static ulong __scmi_clk_set_rate(struct clk *clk, ulong rate)
Etienne Carriere78928e12020-09-09 18:44:04 +0200199{
200 struct scmi_clk_rate_set_in in = {
201 .clock_id = clk->id,
202 .flags = SCMI_CLK_RATE_ROUND_CLOSEST,
203 .rate_lsb = (u32)rate,
204 .rate_msb = (u32)((u64)rate >> 32),
205 };
206 struct scmi_clk_rate_set_out out;
207 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
208 SCMI_CLOCK_RATE_SET,
209 in, out);
210 int ret;
211
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900212 ret = devm_scmi_process_msg(clk->dev, &msg);
Etienne Carriere78928e12020-09-09 18:44:04 +0200213 if (ret < 0)
214 return ret;
215
216 ret = scmi_to_linux_errno(out.status);
217 if (ret < 0)
218 return ret;
219
220 return scmi_clk_get_rate(clk);
221}
222
Alice Guo26d0ed02025-04-28 18:37:31 +0800223static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
224{
225 struct clk_scmi *clkscmi;
226 struct clk *c;
227 int ret;
228
229 if (!CONFIG_IS_ENABLED(CLK_CCF))
230 return __scmi_clk_set_rate(clk, rate);
231
232 ret = clk_get_by_id(clk->id, &c);
233 if (ret)
234 return ret;
235
236 clkscmi = container_of(c, struct clk_scmi, clk);
237
238 if (clkscmi->ctrl_flags & SUPPORT_CLK_RATE_CONTROL)
239 return __scmi_clk_set_rate(clk, rate);
240
241 /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
242 log_debug("%s: SCMI CLOCK: the clock rate cannot be changed by the agent.\n", __func__);
243 return 0;
244}
245
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100246static int scmi_clk_probe(struct udevice *dev)
247{
Alice Guo26d0ed02025-04-28 18:37:31 +0800248 struct clk_scmi *clk_scmi;
249 struct scmi_clock_priv *priv = dev_get_priv(dev);
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100250 size_t num_clocks, i;
251 int ret;
252
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900253 ret = devm_scmi_of_get_channel(dev);
Etienne Carriere528f9882022-05-31 18:09:25 +0200254 if (ret)
255 return ret;
256
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100257 if (!CONFIG_IS_ENABLED(CLK_CCF))
258 return 0;
259
260 /* register CCF children: CLK UCLASS, no probed again */
261 if (device_get_uclass_id(dev->parent) == UCLASS_CLK)
262 return 0;
263
264 ret = scmi_clk_get_num_clock(dev, &num_clocks);
265 if (ret)
266 return ret;
267
Alice Guo26d0ed02025-04-28 18:37:31 +0800268 ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_CLOCK, &priv->version);
269 if (ret) {
270 log_debug("%s: get SCMI clock management protocol version failed\n", __func__);
271 return ret;
272 }
273
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100274 for (i = 0; i < num_clocks; i++) {
Heinrich Schuchardtba9c8922022-04-26 23:26:31 +0200275 char *clock_name;
Alice Guo26d0ed02025-04-28 18:37:31 +0800276 u32 attributes;
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100277
Alice Guo26d0ed02025-04-28 18:37:31 +0800278 if (!scmi_clk_get_attibute(dev, i, &clock_name, &attributes)) {
279 clk_scmi = kzalloc(sizeof(*clk_scmi), GFP_KERNEL);
280 if (!clk_scmi || !clock_name)
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100281 ret = -ENOMEM;
282 else
Alice Guo26d0ed02025-04-28 18:37:31 +0800283 ret = clk_register(&clk_scmi->clk, dev->driver->name,
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100284 clock_name, dev->name);
285
286 if (ret) {
Alice Guo26d0ed02025-04-28 18:37:31 +0800287 free(clk_scmi);
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100288 free(clock_name);
289 return ret;
290 }
291
Alice Guo26d0ed02025-04-28 18:37:31 +0800292 clk_dm(i, &clk_scmi->clk);
293
294 if (CLK_HAS_RESTRICTIONS(attributes)) {
295 u32 perm;
296
297 ret = scmi_clk_get_permissions(dev, i, &perm);
298 if (ret < 0)
299 clk_scmi->ctrl_flags = 0;
300 else
301 clk_scmi->ctrl_flags = perm;
302 } else {
303 clk_scmi->ctrl_flags = SUPPORT_CLK_STAT_CONTROL | SUPPORT_CLK_PARENT_CONTROL |
304 SUPPORT_CLK_RATE_CONTROL;
305 }
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100306 }
307 }
308
309 return 0;
310}
311
Alice Guo26d0ed02025-04-28 18:37:31 +0800312static int __scmi_clk_set_parent(struct clk *clk, struct clk *parent)
Peng Fan3b5eaad2025-04-28 18:37:30 +0800313{
314 struct scmi_clk_parent_set_in in = {
315 .clock_id = clk->id,
316 .parent_clk = parent->id,
317 };
318 struct scmi_clk_parent_set_out out;
319 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
320 SCMI_CLOCK_PARENT_SET,
321 in, out);
322 int ret;
323
324 ret = devm_scmi_process_msg(clk->dev, &msg);
325 if (ret < 0)
326 return ret;
327
328 return scmi_to_linux_errno(out.status);
329}
330
Alice Guo26d0ed02025-04-28 18:37:31 +0800331static int scmi_clk_set_parent(struct clk *clk, struct clk *parent)
332{
333 struct clk_scmi *clkscmi;
334 struct clk *c;
335 int ret;
336
337 if (!CONFIG_IS_ENABLED(CLK_CCF))
338 return -ENOTSUPP;
339
340 ret = clk_get_by_id(clk->id, &c);
341 if (ret)
342 return ret;
343
344 clkscmi = container_of(c, struct clk_scmi, clk);
345
346 if (clkscmi->ctrl_flags & SUPPORT_CLK_PARENT_CONTROL)
347 return __scmi_clk_set_parent(clk, parent);
348
349 /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
350 log_debug("%s: SCMI CLOCK: the clock's parent cannot be changed by the agent.\n", __func__);
351 return 0;
352}
353
Etienne Carriere78928e12020-09-09 18:44:04 +0200354static const struct clk_ops scmi_clk_ops = {
355 .enable = scmi_clk_enable,
356 .disable = scmi_clk_disable,
357 .get_rate = scmi_clk_get_rate,
358 .set_rate = scmi_clk_set_rate,
Peng Fan3b5eaad2025-04-28 18:37:30 +0800359 .set_parent = scmi_clk_set_parent,
Etienne Carriere78928e12020-09-09 18:44:04 +0200360};
361
362U_BOOT_DRIVER(scmi_clock) = {
363 .name = "scmi_clk",
364 .id = UCLASS_CLK,
365 .ops = &scmi_clk_ops,
Etienne Carriere528f9882022-05-31 18:09:25 +0200366 .probe = scmi_clk_probe,
Alice Guo26d0ed02025-04-28 18:37:31 +0800367 .priv_auto = sizeof(struct scmi_clock_priv),
Etienne Carriere78928e12020-09-09 18:44:04 +0200368};
Alice Guof71894b2025-04-28 18:37:25 +0800369
370static struct scmi_proto_match match[] = {
371 { .proto_id = SCMI_PROTOCOL_ID_CLOCK },
372 { /* Sentinel */ }
373};
374
375U_BOOT_SCMI_PROTO_DRIVER(scmi_clock, match);