Skip to content

power: supply: macsmc: support charge_behaviour on newer SMC firmware#435

Open
IntegralPilot wants to merge 17 commits intoAsahiLinux:bits/110-smcfrom
IntegralPilot:fix/charge-behaviour-smc-new-fw
Open

power: supply: macsmc: support charge_behaviour on newer SMC firmware#435
IntegralPilot wants to merge 17 commits intoAsahiLinux:bits/110-smcfrom
IntegralPilot:fix/charge-behaviour-smc-new-fw

Conversation

@IntegralPilot
Copy link

Newer Apple SMC firmware (found on M3 devices and updated M1/M2) has removed the legacy CH0C (Inhibit Charge) and CH0I (Force Discharge) keys. Reading these missing keys results in -EIO (-5) errors, causing the charge_behaviour sysfs property to fail completely.

This patch adds support for the new CHTE key used for charge inhibition on these devices.

For now, it seems that auto and inhibit-charge are the only possible behaviours to set using this new key, however further macOS tracing may reveal additional behaviour states in future.

Changes:

  1. Detects the presence of CHTE, CH0C, and CH0I during probe.
  2. Only exposes force_discharge capability if CH0I is actually present.
  3. Implements read/write support for CHTE using raw byte buffers (this is to avoid endianness issues with the kernel's u32 helpers)

Fully backwards compatible with both old and new firmwares. Tested on M3 with new firmware.

svenpeter42 and others added 16 commits December 5, 2025 21:27
Apple Silicon Macs (M1, etc.) have an RTC that is part of the PMU IC,
but most of the PMU functionality is abstracted out by the SMC.
An additional RTC offset stored inside NVMEM is required to compute
the current date/time.

Reviewed-by: Mark Kettenis <kettenis@openbsd.org>
Reviewed-by: Neal Gompa <neal@gompa.dev>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Sven Peter <sven@kernel.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
Reviewd-by: Mark Kettenis <kettenis@openbsd.org>
Apple Silicon devices integrate a vast array of sensors, monitoring
current, power, temperature, and voltage across almost every part of
the system. The sensors themselves are all connected to the System
Management Controller (SMC). The SMC firmware exposes the data
reported by these sensors via its standard FourCC-based key-value
API. The SMC is also responsible for monitoring and controlling any
fans connected to the system, exposing them in the same way.

For reasons known only to Apple, each device exposes its sensors with
an almost totally unique set of keys. This is true even for devices
which share an SoC. An M1 Mac mini, for example, will report its core
temperatures on different keys to an M1 MacBook Pro. Worse still, the
SMC does not provide a way to enumerate the available keys at runtime,
nor do the keys follow any sort of reasonable or consistent naming
rules that could be used to deduce their purpose. We must therefore
know which keys are present on any given device, and which function
they serve, ahead of time.

Add a schema so that we can describe the available sensors for a given
Apple Silicon device in the Devicetree.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Apple Silicon Macs (M1, etc.) have an RTC that is part of the PMU IC,
but most of the PMU functionality is abstracted out by the SMC.
On T600x machines, the RTC counter must be accessed via the SMC to
get full functionality, and it seems likely that future machines
will move towards making SMC handle all RTC functionality.

The SMC RTC counter access is implemented on all current machines
as of the time of this writing, on firmware 12.x. However, the RTC
offset (needed to set the time) is still only accessible via direct
PMU access. To handle this, we expose the RTC offset as an NVMEM
cell from the SPMI PMU device node, and this driver consumes that
cell and uses it to compute/set the current time.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Sven Peter <sven@kernel.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
Add the new SMC RTC function to the mfd device

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
When using the _SMC_KEY macro in switch/case statements, GCC 15.2.1 errors
out with 'case label does not reduce to an integer constant'. Introduce
a new __SMC_KEY macro that can be used instead.

Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
The System Management Controller on Apple Silicon devices is responsible
for integrating and exposing the data reported by the vast array of
hardware monitoring sensors present on these devices. It is also
responsible for fan control, and allows users to manually set fan
speeds if they so desire. Add a hwmon driver to expose current,
power, temperature, and voltage monitoring sensors, as well as
fan speed monitoring and control via the SMC on Apple Silicon devices.

The SMC firmware has no consistency between devices, even when they
share an SoC. The FourCC keys used to access sensors are almost
random. An M1 Mac mini will have different FourCCs for its CPU core
temperature sensors to an M1 MacBook Pro, for example. For this
reason, the valid sensors for a given device are specified in a
child of the SMC Devicetree node. The driver uses this information
to determine which sensors to make available at runtime.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Co-developed-by: Janne Grunau <j@jannau.net>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
Acked-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Janne Grunau <j@jannau.net>
Add the SMC hwmon functionality to the mfd device

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
This driver implements power button and lid switch support for Apple Mac
devices using SMC controllers driven by the macsmc driver.

In addition to basic input support, this also responds to the final
shutdown warning (when the power button is held down long enough) by
doing an emergency kernel poweroff. This allows the NVMe controller to
be cleanly shut down, which prevents data loss for in-cache data.

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: Hector Martin <marcan@marcan.st>
Co-developed-by: Sven Peter <sven@kernel.org>
Signed-off-by: Sven Peter <sven@kernel.org>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
Add the new SMC input function to the mfd device

Reviewed-by: Neal Gompa <neal@gompa.dev>
Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
…ttons/lid

Signed-off-by: Janne Grunau <j@jannau.net>
MFD will probe sub devices declared with MFD_CELL_OF() even without
match on the device tree compatible. macsmc-reboot depends on nvmem
provided via device tree. Fail probe() with -ENODEV if this information
is missing.

Signed-off-by: Janne Grunau <j@jannau.net>
This driver implements support for battery stats on top of the macsmc
framework, to support Apple M1 Mac machines.

power: supply: macsmc_power: Add cycle count and health props

power: supply: macsmc_power: Add present prop

power: supply: macsmc_power: Add more props, rework others

power: supply: macsmc_power: Use BUIC instead of BRSC for charge

power: supply: macsmc_power: Turn off OBC flags if macOS left them on

power: supply: macsmc_power: Add AC power supply

power: supply: macsmc_power: Add critical level shutdown & misc events

power: supply: macsmc_power: Add CHWA charge thresholds

This is a hardcoded charge threshold feature present in firmware 13.0 or
newer. Userspace settings are rounded to one of the two possible
behaviors.

power: supply: macsmc_power: Report available charge_behaviours

The generic handling if charge_behaviours in the power_supply core
requires power_supply_desc.charge_behaviours to be set.

power: supply: macsmc_power: Add more properties

Report more voltages from the battery, and also fudge energy numbers
from charge numbers. This way userspace doesn't try to convert on its
own (and gets it very wrong).

power: supply: macsmc_power: Add CHLS charge thresholds

Since macOS Sequoia firmware, CHLS replaced CHWA and now allows an
arbitrary end charge threshold to be configured.

Prefer CHWA over CHLS since the SMC firmware from iBoot-10151.1.1
(macOS 14.0) is not compatible with our CHGLS usage. It was working
with the SMC firmware from iBoot-10151.121.1 (macOS 14.5).

power: supply: macsmc_power: Remove CSIL

Gone in Sequoia firmware.

power: supply: macsmc_power: Report not charging for CHLS thresholds

If a CHLS charge threshold is configured and the current SoC is above
the start threshold report a busy BMS as not charging.

power: supply: macsmc_power: Report only supported properties

The SMC firmware in macOS 15.4 dropped "AC-i" and "AC-n" (and all keys
with lower case last letter) without obvious replacement. Stop reporting
VOLTAGE_NOW / INPUT_CURRENT_LIMIT if "AC-n" is not present.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Co-developed-by: Thomas Weißschuh <linux@weissschuh.net>
Signed-off-by: Janne Grunau <j@jannau.net>
Co-developed-by: Janne Grunau <j@jannau.net>
Co-authored-by: Joey Gouly <joey.gouly@arm.com>
Signed-off-by: Hector Martin <marcan@marcan.st>
power: supply: macsmc_power: Log power data on button presses

This helps catch s2idle power stats, since we get early data when the
system resumes due to a power button press.

Signed-off-by: Hector Martin <marcan@marcan.st>
Signed-off-by: Janne Grunau <j@jannau.net>
Hard wakeup events are required to wake from s2idle. The comment in [1]
to always send wakeup events is correct though. To combine both
requirements use pm_wakeup_dev_event() and evaluate the previous
conditions for calling pm_wakeup_hard_event() as hard parameters.
The remark about always reporting KEY_POWER is only partially correct
though. (Some) User space handles that indeed correctly but a system
offering a agetty login prompt shuts down immediately after waking from
s2idle.

1: https://lore.kernel.org/all/qffp7kadq3xojla5k6f5pr37irgytqfsqvabr6ydvulxnkcgnn@bv5mrraxrhhe/

Signed-off-by: Janne Grunau <j@jannau.net>
Signed-off-by: Janne Grunau <j@jannau.net>
@IntegralPilot
Copy link
Author

Oh sorry just realised there's an unrelated 2-line whitespace change in macsmc_log_power_ops, that was caused by check patch.pl --fix so probably is the correct whitespace, although I can undo this change if you'd like as it's not related to the patch.

Newer Apple SMC firmware (found on M3 devices and updated M1/M2) has
removed the legacy `CH0C` (Inhibit Charge) and `CH0I` (Force Discharge) keys.
Reading these missing keys results in -EIO (-5) errors, causing the `charge_behaviour`
sysfs property to fail completely.

This patch adds support for the new `CHTE` key used for charge inhibition on these devices.

For now, it seems that `auto` and `inhibit-charge` are the only possible behaviours to
set using this new key, however further macOS tracing may reveal additional behaviour
states in future.

Changes:
1. Detects the presence of `CHTE`, `CH0C`, and `CH0I` during probe.
2. Only exposes `force_discharge` capability if `CH0I` is actually present.
3. Implements read/write support for `CHTE` using raw byte buffers
(this is to avoid endianness issues with the kernel's u32 helpers)

Fully backwards compatible with both old and new firmwares. Tested on M3 with new firmware.

Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
@IntegralPilot IntegralPilot force-pushed the fix/charge-behaviour-smc-new-fw branch from 49a85dd to aee3b28 Compare January 2, 2026 04:14
@IntegralPilot
Copy link
Author

Just moved a comment to a more logical place.

@nukelet
Copy link

nukelet commented Jan 2, 2026

thanks a ton for looking into this issue! i upgraded MacOS earlier today and was extremely disappointed to find out about this regression.

this patch does fix the -EIO error when reading from charge-behavior for me, but it seems that uevent is broken (the sysfs file reads empty). i can try looking into this later today if you don't have the time

@IntegralPilot
Copy link
Author

IntegralPilot commented Jan 2, 2026

Thank you for testing this out! I can't reproduce the uevent issue on M3 sorry, it is working fine for me. Can you please check if this patch introduced uevent issues, or if they occurred before it?

@nukelet
Copy link

nukelet commented Jan 5, 2026

somehow the error seems to be gone now. i did find out that i was loading the wrong device trees due to a minor corner case in my distro's kernel install hooks, but i haven't fixed the issue and yet the empty read on uevent issue seems to be gone; i wonder if it's somehow transient due to some firmware weirdness? i'm testing on a t8103-j293 (macbook pro m1 13''), fwiw.

either way, it seems to be working fine now. thanks again for looking into this!

@hexchain
Copy link

hexchain commented Jan 5, 2026

Tested on 16-inch M2 Pro 2023. Before applying this patch, setting the charge threshold is flaky, and there are a lot of the following logs:

kernel: power_supply macsmc-battery: driver failed to report `charge_behaviour' property: -5

These are all gone now. Thanks for working on this!

@IntegralPilot
Copy link
Author

IntegralPilot commented Jan 6, 2026

I sent this driver upstream, including this and other improvements: https://lore.kernel.org/asahi/20260105-b4-macsmc-power-v1-0-62954c42a555@gmail.com, though it will probably take several weeks of review, then several weeks for it to land here, so I'll leave this open in case anyone wants just the charge_behaviour patch so optimised charging works, though feel free to close if it's better to wait for upstream.

Happy to help, it was fun reverse engineering to find out how to fix this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants