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