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, ®)) { 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, ®)) { 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, ®)) { 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