From: Marcus Glocker Subject: sambat(4) - Samsung laptop battery monitor To: tech@openbsd.org Date: Sun, 17 May 2026 20:31:43 +0200 On the Samsung Galaxy Book4 Edge qcpas(4) never was returning the battery status. Now I found out that on this laptop the ADSP doesn't host an charger_pd (protection domain), instead we need to read the battery status values directly from the SAM060B EC over I2C. To attach the SAM060B EC we obviously require a new node entry for our DTB, so this diff includes the arm64-qcom-dtb-firmware port update as well. This gives me working apm finally: x1e$ dmesg | grep sambat sambat0 at iic1 addr 0x64: EC flags 0x05 x1e$ sysctl hw.sensors.sambat0 hw.sensors.sambat0.volt0=17.57 VDC (battery voltage) hw.sensors.sambat0.volt1=46.24 VDC (battery design voltage) hw.sensors.sambat0.current0=0.42 A (battery current) hw.sensors.sambat0.amphour0=3.40 Ah (battery remaining) hw.sensors.sambat0.amphour1=3.53 Ah (battery full charge) hw.sensors.sambat0.amphour2=45.75 Ah (battery design capacity) hw.sensors.sambat0.raw0=0 (battery state) hw.sensors.sambat0.percent0=96.29% (battery charge) x1e$ apm Battery state: high, 96% remaining, unknown life estimate AC adapter state: connected Performance adjustment mode: manual (4012 MHz) Ok? Index: sys/arch/arm64/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v diff -u -p -u -p -r1.315 GENERIC --- sys/arch/arm64/conf/GENERIC 14 May 2026 16:20:27 -0000 1.315 +++ sys/arch/arm64/conf/GENERIC 17 May 2026 18:15:09 -0000 @@ -627,6 +627,7 @@ tascodec* at iic? # TAS2770 audio code tcpci* at iic? # USB Type-C controller tipd* at iic? # TPS6598x Type-C controller pijuice* at iic? # PiJuice HAT +sambat* at iic? # Samsung SAM060B battery monitor # GPIO "pin bus" drivers gpioiic* at gpio? # I2C bus bit-banging Index: sys/dev/i2c/files.i2c =================================================================== RCS file: /cvs/src/sys/dev/i2c/files.i2c,v diff -u -p -u -p -r1.74 files.i2c --- sys/dev/i2c/files.i2c 23 Nov 2025 21:28:15 -0000 1.74 +++ sys/dev/i2c/files.i2c 17 May 2026 18:15:10 -0000 @@ -274,6 +274,11 @@ device pijuice attach pijuice at i2c file dev/i2c/pijuice.c pijuice +# Samsung SAM060B battery monitor +device sambat +attach sambat at i2c +file dev/i2c/sambat.c sambat + # Consumer Control Keyboards device icc: hid, hidcc, wskbddev attach icc at ihidbus Index: sys/dev/i2c/sambat.c =================================================================== RCS file: sys/dev/i2c/sambat.c diff -N sys/dev/i2c/sambat.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/i2c/sambat.c 17 May 2026 18:15:10 -0000 @@ -0,0 +1,459 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2026 Marcus Glocker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Battery monitor for Samsung laptops with the "SAM060B" embedded + * controller (ENE KB9058 silicon with Samsung firmware). First + * confirmed on the Galaxy Book4 Edge; the same EC personality is + * likely shared across the Galaxy Book line. + * + * The host talks to the EC via a vendor "Mbox" command protocol on + * I2C, reverse-engineered for Linux by the Saddytech project; this + * driver is an independent reimplementation against that reference: + * https://github.com/Saddytech/Galaxy-Book4-Edge-linux + * + * EC register layout (from the DSDT _SB.ECTC region the Windows + * driver writes to): + * 0x80 bit0 = battery present, bit2 = AC online + * 0x84 bit0 = discharging, bit1 = charging, bit3 = full + * 0xA0 upper big-endian word = remaining capacity (mAh) + * 0xA4 upper BE = voltage (mV), lower BE = signed current (mA) + * 0xB0 upper BE = design capacity (mAh), lower BE = full-charge (mAh) + * 0xB4 lower BE = design voltage (mV) + * + * Each EC register read requires three Mbox transactions: + * write target address (cmd 0xF480 = reg) + * write execute (cmd 0xFF10 = 0x88 "read") + * read result (cmd 0xF480) + */ + +#include +#include +#include +#include + +#include + +#include + +#include "apm.h" + +/* Mbox framing. */ +#define SAM_MBOX_WRITE_PREFIX 0x40 +#define SAM_MBOX_READ_PREFIX 0x30 +#define SAM_MBOX_READ_SUCCESS 0x50 + +/* Mbox command address (the "hi"/"lo" pair). */ +#define SAM_CMD_TARGET_HI 0xf4 +#define SAM_CMD_TARGET_LO 0x80 +#define SAM_CMD_EXEC_HI 0xff +#define SAM_CMD_EXEC_LO 0x10 +#define SAM_EXEC_READ 0x88 + +/* EC register offsets. */ +#define SAM_REG_FLAGS 0x80 +#define SAM_REG_B1ST 0x84 +#define SAM_REG_B1RR 0xa0 +#define SAM_REG_B1PV 0xa4 +#define SAM_REG_B1AF 0xb0 +#define SAM_REG_B1VL 0xb4 + +#define SAM_FLAG_B1EX (1 << 0) +#define SAM_FLAG_ACEX (1 << 2) + +#define SAM_B1ST_DISCHARGE (1 << 0) +#define SAM_B1ST_CHARGE (1 << 1) +#define SAM_B1ST_FULL (1 << 3) + +#define SAM_REFRESH_INTERVAL 30 /* seconds */ + +/* Sensor indices. */ +enum sambat_sensors { + SAMBAT_SENSOR_CHARGE, /* percent */ + SAMBAT_SENSOR_VOLT_NOW, /* current voltage */ + SAMBAT_SENSOR_VOLT_DESIGN, /* design voltage */ + SAMBAT_SENSOR_CURRENT, /* signed current */ + SAMBAT_SENSOR_CHARGE_NOW, /* remaining capacity (Ah) */ + SAMBAT_SENSOR_CHARGE_FULL, /* full-charge capacity */ + SAMBAT_SENSOR_CHARGE_DESIGN, /* design capacity */ + SAMBAT_SENSOR_STATE, /* drive state */ + SAMBAT_NSENSORS +}; + +struct sambat_softc { + struct device sc_dev; + i2c_tag_t sc_tag; + int sc_addr; + + struct ksensor sc_sensor[SAMBAT_NSENSORS]; + struct ksensordev sc_sensordev; + + /* Latest decoded readings. */ + int sc_have_data; + int sc_present; + int sc_ac_online; + uint8_t sc_b1st; + uint16_t sc_remaining_mah; + uint16_t sc_voltage_mv; + int16_t sc_current_ma; + uint16_t sc_design_mah; + uint16_t sc_fullchg_mah; + uint16_t sc_design_mv; +}; + +struct sambat_softc *sambat_sc; + +int sambat_match(struct device *, void *, void *); +void sambat_attach(struct device *, struct device *, void *); + +int sambat_mbox_write(struct sambat_softc *, uint8_t, uint8_t, uint8_t); +int sambat_mbox_read(struct sambat_softc *, uint8_t, uint8_t, uint8_t *); +int sambat_ec_read_byte(struct sambat_softc *, uint8_t, uint8_t *); +int sambat_ec_read_block(struct sambat_softc *, uint8_t, uint8_t *, int); + +void sambat_refresh(void *); +int sambat_apminfo(struct apm_power_info *); + +const struct cfattach sambat_ca = { + sizeof(struct sambat_softc), sambat_match, sambat_attach +}; + +struct cfdriver sambat_cd = { + NULL, "sambat", DV_DULL +}; + +int +sambat_match(struct device *parent, void *match, void *aux) +{ + struct i2c_attach_args *ia = aux; + + if (strcmp(ia->ia_name, "samsung,galaxybook-battery") == 0) + return 1; + + return 0; +} + +void +sambat_attach(struct device *parent, struct device *self, void *aux) +{ + struct sambat_softc *sc = (struct sambat_softc *)self; + struct i2c_attach_args *ia = aux; + uint8_t probe; + int i; + + sambat_sc = sc; + sc->sc_tag = ia->ia_tag; + sc->sc_addr = ia->ia_addr; + + /* + * Probe the EC with a single byte read to confirm we're + * actually talking to it before exposing sensors. + */ + if (sambat_ec_read_byte(sc, SAM_REG_FLAGS, &probe) != 0) { + printf(": EC probe failed\n"); + return; + } + + /* Sensor framework setup. */ + strlcpy(sc->sc_sensor[SAMBAT_SENSOR_CHARGE].desc, + "battery charge", sizeof(sc->sc_sensor[0].desc)); + sc->sc_sensor[SAMBAT_SENSOR_CHARGE].type = SENSOR_PERCENT; + + strlcpy(sc->sc_sensor[SAMBAT_SENSOR_VOLT_NOW].desc, + "battery voltage", sizeof(sc->sc_sensor[0].desc)); + sc->sc_sensor[SAMBAT_SENSOR_VOLT_NOW].type = SENSOR_VOLTS_DC; + + strlcpy(sc->sc_sensor[SAMBAT_SENSOR_VOLT_DESIGN].desc, + "battery design voltage", sizeof(sc->sc_sensor[0].desc)); + sc->sc_sensor[SAMBAT_SENSOR_VOLT_DESIGN].type = SENSOR_VOLTS_DC; + + strlcpy(sc->sc_sensor[SAMBAT_SENSOR_CURRENT].desc, + "battery current", sizeof(sc->sc_sensor[0].desc)); + sc->sc_sensor[SAMBAT_SENSOR_CURRENT].type = SENSOR_AMPS; + + strlcpy(sc->sc_sensor[SAMBAT_SENSOR_CHARGE_NOW].desc, + "battery remaining", sizeof(sc->sc_sensor[0].desc)); + sc->sc_sensor[SAMBAT_SENSOR_CHARGE_NOW].type = SENSOR_AMPHOUR; + + strlcpy(sc->sc_sensor[SAMBAT_SENSOR_CHARGE_FULL].desc, + "battery full charge", sizeof(sc->sc_sensor[0].desc)); + sc->sc_sensor[SAMBAT_SENSOR_CHARGE_FULL].type = SENSOR_AMPHOUR; + + strlcpy(sc->sc_sensor[SAMBAT_SENSOR_CHARGE_DESIGN].desc, + "battery design capacity", sizeof(sc->sc_sensor[0].desc)); + sc->sc_sensor[SAMBAT_SENSOR_CHARGE_DESIGN].type = SENSOR_AMPHOUR; + + strlcpy(sc->sc_sensor[SAMBAT_SENSOR_STATE].desc, + "battery state", sizeof(sc->sc_sensor[0].desc)); + sc->sc_sensor[SAMBAT_SENSOR_STATE].type = SENSOR_INTEGER; + + strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, + sizeof(sc->sc_sensordev.xname)); + for (i = 0; i < SAMBAT_NSENSORS; i++) { + sc->sc_sensor[i].flags |= SENSOR_FINVALID; + sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); + } + sensordev_install(&sc->sc_sensordev); + + if (sensor_task_register(sc, sambat_refresh, + SAM_REFRESH_INTERVAL) == NULL) { + printf(": can't register update task\n"); + return; + } + + printf(": EC flags 0x%02x\n", probe); + +#if NAPM > 0 + apm_setinfohook(sambat_apminfo); +#endif +} + +/* + * Mbox primitives. All Mbox traffic uses the same I2C slave address + * (sc->sc_addr); the "hi"/"lo" pair selects the Mbox register inside + * the EC. Write: 5-byte transfer. Read: 4-byte write followed by + * 2-byte read (first byte must be SAM_MBOX_READ_SUCCESS = 0x50). + */ +int +sambat_mbox_write(struct sambat_softc *sc, uint8_t hi, uint8_t lo, + uint8_t data) +{ + uint8_t buf[5]; + int error; + + buf[0] = SAM_MBOX_WRITE_PREFIX; + buf[1] = 0x00; + buf[2] = hi; + buf[3] = lo; + buf[4] = data; + + iic_acquire_bus(sc->sc_tag, I2C_F_POLL); + error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, + NULL, 0, buf, sizeof(buf), I2C_F_POLL); + iic_release_bus(sc->sc_tag, I2C_F_POLL); + + if (error) + return error; + + /* + * Short settle delay between Mbox commands. The Linux driver + * uses 5ms; the EC tolerates much less in practice and we + * share the I2C bus with the keyboard, so keep this small. + */ + delay(500); + return 0; +} + +int +sambat_mbox_read(struct sambat_softc *sc, uint8_t hi, uint8_t lo, + uint8_t *out) +{ + uint8_t cmd[4]; + uint8_t rsp[2]; + int error; + + cmd[0] = SAM_MBOX_READ_PREFIX; + cmd[1] = 0x00; + cmd[2] = hi; + cmd[3] = lo; + + iic_acquire_bus(sc->sc_tag, I2C_F_POLL); + error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, + cmd, sizeof(cmd), rsp, sizeof(rsp), I2C_F_POLL); + iic_release_bus(sc->sc_tag, I2C_F_POLL); + + if (error) + return error; + if (rsp[0] != SAM_MBOX_READ_SUCCESS) + return EIO; + + *out = rsp[1]; + return 0; +} + +int +sambat_ec_read_byte(struct sambat_softc *sc, uint8_t reg, uint8_t *out) +{ + int error; + + error = sambat_mbox_write(sc, SAM_CMD_TARGET_HI, SAM_CMD_TARGET_LO, + reg); + if (error) + return error; + error = sambat_mbox_write(sc, SAM_CMD_EXEC_HI, SAM_CMD_EXEC_LO, + SAM_EXEC_READ); + if (error) + return error; + return sambat_mbox_read(sc, SAM_CMD_TARGET_HI, SAM_CMD_TARGET_LO, + out); +} + +int +sambat_ec_read_block(struct sambat_softc *sc, uint8_t reg, uint8_t *buf, + int n) +{ + int i, error; + + for (i = 0; i < n; i++) { + error = sambat_ec_read_byte(sc, reg + i, &buf[i]); + if (error) + return error; + } + return 0; +} + +void +sambat_refresh(void *arg) +{ + struct sambat_softc *sc = arg; + uint8_t flags, b1st; + uint8_t rr[4], pv[4], af[4], vl[4]; + uint16_t remaining, voltage, design_c, fullchg_c, design_v; + int16_t cur_ma; + int i; + + for (i = 0; i < SAMBAT_NSENSORS; i++) + sc->sc_sensor[i].flags |= SENSOR_FINVALID; + + if (sambat_ec_read_byte(sc, SAM_REG_FLAGS, &flags) != 0 || + sambat_ec_read_byte(sc, SAM_REG_B1ST, &b1st) != 0 || + sambat_ec_read_block(sc, SAM_REG_B1RR, rr, sizeof(rr)) != 0 || + sambat_ec_read_block(sc, SAM_REG_B1PV, pv, sizeof(pv)) != 0 || + sambat_ec_read_block(sc, SAM_REG_B1AF, af, sizeof(af)) != 0 || + sambat_ec_read_block(sc, SAM_REG_B1VL, vl, sizeof(vl)) != 0) + return; + + /* + * Each 4-byte EC field carries two big-endian 16-bit values. + * The DSDT ByteSwap16(reg >> 16) and ByteSwap16(reg & 0xffff) + * idioms extract the upper and lower BE words respectively. + */ + remaining = ((uint16_t)rr[2] << 8) | rr[3]; + voltage = ((uint16_t)pv[2] << 8) | pv[3]; + cur_ma = (int16_t)(((uint16_t)pv[0] << 8) | pv[1]); + design_c = ((uint16_t)af[2] << 8) | af[3]; + fullchg_c = ((uint16_t)af[0] << 8) | af[1]; + design_v = ((uint16_t)vl[0] << 8) | vl[1]; + + if (remaining == 0xffff) + remaining = 0; + if (design_c == 0xffff) + design_c = 0; + if (fullchg_c == 0xffff) + fullchg_c = 0; + + sc->sc_present = !!(flags & SAM_FLAG_B1EX); + sc->sc_ac_online = !!(flags & SAM_FLAG_ACEX); + sc->sc_b1st = b1st; + sc->sc_remaining_mah = remaining; + sc->sc_voltage_mv = voltage; + sc->sc_current_ma = cur_ma; + sc->sc_design_mah = design_c; + sc->sc_fullchg_mah = fullchg_c ? fullchg_c : design_c; + sc->sc_design_mv = design_v; + sc->sc_have_data = 1; + + if (sc->sc_fullchg_mah > 0) { + /* SENSOR_PERCENT: value = percent * 1000 (100% = 100000). */ + sc->sc_sensor[SAMBAT_SENSOR_CHARGE].value = + (1000ULL * 100 * remaining) / sc->sc_fullchg_mah; + sc->sc_sensor[SAMBAT_SENSOR_CHARGE].flags &= ~SENSOR_FINVALID; + } + sc->sc_sensor[SAMBAT_SENSOR_VOLT_NOW].value = + (uint64_t)voltage * 1000; /* mV -> uV */ + sc->sc_sensor[SAMBAT_SENSOR_VOLT_NOW].flags &= ~SENSOR_FINVALID; + + sc->sc_sensor[SAMBAT_SENSOR_VOLT_DESIGN].value = + (uint64_t)design_v * 1000; + sc->sc_sensor[SAMBAT_SENSOR_VOLT_DESIGN].flags &= ~SENSOR_FINVALID; + + sc->sc_sensor[SAMBAT_SENSOR_CURRENT].value = + (int64_t)cur_ma * 1000; /* mA -> uA */ + sc->sc_sensor[SAMBAT_SENSOR_CURRENT].flags &= ~SENSOR_FINVALID; + + sc->sc_sensor[SAMBAT_SENSOR_CHARGE_NOW].value = + (uint64_t)remaining * 1000; /* mAh -> uAh */ + sc->sc_sensor[SAMBAT_SENSOR_CHARGE_NOW].flags &= ~SENSOR_FINVALID; + + sc->sc_sensor[SAMBAT_SENSOR_CHARGE_FULL].value = + (uint64_t)sc->sc_fullchg_mah * 1000; + sc->sc_sensor[SAMBAT_SENSOR_CHARGE_FULL].flags &= ~SENSOR_FINVALID; + + sc->sc_sensor[SAMBAT_SENSOR_CHARGE_DESIGN].value = + (uint64_t)design_c * 1000; + sc->sc_sensor[SAMBAT_SENSOR_CHARGE_DESIGN].flags &= ~SENSOR_FINVALID; + + sc->sc_sensor[SAMBAT_SENSOR_STATE].value = b1st; + sc->sc_sensor[SAMBAT_SENSOR_STATE].flags &= ~SENSOR_FINVALID; +} + +#if NAPM > 0 +int +sambat_apminfo(struct apm_power_info *info) +{ + struct sambat_softc *sc = sambat_sc; + + info->battery_state = APM_BATT_UNKNOWN; + info->ac_state = APM_AC_UNKNOWN; + info->battery_life = 0; + info->minutes_left = -1; + + if (!sc->sc_have_data) + return 0; + + if (sc->sc_ac_online) + info->ac_state = APM_AC_ON; + else + info->ac_state = APM_AC_OFF; + + if (!sc->sc_present) { + info->battery_state = APM_BATTERY_ABSENT; + return 0; + } + + if (sc->sc_fullchg_mah > 0) + info->battery_life = + (100 * sc->sc_remaining_mah) / sc->sc_fullchg_mah; + + if (sc->sc_b1st & SAM_B1ST_FULL) { + info->battery_state = APM_BATT_HIGH; + } else if (sc->sc_b1st & SAM_B1ST_CHARGE) { + info->battery_state = APM_BATT_CHARGING; + } else { + if (info->battery_life > 50) + info->battery_state = APM_BATT_HIGH; + else if (info->battery_life > 25) + info->battery_state = APM_BATT_LOW; + else + info->battery_state = APM_BATT_CRITICAL; + } + + /* + * Estimate minutes-left from instantaneous current (mA) and + * remaining capacity (mAh). Only meaningful while discharging. + */ + if ((sc->sc_b1st & SAM_B1ST_CHARGE) == 0 && sc->sc_current_ma < 0) { + int draw = -sc->sc_current_ma; + if (draw > 0) + info->minutes_left = + (60 * sc->sc_remaining_mah) / draw; + } + + return 0; +} +#endif Index: share/man/man4/sambat.4 =================================================================== RCS file: share/man/man4/sambat.4 diff -N share/man/man4/sambat.4 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ share/man/man4/sambat.4 17 May 2026 16:59:54 -0000 @@ -0,0 +1,59 @@ +.\" $OpenBSD$ +.\" +.\" Copyright (c) 2026 Marcus Glocker +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: May 17 2026 $ +.Dt SAMBAT 4 +.Os +.Sh NAME +.Nm sambat +.Nd Samsung laptop battery monitor +.Sh SYNOPSIS +.Cd "sambat* at iic?" +.Sh DESCRIPTION +The +.Nm +driver provides support for the embedded controller found in Samsung +laptops based on the Qualcomm Snapdragon X1E80100, such as the Galaxy +Book4 Edge. +The controller, identified as +.Dq SAM060B +in ACPI, is an ENE KB9058 micro controller running Samsung firmware, +and provides battery and AC adapter status over the I2C bus using a +vendor +.Dq Mbox +command protocol. +.Pp +Battery values are exposed through the +.Xr sysctl 8 +sensor framework and the +.Xr apm 8 +power management interface. +.Sh SEE ALSO +.Xr iic 4 , +.Xr intro 4 , +.Xr apm 8 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Ox 8.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Marcus Glocker Aq Mt mglocker@openbsd.org . Index: sysutils/firmware/arm64-qcom-dtb/Makefile =================================================================== RCS file: /cvs/ports/sysutils/firmware/arm64-qcom-dtb/Makefile,v diff -u -p -u -p -r1.28 Makefile --- sysutils/firmware/arm64-qcom-dtb/Makefile 29 Mar 2026 20:53:10 -0000 1.28 +++ sysutils/firmware/arm64-qcom-dtb/Makefile 17 May 2026 17:16:57 -0000 @@ -1,6 +1,6 @@ FW_DRIVER= arm64-qcom-dtb FW_VER= 2.7 -REVISION= 0 +REVISION= 1 DISTNAME= devicetree-rebasing-6.17-dts Index: sysutils/firmware/arm64-qcom-dtb/patches/patch-src_arm64_qcom_x1e80100-samsung-galaxy-book4-edge_dts =================================================================== RCS file: /cvs/ports/sysutils/firmware/arm64-qcom-dtb/patches/patch-src_arm64_qcom_x1e80100-samsung-galaxy-book4-edge_dts,v diff -u -p -u -p -r1.2 patch-src_arm64_qcom_x1e80100-samsung-galaxy-book4-edge_dts --- sysutils/firmware/arm64-qcom-dtb/patches/patch-src_arm64_qcom_x1e80100-samsung-galaxy-book4-edge_dts 8 Sep 2024 18:53:25 -0000 1.2 +++ sysutils/firmware/arm64-qcom-dtb/patches/patch-src_arm64_qcom_x1e80100-samsung-galaxy-book4-edge_dts 17 May 2026 17:16:57 -0000 @@ -1,7 +1,7 @@ Index: src/arm64/qcom/x1e80100-samsung-galaxy-book4-edge.dts --- src/arm64/qcom/x1e80100-samsung-galaxy-book4-edge.dts.orig +++ src/arm64/qcom/x1e80100-samsung-galaxy-book4-edge.dts -@@ -0,0 +1,957 @@ +@@ -0,0 +1,977 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. @@ -549,6 +549,26 @@ Index: src/arm64/qcom/x1e80100-samsung-g + pinctrl-names = "default"; + + wakeup-source; ++ }; ++}; ++ ++/* ++ * Samsung Galaxy Book4 Edge embedded controller (ENE KB9058, Samsung ++ * SAM060B in ACPI). Provides battery and AC adapter status via a ++ * vendor "Mbox" protocol on I2C. Reverse-engineered Linux driver: ++ * https://github.com/Saddytech/Galaxy-Book4-Edge-linux ++ * ++ * The DSDT _SB.ECTC._CRS lists this device on two channels: ++ * - ACPI I2C1 (MMIO 0xb80000, FDT &i2c0) at addr 0x64 -- Mbox ++ * command set used by the battery driver. ++ * - ACPI I2C6 (MMIO 0xb94000, FDT &i2c5) at addr 0x62 -- "SABI" ++ * command set used by fan/keyboard backlight tools. ++ * Only the Mbox channel (0x64 on &i2c0) is needed for battery status. ++ */ ++&i2c0 { ++ ec: ec@64 { ++ compatible = "samsung,galaxybook-battery"; ++ reg = <0x64>; + }; +}; +