/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2026 Abdelkader Boudih * * Ported from FreeBSD's asmc(4) driver. * * DragonFlyBSD port: adapted for kmalloc/kfree, lockmgr 2-arg form, * taskqueue_start_threads ncpu arg, sys/bus_resource.h, acpica paths. * Added MMIO (T2) backend support. */ /* * Apple SMC — MMIO/T2 backend. * * apple_smc_try_enable_mmio() and the MMIO key read/write/getinfo/getbyindex * functions used on T2 (and later) Macs that expose the SMC over a memory- * mapped register window rather than ISA ports. */ #include "smc.h" static int apple_smc_mmio_wait(device_t dev) { struct apple_smc_softc *sc = device_get_softc(dev); int i, delay_us = 10; uint8_t status; for (i = 0; i < ASMC_MMIO_MAX_WAIT; i++) { status = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS); if (status & ASMC_MMIO_STATUS_READY) return (0); DELAY(delay_us); if (delay_us < 3200) delay_us *= 2; } return (ETIMEDOUT); } int apple_smc_mmio_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len) { struct apple_smc_softc *sc = device_get_softc(dev); uint32_t key_int; int error, i; uint8_t cmd_result, rlen; if (len > ASMC_MAXVAL) return (EINVAL); SMC_LOCK(sc); if (bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS)) bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0); memcpy(&key_int, key, 4); bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int); bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0); bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDREAD); error = apple_smc_mmio_wait(dev); if (error != 0) { SMC_UNLOCK(sc); return (error); } cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD); if (cmd_result != 0) { SMC_UNLOCK(sc); return (EIO); } rlen = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA_LEN); if (rlen > len) rlen = len; for (i = 0; i < rlen; i++) buf[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i); SMC_UNLOCK(sc); return (0); } int apple_smc_mmio_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) { struct apple_smc_softc *sc = device_get_softc(dev); uint32_t key_int; int error, i; uint8_t cmd_result; if (len > ASMC_MAXVAL) return (EINVAL); SMC_LOCK(sc); bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0); for (i = 0; i < len; i++) bus_write_1(sc->sc_iomem, ASMC_MMIO_DATA + i, buf[i]); memcpy(&key_int, key, 4); bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int); bus_write_1(sc->sc_iomem, ASMC_MMIO_DATA_LEN, len); bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0); bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDWRITE); error = apple_smc_mmio_wait(dev); if (error != 0) { SMC_UNLOCK(sc); return (error); } cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD); SMC_UNLOCK(sc); return (cmd_result == 0 ? 0 : EIO); } int apple_smc_mmio_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type) { struct apple_smc_softc *sc = device_get_softc(dev); uint32_t key_int; int error, i; uint8_t cmd_result; SMC_LOCK(sc); bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0); memcpy(&key_int, key, 4); bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int); bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0); bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDGETINFO); error = apple_smc_mmio_wait(dev); if (error != 0) { SMC_UNLOCK(sc); return (error); } cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD); if (cmd_result != 0) { SMC_UNLOCK(sc); return (EIO); } if (type != NULL) { for (i = 0; i < ASMC_TYPELEN; i++) type[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i); type[ASMC_TYPELEN] = '\0'; } if (len != NULL) *len = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + 5); SMC_UNLOCK(sc); return (0); } int apple_smc_mmio_key_getbyindex(device_t dev, int index, char *key) { struct apple_smc_softc *sc = device_get_softc(dev); uint32_t idx_val; int error, i; uint8_t cmd_result; SMC_LOCK(sc); bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0); idx_val = htobe32(index); bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, idx_val); bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0); bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDGETBYINDEX); error = apple_smc_mmio_wait(dev); if (error != 0) { SMC_UNLOCK(sc); return (error); } cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD); if (cmd_result != 0) { SMC_UNLOCK(sc); return (EIO); } for (i = 0; i < ASMC_KEYLEN; i++) key[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i); key[ASMC_KEYLEN] = '\0'; SMC_UNLOCK(sc); return (0); } int apple_smc_try_enable_mmio(device_t dev) { struct apple_smc_softc *sc = device_get_softc(dev); u_long size; uint8_t status, ldkn, rgen; int error; size = rman_get_size(sc->sc_iomem); if (size < ASMC_MMIO_MIN_SIZE) return (ENXIO); status = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS); if (status == 0xFF) return (ENXIO); error = apple_smc_mmio_key_read(dev, ASMC_KEY_LDKN, &ldkn, 1); if (error != 0) return (ENXIO); /* RGEN=3 indicates T2 coprocessor; LDKN just tracks firmware key gen */ rgen = 0; error = apple_smc_mmio_key_read(dev, ASMC_KEY_RGEN, &rgen, 1); if (error != 0) { device_printf(dev, "MMIO: failed to read RGEN: %d\n", error); } sc->sc_is_t2 = (rgen == 3); device_printf(dev, "MMIO: LDKN=%d%s\n", ldkn, sc->sc_is_t2 ? ", T2 detected" : ""); return (0); }