xref: /openbsd-src/sys/dev/fdt/scmi.c (revision 44683af5ac2ada861c78fb73642249186b9484a5)
1*44683af5Stobhe /*	$OpenBSD: scmi.c,v 1.2 2024/11/25 22:12:18 tobhe Exp $	*/
22fb1186fSkettenis 
32fb1186fSkettenis /*
42fb1186fSkettenis  * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
5*44683af5Stobhe  * Copyright (c) 2024 Tobias Heider <tobhe@openbsd.org>
62fb1186fSkettenis  *
72fb1186fSkettenis  * Permission to use, copy, modify, and distribute this software for any
82fb1186fSkettenis  * purpose with or without fee is hereby granted, provided that the above
92fb1186fSkettenis  * copyright notice and this permission notice appear in all copies.
102fb1186fSkettenis  *
112fb1186fSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
122fb1186fSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
132fb1186fSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
142fb1186fSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
152fb1186fSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
162fb1186fSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
172fb1186fSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
182fb1186fSkettenis  */
192fb1186fSkettenis 
202fb1186fSkettenis #include <sys/param.h>
212fb1186fSkettenis #include <sys/device.h>
222fb1186fSkettenis #include <sys/systm.h>
23*44683af5Stobhe #include <sys/malloc.h>
24*44683af5Stobhe #include <sys/sensors.h>
25*44683af5Stobhe #include <sys/sysctl.h>
262fb1186fSkettenis 
272fb1186fSkettenis #include <machine/bus.h>
282fb1186fSkettenis #include <machine/fdt.h>
292fb1186fSkettenis 
302fb1186fSkettenis #include <dev/ofw/openfirm.h>
312fb1186fSkettenis #include <dev/ofw/ofw_clock.h>
32*44683af5Stobhe #include <dev/ofw/ofw_misc.h>
332fb1186fSkettenis #include <dev/ofw/fdt.h>
342fb1186fSkettenis 
352fb1186fSkettenis #include <dev/fdt/pscivar.h>
362fb1186fSkettenis 
372fb1186fSkettenis struct scmi_shmem {
382fb1186fSkettenis 	uint32_t reserved1;
392fb1186fSkettenis 	uint32_t channel_status;
402fb1186fSkettenis #define SCMI_CHANNEL_ERROR		(1 << 1)
412fb1186fSkettenis #define SCMI_CHANNEL_FREE		(1 << 0)
422fb1186fSkettenis 	uint32_t reserved2;
432fb1186fSkettenis 	uint32_t reserved3;
442fb1186fSkettenis 	uint32_t channel_flags;
452fb1186fSkettenis 	uint32_t length;
462fb1186fSkettenis 	uint32_t message_header;
472fb1186fSkettenis 	uint32_t message_payload[];
482fb1186fSkettenis };
492fb1186fSkettenis 
502fb1186fSkettenis #define SCMI_SUCCESS		0
512fb1186fSkettenis #define SCMI_NOT_SUPPORTED	-1
522fb1186fSkettenis #define SCMI_BUSY		-6
532fb1186fSkettenis #define SCMI_COMMS_ERROR	-7
542fb1186fSkettenis 
552fb1186fSkettenis /* Protocols */
562fb1186fSkettenis #define SCMI_BASE		0x10
57*44683af5Stobhe #define SCMI_PERF		0x13
582fb1186fSkettenis #define SCMI_CLOCK		0x14
592fb1186fSkettenis 
602fb1186fSkettenis /* Common messages */
612fb1186fSkettenis #define SCMI_PROTOCOL_VERSION			0x0
622fb1186fSkettenis #define SCMI_PROTOCOL_ATTRIBUTES		0x1
632fb1186fSkettenis #define SCMI_PROTOCOL_MESSAGE_ATTRIBUTES	0x2
642fb1186fSkettenis 
652fb1186fSkettenis /* Clock management messages */
662fb1186fSkettenis #define SCMI_CLOCK_ATTRIBUTES			0x3
672fb1186fSkettenis #define SCMI_CLOCK_DESCRIBE_RATES		0x4
682fb1186fSkettenis #define SCMI_CLOCK_RATE_SET			0x5
692fb1186fSkettenis #define SCMI_CLOCK_RATE_GET			0x6
702fb1186fSkettenis #define SCMI_CLOCK_CONFIG_SET			0x7
712fb1186fSkettenis #define  SCMI_CLOCK_CONFIG_SET_ENABLE		(1 << 0)
722fb1186fSkettenis 
73*44683af5Stobhe /* Performance management messages */
74*44683af5Stobhe #define SCMI_PERF_DOMAIN_ATTRIBUTES		0x3
75*44683af5Stobhe #define SCMI_PERF_DESCRIBE_LEVELS		0x4
76*44683af5Stobhe #define SCMI_PERF_LEVEL_GET			0x8
77*44683af5Stobhe 
78*44683af5Stobhe struct scmi_resp_perf_describe_levels_40 {
79*44683af5Stobhe 	uint16_t pl_nret;
80*44683af5Stobhe 	uint16_t pl_nrem;
81*44683af5Stobhe 	struct {
82*44683af5Stobhe 		uint32_t	pe_perf;
83*44683af5Stobhe 		uint32_t	pe_cost;
84*44683af5Stobhe 		uint16_t	pe_latency;
85*44683af5Stobhe 		uint16_t	pe_reserved;
86*44683af5Stobhe 		uint32_t	pe_ifreq;
87*44683af5Stobhe 		uint32_t	pe_lindex;
88*44683af5Stobhe 	} pl_entry[];
89*44683af5Stobhe };
90*44683af5Stobhe 
912fb1186fSkettenis static inline void
922fb1186fSkettenis scmi_message_header(volatile struct scmi_shmem *shmem,
932fb1186fSkettenis     uint32_t protocol_id, uint32_t message_id)
942fb1186fSkettenis {
952fb1186fSkettenis 	shmem->message_header = (protocol_id << 10) | (message_id << 0);
962fb1186fSkettenis }
972fb1186fSkettenis 
98*44683af5Stobhe struct scmi_perf_level {
99*44683af5Stobhe 	uint32_t	pl_perf;
100*44683af5Stobhe 	uint32_t	pl_cost;
101*44683af5Stobhe 	uint32_t	pl_ifreq;
102*44683af5Stobhe };
103*44683af5Stobhe 
104*44683af5Stobhe struct scmi_perf_domain {
105*44683af5Stobhe 	size_t				pd_nlevels;
106*44683af5Stobhe 	struct scmi_perf_level		*pd_levels;
107*44683af5Stobhe 	int				pd_curlevel;
108*44683af5Stobhe };
1092fb1186fSkettenis 
1102fb1186fSkettenis struct scmi_softc {
1112fb1186fSkettenis 	struct device			sc_dev;
1122fb1186fSkettenis 	bus_space_tag_t			sc_iot;
113*44683af5Stobhe 	int				sc_node;
114*44683af5Stobhe 
115*44683af5Stobhe 	bus_space_handle_t		sc_ioh_tx;
116*44683af5Stobhe 	bus_space_handle_t		sc_ioh_rx;
117*44683af5Stobhe 	volatile struct scmi_shmem	*sc_shmem_tx;
118*44683af5Stobhe 	volatile struct scmi_shmem	*sc_shmem_rx;
1192fb1186fSkettenis 
1202fb1186fSkettenis 	uint32_t			sc_smc_id;
121*44683af5Stobhe 	struct mbox_channel		*sc_mc_tx;
122*44683af5Stobhe 	struct mbox_channel		*sc_mc_rx;
1232fb1186fSkettenis 
124*44683af5Stobhe 	uint16_t			sc_ver_major;
125*44683af5Stobhe 	uint16_t			sc_ver_minor;
126*44683af5Stobhe 
127*44683af5Stobhe 	/* SCMI_CLOCK */
1282fb1186fSkettenis 	struct clock_device		sc_cd;
129*44683af5Stobhe 
130*44683af5Stobhe 	/* SCMI_PERF */
131*44683af5Stobhe 	int				sc_perf_power_unit;
132*44683af5Stobhe #define SCMI_POWER_UNIT_UW	0x2
133*44683af5Stobhe #define SCMI_POWER_UNIT_MW	0x1
134*44683af5Stobhe #define SCMI_POWER_UNIT_NONE	0x0
135*44683af5Stobhe 	size_t				sc_perf_ndomains;
136*44683af5Stobhe 	struct scmi_perf_domain		*sc_perf_domains;
137*44683af5Stobhe 
138*44683af5Stobhe 	struct ksensordev		sc_perf_sensordev;
139*44683af5Stobhe 	struct ksensordev		sc_perf_psensordev;
140*44683af5Stobhe 	struct ksensor			*sc_perf_fsensors;
141*44683af5Stobhe 	struct ksensor			*sc_perf_psensors;
142*44683af5Stobhe 
143*44683af5Stobhe 	int32_t				(*sc_command)(struct scmi_softc *);
1442fb1186fSkettenis };
1452fb1186fSkettenis 
1462fb1186fSkettenis int	scmi_match(struct device *, void *, void *);
1472fb1186fSkettenis void	scmi_attach(struct device *, struct device *, void *);
148*44683af5Stobhe int	scmi_attach_smc(struct scmi_softc *, struct fdt_attach_args *);
149*44683af5Stobhe void	scmi_attach_mbox_deferred(struct device *);
1502fb1186fSkettenis 
1512fb1186fSkettenis const struct cfattach scmi_ca = {
1522fb1186fSkettenis 	sizeof(struct scmi_softc), scmi_match, scmi_attach
1532fb1186fSkettenis };
1542fb1186fSkettenis 
1552fb1186fSkettenis struct cfdriver scmi_cd = {
1562fb1186fSkettenis 	NULL, "scmi", DV_DULL
1572fb1186fSkettenis };
1582fb1186fSkettenis 
1592fb1186fSkettenis void	scmi_attach_proto(struct scmi_softc *, int);
1602fb1186fSkettenis void	scmi_attach_clock(struct scmi_softc *, int);
161*44683af5Stobhe void	scmi_attach_perf(struct scmi_softc *, int);
162*44683af5Stobhe 
163*44683af5Stobhe int32_t	scmi_smc_command(struct scmi_softc *);
164*44683af5Stobhe int32_t	scmi_mbox_command(struct scmi_softc *);
1652fb1186fSkettenis 
1662fb1186fSkettenis int
1672fb1186fSkettenis scmi_match(struct device *parent, void *match, void *aux)
1682fb1186fSkettenis {
1692fb1186fSkettenis 	struct fdt_attach_args *faa = aux;
1702fb1186fSkettenis 
171*44683af5Stobhe 	return OF_is_compatible(faa->fa_node, "arm,scmi-smc") ||
172*44683af5Stobhe 	    OF_is_compatible(faa->fa_node, "arm,scmi");
1732fb1186fSkettenis }
1742fb1186fSkettenis 
1752fb1186fSkettenis void
1762fb1186fSkettenis scmi_attach(struct device *parent, struct device *self, void *aux)
1772fb1186fSkettenis {
1782fb1186fSkettenis 	struct scmi_softc *sc = (struct scmi_softc *)self;
1792fb1186fSkettenis 	struct fdt_attach_args *faa = aux;
180*44683af5Stobhe 
181*44683af5Stobhe 	sc->sc_iot = faa->fa_iot;
182*44683af5Stobhe 	sc->sc_node = faa->fa_node;
183*44683af5Stobhe 
184*44683af5Stobhe 	if (OF_is_compatible(faa->fa_node, "arm,scmi-smc")) {
185*44683af5Stobhe 		scmi_attach_smc(sc, faa);
186*44683af5Stobhe 	} else if (OF_is_compatible(faa->fa_node, "arm,scmi")) {
187*44683af5Stobhe 		printf("\n");
188*44683af5Stobhe 		/* Defer because we need the mailbox driver attached first */
189*44683af5Stobhe 		config_defer(self, scmi_attach_mbox_deferred);
190*44683af5Stobhe 	}
191*44683af5Stobhe }
192*44683af5Stobhe 
193*44683af5Stobhe int
194*44683af5Stobhe scmi_attach_smc(struct scmi_softc *sc, struct fdt_attach_args *faa)
195*44683af5Stobhe {
196*44683af5Stobhe 	volatile struct scmi_shmem *shmem;
1972fb1186fSkettenis 	struct fdt_reg reg;
1982fb1186fSkettenis 	int32_t status;
1992fb1186fSkettenis 	uint32_t version;
2002fb1186fSkettenis 	uint32_t phandle;
2012fb1186fSkettenis 	void *node;
2022fb1186fSkettenis 	int proto;
2032fb1186fSkettenis 
204*44683af5Stobhe 	sc->sc_smc_id = OF_getpropint(faa->fa_node, "arm,smc-id", 0);
205*44683af5Stobhe 	if (sc->sc_smc_id == 0) {
206*44683af5Stobhe 		printf(": no SMC id\n");
207*44683af5Stobhe 		return -1;
208*44683af5Stobhe 	}
209*44683af5Stobhe 
2102fb1186fSkettenis 	phandle = OF_getpropint(faa->fa_node, "shmem", 0);
2112fb1186fSkettenis 	node = fdt_find_phandle(phandle);
2122fb1186fSkettenis 	if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
2132fb1186fSkettenis 	    fdt_get_reg(node, 0, &reg)) {
2142fb1186fSkettenis 		printf(": no shared memory\n");
215*44683af5Stobhe 		return -1;
2162fb1186fSkettenis 	}
2172fb1186fSkettenis 
2182fb1186fSkettenis 	if (bus_space_map(sc->sc_iot, reg.addr,
219*44683af5Stobhe 	    reg.size, 0, &sc->sc_ioh_tx)) {
2202fb1186fSkettenis 		printf(": can't map shared memory\n");
221*44683af5Stobhe 		return -1;
2222fb1186fSkettenis 	}
223*44683af5Stobhe 	sc->sc_shmem_tx = bus_space_vaddr(sc->sc_iot, sc->sc_ioh_tx);
224*44683af5Stobhe 	shmem = sc->sc_shmem_tx;
225*44683af5Stobhe 
226*44683af5Stobhe 	sc->sc_command = scmi_smc_command;
2272fb1186fSkettenis 
2282fb1186fSkettenis 	if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0) {
2292fb1186fSkettenis 		printf(": channel busy\n");
230*44683af5Stobhe 		return -1;
2312fb1186fSkettenis 	}
2322fb1186fSkettenis 
2332fb1186fSkettenis 	scmi_message_header(shmem, SCMI_BASE, SCMI_PROTOCOL_VERSION);
2342fb1186fSkettenis 	shmem->length = sizeof(uint32_t);
235*44683af5Stobhe 	status = sc->sc_command(sc);
2362fb1186fSkettenis 	if (status != SCMI_SUCCESS) {
2372fb1186fSkettenis 		printf(": protocol version command failed\n");
238*44683af5Stobhe 		return -1;
2392fb1186fSkettenis 	}
2402fb1186fSkettenis 
2412fb1186fSkettenis 	version = shmem->message_payload[1];
242*44683af5Stobhe 	sc->sc_ver_major = version >> 16;
243*44683af5Stobhe 	sc->sc_ver_minor = version & 0xfffff;
244*44683af5Stobhe 	printf(": SCMI %d.%d\n", sc->sc_ver_major, sc->sc_ver_minor);
2452fb1186fSkettenis 
2462fb1186fSkettenis 	for (proto = OF_child(faa->fa_node); proto; proto = OF_peer(proto))
2472fb1186fSkettenis 		scmi_attach_proto(sc, proto);
248*44683af5Stobhe 
249*44683af5Stobhe 	return 0;
250*44683af5Stobhe }
251*44683af5Stobhe 
252*44683af5Stobhe void
253*44683af5Stobhe scmi_attach_mbox_deferred(struct device *self)
254*44683af5Stobhe {
255*44683af5Stobhe 	struct scmi_softc *sc = (struct scmi_softc *)self;
256*44683af5Stobhe 	uint32_t *shmems;
257*44683af5Stobhe 	int32_t status;
258*44683af5Stobhe 	uint32_t version;
259*44683af5Stobhe 	struct fdt_reg reg;
260*44683af5Stobhe 	int len;
261*44683af5Stobhe 	void *node;
262*44683af5Stobhe 	int proto;
263*44683af5Stobhe 
264*44683af5Stobhe 	/* we only support the 2 mbox / 2 shmem case */
265*44683af5Stobhe 	len = OF_getproplen(sc->sc_node, "mboxes");
266*44683af5Stobhe 	if (len != 4 * sizeof(uint32_t)) {
267*44683af5Stobhe 		printf("%s: invalid number of mboxes\n", sc->sc_dev.dv_xname);
268*44683af5Stobhe 		return;
269*44683af5Stobhe 	}
270*44683af5Stobhe 
271*44683af5Stobhe 	len = OF_getproplen(sc->sc_node, "shmem");
272*44683af5Stobhe 	if (len != 2 * sizeof(uint32_t)) {
273*44683af5Stobhe 		printf("%s: invalid number of shmems\n", sc->sc_dev.dv_xname);
274*44683af5Stobhe 		return;
275*44683af5Stobhe 	}
276*44683af5Stobhe 
277*44683af5Stobhe 	shmems = malloc(len, M_DEVBUF, M_WAITOK);
278*44683af5Stobhe 	OF_getpropintarray(sc->sc_node, "shmem", shmems, len);
279*44683af5Stobhe 
280*44683af5Stobhe 	sc->sc_mc_tx = mbox_channel(sc->sc_node, "tx", NULL);
281*44683af5Stobhe 	if (sc->sc_mc_tx == NULL) {
282*44683af5Stobhe 		printf("%s: no tx mbox\n", sc->sc_dev.dv_xname);
283*44683af5Stobhe 		return;
284*44683af5Stobhe 	}
285*44683af5Stobhe 	sc->sc_mc_rx = mbox_channel(sc->sc_node, "rx", NULL);
286*44683af5Stobhe 	if (sc->sc_mc_rx == NULL) {
287*44683af5Stobhe 		printf("%s: no rx mbox\n", sc->sc_dev.dv_xname);
288*44683af5Stobhe 		return;
289*44683af5Stobhe 	}
290*44683af5Stobhe 
291*44683af5Stobhe 	node = fdt_find_phandle(shmems[0]);
292*44683af5Stobhe 	if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
293*44683af5Stobhe 	    fdt_get_reg(node, 0, &reg)) {
294*44683af5Stobhe 		printf("%s: no shared memory\n", sc->sc_dev.dv_xname);
295*44683af5Stobhe 		return;
296*44683af5Stobhe 	}
297*44683af5Stobhe 	if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_ioh_tx)) {
298*44683af5Stobhe 		printf("%s: can't map shared memory\n", sc->sc_dev.dv_xname);
299*44683af5Stobhe 		return;
300*44683af5Stobhe 	}
301*44683af5Stobhe 	sc->sc_shmem_tx = bus_space_vaddr(sc->sc_iot, sc->sc_ioh_tx);
302*44683af5Stobhe 
303*44683af5Stobhe 	node = fdt_find_phandle(shmems[1]);
304*44683af5Stobhe 	if (node == NULL || !fdt_is_compatible(node, "arm,scmi-shmem") ||
305*44683af5Stobhe 	    fdt_get_reg(node, 0, &reg)) {
306*44683af5Stobhe 		printf("%s: no shared memory\n", sc->sc_dev.dv_xname);
307*44683af5Stobhe 		return;
308*44683af5Stobhe 	}
309*44683af5Stobhe 	if (bus_space_map(sc->sc_iot, reg.addr, reg.size, 0, &sc->sc_ioh_rx)) {
310*44683af5Stobhe 		printf("%s: can't map shared memory\n", sc->sc_dev.dv_xname);
311*44683af5Stobhe 		return;
312*44683af5Stobhe 	}
313*44683af5Stobhe 	sc->sc_shmem_rx = bus_space_vaddr(sc->sc_iot, sc->sc_ioh_rx);
314*44683af5Stobhe 
315*44683af5Stobhe 	sc->sc_command = scmi_mbox_command;
316*44683af5Stobhe 
317*44683af5Stobhe 	scmi_message_header(sc->sc_shmem_tx, SCMI_BASE, SCMI_PROTOCOL_VERSION);
318*44683af5Stobhe 	sc->sc_shmem_tx->length = sizeof(uint32_t);
319*44683af5Stobhe 	status = sc->sc_command(sc);
320*44683af5Stobhe 	if (status != SCMI_SUCCESS) {
321*44683af5Stobhe 		printf("%s: protocol version command failed\n",
322*44683af5Stobhe 		    sc->sc_dev.dv_xname);
323*44683af5Stobhe 		return;
324*44683af5Stobhe 	}
325*44683af5Stobhe 
326*44683af5Stobhe 	version = sc->sc_shmem_tx->message_payload[1];
327*44683af5Stobhe 	sc->sc_ver_major = version >> 16;
328*44683af5Stobhe 	sc->sc_ver_minor = version & 0xfffff;
329*44683af5Stobhe 	printf("%s: SCMI %d.%d\n", sc->sc_dev.dv_xname, sc->sc_ver_major,
330*44683af5Stobhe 	    sc->sc_ver_minor);
331*44683af5Stobhe 
332*44683af5Stobhe 	for (proto = OF_child(sc->sc_node); proto; proto = OF_peer(proto))
333*44683af5Stobhe 		scmi_attach_proto(sc, proto);
3342fb1186fSkettenis }
3352fb1186fSkettenis 
3362fb1186fSkettenis int32_t
337*44683af5Stobhe scmi_smc_command(struct scmi_softc *sc)
3382fb1186fSkettenis {
339*44683af5Stobhe 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
3402fb1186fSkettenis 	int32_t status;
3412fb1186fSkettenis 
3422fb1186fSkettenis 	shmem->channel_status = 0;
3432fb1186fSkettenis 	status = smccc(sc->sc_smc_id, 0, 0, 0);
3442fb1186fSkettenis 	if (status != PSCI_SUCCESS)
3452fb1186fSkettenis 		return SCMI_NOT_SUPPORTED;
3462fb1186fSkettenis 	if ((shmem->channel_status & SCMI_CHANNEL_ERROR))
3472fb1186fSkettenis 		return SCMI_COMMS_ERROR;
3482fb1186fSkettenis 	if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0)
3492fb1186fSkettenis 		return SCMI_BUSY;
3502fb1186fSkettenis 	return shmem->message_payload[0];
3512fb1186fSkettenis }
3522fb1186fSkettenis 
353*44683af5Stobhe int32_t
354*44683af5Stobhe scmi_mbox_command(struct scmi_softc *sc)
355*44683af5Stobhe {
356*44683af5Stobhe 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
357*44683af5Stobhe 	int ret;
358*44683af5Stobhe 	int i;
359*44683af5Stobhe 
360*44683af5Stobhe 	shmem->channel_status = 0;
361*44683af5Stobhe 	ret = mbox_send(sc->sc_mc_tx, NULL, 0);
362*44683af5Stobhe 	if (ret != 0)
363*44683af5Stobhe 		return SCMI_NOT_SUPPORTED;
364*44683af5Stobhe 
365*44683af5Stobhe 	/* XXX: poll for now */
366*44683af5Stobhe 	for (i = 0; i < 20; i++) {
367*44683af5Stobhe 		if (shmem->channel_status & SCMI_CHANNEL_FREE)
368*44683af5Stobhe 			break;
369*44683af5Stobhe 		delay(10);
370*44683af5Stobhe 	}
371*44683af5Stobhe 	if ((shmem->channel_status & SCMI_CHANNEL_ERROR))
372*44683af5Stobhe 		return SCMI_COMMS_ERROR;
373*44683af5Stobhe 	if ((shmem->channel_status & SCMI_CHANNEL_FREE) == 0)
374*44683af5Stobhe 		return SCMI_BUSY;
375*44683af5Stobhe 
376*44683af5Stobhe 	return shmem->message_payload[0];
377*44683af5Stobhe }
378*44683af5Stobhe 
3792fb1186fSkettenis void
3802fb1186fSkettenis scmi_attach_proto(struct scmi_softc *sc, int node)
3812fb1186fSkettenis {
3822fb1186fSkettenis 	switch (OF_getpropint(node, "reg", -1)) {
3832fb1186fSkettenis 	case SCMI_CLOCK:
3842fb1186fSkettenis 		scmi_attach_clock(sc, node);
3852fb1186fSkettenis 		break;
386*44683af5Stobhe 	case SCMI_PERF:
387*44683af5Stobhe 		scmi_attach_perf(sc, node);
388*44683af5Stobhe 		break;
3892fb1186fSkettenis 	default:
3902fb1186fSkettenis 		break;
3912fb1186fSkettenis 	}
3922fb1186fSkettenis }
3932fb1186fSkettenis 
3942fb1186fSkettenis /* Clock management. */
3952fb1186fSkettenis 
3962fb1186fSkettenis void	scmi_clock_enable(void *, uint32_t *, int);
3972fb1186fSkettenis uint32_t scmi_clock_get_frequency(void *, uint32_t *);
3982fb1186fSkettenis int	scmi_clock_set_frequency(void *, uint32_t *, uint32_t);
3992fb1186fSkettenis 
4002fb1186fSkettenis void
4012fb1186fSkettenis scmi_attach_clock(struct scmi_softc *sc, int node)
4022fb1186fSkettenis {
403*44683af5Stobhe 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
4042fb1186fSkettenis 	int32_t status;
4052fb1186fSkettenis 	int nclocks;
4062fb1186fSkettenis 
4072fb1186fSkettenis 	scmi_message_header(shmem, SCMI_CLOCK, SCMI_PROTOCOL_ATTRIBUTES);
4082fb1186fSkettenis 	shmem->length = sizeof(uint32_t);
409*44683af5Stobhe 	status = sc->sc_command(sc);
4102fb1186fSkettenis 	if (status != SCMI_SUCCESS)
4112fb1186fSkettenis 		return;
4122fb1186fSkettenis 
4132fb1186fSkettenis 	nclocks = shmem->message_payload[1] & 0xffff;
4142fb1186fSkettenis 	if (nclocks == 0)
4152fb1186fSkettenis 		return;
4162fb1186fSkettenis 
4172fb1186fSkettenis 	sc->sc_cd.cd_node = node;
4182fb1186fSkettenis 	sc->sc_cd.cd_cookie = sc;
4192fb1186fSkettenis 	sc->sc_cd.cd_enable = scmi_clock_enable;
4202fb1186fSkettenis 	sc->sc_cd.cd_get_frequency = scmi_clock_get_frequency;
4212fb1186fSkettenis 	sc->sc_cd.cd_set_frequency = scmi_clock_set_frequency;
4222fb1186fSkettenis 	clock_register(&sc->sc_cd);
4232fb1186fSkettenis }
4242fb1186fSkettenis 
4252fb1186fSkettenis void
4262fb1186fSkettenis scmi_clock_enable(void *cookie, uint32_t *cells, int on)
4272fb1186fSkettenis {
4282fb1186fSkettenis 	struct scmi_softc *sc = cookie;
429*44683af5Stobhe 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
4302fb1186fSkettenis 	uint32_t idx = cells[0];
4312fb1186fSkettenis 
4322fb1186fSkettenis 	scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_CONFIG_SET);
4332fb1186fSkettenis 	shmem->length = 3 * sizeof(uint32_t);
4342fb1186fSkettenis 	shmem->message_payload[0] = idx;
4352fb1186fSkettenis 	shmem->message_payload[1] = on ? SCMI_CLOCK_CONFIG_SET_ENABLE : 0;
436*44683af5Stobhe 	sc->sc_command(sc);
4372fb1186fSkettenis }
4382fb1186fSkettenis 
4392fb1186fSkettenis uint32_t
4402fb1186fSkettenis scmi_clock_get_frequency(void *cookie, uint32_t *cells)
4412fb1186fSkettenis {
4422fb1186fSkettenis 	struct scmi_softc *sc = cookie;
443*44683af5Stobhe 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
4442fb1186fSkettenis 	uint32_t idx = cells[0];
4452fb1186fSkettenis 	int32_t status;
4462fb1186fSkettenis 
4472fb1186fSkettenis 	scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_RATE_GET);
4482fb1186fSkettenis 	shmem->length = 2 * sizeof(uint32_t);
4492fb1186fSkettenis 	shmem->message_payload[0] = idx;
450*44683af5Stobhe 	status = sc->sc_command(sc);
4512fb1186fSkettenis 	if (status != SCMI_SUCCESS)
4522fb1186fSkettenis 		return 0;
4532fb1186fSkettenis 	if (shmem->message_payload[2] != 0)
4542fb1186fSkettenis 		return 0;
4552fb1186fSkettenis 
4562fb1186fSkettenis 	return shmem->message_payload[1];
4572fb1186fSkettenis }
4582fb1186fSkettenis 
4592fb1186fSkettenis int
4602fb1186fSkettenis scmi_clock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
4612fb1186fSkettenis {
4622fb1186fSkettenis 	struct scmi_softc *sc = cookie;
463*44683af5Stobhe 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
4642fb1186fSkettenis 	uint32_t idx = cells[0];
4652fb1186fSkettenis 	int32_t status;
4662fb1186fSkettenis 
4672fb1186fSkettenis 	scmi_message_header(shmem, SCMI_CLOCK, SCMI_CLOCK_RATE_SET);
4682fb1186fSkettenis 	shmem->length = 5 * sizeof(uint32_t);
4692fb1186fSkettenis 	shmem->message_payload[0] = 0;
4702fb1186fSkettenis 	shmem->message_payload[1] = idx;
4712fb1186fSkettenis 	shmem->message_payload[2] = freq;
4722fb1186fSkettenis 	shmem->message_payload[3] = 0;
473*44683af5Stobhe 	status = sc->sc_command(sc);
4742fb1186fSkettenis 	if (status != SCMI_SUCCESS)
4752fb1186fSkettenis 		return -1;
4762fb1186fSkettenis 
4772fb1186fSkettenis 	return 0;
4782fb1186fSkettenis }
479*44683af5Stobhe 
480*44683af5Stobhe /* Performance management */
481*44683af5Stobhe void	scmi_perf_descr_levels(struct scmi_softc *, int);
482*44683af5Stobhe void	scmi_perf_refresh_sensor(void *);
483*44683af5Stobhe 
484*44683af5Stobhe void
485*44683af5Stobhe scmi_attach_perf(struct scmi_softc *sc, int node)
486*44683af5Stobhe {
487*44683af5Stobhe 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
488*44683af5Stobhe 	int32_t status;
489*44683af5Stobhe 	uint32_t version;
490*44683af5Stobhe 	int i;
491*44683af5Stobhe 
492*44683af5Stobhe 	scmi_message_header(sc->sc_shmem_tx, SCMI_PERF, SCMI_PROTOCOL_VERSION);
493*44683af5Stobhe 	sc->sc_shmem_tx->length = sizeof(uint32_t);
494*44683af5Stobhe 	status = sc->sc_command(sc);
495*44683af5Stobhe 	if (status != SCMI_SUCCESS) {
496*44683af5Stobhe 		printf("%s: SCMI_PROTOCOL_VERSION failed\n",
497*44683af5Stobhe 		    sc->sc_dev.dv_xname);
498*44683af5Stobhe 		return;
499*44683af5Stobhe 	}
500*44683af5Stobhe 
501*44683af5Stobhe 	version = shmem->message_payload[1];
502*44683af5Stobhe 	if (version != 0x40000) {
503*44683af5Stobhe 		printf("%s: invalid perf protocol version (0x%x != 0x4000)",
504*44683af5Stobhe 		    sc->sc_dev.dv_xname, version);
505*44683af5Stobhe 		return;
506*44683af5Stobhe 	}
507*44683af5Stobhe 
508*44683af5Stobhe 	scmi_message_header(shmem, SCMI_PERF, SCMI_PROTOCOL_ATTRIBUTES);
509*44683af5Stobhe 	shmem->length = sizeof(uint32_t);
510*44683af5Stobhe 	status = sc->sc_command(sc);
511*44683af5Stobhe 	if (status != SCMI_SUCCESS) {
512*44683af5Stobhe 		printf("%s: SCMI_PROTOCOL_ATTRIBUTES failed\n",
513*44683af5Stobhe 		    sc->sc_dev.dv_xname);
514*44683af5Stobhe 		return;
515*44683af5Stobhe 	}
516*44683af5Stobhe 
517*44683af5Stobhe 	sc->sc_perf_ndomains = shmem->message_payload[1] & 0xffff;
518*44683af5Stobhe 	sc->sc_perf_domains = malloc(sc->sc_perf_ndomains *
519*44683af5Stobhe 	    sizeof(struct scmi_perf_domain), M_DEVBUF, M_ZERO | M_WAITOK);
520*44683af5Stobhe 	sc->sc_perf_power_unit = (shmem->message_payload[1] >> 16) & 0x3;
521*44683af5Stobhe 
522*44683af5Stobhe 	strlcpy(sc->sc_perf_sensordev.xname, sc->sc_dev.dv_xname,
523*44683af5Stobhe 	    sizeof(sc->sc_perf_sensordev.xname));
524*44683af5Stobhe 
525*44683af5Stobhe 	sc->sc_perf_fsensors =
526*44683af5Stobhe 	    malloc(sc->sc_perf_ndomains * sizeof(struct ksensor),
527*44683af5Stobhe 	    M_DEVBUF, M_ZERO | M_WAITOK);
528*44683af5Stobhe 	sc->sc_perf_psensors =
529*44683af5Stobhe 	    malloc(sc->sc_perf_ndomains * sizeof(struct ksensor),
530*44683af5Stobhe 	    M_DEVBUF, M_ZERO | M_WAITOK);
531*44683af5Stobhe 
532*44683af5Stobhe 	/* Add one frequency sensor per perf domain */
533*44683af5Stobhe 	for (i = 0; i < sc->sc_perf_ndomains; i++) {
534*44683af5Stobhe 		scmi_message_header(shmem, SCMI_PERF,
535*44683af5Stobhe 		    SCMI_PERF_DOMAIN_ATTRIBUTES);
536*44683af5Stobhe 		shmem->length = 2 * sizeof(uint32_t);
537*44683af5Stobhe 		shmem->message_payload[0] = i;
538*44683af5Stobhe 		status = sc->sc_command(sc);
539*44683af5Stobhe 		if (status != SCMI_SUCCESS) {
540*44683af5Stobhe 			printf("%s: SCMI_PERF_DOMAIN_ATTRIBUTES failed\n",
541*44683af5Stobhe 			    sc->sc_dev.dv_xname);
542*44683af5Stobhe 			goto err;
543*44683af5Stobhe 		}
544*44683af5Stobhe 
545*44683af5Stobhe 		scmi_perf_descr_levels(sc, i);
546*44683af5Stobhe 
547*44683af5Stobhe 		sc->sc_perf_fsensors[i].type = SENSOR_FREQ;
548*44683af5Stobhe 		sensor_attach(&sc->sc_perf_sensordev, &sc->sc_perf_fsensors[i]);
549*44683af5Stobhe 		sc->sc_perf_psensors[i].type = SENSOR_WATTS;
550*44683af5Stobhe 		sensor_attach(&sc->sc_perf_sensordev, &sc->sc_perf_psensors[i]);
551*44683af5Stobhe 	}
552*44683af5Stobhe 	sensordev_install(&sc->sc_perf_sensordev);
553*44683af5Stobhe 	sensor_task_register(sc, scmi_perf_refresh_sensor, 1);
554*44683af5Stobhe 	return;
555*44683af5Stobhe err:
556*44683af5Stobhe 	free(sc->sc_perf_fsensors, M_DEVBUF,
557*44683af5Stobhe 	    sc->sc_perf_ndomains * sizeof(struct ksensor));
558*44683af5Stobhe 	free(sc->sc_perf_psensors, M_DEVBUF,
559*44683af5Stobhe 	    sc->sc_perf_ndomains * sizeof(struct ksensor));
560*44683af5Stobhe }
561*44683af5Stobhe 
562*44683af5Stobhe void
563*44683af5Stobhe scmi_perf_descr_levels(struct scmi_softc *sc, int domain)
564*44683af5Stobhe {
565*44683af5Stobhe 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
566*44683af5Stobhe 	volatile struct scmi_resp_perf_describe_levels_40 *pl;
567*44683af5Stobhe 	struct scmi_perf_domain *pd = &sc->sc_perf_domains[domain];
568*44683af5Stobhe 	int status, i, idx;
569*44683af5Stobhe 
570*44683af5Stobhe 	idx = 0;
571*44683af5Stobhe 	do {
572*44683af5Stobhe 		scmi_message_header(shmem, SCMI_PERF,
573*44683af5Stobhe 		    SCMI_PERF_DESCRIBE_LEVELS);
574*44683af5Stobhe 		shmem->length = sizeof(uint32_t) * 3;
575*44683af5Stobhe 		shmem->message_payload[0] = domain;
576*44683af5Stobhe 		shmem->message_payload[1] = idx;
577*44683af5Stobhe 		status = sc->sc_command(sc);
578*44683af5Stobhe 		if (status != SCMI_SUCCESS) {
579*44683af5Stobhe 			printf("%s: SCMI_PERF_DESCRIBE_LEVELS failed\n",
580*44683af5Stobhe 			    sc->sc_dev.dv_xname);
581*44683af5Stobhe 			return;
582*44683af5Stobhe 		}
583*44683af5Stobhe 
584*44683af5Stobhe 		pl = (struct scmi_resp_perf_describe_levels_40 *)
585*44683af5Stobhe 		    &shmem->message_payload[1];
586*44683af5Stobhe 
587*44683af5Stobhe 		if (pd->pd_levels == NULL) {
588*44683af5Stobhe 			pd->pd_nlevels = pl->pl_nret + pl->pl_nrem;
589*44683af5Stobhe 			pd->pd_levels = malloc(pd->pd_nlevels *
590*44683af5Stobhe 			    sizeof(struct scmi_perf_level),
591*44683af5Stobhe 			    M_DEVBUF, M_ZERO | M_WAITOK);
592*44683af5Stobhe 		}
593*44683af5Stobhe 
594*44683af5Stobhe 		for (i = 0; i < pl->pl_nret; i++) {
595*44683af5Stobhe 			pd->pd_levels[idx + i].pl_cost =
596*44683af5Stobhe 			    pl->pl_entry[i].pe_cost;
597*44683af5Stobhe 			pd->pd_levels[idx + i].pl_perf =
598*44683af5Stobhe 			    pl->pl_entry[i].pe_perf;
599*44683af5Stobhe 			pd->pd_levels[idx + i].pl_ifreq =
600*44683af5Stobhe 			    pl->pl_entry[i].pe_ifreq;
601*44683af5Stobhe 		}
602*44683af5Stobhe 		idx += pl->pl_nret;
603*44683af5Stobhe 	} while (pl->pl_nrem);
604*44683af5Stobhe }
605*44683af5Stobhe 
606*44683af5Stobhe void
607*44683af5Stobhe scmi_perf_refresh_sensor(void *arg)
608*44683af5Stobhe {
609*44683af5Stobhe 	struct scmi_softc *sc = arg;
610*44683af5Stobhe 	volatile struct scmi_shmem *shmem = sc->sc_shmem_tx;
611*44683af5Stobhe 	uint64_t power_cost;
612*44683af5Stobhe 	int32_t status;
613*44683af5Stobhe 	int level, i;
614*44683af5Stobhe 
615*44683af5Stobhe 	if (sc->sc_perf_domains == NULL)
616*44683af5Stobhe 		return;
617*44683af5Stobhe 
618*44683af5Stobhe 	for (i = 0; i < sc->sc_perf_ndomains; i++) {
619*44683af5Stobhe 		if (sc->sc_perf_domains[i].pd_levels == NULL)
620*44683af5Stobhe 			return;
621*44683af5Stobhe 
622*44683af5Stobhe 		scmi_message_header(shmem, SCMI_PERF,
623*44683af5Stobhe 		    SCMI_PERF_LEVEL_GET);
624*44683af5Stobhe 		shmem->length = sizeof(uint32_t) * 2;
625*44683af5Stobhe 		shmem->message_payload[0] = i;
626*44683af5Stobhe 		status = sc->sc_command(sc);
627*44683af5Stobhe 		if (status != SCMI_SUCCESS) {
628*44683af5Stobhe 			printf("%s: SCMI_PERF_LEVEL_GET failed\n",
629*44683af5Stobhe 			    sc->sc_dev.dv_xname);
630*44683af5Stobhe 			return;
631*44683af5Stobhe 		}
632*44683af5Stobhe 
633*44683af5Stobhe 		level = shmem->message_payload[1];
634*44683af5Stobhe 		if (sc->sc_perf_fsensors == NULL ||
635*44683af5Stobhe 		    sc->sc_perf_psensors == NULL)
636*44683af5Stobhe 			return;
637*44683af5Stobhe 
638*44683af5Stobhe 		sc->sc_perf_domains[i].pd_curlevel = level;
639*44683af5Stobhe 		sc->sc_perf_fsensors[i].value =
640*44683af5Stobhe 		    (uint64_t)sc->sc_perf_domains[i].
641*44683af5Stobhe 		    pd_levels[level].pl_ifreq * 1000000000;
642*44683af5Stobhe 
643*44683af5Stobhe 		switch (sc->sc_perf_power_unit) {
644*44683af5Stobhe 		case SCMI_POWER_UNIT_UW:
645*44683af5Stobhe 			power_cost = (uint64_t)sc->sc_perf_domains[i].
646*44683af5Stobhe 			    pd_levels[level].pl_cost;
647*44683af5Stobhe 			break;
648*44683af5Stobhe 		case SCMI_POWER_UNIT_MW:
649*44683af5Stobhe 			power_cost = (uint64_t)sc->sc_perf_domains[i].
650*44683af5Stobhe 			    pd_levels[level].pl_cost * 1000;
651*44683af5Stobhe 			break;
652*44683af5Stobhe 		default:
653*44683af5Stobhe 			continue;
654*44683af5Stobhe 		}
655*44683af5Stobhe 		sc->sc_perf_psensors[i].value = power_cost;
656*44683af5Stobhe 	}
657*44683af5Stobhe }
658