asmc: add MMIO backend for T2 Macs

T2 Macs (2018+) expose the SMC via memory-mapped registers instead of
I/O ports. Add asmcmmio.c/asmcmmio.h implementing the MMIO transport:
key read/write, getinfo, getbyindex, and a poll-based wait with
exponential backoff.

The driver probes for MMIO at attach time by checking the LDKN firmware
version key; if MMIO is available it is used, otherwise the standard
I/O port backend is used.

T2 fan speeds use IEEE 754 floats instead of fpe2 fixed-point.
Per-fan manual mode uses F%dMd keys instead of the FS! bitmask.
Battery charge limit is exposed via dev.asmc.N.battery_charge_limit.

Tested on:
  MacBookPro16,2 (A2251, iBridge2,10)
  MacBookPro15,4 (A2159, iBridge2,8)
  MacBookAir8,2  (A1932, iBridge2,5)
  Mac mini 8,1   (A1993, iBridge2,7)
  iMac20,2       (A2115, iBridge2,16)
  iMacPro1,1     (A1862, iBridge1,1)

MFC after:	2 weeks
Reviewed by:	ngie, adrian
Differential Revision:	https://reviews.freebsd.org/D57086
This commit is contained in:
Abdelkader Boudih
2026-06-02 22:57:49 -07:00
committed by Enji Cooper
parent e949ce9dc0
commit a48b900300
6 changed files with 611 additions and 25 deletions
+1
View File
@@ -113,6 +113,7 @@ crypto/openssl/amd64/ossl_aes_gcm_avx512.c optional ossl
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
+146 -24
View File
@@ -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));
+402
View File
@@ -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);
}
+56
View File
@@ -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_ */
+5
View File
@@ -53,6 +53,11 @@ struct asmc_softc {
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;
+1 -1
View File
@@ -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>