xref: /freebsd/sys/dev/firmware/arm/scmi_clk.c (revision 35f93203)
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", &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