From d80548ace53d64d712ae062359a16cdad3988e24 Mon Sep 17 00:00:00 2001 From: kettenis Date: Mon, 10 Jan 2022 09:07:28 +0000 Subject: [PATCH] Add aplsmc(4), a driver for the SMC found on Apple M1 SoCs. The SMC implements a lot of functionality. For now the driver only implements a bunch of sensors. This is a small subset of the sensors that are made available by the SMC as we don't know what measurments are provided for most sensors. ok patrick@ --- sys/arch/arm64/conf/GENERIC | 3 +- sys/arch/arm64/conf/files.arm64 | 6 +- sys/arch/arm64/dev/aplns.c | 10 +- sys/arch/arm64/dev/aplsmc.c | 349 ++++++++++++++++++++++++++++++++ sys/arch/arm64/dev/rtkit.c | 145 +++++++++---- sys/arch/arm64/dev/rtkit.h | 9 +- 6 files changed, 474 insertions(+), 48 deletions(-) create mode 100644 sys/arch/arm64/dev/aplsmc.c diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC index 91e926c198c..ba72ff6fa2f 100644 --- a/sys/arch/arm64/conf/GENERIC +++ b/sys/arch/arm64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.221 2022/01/08 15:30:46 kettenis Exp $ +# $OpenBSD: GENERIC,v 1.222 2022/01/10 09:07:28 kettenis Exp $ # # GENERIC machine description file # @@ -150,6 +150,7 @@ aplpcie* at fdt? pci* at aplpcie? aplpinctrl* at fdt? early 1 aplpmgr* at fdt? early 1 +aplsmc* at fdt? aplspi* at fdt? aplhidev* at spi? aplkbd* at aplhidev? diff --git a/sys/arch/arm64/conf/files.arm64 b/sys/arch/arm64/conf/files.arm64 index 7b216bbc74d..f3b4bffe3eb 100644 --- a/sys/arch/arm64/conf/files.arm64 +++ b/sys/arch/arm64/conf/files.arm64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.arm64,v 1.52 2022/01/08 15:30:46 kettenis Exp $ +# $OpenBSD: files.arm64,v 1.53 2022/01/10 09:07:28 kettenis Exp $ maxpartitions 16 maxusers 2 8 128 @@ -185,6 +185,10 @@ attach aplns at fdt attach nvme at aplns with nvme_ans file arch/arm64/dev/aplns.c aplns | nvme_ans +device aplsmc +attach aplsmc at fdt +file arch/arm64/dev/aplsmc.c aplsmc + define spmi {} device aplpmu attach aplpmu at spmi diff --git a/sys/arch/arm64/dev/aplns.c b/sys/arch/arm64/dev/aplns.c index 865dc6c2a3e..dbf7795960e 100644 --- a/sys/arch/arm64/dev/aplns.c +++ b/sys/arch/arm64/dev/aplns.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aplns.c,v 1.9 2022/01/08 15:23:42 kettenis Exp $ */ +/* $OpenBSD: aplns.c,v 1.10 2022/01/10 09:07:28 kettenis Exp $ */ /* * Copyright (c) 2014, 2021 David Gwynne * @@ -117,7 +117,7 @@ struct nvme_ans_softc { bus_space_tag_t asc_iot; bus_space_handle_t asc_ioh; - struct mbox_channel *asc_mbox; + struct rtkit_state *asc_rtkit; struct nvme_dmamem *asc_nvmmu; }; @@ -205,8 +205,8 @@ nvme_ans_attach(struct device *parent, struct device *self, void *aux) goto unmap; } - asc->asc_mbox = mbox_channel(faa->fa_node, NULL, NULL); - if (asc->asc_mbox == NULL) { + asc->asc_rtkit = rtkit_init(faa->fa_node, NULL); + if (asc->asc_rtkit == NULL) { printf(": can't map mailbox channel\n"); goto disestablish; } @@ -217,7 +217,7 @@ nvme_ans_attach(struct device *parent, struct device *self, void *aux) status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ANS_BOOT_STATUS); if (status != ANS_BOOT_STATUS_OK) - rtkit_init(asc->asc_mbox); + rtkit_boot(asc->asc_rtkit); status = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ANS_BOOT_STATUS); if (status != ANS_BOOT_STATUS_OK) { diff --git a/sys/arch/arm64/dev/aplsmc.c b/sys/arch/arm64/dev/aplsmc.c new file mode 100644 index 00000000000..ca3b923f919 --- /dev/null +++ b/sys/arch/arm64/dev/aplsmc.c @@ -0,0 +1,349 @@ +/* $OpenBSD: aplsmc.c,v 1.1 2022/01/10 09:07:28 kettenis Exp $ */ +/* + * Copyright (c) 2021 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#define SMC_EP 32 + +#define SMC_READ_KEY 0x10 +#define SMC_WRITE_KEY 0x11 +#define SMC_GET_KEY_BY_INDEX 0x12 +#define SMC_GET_KEY_INFO 0x13 +#define SMC_GET_SRAM_ADDR 0x17 + +#define SMC_ERROR(d) ((d) & 0xff) +#define SMC_OK 0x00 +#define SMC_KEYNOTFOUND 0x84 + +#define SMC_SRAM_SIZE 0x4000 + +#define SMC_KEY(s) ((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]) + +struct smc_key_info { + uint8_t size; + uint8_t type[4]; + uint8_t flags; +}; + +struct aplsmc_sensor { + const char *key; + const char *key_type; + enum sensor_type type; + int scale; + const char *desc; + int flags; +}; + +#define APLSMC_BE (1 << 0) + +#define APLSMC_MAX_SENSORS 16 + +struct aplsmc_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_space_handle_t sc_sram_ioh; + + void *sc_ih; + + struct rtkit_state *sc_rs; + uint8_t sc_msgid; + uint64_t sc_data; + + struct aplsmc_sensor *sc_smcsensors[APLSMC_MAX_SENSORS]; + struct ksensor sc_sensors[APLSMC_MAX_SENSORS]; + int sc_nsensors; + struct ksensordev sc_sensordev; +}; + +struct aplsmc_sensor aplsmc_sensors[] = { + { "B0RM", "ui16", SENSOR_AMPHOUR, 1000, "remaining battery capacity", + APLSMC_BE }, + { "B0FC", "ui16", SENSOR_AMPHOUR, 1000, "last full battery capacity" }, + { "B0DC", "ui16", SENSOR_AMPHOUR, 1000, "battery design capacity" }, + { "B0AV", "ui16", SENSOR_VOLTS_DC, 1000, "battery" }, + { "B0CT", "ui16", SENSOR_INTEGER, 1, "battery discharge cycles" }, + { "F0Ac", "flt ", SENSOR_FANRPM, 1, "" }, + { "ID0R", "flt ", SENSOR_AMPS, 1000000, "input" }, + { "PDTR", "flt ", SENSOR_WATTS, 1000000, "input" }, + { "PSTR", "flt ", SENSOR_WATTS, 1000000, "system" }, + { "TB0T", "flt ", SENSOR_TEMP, 1000000, "battery" }, + { "TCHP", "flt ", SENSOR_TEMP, 1000000, "charger" }, + { "TW0P", "flt ", SENSOR_TEMP, 1000000, "wireless" }, + { "Ts0P", "flt ", SENSOR_TEMP, 1000000, "palm rest" }, + { "Ts1P", "flt ", SENSOR_TEMP, 1000000, "palm rest" }, + { "VD0R", "flt ", SENSOR_VOLTS_DC, 1000000, "input" }, +}; + +int aplsmc_match(struct device *, void *, void *); +void aplsmc_attach(struct device *, struct device *, void *); + +const struct cfattach aplsmc_ca = { + sizeof (struct aplsmc_softc), aplsmc_match, aplsmc_attach +}; + +struct cfdriver aplsmc_cd = { + NULL, "aplsmc", DV_DULL +}; + +void aplsmc_callback(void *, uint64_t); +int aplsmc_send_cmd(struct aplsmc_softc *, uint16_t, uint32_t, uint16_t); +int aplsmc_wait_cmd(struct aplsmc_softc *sc); +int aplsmc_read_key(struct aplsmc_softc *, uint32_t, void *, size_t); +void aplsmc_refresh_sensors(void *); + +int +aplsmc_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "apple,smc"); +} + +void +aplsmc_attach(struct device *parent, struct device *self, void *aux) +{ + struct aplsmc_softc *sc = (struct aplsmc_softc *)self; + struct fdt_attach_args *faa = aux; + int error; + int i; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + + sc->sc_rs = rtkit_init(faa->fa_node, NULL); + if (sc->sc_rs == NULL) { + printf(": can't map mailbox channel\n"); + return; + } + + error = rtkit_boot(sc->sc_rs); + if (error) { + printf(": can't boot firmware\n"); + return; + } + + error = rtkit_start_endpoint(sc->sc_rs, SMC_EP, aplsmc_callback, sc); + if (error) { + printf(": can't start SMC endpoint\n"); + return; + } + + aplsmc_send_cmd(sc, SMC_GET_SRAM_ADDR, 0, 0); + error = aplsmc_wait_cmd(sc); + if (error) { + printf(": can't get SRAM address\n"); + return; + } + + if (bus_space_map(sc->sc_iot, sc->sc_data, + SMC_SRAM_SIZE, 0, &sc->sc_sram_ioh)) { + printf(": can't map SRAM\n"); + return; + } + + printf("\n"); + + for (i = 0; i < nitems(aplsmc_sensors); i++) { + struct smc_key_info info; + + aplsmc_send_cmd(sc, SMC_GET_KEY_INFO, + SMC_KEY(aplsmc_sensors[i].key), 0); + error = aplsmc_wait_cmd(sc); + if (error || SMC_ERROR(sc->sc_data) != SMC_OK) + continue; + + bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0, + (uint8_t *)&info, sizeof(info)); + + /* Skip if the key type doesn't match. */ + if (memcmp(aplsmc_sensors[i].key_type, info.type, + sizeof(info.type)) != 0) + continue; + + if (sc->sc_nsensors >= APLSMC_MAX_SENSORS) { + printf("%s: maxumum number of sensors exceeded\n", + sc->sc_dev.dv_xname); + break; + } + + sc->sc_smcsensors[sc->sc_nsensors] = &aplsmc_sensors[i]; + strlcpy(sc->sc_sensors[sc->sc_nsensors].desc, + aplsmc_sensors[i].desc, sizeof(sc->sc_sensors[0].desc)); + sc->sc_sensors[sc->sc_nsensors].type = aplsmc_sensors[i].type; + sensor_attach(&sc->sc_sensordev, + &sc->sc_sensors[sc->sc_nsensors]); + sc->sc_nsensors++; + } + + aplsmc_refresh_sensors(sc); + + strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, + sizeof(sc->sc_sensordev.xname)); + sensordev_install(&sc->sc_sensordev); + sensor_task_register(sc, aplsmc_refresh_sensors, 5); +} + +void +aplsmc_callback(void *arg, uint64_t data) +{ + struct aplsmc_softc *sc = arg; + + sc->sc_data = data; + wakeup(&sc->sc_data); +} + +int +aplsmc_send_cmd(struct aplsmc_softc *sc, uint16_t cmd, uint32_t key, + uint16_t len) +{ + uint64_t data; + + data = cmd; + data |= (uint64_t)len << 16; + data |= (uint64_t)key << 32; + data |= (sc->sc_msgid++ & 0xf) << 12; + + return rtkit_send_endpoint(sc->sc_rs, SMC_EP, data); +} + +int +aplsmc_wait_cmd(struct aplsmc_softc *sc) +{ + if (cold) { + int error, timo; + + /* Poll for completion. */ + for (timo = 1000; timo > 0; timo--) { + error = rtkit_poll(sc->sc_rs); + if (error == 0) + return 0; + delay(10); + } + + return EWOULDBLOCK; + } + + /* Sleep until the callback wakes us up. */ + return tsleep_nsec(&sc->sc_data, PWAIT, "apsmc", 10000000); +} + +int +aplsmc_read_key(struct aplsmc_softc *sc, uint32_t key, void *data, size_t len) +{ + int error; + + aplsmc_send_cmd(sc, SMC_READ_KEY, key, len); + error = aplsmc_wait_cmd(sc); + if (error) + return error; + + len = min(len, (sc->sc_data >> 16) & 0xffff); + if (len > sizeof(uint32_t)) { + bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0, + data, len); + } else { + uint32_t tmp = (sc->sc_data >> 32); + memcpy(data, &tmp, len); + } + + return 0; +} + +void +aplsmc_refresh_sensors(void *arg) +{ + struct aplsmc_softc *sc = arg; + struct aplsmc_sensor *sensor; + int64_t value; + uint32_t key; + int i, error; + + for (i = 0; i < sc->sc_nsensors; i++) { + sensor = sc->sc_smcsensors[i]; + key = SMC_KEY(sensor->key); + + if (strcmp(sensor->key_type, "ui8 ") == 0) { + uint8_t ui8; + + error = aplsmc_read_key(sc, key, &ui8, sizeof(ui8)); + value = (int64_t)ui8 * sensor->scale; + } else if (strcmp(sensor->key_type, "ui16") == 0) { + uint16_t ui16; + + error = aplsmc_read_key(sc, key, &ui16, sizeof(ui16)); + if (sensor->flags & APLSMC_BE) + ui16 = betoh16(ui16); + value = (int64_t)ui16 * sensor->scale; + } else if (strcmp(sensor->key_type, "flt ") == 0) { + uint32_t flt; + int64_t mant; + int sign, exp; + + error = aplsmc_read_key(sc, key, &flt, sizeof(flt)); + if (sensor->flags & APLSMC_BE) + flt = betoh32(flt); + + /* + * Convert floating-point to integer, trying + * to keep as much resolution as possible + * given the scaling factor for this sensor. + */ + sign = (flt >> 31) ? -1 : 1; + exp = ((flt >> 23) & 0xff) - 127; + mant = (flt & 0x7fffff) | 0x800000; + mant *= sensor->scale; + if (exp < 23) + value = sign * (mant >> (23 - exp)); + else + value = sign * (mant << (exp - 23)); + } + + /* Apple reports temperatures in degC. */ + if (sensor->type == SENSOR_TEMP) + value += 273150000; + + if (error) { + sc->sc_sensors[0].flags |= SENSOR_FUNKNOWN; + } else { + sc->sc_sensors[i].flags &= ~SENSOR_FUNKNOWN; + sc->sc_sensors[i].value = value; + } + } +} diff --git a/sys/arch/arm64/dev/rtkit.c b/sys/arch/arm64/dev/rtkit.c index d26ea475694..fbf32c31e47 100644 --- a/sys/arch/arm64/dev/rtkit.c +++ b/sys/arch/arm64/dev/rtkit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtkit.c,v 1.2 2022/01/02 02:31:08 jsg Exp $ */ +/* $OpenBSD: rtkit.c,v 1.3 2022/01/10 09:07:28 kettenis Exp $ */ /* * Copyright (c) 2021 Mark Kettenis * @@ -28,6 +28,7 @@ #include #include +#include #define RTKIT_EP_MGMT 0 #define RTKIT_EP_CRASHLOG 1 @@ -75,6 +76,8 @@ struct rtkit_state { struct mbox_channel *mc; int pwrstate; uint64_t epmap; + void (*callback[32])(void *, uint64_t); + void *arg[32]; }; int @@ -104,15 +107,12 @@ rtkit_send(struct mbox_channel *mc, uint32_t endpoint, } int -rtkit_start_ep(struct rtkit_state *state, uint64_t ep) +rtkit_start(struct rtkit_state *state, uint32_t endpoint) { struct mbox_channel *mc = state->mc; uint64_t reply; - if ((state->epmap & (1ULL << ep)) == 0) - return EINVAL; - - reply = (ep << RTKIT_MGMT_STARTEP_EP_SHIFT); + reply = ((uint64_t)endpoint << RTKIT_MGMT_STARTEP_EP_SHIFT); reply |= RTKIT_MGMT_STARTEP_START; return rtkit_send(mc, RTKIT_EP_MGMT, RTKIT_MGMT_STARTEP, reply); } @@ -123,8 +123,8 @@ rtkit_handle_mgmt(struct rtkit_state *state, struct aplmbox_msg *msg) struct mbox_channel *mc = state->mc; uint64_t minver, maxver, ver; uint64_t base, bitmap, reply; + uint32_t endpoint; int error; - uint8_t ep; switch (RTKIT_MGMT_TYPE(msg->data0)) { case RTKIT_MGMT_HELLO: @@ -166,15 +166,15 @@ rtkit_handle_mgmt(struct rtkit_state *state, struct aplmbox_msg *msg) if (error) return error; if (msg->data0 & RTKIT_MGMT_EPMAP_LAST) { - for (ep = 1; ep < 32; ep++) { - if ((state->epmap & (1ULL << ep)) == 0) + for (endpoint = 1; endpoint < 32; endpoint++) { + if ((state->epmap & (1ULL << endpoint)) == 0) continue; - switch (ep) { + switch (endpoint) { case RTKIT_EP_CRASHLOG: case RTKIT_EP_DEBUG: case RTKIT_EP_IOREPORT: - error = rtkit_start_ep(state, ep); + error = rtkit_start(state, endpoint); if (error) return error; break; @@ -247,47 +247,112 @@ rtkit_handle_ioreport(struct rtkit_state *state, struct aplmbox_msg *msg) } int -rtkit_init(struct mbox_channel *mc) +rtkit_poll(struct rtkit_state *state) { - struct rtkit_state state; + struct mbox_channel *mc = state->mc; struct aplmbox_msg msg; + void (*callback)(void *, uint64_t); + void *arg; + uint32_t endpoint; int error; - memset(&state, 0, sizeof(state)); - state.mc = mc; - - /* Wake up! */ - error = rtkit_send(state.mc, RTKIT_EP_MGMT, RTKIT_MGMT_IOP_PWR_STATE, - RTKIT_MGMT_PWR_STATE_ON); + error = rtkit_recv(mc, &msg); if (error) return error; - while (state.pwrstate != RTKIT_MGMT_PWR_STATE_ON) { - error = rtkit_recv(state.mc, &msg); + endpoint = msg.data1; + switch (endpoint) { + case RTKIT_EP_MGMT: + error = rtkit_handle_mgmt(state, &msg); if (error) return error; - - switch (msg.data1) { - case RTKIT_EP_MGMT: - error = rtkit_handle_mgmt(&state, &msg); - if (error) - return error; - break; - case RTKIT_EP_CRASHLOG: - error = rtkit_handle_crashlog(&state, &msg); - if (error) - return error; - break; - case RTKIT_EP_IOREPORT: - error = rtkit_handle_ioreport(&state, &msg); - if (error) - return error; + break; + case RTKIT_EP_CRASHLOG: + error = rtkit_handle_crashlog(state, &msg); + if (error) + return error; + break; + case RTKIT_EP_IOREPORT: + error = rtkit_handle_ioreport(state, &msg); + if (error) + return error; + break; + default: + if (endpoint >= 32 && endpoint < 64 && + state->callback[endpoint - 32]) { + callback = state->callback[endpoint - 32]; + arg = state->arg[endpoint - 32]; + callback(arg, msg.data0); break; - default: - printf("unhandled endpoint %d\n", msg.data1); - return EIO; } + + printf("unhandled endpoint %d\n", msg.data1); + return EIO; } return 0; } + +void +rtkit_rx_callback(void *cookie) +{ + rtkit_poll(cookie); +} + +struct rtkit_state * +rtkit_init(int node, const char *name) +{ + struct rtkit_state *state; + struct mbox_client client; + + state = malloc(sizeof(*state), M_DEVBUF, M_WAITOK | M_ZERO); + client.mc_rx_callback = rtkit_rx_callback; + client.mc_rx_arg = state; + state->mc = mbox_channel(node, name, &client); + if (state->mc == NULL) { + free(state, M_DEVBUF, sizeof(*state)); + return NULL; + } + + return state; +} + +int +rtkit_boot(struct rtkit_state *state) +{ + struct mbox_channel *mc = state->mc; + int error; + + /* Wake up! */ + error = rtkit_send(mc, RTKIT_EP_MGMT, RTKIT_MGMT_IOP_PWR_STATE, + RTKIT_MGMT_PWR_STATE_ON); + if (error) + return error; + + while (state->pwrstate != RTKIT_MGMT_PWR_STATE_ON) + rtkit_poll(state); + + return 0; +} + +int +rtkit_start_endpoint(struct rtkit_state *state, uint32_t endpoint, + void (*callback)(void *, uint64_t), void *arg) +{ + if (endpoint < 32 || endpoint >= 64) + return EINVAL; + + if ((state->epmap & (1ULL << endpoint)) == 0) + return EINVAL; + + state->callback[endpoint - 32] = callback; + state->arg[endpoint - 32] = arg; + return rtkit_start(state, endpoint); +} + +int +rtkit_send_endpoint(struct rtkit_state *state, uint32_t endpoint, + uint64_t data) +{ + return rtkit_send(state->mc, endpoint, 0, data); +} diff --git a/sys/arch/arm64/dev/rtkit.h b/sys/arch/arm64/dev/rtkit.h index c8df0b6f32d..cdfd7ca0260 100644 --- a/sys/arch/arm64/dev/rtkit.h +++ b/sys/arch/arm64/dev/rtkit.h @@ -1,3 +1,10 @@ /* public domain */ -int rtkit_init(struct mbox_channel *); +struct rtkit_state; + +struct rtkit_state *rtkit_init(int, const char *); +int rtkit_boot(struct rtkit_state *); +int rtkit_poll(struct rtkit_state *); +int rtkit_start_endpoint(struct rtkit_state *, uint32_t, + void (*)(void *, uint64_t), void *); +int rtkit_send_endpoint(struct rtkit_state *, uint32_t, uint64_t); -- 2.20.1