aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/asmc/asmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/asmc/asmc.c')
-rw-r--r--sys/dev/asmc/asmc.c170
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));