Download raw body.
Apple firmware change battery capacity
After updating MacOS on my M1 macbook air, not to MacOS27, it was a minor update for Macos 15, my battery sensor became erratic. apm reports wild numbers, often above 100%.
Found the behavior and cause documented here - https://github.com/torvalds/linux/commit/f7b253a6e217f71d754d70c525e9b4c1dcbd4414. Apple changed the byte order for the remaining battery capacity sensor(B0RM). The diff below checks for a teltale sign of the new firmware and reverses the order in that case. This fixed the apm output on my laptop. Claude helped.
Index: sys/arch/arm64/dev/aplsmc.c
===================================================================
RCS file: /cvs/src/sys/arch/arm64/dev/aplsmc.c,v
diff -u -p -u -p -r1.34 aplsmc.c
--- sys/arch/arm64/dev/aplsmc.c 13 Oct 2026 12:30:12 -0000 1.34
+++ sys/arch/arm64/dev/aplsmc.c 5 Nov 2026 00:30:04 -0000
@@ -114,6 +114,7 @@ struct aplsmc_sensor {
#define APLSMC_BE (1 << 0)
#define APLSMC_HIDDEN (1 << 1)
+#define APLSMC_BE_OLDFW (1 << 2) /* BE on older firmware only */
#define APLSMC_MAX_SENSORS 19
@@ -143,6 +144,7 @@ struct aplsmc_softc {
struct ksensordev sc_sensordev;
int sc_nbatteries;
uint32_t sc_suspend_pstr;
+ int sc_newfw; /* 1-byte BCF0, LE B0RM */
};
#define CH0I_DISCHARGE (1 << 0)
@@ -156,7 +158,7 @@ struct aplsmc_softc *aplsmc_sc;
struct aplsmc_sensor aplsmc_sensors[] = {
{ "ACDI", "ui16", SENSOR_INDICATOR, 1, "power supply" },
{ "B0RM", "ui16", SENSOR_AMPHOUR, 1000, "remaining battery capacity",
- APLSMC_BE },
+ APLSMC_BE_OLDFW },
{ "B0FC", "ui16", SENSOR_AMPHOUR, 1000, "last full battery capacity" },
{ "B0DC", "ui16", SENSOR_AMPHOUR, 1000, "battery design capacity" },
{ "B0AV", "ui16", SENSOR_VOLTS_DC, 1000, "battery" },
@@ -198,6 +200,8 @@ int aplsmc_send_cmd(struct aplsmc_softc
int aplsmc_wait_cmd(struct aplsmc_softc *sc);
int aplsmc_read_key(struct aplsmc_softc *, uint32_t, void *, size_t);
int aplsmc_write_key(struct aplsmc_softc *, uint32_t, void *, size_t);
+int aplsmc_get_key_info(struct aplsmc_softc *, uint32_t,
+ struct smc_key_info *);
int64_t aplsmc_convert_flt(uint32_t, int);
void aplsmc_refresh_sensors(void *);
int aplsmc_apminfo(struct apm_power_info *);
@@ -231,6 +235,7 @@ aplsmc_attach(struct device *parent, str
uint8_t ntap;
int error, node;
#ifndef SMALL_KERNEL
+ struct smc_key_info info;
int i;
#endif
@@ -322,18 +327,20 @@ aplsmc_attach(struct device *parent, str
#ifndef SMALL_KERNEL
- for (i = 0; i < nitems(aplsmc_sensors); i++) {
- struct smc_key_info info;
+ /*
+ * Newer SMC firmware changed the BCF0 key from 4 bytes to 1
+ * byte and at the same time switched B0RM from big-endian to
+ * little-endian. Use the size of BCF0 to tell them apart.
+ */
+ if (aplsmc_get_key_info(sc, SMC_KEY("BCF0"), &info) == 0 &&
+ info.size == 1)
+ sc->sc_newfw = 1;
- aplsmc_send_cmd(sc, SMC_GET_KEY_INFO,
- SMC_KEY(aplsmc_sensors[i].key), 0);
- error = aplsmc_wait_cmd(sc);
- if (error || SMC_ERROR(sc->sc_data) != SMC_OK)
+ for (i = 0; i < nitems(aplsmc_sensors); i++) {
+ if (aplsmc_get_key_info(sc, SMC_KEY(aplsmc_sensors[i].key),
+ &info) != 0)
continue;
- bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0,
- (uint8_t *)&info, sizeof(info));
-
/* Skip if the key type doesn't match. */
if (memcmp(aplsmc_sensors[i].key_type, info.type,
sizeof(info.type)) != 0)
@@ -581,6 +588,24 @@ aplsmc_write_key(struct aplsmc_softc *sc
#ifndef SMALL_KERNEL
+int
+aplsmc_get_key_info(struct aplsmc_softc *sc, uint32_t key,
+ struct smc_key_info *info)
+{
+ int error;
+
+ aplsmc_send_cmd(sc, SMC_GET_KEY_INFO, key, 0);
+ error = aplsmc_wait_cmd(sc);
+ if (error)
+ return error;
+ if (SMC_ERROR(sc->sc_data) != SMC_OK)
+ return EINVAL;
+
+ bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0,
+ (uint8_t *)info, sizeof(*info));
+ return 0;
+}
+
int64_t
aplsmc_convert_flt(uint32_t flt, int scale)
{
@@ -624,7 +649,9 @@ aplsmc_refresh_sensors(void *arg)
uint16_t ui16;
error = aplsmc_read_key(sc, key, &ui16, sizeof(ui16));
- if (sensor->flags & APLSMC_BE)
+ if ((sensor->flags & APLSMC_BE) ||
+ ((sensor->flags & APLSMC_BE_OLDFW) &&
+ !sc->sc_newfw))
ui16 = betoh16(ui16);
value = (int64_t)ui16 * sensor->scale;
} else if (strcmp(sensor->key_type, "flt ") == 0) {
Apple firmware change battery capacity