Page MenuHomeFreeBSD

D57086.diff
No OneTemporary

D57086.diff

diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -113,6 +113,7 @@
crypto/openssl/ossl_aes_gcm.c optional ossl
dev/amdgpio/amdgpio.c optional amdgpio
dev/asmc/asmc.c optional asmc isa
+dev/asmc/asmcmmio.c optional asmc isa
dev/axgbe/if_axgbe_pci.c optional axp
dev/axgbe/xgbe-desc.c optional axp
dev/axgbe/xgbe-dev.c optional axp
diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c
--- 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 @@
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 @@
"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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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));
diff --git a/sys/dev/asmc/asmcmmio.h b/sys/dev/asmc/asmcmmio.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/asmc/asmcmmio.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _DEV_ASMC_ASMCMMIO_H_
+#define _DEV_ASMC_ASMCMMIO_H_
+
+struct asmc_softc;
+
+/*
+ * MMIO register offsets.
+ */
+#define ASMC_MMIO_DATA 0x0000
+#define ASMC_MMIO_KEY_NAME 0x0078
+#define ASMC_MMIO_DATA_LEN 0x007D
+#define ASMC_MMIO_SMC_ID 0x007E
+#define ASMC_MMIO_CMD 0x007F
+#define ASMC_MMIO_STATUS 0x4005
+#define ASMC_MMIO_MIN_SIZE 0x4006
+#define ASMC_MMIO_STATUS_READY 0x20 /* Bit 5 */
+#define ASMC_MMIO_MAX_WAIT 24
+
+/*
+ * T2-specific keys.
+ */
+#define ASMC_KEY_LDKN "LDKN" /* RO; 1 byte, firmware version */
+#define ASMC_KEY_BCLM "BCLM" /* RW; 1 byte, battery charge limit 0-100 */
+#define ASMC_KEY_FANMANUAL_T2 "F%dMd" /* RW; 1 byte per fan (T2) */
+
+/*
+ * MMIO backend functions.
+ */
+int asmc_mmio_probe(device_t dev);
+void asmc_mmio_detach(device_t dev, struct asmc_softc *sc);
+int asmc_mmio_key_read(device_t dev, const char *key,
+ uint8_t *buf, uint8_t len);
+int asmc_mmio_key_write(device_t dev, const char *key,
+ uint8_t *buf, uint8_t len);
+int asmc_mmio_key_getinfo(device_t dev, const char *key,
+ uint8_t *len, char *type);
+int asmc_mmio_key_getbyindex(device_t dev, int index, char *key);
+
+/*
+ * IEEE 754 float <-> uint32 conversion for T2 fan RPM values.
+ */
+uint32_t asmc_float_to_u32(uint32_t d);
+uint32_t asmc_u32_to_float(uint32_t d);
+
+/*
+ * T2-specific sysctls.
+ */
+int asmc_bclm_sysctl(SYSCTL_HANDLER_ARGS);
+
+#endif /* _DEV_ASMC_ASMCMMIO_H_ */
diff --git a/sys/dev/asmc/asmcmmio.c b/sys/dev/asmc/asmcmmio.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/asmc/asmcmmio.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/*
+ * MMIO backend for Apple SMC (T2 and later Macs).
+ *
+ * T2 Macs expose the SMC via memory-mapped registers instead of I/O ports.
+ * Protocol: clear status, write key/cmd, poll for ready, read result.
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+
+#include <dev/asmc/asmcvar.h>
+#include <dev/asmc/asmcmmio.h>
+
+/*
+ * Wait for MMIO status register bit 5 (ready) with exponential backoff.
+ * Caller must hold sc_mtx.
+ */
+static int
+asmc_mmio_wait(device_t dev)
+{
+ struct asmc_softc *sc = device_get_softc(dev);
+ int i;
+ uint8_t status;
+ int delay_us = 10;
+
+ 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
+asmc_mmio_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
+{
+ struct asmc_softc *sc = device_get_softc(dev);
+ uint32_t key_int;
+ int error, i;
+ uint8_t cmd_result, rlen;
+
+ if (len > ASMC_MAXVAL)
+ return (EINVAL);
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+ /* Clear status if non-zero */
+ if (bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS))
+ bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0);
+
+ /* Write key name as raw 4 bytes */
+ memcpy(&key_int, key, 4);
+ bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int);
+
+ /* Write SMC ID (always 0) and command */
+ bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0);
+ bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDREAD);
+
+ /* Wait for ready */
+ error = asmc_mmio_wait(dev);
+ if (error != 0) {
+ uint8_t st = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS);
+ uint8_t cm = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
+ mtx_unlock_spin(&sc->sc_mtx);
+ device_printf(dev,
+ "%s: timeout key %.4s status=0x%02x cmd=0x%02x\n",
+ __func__, key, st, cm);
+ return (error);
+ }
+
+ /* Check command result (0 = success, 0x84 = key not found) */
+ cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
+ if (cmd_result != 0) {
+ mtx_unlock_spin(&sc->sc_mtx);
+ device_printf(dev,
+ "%s: key %.4s cmd error 0x%02x\n",
+ __func__, key, cmd_result);
+ return (EIO);
+ }
+
+ /* Read data length and data bytes; zero-fill remainder */
+ rlen = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA_LEN);
+ rlen = MIN(rlen, len);
+ for (i = rlen; i < len; i++)
+ buf[i] = 0;
+ for (i = 0; i < rlen; i++)
+ buf[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i);
+
+ mtx_unlock_spin(&sc->sc_mtx);
+ return (0);
+}
+
+int
+asmc_mmio_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
+{
+ struct asmc_softc *sc = device_get_softc(dev);
+ uint32_t key_int;
+ int error, i;
+ uint8_t cmd_result;
+
+ if (len > ASMC_MAXVAL)
+ return (EINVAL);
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+ /* Clear status */
+ bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0);
+
+ /* Write data bytes first */
+ for (i = 0; i < len; i++)
+ bus_write_1(sc->sc_iomem, ASMC_MMIO_DATA + i, buf[i]);
+
+ /* Write key name as raw 4 bytes */
+ memcpy(&key_int, key, 4);
+ bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int);
+
+ /* Write length, SMC ID, command */
+ 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);
+
+ /* Wait for ready */
+ error = asmc_mmio_wait(dev);
+ if (error != 0) {
+ mtx_unlock_spin(&sc->sc_mtx);
+ device_printf(dev, "%s: timeout writing key %.4s\n",
+ __func__, key);
+ return (error);
+ }
+
+ cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
+ mtx_unlock_spin(&sc->sc_mtx);
+
+ return (cmd_result == 0 ? 0 : EIO);
+}
+
+int
+asmc_mmio_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type)
+{
+ struct asmc_softc *sc = device_get_softc(dev);
+ uint32_t key_int;
+ int error, i;
+ uint8_t cmd_result;
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+ /* Clear status */
+ bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0);
+
+ /* Write key name as raw 4 bytes */
+ 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 = asmc_mmio_wait(dev);
+ if (error != 0) {
+ mtx_unlock_spin(&sc->sc_mtx);
+ return (error);
+ }
+
+ cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
+ if (cmd_result != 0) {
+ mtx_unlock_spin(&sc->sc_mtx);
+ return (EIO);
+ }
+
+ /*
+ * GETINFO response layout (MMIO):
+ * data[0..3] = type code (4 chars)
+ * data[4] = reserved
+ * data[5] = data length
+ * data[6] = flags/attributes
+ */
+ 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);
+
+ mtx_unlock_spin(&sc->sc_mtx);
+ return (0);
+}
+
+int
+asmc_mmio_key_getbyindex(device_t dev, int index, char *key)
+{
+ struct asmc_softc *sc = device_get_softc(dev);
+ uint32_t idx_val;
+ int error, i;
+ uint8_t cmd_result;
+
+ mtx_lock_spin(&sc->sc_mtx);
+
+ bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0);
+
+ /* Write index as big-endian 4 bytes to key name register */
+ 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 = asmc_mmio_wait(dev);
+ if (error != 0) {
+ mtx_unlock_spin(&sc->sc_mtx);
+ return (error);
+ }
+
+ cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
+ if (cmd_result != 0) {
+ mtx_unlock_spin(&sc->sc_mtx);
+ return (EIO);
+ }
+
+ /* Result: 4-byte key name in DATA */
+ for (i = 0; i < ASMC_KEYLEN; i++)
+ key[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i);
+ key[ASMC_KEYLEN] = '\0';
+
+ mtx_unlock_spin(&sc->sc_mtx);
+ return (0);
+}
+
+/*
+ * Validate MMIO and detect T2.
+ * Check that status register is accessible and LDKN firmware version >= 2.
+ */
+int
+asmc_mmio_probe(device_t dev)
+{
+ struct asmc_softc *sc = device_get_softc(dev);
+ rman_res_t size;
+ uint8_t status, ldkn;
+ int error;
+
+ size = rman_get_size(sc->sc_iomem);
+ if (size < ASMC_MMIO_MIN_SIZE) {
+ device_printf(dev, "MMIO region too small (%jd < %d)\n",
+ (intmax_t)size, ASMC_MMIO_MIN_SIZE);
+ return (ENXIO);
+ }
+
+ /* Check status register isn't stuck at 0xFF */
+ status = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS);
+ if (status == 0xFF) {
+ device_printf(dev, "MMIO status register reads 0xFF\n");
+ return (ENXIO);
+ }
+
+ /*
+ * We need the mutex initialized before calling mmio_key_read,
+ * but attach hasn't done it yet. Initialize early.
+ */
+ if (!mtx_initialized(&sc->sc_mtx))
+ mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
+
+ /* Read LDKN (firmware version) -- must be >= 2 for MMIO */
+ error = asmc_mmio_key_read(dev, ASMC_KEY_LDKN, &ldkn, 1);
+ if (error != 0) {
+ device_printf(dev, "MMIO: failed to read LDKN key\n");
+ return (ENXIO);
+ }
+
+ if (ldkn < 2) {
+ device_printf(dev, "MMIO: LDKN=%d (need >= 2)\n", ldkn);
+ return (ENXIO);
+ }
+
+ device_printf(dev, "MMIO: LDKN=%d, T2 SMC detected\n", ldkn);
+ sc->sc_is_t2 = 1;
+
+ return (0);
+}
+
+void
+asmc_mmio_detach(device_t dev, struct asmc_softc *sc)
+{
+
+ if (sc->sc_iomem != NULL) {
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid_mem,
+ sc->sc_iomem);
+ sc->sc_iomem = NULL;
+ }
+ sc->sc_is_mmio = 0;
+ sc->sc_is_t2 = 0;
+}
+
+/*
+ * Convert IEEE 754 float (as u32) to unsigned integer.
+ * Kernel soft-float: extract integer part only.
+ * Used for T2 fan RPM values (always positive, reasonable range).
+ */
+uint32_t
+asmc_float_to_u32(uint32_t d)
+{
+ int32_t exp;
+ uint32_t fr;
+
+ /* Negative or zero */
+ if (d == 0 || (d >> 31) != 0)
+ return (0);
+
+ exp = (int32_t)((d >> 23) & 0xff) - 0x7f;
+ fr = d & 0x7fffff; /* 23-bit mantissa */
+
+ if (exp < 0)
+ return (0);
+ if (exp > 23) {
+ if (exp > 30)
+ return (0xffffffffu);
+ return ((1u << exp) | (fr << (exp - 23)));
+ }
+ /* Normal case: 0 <= exp <= 23 */
+ return ((1u << exp) + (fr >> (23 - exp)));
+}
+
+/*
+ * Convert unsigned integer to IEEE 754 float (as u32).
+ * Only handles values in fan RPM range (0-65535).
+ */
+uint32_t
+asmc_u32_to_float(uint32_t d)
+{
+ uint32_t dc, bc, exp;
+
+ if (d == 0)
+ return (0);
+
+ /* Find highest set bit position */
+ dc = d;
+ bc = 0;
+ while (dc >>= 1)
+ ++bc;
+
+ bc = MIN(bc, 30);
+
+ exp = 0x7f + bc;
+
+ /*
+ * Mantissa: strip the implicit leading 1-bit and place
+ * remaining bits into the 23-bit mantissa field.
+ */
+ if (bc >= 23)
+ return ((exp << 23) | ((d >> (bc - 23)) & 0x7fffff));
+ else
+ return ((exp << 23) | ((d << (23 - bc)) & 0x7fffff));
+}
+
+/*
+ * Battery charge limit sysctl (T2 Macs).
+ * BCLM key: 1 byte, 0-100 (percentage).
+ */
+int
+asmc_bclm_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = (device_t)arg1;
+ uint8_t bclm;
+ int val, error;
+
+ error = asmc_mmio_key_read(dev, ASMC_KEY_BCLM, &bclm, 1);
+ if (error != 0)
+ return (EIO);
+
+ val = (int)bclm;
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (val < 0 || val > 100)
+ return (EINVAL);
+
+ bclm = (uint8_t)val;
+ error = asmc_mmio_key_write(dev, ASMC_KEY_BCLM, &bclm, 1);
+
+ return (error != 0 ? EIO : 0);
+}
diff --git a/sys/dev/asmc/asmcvar.h b/sys/dev/asmc/asmcvar.h
--- a/sys/dev/asmc/asmcvar.h
+++ b/sys/dev/asmc/asmcvar.h
@@ -53,6 +53,11 @@
struct resource *sc_ioport;
struct resource *sc_irq;
void *sc_cookie;
+ /* MMIO backend (T2 Macs) */
+ int sc_rid_mem;
+ struct resource *sc_iomem;
+ int sc_is_mmio;
+ int sc_is_t2; /* T2 fan float + per-fan manual */
int sc_sms_intrtype;
struct taskqueue *sc_sms_tq;
struct task sc_sms_task;
diff --git a/sys/modules/asmc/Makefile b/sys/modules/asmc/Makefile
--- a/sys/modules/asmc/Makefile
+++ b/sys/modules/asmc/Makefile
@@ -1,7 +1,7 @@
.PATH: ${SRCTOP}/sys/dev/asmc
KMOD= asmc
-SRCS= asmc.c opt_acpi.h opt_asmc.h
+SRCS= asmc.c asmcmmio.c opt_acpi.h opt_asmc.h
SRCS+= acpi_if.h backlight_if.h bus_if.h device_if.h
.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Sat, May 23, 6:50 PM (4 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33451590
Default Alt Text
D57086.diff (19 KB)

Event Timeline