diff options
Diffstat (limited to 'sys/dev/asmc/asmc.c')
| -rw-r--r-- | sys/dev/asmc/asmc.c | 170 |
1 files changed, 146 insertions, 24 deletions
diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c index 8cd7842d03fd..9b7adb61e6de 100644 --- a/sys/dev/asmc/asmc.c +++ b/sys/dev/asmc/asmc.c @@ -57,6 +57,7 @@ #include <dev/acpica/acpivar.h> #include <dev/asmc/asmcvar.h> +#include <dev/asmc/asmcmmio.h> #include <dev/backlight/backlight.h> #include "backlight_if.h" @@ -426,17 +427,40 @@ asmc_attach(device_t dev) struct sysctl_ctx_list *sysctlctx; struct sysctl_oid *sysctlnode; - sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, - &sc->sc_rid_port, RF_ACTIVE); - if (sc->sc_ioport == NULL) { - device_printf(dev, "unable to allocate IO port\n"); - return (ENOMEM); + /* + * Try MMIO first (T2 Macs expose SMC via memory-mapped I/O). + * Fall back to standard I/O port if MMIO is not available. + */ + sc->sc_rid_mem = 0; + sc->sc_iomem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_rid_mem, RF_ACTIVE); + if (sc->sc_iomem != NULL) { + if (asmc_mmio_probe(dev) == 0) { + sc->sc_is_mmio = 1; + device_printf(dev, "using MMIO backend (T2)\n"); + } else { + bus_release_resource(dev, SYS_RES_MEMORY, + sc->sc_rid_mem, sc->sc_iomem); + sc->sc_iomem = NULL; + } + } + + if (!sc->sc_is_mmio) { + sc->sc_ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &sc->sc_rid_port, RF_ACTIVE); + if (sc->sc_ioport == NULL) { + device_printf(dev, "unable to allocate IO port\n"); + ret = ENOMEM; + goto err; + } } sysctlctx = device_get_sysctl_ctx(dev); sysctlnode = device_get_sysctl_tree(dev); - mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); + /* Mutex may already be initialized by asmc_mmio_probe() */ + if (!mtx_initialized(&sc->sc_mtx)) + mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN); /* Read SMC revision, key count, fan count */ ret = asmc_init(dev); @@ -615,6 +639,18 @@ asmc_attach(device_t dev) "SMC key type (4 chars)"); #endif + /* + * Battery charge limit (T2 Macs). + */ + if (sc->sc_is_t2 && + asmc_key_getinfo(dev, ASMC_KEY_BCLM, NULL, NULL) == 0) { + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "battery_charge_limit", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, + dev, 0, asmc_bclm_sysctl, "I", + "Battery charge limit (0-100)"); + } + if (!sc->sc_has_sms) goto nosms; @@ -736,6 +772,7 @@ asmc_detach(device_t dev) sc->sc_ioport); sc->sc_ioport = NULL; } + asmc_mmio_detach(dev, sc); if (mtx_initialized(&sc->sc_mtx)) { mtx_destroy(&sc->sc_mtx); } @@ -788,10 +825,25 @@ asmc_init(device_t dev) sysctlctx = device_get_sysctl_ctx(dev); error = asmc_key_read(dev, ASMC_KEY_REV, buf, 6); - if (error != 0) - goto out; - device_printf(dev, "SMC revision: %x.%x%x%x\n", buf[0], buf[1], buf[2], - ntohs(*(uint16_t *)buf + 4)); + if (error != 0) { + /* + * Could not read REV key; T2 Macs may not have it. + * Use #KEY as a liveness check instead. + */ + if (sc->sc_is_t2) { + error = asmc_key_read(dev, ASMC_NKEYS, buf, 4); + if (error != 0) + goto out; + device_printf(dev, "T2 SMC: %d keys\n", + be32dec(buf)); + } else { + goto out; + } + } else { + device_printf(dev, "SMC revision: %x.%x%x%x\n", + buf[0], buf[1], buf[2], + ntohs(*(uint16_t *)buf + 4)); + } /* Auto power-on after AC power loss (AUPO). */ if (asmc_key_read(dev, ASMC_KEY_AUPO, buf, 1) == 0) { @@ -1041,8 +1093,11 @@ asmc_command(device_t dev, uint8_t command) static int asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len) { - int i, error = 1, try = 0; struct asmc_softc *sc = device_get_softc(dev); + int i, error = 1, try = 0; + + if (sc->sc_is_mmio) + return (asmc_mmio_key_read(dev, key, buf, len)); mtx_lock_spin(&sc->sc_mtx); @@ -1180,6 +1235,9 @@ asmc_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type) uint8_t info[ASMC_KEYINFO_RESPLEN]; int i, error = -1, try = 0; + if (sc->sc_is_mmio) + return (asmc_mmio_key_getinfo(dev, key, len, type)); + mtx_lock_spin(&sc->sc_mtx); begin: @@ -1715,6 +1773,14 @@ asmc_key_dump_by_index(device_t dev, int index, char *key_out, int error = ENXIO, try = 0; int i; + if (sc->sc_is_mmio) { + error = asmc_mmio_key_getbyindex(dev, index, key_out); + if (error != 0) + return (error); + return (asmc_mmio_key_getinfo(dev, key_out, len_out, + type_out)); + } + mtx_lock_spin(&sc->sc_mtx); index_buf[0] = (index >> 24) & 0xff; @@ -1808,8 +1874,11 @@ asmc_key_search(device_t dev, const char *prefix, unsigned int *idx) static int asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len) { - int i, error = -1, try = 0; struct asmc_softc *sc = device_get_softc(dev); + int i, error = -1, try = 0; + + if (sc->sc_is_mmio) + return (asmc_mmio_key_write(dev, key, buf, len)); mtx_lock_spin(&sc->sc_mtx); @@ -1865,14 +1934,30 @@ asmc_fan_count(device_t dev) static int asmc_fan_getvalue(device_t dev, const char *key, int fan) { + struct asmc_softc *sc = device_get_softc(dev); int speed; - uint8_t buf[2]; + uint8_t buf[4]; char fankey[5]; + char type[ASMC_TYPELEN + 1]; snprintf(fankey, sizeof(fankey), key, fan); - if (asmc_key_read(dev, fankey, buf, sizeof(buf)) != 0) - return (-1); - speed = (buf[0] << 6) | (buf[1] >> 2); + + /* + * T2 Macs use IEEE 754 float ("flt ") for fan speeds, + * stored little-endian in the MMIO data register. + * Standard Macs use s14.2 fixed-point ("fpe2", 2 bytes). + */ + if (sc->sc_is_t2 && + asmc_key_getinfo(dev, fankey, NULL, type) == 0 && + strncmp(type, "flt ", 4) == 0) { + if (asmc_key_read(dev, fankey, buf, 4) != 0) + return (-1); + speed = (int)asmc_float_to_u32(le32dec(buf)); + } else { + if (asmc_key_read(dev, fankey, buf, 2) != 0) + return (-1); + speed = (buf[0] << 6) | (buf[1] >> 2); + } return (speed); } @@ -1895,17 +1980,30 @@ asmc_fan_getstring(device_t dev, const char *key, int fan, uint8_t *buf, static int asmc_fan_setvalue(device_t dev, const char *key, int fan, int speed) { - uint8_t buf[2]; + struct asmc_softc *sc = device_get_softc(dev); + uint8_t buf[4]; char fankey[5]; - - speed *= 4; - - buf[0] = speed >> 8; - buf[1] = speed; + char type[ASMC_TYPELEN + 1]; snprintf(fankey, sizeof(fankey), key, fan); - if (asmc_key_write(dev, fankey, buf, sizeof(buf)) < 0) - return (-1); + + if (sc->sc_is_t2 && + asmc_key_getinfo(dev, fankey, NULL, type) == 0 && + strncmp(type, "flt ", 4) == 0) { + uint32_t fval; + speed = MAX(speed, 0); + speed = MIN(speed, 65535); + fval = asmc_u32_to_float((uint32_t)speed); + le32enc(buf, fval); + if (asmc_key_write(dev, fankey, buf, 4) != 0) + return (-1); + } else { + speed *= 4; + buf[0] = speed >> 8; + buf[1] = speed; + if (asmc_key_write(dev, fankey, buf, 2) != 0) + return (-1); + } return (0); } @@ -2016,11 +2114,35 @@ static int asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS) { device_t dev = (device_t)arg1; + struct asmc_softc *sc = device_get_softc(dev); int fan = arg2; int error; int32_t v; uint8_t buf[2]; uint16_t val; + char fmkey[5]; + + /* + * T2 Macs use per-fan F%dMd keys (1 byte each). + * Standard Macs use FS! bitmask (2 bytes). + */ + snprintf(fmkey, sizeof(fmkey), ASMC_KEY_FANMANUAL_T2, fan); + if (sc->sc_is_t2 && + asmc_key_getinfo(dev, fmkey, NULL, NULL) == 0) { + error = asmc_key_read(dev, fmkey, buf, 1); + if (error != 0) + return (error); + v = buf[0] ? 1 : 0; + + error = sysctl_handle_int(oidp, &v, 0, req); + if (error == 0 && req->newptr != NULL) { + if (v != 0 && v != 1) + return (EINVAL); + buf[0] = (uint8_t)v; + error = asmc_key_write(dev, fmkey, buf, 1); + } + return (error); + } /* Read current FS! bitmask (asmc_key_read locks internally) */ error = asmc_key_read(dev, ASMC_KEY_FANMANUAL, buf, sizeof(buf)); |
