1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
5 * Copyright (c) 2023 Arm Ltd
6 *
7 * This work was supported by Innovate UK project 105694, "Digital Security
8 * by Design (DSbD) Technology Platform Prototype".
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/cpu.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38
39 #include <dev/clk/clk.h>
40 #include <dev/fdt/simplebus.h>
41 #include <dev/fdt/fdt_common.h>
42 #include <dev/ofw/ofw_bus_subr.h>
43
44 #include "scmi.h"
45 #include "scmi_protocols.h"
46 #include "scmi_clk.h"
47
48 struct scmi_clk_softc {
49 device_t dev;
50 device_t scmi;
51 struct clkdom *clkdom;
52 };
53
54 struct scmi_clknode_softc {
55 device_t dev;
56 int clock_id;
57 };
58
59 static int
scmi_clk_get_rate(struct scmi_clk_softc * sc,int clk_id,uint64_t * rate)60 scmi_clk_get_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t *rate)
61 {
62 struct scmi_clk_rate_get_out *out;
63 struct scmi_clk_rate_get_in *in;
64 int error;
65
66 in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
67 SCMI_CLOCK_RATE_GET, sizeof(*in), sizeof(*out));
68 if (in == NULL)
69 return (ENXIO);
70
71 in->clock_id = clk_id;
72 error = scmi_request(sc->scmi, in, (void **)&out);
73 if (error == 0)
74 *rate = out->rate_lsb | ((uint64_t)out->rate_msb << 32);
75
76 scmi_buf_put(sc->scmi, in);
77
78 return (error);
79 }
80
81 static int
scmi_clk_set_rate(struct scmi_clk_softc * sc,int clk_id,uint64_t rate)82 scmi_clk_set_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t rate)
83 {
84 struct scmi_clk_rate_set_in *in;
85 void *out;
86 int error;
87
88 in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
89 SCMI_CLOCK_RATE_SET, sizeof(*in), 0);
90 if (in == NULL)
91 return (ENXIO);
92
93 in->clock_id = clk_id;
94 in->flags = SCMI_CLK_RATE_ROUND_CLOSEST;
95 in->rate_lsb = (uint32_t)rate;
96 in->rate_msb = (uint32_t)(rate >> 32);
97
98 error = scmi_request(sc->scmi, in, &out);
99
100 scmi_buf_put(sc->scmi, in);
101
102 return (error);
103 }
104
105 static int __unused
scmi_clk_gate(struct scmi_clk_softc * sc,int clk_id,int enable)106 scmi_clk_gate(struct scmi_clk_softc *sc, int clk_id, int enable)
107 {
108 struct scmi_clk_state_in *in;
109 void *out;
110 int error;
111
112 in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
113 SCMI_CLOCK_CONFIG_SET, sizeof(*in), 0);
114 if (in == NULL)
115 return (ENXIO);
116
117 in->clock_id = clk_id;
118 in->attributes = enable;
119 error = scmi_request(sc->scmi, in, &out);
120
121 scmi_buf_put(sc->scmi, in);
122
123 return (error);
124 }
125
126 static int
scmi_clknode_init(struct clknode * clk,device_t dev)127 scmi_clknode_init(struct clknode *clk, device_t dev)
128 {
129
130 clknode_init_parent_idx(clk, 0);
131
132 return (0);
133 }
134
135 static int
scmi_clknode_recalc_freq(struct clknode * clk,uint64_t * freq)136 scmi_clknode_recalc_freq(struct clknode *clk, uint64_t *freq)
137 {
138 struct scmi_clknode_softc *clk_sc;
139 struct scmi_clk_softc *sc;
140 uint64_t rate;
141 int ret;
142
143 clk_sc = clknode_get_softc(clk);
144 sc = device_get_softc(clk_sc->dev);
145 ret = scmi_clk_get_rate(sc, clk_sc->clock_id, &rate);
146 if (ret == 0)
147 *freq = rate;
148
149 return (ret);
150 }
151
152 static int
scmi_clknode_set_freq(struct clknode * clk,uint64_t fin,uint64_t * fout,int flags,int * stop)153 scmi_clknode_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
154 int flags, int *stop)
155 {
156 struct scmi_clknode_softc *clk_sc;
157 struct scmi_clk_softc *sc;
158
159 clk_sc = clknode_get_softc(clk);
160 sc = device_get_softc(clk_sc->dev);
161
162 scmi_clk_set_rate(sc, clk_sc->clock_id, *fout);
163
164 *stop = 1;
165
166 return (0);
167 }
168
169 static clknode_method_t scmi_clknode_methods[] = {
170 /* Device interface */
171 CLKNODEMETHOD(clknode_init, scmi_clknode_init),
172 CLKNODEMETHOD(clknode_recalc_freq, scmi_clknode_recalc_freq),
173 CLKNODEMETHOD(clknode_set_freq, scmi_clknode_set_freq),
174 CLKNODEMETHOD_END
175 };
176
177 DEFINE_CLASS_1(scmi_clknode, scmi_clknode_class, scmi_clknode_methods,
178 sizeof(struct scmi_clknode_softc), clknode_class);
179
180 static int
scmi_clk_add_node(struct scmi_clk_softc * sc,int index,char * clock_name)181 scmi_clk_add_node(struct scmi_clk_softc *sc, int index, char *clock_name)
182 {
183 struct scmi_clknode_softc *clk_sc;
184 struct clknode_init_def def;
185 struct clknode *clk;
186
187 memset(&def, 0, sizeof(def));
188 def.id = index;
189 def.name = clock_name;
190 def.parent_names = NULL;
191 def.parent_cnt = 0;
192
193 clk = clknode_create(sc->clkdom, &scmi_clknode_class, &def);
194 if (clk == NULL) {
195 device_printf(sc->dev, "Cannot create clknode.\n");
196 return (ENXIO);
197 }
198
199 clk_sc = clknode_get_softc(clk);
200 clk_sc->dev = sc->dev;
201 clk_sc->clock_id = index;
202
203 if (clknode_register(sc->clkdom, clk) == NULL) {
204 device_printf(sc->dev, "Could not register clock '%s'.\n",
205 def.name);
206 return (ENXIO);
207 }
208
209 device_printf(sc->dev, "Clock '%s' registered.\n", def.name);
210
211 return (0);
212 }
213
214 static int
scmi_clk_get_name(struct scmi_clk_softc * sc,int index,char ** result)215 scmi_clk_get_name(struct scmi_clk_softc *sc, int index, char **result)
216 {
217 struct scmi_clk_name_get_out *out;
218 struct scmi_clk_name_get_in *in;
219 int error;
220
221 in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
222 SCMI_CLOCK_NAME_GET, sizeof(*in), sizeof(*out));
223 if (in == NULL)
224 return (ENXIO);
225
226 in->clock_id = index;
227 error = scmi_request(sc->scmi, in, (void **)&out);
228 if (error == 0) {
229 char *clock_name;
230
231 clock_name = malloc(sizeof(out->name), M_DEVBUF, M_WAITOK);
232 strncpy(clock_name, out->name, sizeof(out->name));
233 *result = clock_name;
234 }
235
236 scmi_buf_put(sc->scmi, in);
237
238 return (error);
239 }
240
241 static int
scmi_clk_attrs(struct scmi_clk_softc * sc,int index)242 scmi_clk_attrs(struct scmi_clk_softc *sc, int index)
243 {
244 struct scmi_clk_attrs_out *out;
245 struct scmi_clk_attrs_in *in;
246 char *clock_name;
247 int error;
248
249 in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
250 SCMI_CLOCK_ATTRIBUTES, sizeof(*in), sizeof(*out));
251 if (in == NULL)
252 return (ENXIO);
253
254 in->clock_id = index;
255 error = scmi_request(sc->scmi, in, (void **)&out);
256 if (error == 0) {
257 if (out->attributes & CLK_ATTRS_EXT_CLK_NAME) {
258 error = scmi_clk_get_name(sc, index, &clock_name);
259 } else {
260 clock_name = malloc(sizeof(out->clock_name),
261 M_DEVBUF, M_WAITOK);
262 strncpy(clock_name, out->clock_name,
263 sizeof(out->clock_name));
264 }
265
266 if (error == 0)
267 error = scmi_clk_add_node(sc, index, clock_name);
268 }
269
270 scmi_buf_put(sc->scmi, in);
271
272 return (error);
273 }
274
275 static int
scmi_clk_discover(struct scmi_clk_softc * sc)276 scmi_clk_discover(struct scmi_clk_softc *sc)
277 {
278 struct scmi_clk_protocol_attrs_out *out;
279 void *in;
280 int nclocks;
281 int failing;
282 int error;
283 int i;
284
285 in = scmi_buf_get(sc->scmi, SCMI_PROTOCOL_ID_CLOCK,
286 SCMI_PROTOCOL_ATTRIBUTES, 0, sizeof(*out));
287 if (in == NULL)
288 return (ENXIO);
289
290 error = scmi_request(sc->scmi, in, (void **)&out);
291 if (error == 0) {
292 nclocks = (out->attributes & CLK_ATTRS_NCLOCKS_M) >>
293 CLK_ATTRS_NCLOCKS_S;
294
295 device_printf(sc->dev, "Found %d clocks.\n", nclocks);
296
297 failing = 0;
298
299 for (i = 0; i < nclocks; i++) {
300 error = scmi_clk_attrs(sc, i);
301 if (error) {
302 device_printf(sc->dev,
303 "Could not process clock index %d.\n", i);
304 failing++;
305 error = 0;
306 }
307 }
308 if (failing == nclocks)
309 error = ENXIO;
310 } else {
311 error = ENXIO;
312 }
313
314 scmi_buf_put(sc->scmi, in);
315
316 return (error);
317 }
318
319 static int
scmi_clk_init(struct scmi_clk_softc * sc)320 scmi_clk_init(struct scmi_clk_softc *sc)
321 {
322 int error;
323
324 /* Create clock domain */
325 sc->clkdom = clkdom_create(sc->dev);
326 if (sc->clkdom == NULL)
327 return (ENXIO);
328
329 error = scmi_clk_discover(sc);
330 if (error) {
331 device_printf(sc->dev, "Could not discover clocks.\n");
332 return (ENXIO);
333 }
334
335 error = clkdom_finit(sc->clkdom);
336 if (error) {
337 device_printf(sc->dev, "Failed to init clock domain.\n");
338 return (ENXIO);
339 }
340
341 return (0);
342 }
343
344 static int
scmi_clk_probe(device_t dev)345 scmi_clk_probe(device_t dev)
346 {
347 phandle_t node;
348 uint32_t reg;
349 int error;
350
351 node = ofw_bus_get_node(dev);
352
353 error = OF_getencprop(node, "reg", ®, sizeof(uint32_t));
354 if (error < 0)
355 return (ENXIO);
356
357 if (reg != SCMI_PROTOCOL_ID_CLOCK)
358 return (ENXIO);
359
360 device_set_desc(dev, "SCMI Clock Management Unit");
361
362 return (BUS_PROBE_DEFAULT);
363 }
364
365 static int
scmi_clk_attach(device_t dev)366 scmi_clk_attach(device_t dev)
367 {
368 struct scmi_clk_softc *sc;
369 phandle_t node;
370
371 sc = device_get_softc(dev);
372 sc->dev = dev;
373 sc->scmi = device_get_parent(dev);
374
375 node = ofw_bus_get_node(sc->dev);
376
377 OF_device_register_xref(OF_xref_from_node(node), sc->dev);
378
379 scmi_clk_init(sc);
380
381 return (0);
382 }
383
384 static int
scmi_clk_detach(device_t dev)385 scmi_clk_detach(device_t dev)
386 {
387
388 return (0);
389 }
390
391 static device_method_t scmi_clk_methods[] = {
392 /* Device interface */
393 DEVMETHOD(device_probe, scmi_clk_probe),
394 DEVMETHOD(device_attach, scmi_clk_attach),
395 DEVMETHOD(device_detach, scmi_clk_detach),
396 DEVMETHOD_END
397 };
398
399 static driver_t scmi_clk_driver = {
400 "scmi_clk",
401 scmi_clk_methods,
402 sizeof(struct scmi_clk_softc),
403 };
404
405 EARLY_DRIVER_MODULE(scmi_clk, scmi, scmi_clk_driver, 0, 0,
406 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
407 MODULE_VERSION(scmi_clk, 1);
408