Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F157610422
D57086.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
19 KB
Referenced Files
None
Subscribers
None
D57086.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sun, May 24, 9:31 AM (17 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33451590
Default Alt Text
D57086.diff (19 KB)
Attached To
Mode
D57086: asmc: add MMIO backend for T2 Macs
Attached
Detach File
Event Timeline
Log In to Comment