xref: /openbsd/sys/dev/fdt/scmi.c (revision 44683af5)
1 /*	$OpenBSD: scmi.c,v 1.2 2024/11/25 22:12:18 tobhe Exp $	*/
2 
3 /*
4  * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
5  * Copyright (c) 2024 Tobias Heider <tobhe@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/device.h>
22 #include <sys/systm.h>
23 #include <sys/malloc.h>
24 #include <sys/sensors.h>
25 #include <sys/sysctl.h>
26 
27 #include <machine/bus.h>
28 #include <machine/fdt.h>
29 
30 #include <dev/ofw/openfirm.h>
31 #include <dev/ofw/ofw_clock.h>
32 #include <dev/ofw/ofw_misc.h>
33 #include <dev/ofw/fdt.h>
34 
35 #include <dev/fdt/pscivar.h>
36 
37 struct scmi_shmem {
38 	uint32_t reserved1;
39 	uint32_t channel_status;
40 #define SCMI_CHANNEL_ERROR		(1 << 1)
41 #define SCMI_CHANNEL_FREE		(1 << 0)
42 	uint32_t reserved2;
43 	uint32_t reserved3;
44 	uint32_t channel_flags;
45 	uint32_t length;
46 	uint32_t message_header;
47 	uint32_t message_payload[];
48 };
49 
50 #define SCMI_SUCCESS		0
51 #define SCMI_NOT_SUPPORTED	-1
52 #define SCMI_BUSY		-6
53 #define SCMI_COMMS_ERROR	-7
54 
55 /* Protocols */
56 #define SCMI_BASE		0x10
57 #define SCMI_PERF		0x13
58 #define SCMI_CLOCK		0x14
59 
60 /* Common messages */
61 #define SCMI_PROTOCOL_VERSION			0x0
62 #define SCMI_PROTOCOL_ATTRIBUTES		0x1
63 #define SCMI_PROTOCOL_MESSAGE_ATTRIBUTES	0x2
64 
65 /* Clock management messages */
66 #define SCMI_CLOCK_ATTRIBUTES			0x3
67 #define SCMI_CLOCK_DESCRIBE_RATES		0x4
68 #define SCMI_CLOCK_RATE_SET			0x5
69 #define SCMI_CLOCK_RATE_GET			0x6
70 #define SCMI_CLOCK_CONFIG_SET			0x7
71 #define  SCMI_CLOCK_CONFIG_SET_ENABLE		(1 << 0)
72 
73 /* Performance management messages */
74 #define SCMI_PERF_DOMAIN_ATTRIBUTES		0x3
75 #define SCMI_PERF_DESCRIBE_LEVELS		0x4
76 #define SCMI_PERF_LEVEL_GET			0x8
77 
78 struct scmi_resp_perf_describe_levels_40 {
79 	uint16_t pl_nret;
80 	uint16_t pl_nrem;
81 	struct {
82 		uint32_t	pe_perf;
83 		uint32_t	pe_cost;
84 		uint16_t	pe_latency;
85 		uint16_t	pe_reserved;
86 		uint32_t	pe_ifreq;
87 		uint32_t	pe_lindex;
88 	} pl_entry[];
89 };
90 
91 static inline void
scmi_message_header(volatile struct scmi_shmem * shmem,uint32_t protocol_id,uint32_t message_id)92 scmi_message_header(volatile struct scmi_shmem *shmem,
93     uint32_t protocol_id, uint32_t message_id)
94 {
95 	shmem->message_header = (protocol_id << 10) | (message_id << 0);
96 }
97 
98 struct scmi_perf_level {
99 	uint32_t	pl_perf;
100 	uint32_t	pl_cost;
101 	uint32_t	pl_ifreq;
102 };
103 
104 struct scmi_perf_domain {
105 	size_t				pd_nlevels;
106 	struct scmi_perf_level		*pd_levels;
107 	int				pd_curlevel;
108 };
109 
110 struct scmi_softc {
111 	struct device			sc_dev;
112 	bus_space_tag_t			sc_iot;
113 	int				sc_node;
114 
115 	bus_space_handle_t		sc_ioh_tx;
116 	bus_space_handle_t		sc_ioh_rx;
117 	volatile struct scmi_shmem	*sc_shmem_tx;
118 	volatile struct scmi_shmem	*sc_shmem_rx;
119 
120 	uint32_t			sc_smc_id;
121 	struct mbox_channel		*sc_mc_tx;
122 	struct mbox_channel		*sc_mc_rx;
123 
124 	uint16_t			sc_ver_major;
125 	uint16_t			sc_ver_minor;
126 
127 	/* SCMI_CLOCK */
128 	struct clock_device		sc_cd;
129 
130 	/* SCMI_PERF */
131 	int				sc_perf_power_unit;
132 #define SCMI_POWER_UNIT_UW	0x2
133 #define SCMI_POWER_UNIT_MW	0x1
134 #define SCMI_POWER_UNIT_NONE	0x0
135 	size_t				sc_perf_ndomains;
136 	struct scmi_perf_domain		*sc_perf_domains;
137 
138 	struct ksensordev		sc_perf_sensordev;
139 	struct ksensordev		sc_perf_psensordev;
140 	struct ksensor			*sc_perf_fsensors;
141 	struct ksensor			*sc_perf_psensors;
142 
143 	int32_t				(*sc_command)(struct scmi_softc *);
144 };
145 
146 int	scmi_match(struct device *, void *, void *);
147 void	scmi_attach(struct device *, struct device *, void *);
148 int	scmi_attach_smc(struct scmi_softc *, struct fdt_attach_args *);
149 void	scmi_attach_mbox_deferred(struct device *);
150 
151 const struct cfattach scmi_ca = {
152 	sizeof(struct scmi_softc), scmi_match, scmi_attach
153 };
154 
155 struct cfdriver scmi_cd = {
156 	NULL, "scmi", DV_DULL
157 };
158 
159 void	scmi_attach_proto(struct scmi_softc *, int);
160 void	scmi_attach_clock(struct scmi_softc *, int);
161 void	scmi_attach_perf(struct scmi_softc *, int);
162 
163 int32_t	scmi_smc_command(struct scmi_softc *);
164 int32_t	scmi_mbox_command(struct scmi_softc *);
165 
166 int
scmi_match(struct device * parent,void * match,void * aux)167 scmi_match(struct device *parent, void *match, void *aux)
168 {
169 	struct fdt_attach_args *faa = aux;
170 
171 	return OF_is_compatible(faa->fa_node, "arm,scmi-smc") ||
172 	    OF_is_compatible(faa->fa_node, "arm,scmi");
173 }
174 
175 void
scmi_attach(struct device * parent,struct device * self,void * aux)176 scmi_attach(struct device *parent, struct device *self, void *aux)
177 {
178 	struct scmi_softc *sc = (struct scmi_softc *)self;
179 	struct fdt_attach_args *faa = aux;
180 
181 	sc->sc_iot = faa->fa_iot;
182 	sc->sc_node = faa->fa_node;
183 
184 	if (OF_is_compatible(faa->fa_node, "arm,scmi-smc")) {
185 		scmi_attach_smc(sc, faa);
186 	} else if (OF_is_compatible(faa->fa_node, "arm,scmi")) {
187 		printf("\n");
188 		/* Defer because we need the mailbox driver attached first */
189 		config_defer(self, scmi_attach_mbox_deferred);
190 	}
191 }
192 
193 int
scmi_attach_smc(struct scmi_softc * sc,struct fdt_attach_args * faa)194 scmi_attach_smc(struct scmi_softc *sc, struct fdt_attach_args *faa)
195 {
196 	volatile struct scmi_shmem *shmem;
197 	struct fdt_reg reg;
198 	int32_t status;
199 	uint32_t version;
200 	uint32_t phandle;
201 	void *node;
202 	int proto;
203 
204 	sc->sc_smc_id = OF_getpropint(faa->fa_node, "arm,smc-id", 0);
205 	if (sc->sc_smc_id == 0) {
206 		printf(": no SMC id\n");
207 		return -1;
208 	}
209 
210 	phandle = OF_getpropint(faa->fa_node, "shmem", 0);
211 	node = fdt_find_phandle(phandle);
212 	if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
213 	    fdt_get_reg(node, 0, &reg)) {
214 		printf(": no shared memory\n");
215 		return -1;
216 	}
217 
218 	if (bus_space_map(sc->sc_iot, reg.addr,
219 	    reg.size, 0, &sc->sc_ioh_tx)) {
220 		printf(": can't map shared memory\n");
221 		return -1;
222 	}
223 	sc->sc_shmem_tx = bus_space_vaddr(sc->sc_iot, sc->sc_ioh_tx);
224 	shmem = sc->sc_shmem_tx;
225 
226 	sc->sc_command = scmi_smc_command;
227 
228 	if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0) {
229 		printf(": channel busy\n");
230 		return -1;
231 	}
232 
233 	scmi_message_header(shmem, SCMI_BASE, SCMI_PROTOCOL_VERSION);
234 	shmem->length = sizeof(uint32_t);
235 	status = sc->sc_command(sc);
236 	if (status != SCMI_SUCCESS) {
237 		printf(": protocol version command failed\n");
238 		return -1;
239 	}
240 
241 	version = shmem->message_payload[1];
242 	sc->sc_ver_major = version >> 16;
243 	sc->sc_ver_minor = version & 0xfffff;
244 	printf(": SCMI %d.%d\n", sc->sc_ver_major, sc->sc_ver_minor);
245 
246 	for (proto = OF_child(faa->fa_node); proto; proto = OF_peer(proto))
247 		scmi_attach_proto(sc, proto);
248 
249 	return 0;
250 }
251 
252 void
scmi_attach_mbox_deferred(struct device * self)253 scmi_attach_mbox_deferred(struct device *self)
254 {
255 	struct scmi_softc *sc = (struct scmi_softc *)self;
256 	uint32_t *shmems;
257 	int32_t status;
258 	uint32_t version;
259 	struct fdt_reg reg;
260 	int len;
261 	void *node;
262 	int proto;
263 
264 	/* we only support the 2 mbox / 2 shmem case */
265 	len = OF_getproplen(sc->sc_node, "mboxes");
266 	if (len != 4 * sizeof(uint32_t)) {
267 		printf("%s: invalid number of mboxes\n", sc->sc_dev.dv_xname);
268 		return;
269 	}
270 
271 	len = OF_getproplen(sc->sc_node, "shmem");
272 	if (len != 2 * sizeof(uint32_t)) {
273 		printf("%s: invalid number of shmems\n", sc->sc_dev.dv_xname);
274 		return;
275 	}
276 
277 	shmems = malloc(len, M_DEVBUF, M_WAITOK);
278 	OF_getpropintarray(sc->sc_node, "shmem", shmems, len);
279 
280 	sc->sc_mc_tx = mbox_channel(sc->sc_node, "tx", NULL);
281 	if (sc->sc_mc_tx == NULL) {
282 		printf("%s: no tx mbox\n", sc->sc_dev.dv_xname);
283 		return;
284 	}
285 	sc->sc_mc_rx = mbox_channel(sc->sc_node, "rx", NULL);
286 	if (sc->sc_mc_rx == NULL) {
287 		printf("%s: no rx mbox\n", sc->sc_dev.dv_xname);
288 		return;
289 	}
290 
291 	node = fdt_find_phandle(shmems[0]);
292 	if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
293 	    fdt_get_reg(node, 0, &reg)) {
294 		printf("%s: no shared memory\n", sc->sc_dev.dv_xname);
295 		return;
296 	}
297 	if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_ioh_tx)) {
298 		printf("%s: can't map shared memory\n", sc->sc_dev.dv_xname);
299 		return;
300 	}
301 	sc->sc_shmem_tx = bus_space_vaddr(sc->sc_iot, sc->sc_ioh_tx);
302 
303 	node = fdt_find_phandle(shmems[1]);
304 	if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
305 	    fdt_get_reg(node, 0, &reg)) {
306 		printf("%s: no shared memory\n", sc->sc_dev.dv_xname);
307 		return;
308 	}
309 	if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_ioh_rx)) {
310 		printf("%s: can't map shared memory\n", sc->sc_dev.dv_xname);
311 		return;
312 	}
313 	sc->sc_shmem_rx = bus_space_vaddr(sc->sc_iot, sc->sc_ioh_rx);
314 
315 	sc->sc_command = scmi_mbox_command;
316 
317 	scmi_message_header(sc->sc_shmem_tx, SCMI_BASE, SCMI_PROTOCOL_VERSION);
318 	sc->sc_shmem_tx->length = sizeof(uint32_t);
319 	status = sc->sc_command(sc);
320 	if (status != SCMI_SUCCESS) {
321 		printf("%s: protocol version command failed\n",
322 		    sc->sc_dev.dv_xname);
323 		return;
324 	}
325 
326 	version = sc->sc_shmem_tx->message_payload[1];
327 	sc->sc_ver_major = version >> 16;
328 	sc->sc_ver_minor = version & 0xfffff;
329 	printf("%s: SCMI %d.%d\n", sc->sc_dev.dv_xname, sc->sc_ver_major,
330 	    sc->sc_ver_minor);
331 
332 	for (proto = OF_child(sc->sc_node); proto; proto = OF_peer(proto))
333 		scmi_attach_proto(sc, proto);
334 }
335 
336 int32_t
scmi_smc_command(struct scmi_softc * sc)337 scmi_smc_command(struct scmi_softc *sc)
338 {
339 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
340 	int32_t status;
341 
342 	shmem->channel_status = 0;
343 	status = smccc(sc->sc_smc_id, 0, 0, 0);
344 	if (status != PSCI_SUCCESS)
345 		return SCMI_NOT_SUPPORTED;
346 	if ((shmem->channel_status & SCMI_CHANNEL_ERROR))
347 		return SCMI_COMMS_ERROR;
348 	if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0)
349 		return SCMI_BUSY;
350 	return shmem->message_payload[0];
351 }
352 
353 int32_t
scmi_mbox_command(struct scmi_softc * sc)354 scmi_mbox_command(struct scmi_softc *sc)
355 {
356 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
357 	int ret;
358 	int i;
359 
360 	shmem->channel_status = 0;
361 	ret = mbox_send(sc->sc_mc_tx, NULL, 0);
362 	if (ret != 0)
363 		return SCMI_NOT_SUPPORTED;
364 
365 	/* XXX: poll for now */
366 	for (i = 0; i < 20; i++) {
367 		if (shmem->channel_status & SCMI_CHANNEL_FREE)
368 			break;
369 		delay(10);
370 	}
371 	if ((shmem->channel_status & SCMI_CHANNEL_ERROR))
372 		return SCMI_COMMS_ERROR;
373 	if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0)
374 		return SCMI_BUSY;
375 
376 	return shmem->message_payload[0];
377 }
378 
379 void
scmi_attach_proto(struct scmi_softc * sc,int node)380 scmi_attach_proto(struct scmi_softc *sc, int node)
381 {
382 	switch (OF_getpropint(node, "reg", -1)) {
383 	case SCMI_CLOCK:
384 		scmi_attach_clock(sc, node);
385 		break;
386 	case SCMI_PERF:
387 		scmi_attach_perf(sc, node);
388 		break;
389 	default:
390 		break;
391 	}
392 }
393 
394 /* Clock management. */
395 
396 void	scmi_clock_enable(void *, uint32_t *, int);
397 uint32_t scmi_clock_get_frequency(void *, uint32_t *);
398 int	scmi_clock_set_frequency(void *, uint32_t *, uint32_t);
399 
400 void
scmi_attach_clock(struct scmi_softc * sc,int node)401 scmi_attach_clock(struct scmi_softc *sc, int node)
402 {
403 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
404 	int32_t status;
405 	int nclocks;
406 
407 	scmi_message_header(shmem, SCMI_CLOCK, SCMI_PROTOCOL_ATTRIBUTES);
408 	shmem->length = sizeof(uint32_t);
409 	status = sc->sc_command(sc);
410 	if (status != SCMI_SUCCESS)
411 		return;
412 
413 	nclocks = shmem->message_payload[1] & 0xffff;
414 	if (nclocks == 0)
415 		return;
416 
417 	sc->sc_cd.cd_node = node;
418 	sc->sc_cd.cd_cookie = sc;
419 	sc->sc_cd.cd_enable = scmi_clock_enable;
420 	sc->sc_cd.cd_get_frequency = scmi_clock_get_frequency;
421 	sc->sc_cd.cd_set_frequency = scmi_clock_set_frequency;
422 	clock_register(&sc->sc_cd);
423 }
424 
425 void
scmi_clock_enable(void * cookie,uint32_t * cells,int on)426 scmi_clock_enable(void *cookie, uint32_t *cells, int on)
427 {
428 	struct scmi_softc *sc = cookie;
429 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
430 	uint32_t idx = cells[0];
431 
432 	scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_CONFIG_SET);
433 	shmem->length = 3 * sizeof(uint32_t);
434 	shmem->message_payload[0] = idx;
435 	shmem->message_payload[1] = on ? SCMI_CLOCK_CONFIG_SET_ENABLE : 0;
436 	sc->sc_command(sc);
437 }
438 
439 uint32_t
scmi_clock_get_frequency(void * cookie,uint32_t * cells)440 scmi_clock_get_frequency(void *cookie, uint32_t *cells)
441 {
442 	struct scmi_softc *sc = cookie;
443 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
444 	uint32_t idx = cells[0];
445 	int32_t status;
446 
447 	scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_RATE_GET);
448 	shmem->length = 2 * sizeof(uint32_t);
449 	shmem->message_payload[0] = idx;
450 	status = sc->sc_command(sc);
451 	if (status != SCMI_SUCCESS)
452 		return 0;
453 	if (shmem->message_payload[2] != 0)
454 		return 0;
455 
456 	return shmem->message_payload[1];
457 }
458 
459 int
scmi_clock_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)460 scmi_clock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
461 {
462 	struct scmi_softc *sc = cookie;
463 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
464 	uint32_t idx = cells[0];
465 	int32_t status;
466 
467 	scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_RATE_SET);
468 	shmem->length = 5 * sizeof(uint32_t);
469 	shmem->message_payload[0] = 0;
470 	shmem->message_payload[1] = idx;
471 	shmem->message_payload[2] = freq;
472 	shmem->message_payload[3] = 0;
473 	status = sc->sc_command(sc);
474 	if (status != SCMI_SUCCESS)
475 		return -1;
476 
477 	return 0;
478 }
479 
480 /* Performance management */
481 void	scmi_perf_descr_levels(struct scmi_softc *, int);
482 void	scmi_perf_refresh_sensor(void *);
483 
484 void
scmi_attach_perf(struct scmi_softc * sc,int node)485 scmi_attach_perf(struct scmi_softc *sc, int node)
486 {
487 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
488 	int32_t status;
489 	uint32_t version;
490 	int i;
491 
492 	scmi_message_header(sc->sc_shmem_tx, SCMI_PERF, SCMI_PROTOCOL_VERSION);
493 	sc->sc_shmem_tx->length = sizeof(uint32_t);
494 	status = sc->sc_command(sc);
495 	if (status != SCMI_SUCCESS) {
496 		printf("%s: SCMI_PROTOCOL_VERSION failed\n",
497 		    sc->sc_dev.dv_xname);
498 		return;
499 	}
500 
501 	version = shmem->message_payload[1];
502 	if (version != 0x40000) {
503 		printf("%s: invalid perf protocol version (0x%x != 0x4000)",
504 		    sc->sc_dev.dv_xname, version);
505 		return;
506 	}
507 
508 	scmi_message_header(shmem, SCMI_PERF, SCMI_PROTOCOL_ATTRIBUTES);
509 	shmem->length = sizeof(uint32_t);
510 	status = sc->sc_command(sc);
511 	if (status != SCMI_SUCCESS) {
512 		printf("%s: SCMI_PROTOCOL_ATTRIBUTES failed\n",
513 		    sc->sc_dev.dv_xname);
514 		return;
515 	}
516 
517 	sc->sc_perf_ndomains = shmem->message_payload[1] & 0xffff;
518 	sc->sc_perf_domains = malloc(sc->sc_perf_ndomains *
519 	    sizeof(struct scmi_perf_domain), M_DEVBUF, M_ZERO | M_WAITOK);
520 	sc->sc_perf_power_unit = (shmem->message_payload[1] >> 16) & 0x3;
521 
522 	strlcpy(sc->sc_perf_sensordev.xname, sc->sc_dev.dv_xname,
523 	    sizeof(sc->sc_perf_sensordev.xname));
524 
525 	sc->sc_perf_fsensors =
526 	    malloc(sc->sc_perf_ndomains * sizeof(struct ksensor),
527 	    M_DEVBUF, M_ZERO | M_WAITOK);
528 	sc->sc_perf_psensors =
529 	    malloc(sc->sc_perf_ndomains * sizeof(struct ksensor),
530 	    M_DEVBUF, M_ZERO | M_WAITOK);
531 
532 	/* Add one frequency sensor per perf domain */
533 	for (i = 0; i < sc->sc_perf_ndomains; i++) {
534 		scmi_message_header(shmem, SCMI_PERF,
535 		    SCMI_PERF_DOMAIN_ATTRIBUTES);
536 		shmem->length = 2 * sizeof(uint32_t);
537 		shmem->message_payload[0] = i;
538 		status = sc->sc_command(sc);
539 		if (status != SCMI_SUCCESS) {
540 			printf("%s: SCMI_PERF_DOMAIN_ATTRIBUTES failed\n",
541 			    sc->sc_dev.dv_xname);
542 			goto err;
543 		}
544 
545 		scmi_perf_descr_levels(sc, i);
546 
547 		sc->sc_perf_fsensors[i].type = SENSOR_FREQ;
548 		sensor_attach(&sc->sc_perf_sensordev, &sc->sc_perf_fsensors[i]);
549 		sc->sc_perf_psensors[i].type = SENSOR_WATTS;
550 		sensor_attach(&sc->sc_perf_sensordev, &sc->sc_perf_psensors[i]);
551 	}
552 	sensordev_install(&sc->sc_perf_sensordev);
553 	sensor_task_register(sc, scmi_perf_refresh_sensor, 1);
554 	return;
555 err:
556 	free(sc->sc_perf_fsensors, M_DEVBUF,
557 	    sc->sc_perf_ndomains * sizeof(struct ksensor));
558 	free(sc->sc_perf_psensors, M_DEVBUF,
559 	    sc->sc_perf_ndomains * sizeof(struct ksensor));
560 }
561 
562 void
scmi_perf_descr_levels(struct scmi_softc * sc,int domain)563 scmi_perf_descr_levels(struct scmi_softc *sc, int domain)
564 {
565 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
566 	volatile struct scmi_resp_perf_describe_levels_40 *pl;
567 	struct scmi_perf_domain *pd = &sc->sc_perf_domains[domain];
568 	int status, i, idx;
569 
570 	idx = 0;
571 	do {
572 		scmi_message_header(shmem, SCMI_PERF,
573 		    SCMI_PERF_DESCRIBE_LEVELS);
574 		shmem->length = sizeof(uint32_t) * 3;
575 		shmem->message_payload[0] = domain;
576 		shmem->message_payload[1] = idx;
577 		status = sc->sc_command(sc);
578 		if (status != SCMI_SUCCESS) {
579 			printf("%s: SCMI_PERF_DESCRIBE_LEVELS failed\n",
580 			    sc->sc_dev.dv_xname);
581 			return;
582 		}
583 
584 		pl = (struct scmi_resp_perf_describe_levels_40 *)
585 		    &shmem->message_payload[1];
586 
587 		if (pd->pd_levels == NULL) {
588 			pd->pd_nlevels = pl->pl_nret + pl->pl_nrem;
589 			pd->pd_levels = malloc(pd->pd_nlevels *
590 			    sizeof(struct scmi_perf_level),
591 			    M_DEVBUF, M_ZERO | M_WAITOK);
592 		}
593 
594 		for (i = 0; i < pl->pl_nret; i++) {
595 			pd->pd_levels[idx + i].pl_cost =
596 			    pl->pl_entry[i].pe_cost;
597 			pd->pd_levels[idx + i].pl_perf =
598 			    pl->pl_entry[i].pe_perf;
599 			pd->pd_levels[idx + i].pl_ifreq =
600 			    pl->pl_entry[i].pe_ifreq;
601 		}
602 		idx += pl->pl_nret;
603 	} while (pl->pl_nrem);
604 }
605 
606 void
scmi_perf_refresh_sensor(void * arg)607 scmi_perf_refresh_sensor(void *arg)
608 {
609 	struct scmi_softc *sc = arg;
610 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
611 	uint64_t power_cost;
612 	int32_t status;
613 	int level, i;
614 
615 	if (sc->sc_perf_domains == NULL)
616 		return;
617 
618 	for (i = 0; i < sc->sc_perf_ndomains; i++) {
619 		if (sc->sc_perf_domains[i].pd_levels == NULL)
620 			return;
621 
622 		scmi_message_header(shmem, SCMI_PERF,
623 		    SCMI_PERF_LEVEL_GET);
624 		shmem->length = sizeof(uint32_t) * 2;
625 		shmem->message_payload[0] = i;
626 		status = sc->sc_command(sc);
627 		if (status != SCMI_SUCCESS) {
628 			printf("%s: SCMI_PERF_LEVEL_GET failed\n",
629 			    sc->sc_dev.dv_xname);
630 			return;
631 		}
632 
633 		level = shmem->message_payload[1];
634 		if (sc->sc_perf_fsensors == NULL ||
635 		    sc->sc_perf_psensors == NULL)
636 			return;
637 
638 		sc->sc_perf_domains[i].pd_curlevel = level;
639 		sc->sc_perf_fsensors[i].value =
640 		    (uint64_t)sc->sc_perf_domains[i].
641 		    pd_levels[level].pl_ifreq * 1000000000;
642 
643 		switch (sc->sc_perf_power_unit) {
644 		case SCMI_POWER_UNIT_UW:
645 			power_cost = (uint64_t)sc->sc_perf_domains[i].
646 			    pd_levels[level].pl_cost;
647 			break;
648 		case SCMI_POWER_UNIT_MW:
649 			power_cost = (uint64_t)sc->sc_perf_domains[i].
650 			    pd_levels[level].pl_cost * 1000;
651 			break;
652 		default:
653 			continue;
654 		}
655 		sc->sc_perf_psensors[i].value = power_cost;
656 	}
657 }
658