blob: cfb372e619049f02a86738bd4c5fc194cfa828f0 [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{
Valentin Caronc81e91b2025-05-27 15:27:43 +020087 struct scmi_clock_priv *priv = dev_get_priv(dev);
Etienne Carriere4c4ec902022-02-21 09:22:42 +010088 struct scmi_clk_attribute_in in = {
89 .clock_id = clkid,
90 };
Etienne Carriere4c4ec902022-02-21 09:22:42 +010091 int ret;
92
Valentin Caronc81e91b2025-05-27 15:27:43 +020093 if (priv->version >= 0x20000) {
94 struct scmi_clk_attribute_out_v2 out;
95 struct scmi_msg msg = {
96 .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
97 .message_id = SCMI_CLOCK_ATTRIBUTES,
98 .in_msg = (u8 *)&in,
99 .in_msg_sz = sizeof(in),
100 .out_msg = (u8 *)&out,
101 .out_msg_sz = sizeof(out),
102 };
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100103
Valentin Caronc81e91b2025-05-27 15:27:43 +0200104 ret = devm_scmi_process_msg(dev, &msg);
105 if (ret)
106 return ret;
107
108 *name = strdup(out.clock_name);
109 *attr = out.attributes;
110 } else {
111 struct scmi_clk_attribute_out out;
112 struct scmi_msg msg = {
113 .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
114 .message_id = SCMI_CLOCK_ATTRIBUTES,
115 .in_msg = (u8 *)&in,
116 .in_msg_sz = sizeof(in),
117 .out_msg = (u8 *)&out,
118 .out_msg_sz = sizeof(out),
119 };
120
121 ret = devm_scmi_process_msg(dev, &msg);
122 if (ret)
123 return ret;
124
125 *name = strdup(out.clock_name);
126 *attr = out.attributes;
127 }
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100128
129 return 0;
130}
Etienne Carriere78928e12020-09-09 18:44:04 +0200131
132static int scmi_clk_gate(struct clk *clk, int enable)
133{
134 struct scmi_clk_state_in in = {
Patrick Delaunay85b9ddc2025-05-27 15:27:50 +0200135 .clock_id = clk_get_id(clk),
Etienne Carriere78928e12020-09-09 18:44:04 +0200136 .attributes = enable,
137 };
138 struct scmi_clk_state_out out;
139 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
140 SCMI_CLOCK_CONFIG_SET,
141 in, out);
142 int ret;
143
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900144 ret = devm_scmi_process_msg(clk->dev, &msg);
Etienne Carriere78928e12020-09-09 18:44:04 +0200145 if (ret)
146 return ret;
147
148 return scmi_to_linux_errno(out.status);
149}
150
151static int scmi_clk_enable(struct clk *clk)
152{
Alice Guo26d0ed02025-04-28 18:37:31 +0800153 struct clk_scmi *clkscmi;
154 struct clk *c;
155 int ret;
156
157 if (!CONFIG_IS_ENABLED(CLK_CCF))
158 return scmi_clk_gate(clk, 1);
159
160 ret = clk_get_by_id(clk->id, &c);
161 if (ret)
162 return ret;
163
164 clkscmi = container_of(c, struct clk_scmi, clk);
165
166 if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
167 return scmi_clk_gate(clk, 1);
168
169 /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
170 log_debug("%s: SCMI CLOCK: the clock cannot be enabled by the agent.\n", __func__);
171 return 0;
Etienne Carriere78928e12020-09-09 18:44:04 +0200172}
173
174static int scmi_clk_disable(struct clk *clk)
175{
Alice Guo26d0ed02025-04-28 18:37:31 +0800176 struct clk_scmi *clkscmi;
177 struct clk *c;
178 int ret;
179
180 if (!CONFIG_IS_ENABLED(CLK_CCF))
181 return scmi_clk_gate(clk, 0);
182
183 ret = clk_get_by_id(clk->id, &c);
184 if (ret)
185 return ret;
186
187 clkscmi = container_of(c, struct clk_scmi, clk);
188
189 if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
190 return scmi_clk_gate(clk, 0);
191
192 /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
193 log_debug("%s: SCMI CLOCK: the clock cannot be disabled by the agent.\n", __func__);
194 return 0;
Etienne Carriere78928e12020-09-09 18:44:04 +0200195}
196
197static ulong scmi_clk_get_rate(struct clk *clk)
198{
199 struct scmi_clk_rate_get_in in = {
Patrick Delaunay85b9ddc2025-05-27 15:27:50 +0200200 .clock_id = clk_get_id(clk),
Etienne Carriere78928e12020-09-09 18:44:04 +0200201 };
202 struct scmi_clk_rate_get_out out;
203 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
204 SCMI_CLOCK_RATE_GET,
205 in, out);
206 int ret;
207
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900208 ret = devm_scmi_process_msg(clk->dev, &msg);
Etienne Carriere78928e12020-09-09 18:44:04 +0200209 if (ret < 0)
210 return ret;
211
212 ret = scmi_to_linux_errno(out.status);
213 if (ret < 0)
214 return ret;
215
216 return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
217}
218
Alice Guo26d0ed02025-04-28 18:37:31 +0800219static ulong __scmi_clk_set_rate(struct clk *clk, ulong rate)
Etienne Carriere78928e12020-09-09 18:44:04 +0200220{
221 struct scmi_clk_rate_set_in in = {
Patrick Delaunay85b9ddc2025-05-27 15:27:50 +0200222 .clock_id = clk_get_id(clk),
Etienne Carriere78928e12020-09-09 18:44:04 +0200223 .flags = SCMI_CLK_RATE_ROUND_CLOSEST,
224 .rate_lsb = (u32)rate,
225 .rate_msb = (u32)((u64)rate >> 32),
226 };
227 struct scmi_clk_rate_set_out out;
228 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
229 SCMI_CLOCK_RATE_SET,
230 in, out);
231 int ret;
232
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900233 ret = devm_scmi_process_msg(clk->dev, &msg);
Etienne Carriere78928e12020-09-09 18:44:04 +0200234 if (ret < 0)
235 return ret;
236
237 ret = scmi_to_linux_errno(out.status);
238 if (ret < 0)
239 return ret;
240
241 return scmi_clk_get_rate(clk);
242}
243
Alice Guo26d0ed02025-04-28 18:37:31 +0800244static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
245{
246 struct clk_scmi *clkscmi;
247 struct clk *c;
248 int ret;
249
250 if (!CONFIG_IS_ENABLED(CLK_CCF))
251 return __scmi_clk_set_rate(clk, rate);
252
253 ret = clk_get_by_id(clk->id, &c);
254 if (ret)
255 return ret;
256
257 clkscmi = container_of(c, struct clk_scmi, clk);
258
259 if (clkscmi->ctrl_flags & SUPPORT_CLK_RATE_CONTROL)
260 return __scmi_clk_set_rate(clk, rate);
261
262 /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
263 log_debug("%s: SCMI CLOCK: the clock rate cannot be changed by the agent.\n", __func__);
264 return 0;
265}
266
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100267static int scmi_clk_probe(struct udevice *dev)
268{
Alice Guo26d0ed02025-04-28 18:37:31 +0800269 struct clk_scmi *clk_scmi;
270 struct scmi_clock_priv *priv = dev_get_priv(dev);
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100271 size_t num_clocks, i;
272 int ret;
273
AKASHI Takahiro7b3aa372023-10-11 19:06:54 +0900274 ret = devm_scmi_of_get_channel(dev);
Etienne Carriere528f9882022-05-31 18:09:25 +0200275 if (ret)
276 return ret;
277
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100278 if (!CONFIG_IS_ENABLED(CLK_CCF))
279 return 0;
280
Valentin Caronc81e91b2025-05-27 15:27:43 +0200281 ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_CLOCK,
282 &priv->version);
283
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100284 /* register CCF children: CLK UCLASS, no probed again */
285 if (device_get_uclass_id(dev->parent) == UCLASS_CLK)
286 return 0;
287
288 ret = scmi_clk_get_num_clock(dev, &num_clocks);
289 if (ret)
290 return ret;
291
Alice Guo26d0ed02025-04-28 18:37:31 +0800292 ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_CLOCK, &priv->version);
293 if (ret) {
294 log_debug("%s: get SCMI clock management protocol version failed\n", __func__);
295 return ret;
296 }
297
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100298 for (i = 0; i < num_clocks; i++) {
Heinrich Schuchardtba9c8922022-04-26 23:26:31 +0200299 char *clock_name;
Alice Guo26d0ed02025-04-28 18:37:31 +0800300 u32 attributes;
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100301
Alice Guo26d0ed02025-04-28 18:37:31 +0800302 if (!scmi_clk_get_attibute(dev, i, &clock_name, &attributes)) {
303 clk_scmi = kzalloc(sizeof(*clk_scmi), GFP_KERNEL);
304 if (!clk_scmi || !clock_name)
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100305 ret = -ENOMEM;
306 else
Alice Guo26d0ed02025-04-28 18:37:31 +0800307 ret = clk_register(&clk_scmi->clk, dev->driver->name,
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100308 clock_name, dev->name);
309
310 if (ret) {
Alice Guo26d0ed02025-04-28 18:37:31 +0800311 free(clk_scmi);
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100312 free(clock_name);
313 return ret;
314 }
315
Patrick Delaunay85b9ddc2025-05-27 15:27:50 +0200316 dev_clk_dm(dev, i, &clk_scmi->clk);
Alice Guo26d0ed02025-04-28 18:37:31 +0800317
318 if (CLK_HAS_RESTRICTIONS(attributes)) {
319 u32 perm;
320
321 ret = scmi_clk_get_permissions(dev, i, &perm);
322 if (ret < 0)
323 clk_scmi->ctrl_flags = 0;
324 else
325 clk_scmi->ctrl_flags = perm;
326 } else {
327 clk_scmi->ctrl_flags = SUPPORT_CLK_STAT_CONTROL | SUPPORT_CLK_PARENT_CONTROL |
328 SUPPORT_CLK_RATE_CONTROL;
329 }
Etienne Carriere4c4ec902022-02-21 09:22:42 +0100330 }
331 }
332
333 return 0;
334}
335
Alice Guo26d0ed02025-04-28 18:37:31 +0800336static int __scmi_clk_set_parent(struct clk *clk, struct clk *parent)
Peng Fan3b5eaad2025-04-28 18:37:30 +0800337{
338 struct scmi_clk_parent_set_in in = {
339 .clock_id = clk->id,
340 .parent_clk = parent->id,
341 };
342 struct scmi_clk_parent_set_out out;
343 struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
344 SCMI_CLOCK_PARENT_SET,
345 in, out);
346 int ret;
347
348 ret = devm_scmi_process_msg(clk->dev, &msg);
349 if (ret < 0)
350 return ret;
351
352 return scmi_to_linux_errno(out.status);
353}
354
Alice Guo26d0ed02025-04-28 18:37:31 +0800355static int scmi_clk_set_parent(struct clk *clk, struct clk *parent)
356{
357 struct clk_scmi *clkscmi;
358 struct clk *c;
359 int ret;
360
361 if (!CONFIG_IS_ENABLED(CLK_CCF))
362 return -ENOTSUPP;
363
364 ret = clk_get_by_id(clk->id, &c);
365 if (ret)
366 return ret;
367
368 clkscmi = container_of(c, struct clk_scmi, clk);
369
370 if (clkscmi->ctrl_flags & SUPPORT_CLK_PARENT_CONTROL)
371 return __scmi_clk_set_parent(clk, parent);
372
373 /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
374 log_debug("%s: SCMI CLOCK: the clock's parent cannot be changed by the agent.\n", __func__);
375 return 0;
376}
377
Etienne Carriere78928e12020-09-09 18:44:04 +0200378static const struct clk_ops scmi_clk_ops = {
379 .enable = scmi_clk_enable,
380 .disable = scmi_clk_disable,
381 .get_rate = scmi_clk_get_rate,
382 .set_rate = scmi_clk_set_rate,
Peng Fan3b5eaad2025-04-28 18:37:30 +0800383 .set_parent = scmi_clk_set_parent,
Etienne Carriere78928e12020-09-09 18:44:04 +0200384};
385
386U_BOOT_DRIVER(scmi_clock) = {
387 .name = "scmi_clk",
388 .id = UCLASS_CLK,
389 .ops = &scmi_clk_ops,
Etienne Carriere528f9882022-05-31 18:09:25 +0200390 .probe = scmi_clk_probe,
Alice Guo26d0ed02025-04-28 18:37:31 +0800391 .priv_auto = sizeof(struct scmi_clock_priv),
Etienne Carriere78928e12020-09-09 18:44:04 +0200392};
Alice Guof71894b2025-04-28 18:37:25 +0800393
394static struct scmi_proto_match match[] = {
395 { .proto_id = SCMI_PROTOCOL_ID_CLOCK },
396 { /* Sentinel */ }
397};
398
399U_BOOT_SCMI_PROTO_DRIVER(scmi_clock, match);