zackelia / bclm

macOS command-line utility to limit max battery charge
MIT License
1.79k stars 87 forks source link

Implement Apple Silicon Support (there is a way!) #20

Closed godly-devotion closed 10 months ago

godly-devotion commented 3 years ago

Although it was not known before, there is now a way to support Apple Silicon based MacBooks due to the hard work over at another similar project.

Code: https://github.com/davidwernhart/AlDente/commit/77119a1a13c5c72455d23a37595cfd9cce80f036#diff-49a0a057547a2cf2e243b33525c55136dd7c41fc9fb133bed10746e349bda1ff Discussion: https://github.com/davidwernhart/AlDente/issues/52

Hopefully, it can eventually be implemented in bclm as well.

johnzweng commented 2 years ago

In the (linked) long discussion, this is the first comment, where the new SMC key CH0B first was mentioned: https://github.com/davidwernhart/AlDente/issues/52#issuecomment-777627075 (see also the following).

As it seems (according to this discussion), writing 00 into SMC key CH0B enables charging and writing 02 disables charging.

This is also the way AlDente seems to use, see this line: https://github.com/davidwernhart/AlDente/blob/0abfeafbd2232d16116c0fe5a6fbd0acb6f9826b/AlDente/Helper.swift#L227

Also the behaviour seems to be different than the current BCLM value where you set a charging maximum value (and let the charging logic decide when to turn actually on or off charging). This new CH0B seems to directly simply turn on/off charging (at any level).

actuallymentor commented 2 years ago

@johnzweng so you have some resources on how SMC values are written? If this repo stagnates I could write a script to do something similar.

zackelia commented 2 years ago

Apologies for not getting around to this sooner. Looking at the changes, am I correct that for M1 we are only able to enable/disable charging as opposed to Intel where we set a max charging value? These seem like unrelated ideas to me. If so, is there really a reason to enable/disable charging manually, especially without something polling battery percentage?

actuallymentor commented 2 years ago

Apologies for not getting around to this sooner.

Never apologise for open source work :)

Looking at the changes, am I correct that for M1 we are only able to enable/disable charging as opposed to Intel where we set a max charging value?

Can't speak for @johnzweng but I am solidly not this familiar with how macs manage their hardware.

I can help with a digest of the above thread though:

TL;DR: Aldente thread

My cursory read:

pmset -g batt is able to read battery/charge state.

Setting CH0C to 01 stops charging (but retains adapter power usage) and 00 continues charging. But for some reason Aldente uses the CH0B key. Not sure what the difference is. convo source.

This script shared by @thloh85 illustrated a potential approach:

while [ true ];
do
BATT_PERCENT=`pmset -g batt | tail -n1 | awk '{print $3}' | sed s:\%\;::`
echo Battery percent : $BATT_PERCENT
if [ $BATT_PERCENT -ge 43 ]; then
    ./smc -k CH0C -w 01
else
    ./smc -k CH0C -w 00
fi
sleep 60
done

@joelucid made a GUI app here.

It appears Aldente does have to force the system into an awake state:

@inprealpha From my knowledge, it is not possible for an application to get any CPU time while the system is asleep. Since this necessary for the M1 version because charge has to be inhibited in just the right moment, AlDente stops the System from going to sleep until the desired value is reached. This is done using IOPMAssertions. source

actuallymentor commented 2 years ago

@zackelia how would you feel about using the above info to simply include a "enable/disable charging" toggle?

I think many M1 users such as myself would already be very happy to be able to do that from CLI.

actuallymentor commented 2 years ago

In case anyone finds it useful, I made a little open source tool that adds the following CLI commands:

battery charging on: sets CH0B to 00 (allow charging) battery charging off: sets CH0B to 02 (disallow charging)

Open source and without warranty.

krackers commented 2 years ago

From my knowledge, it is not possible for an application to get any CPU time while the system is asleep.

If you can trigger a dark wake I think you will get cpu time then. I have a suspicion that when native battery optimization is enabled, there is a periodic wake up scheduled at which point the charging state is changed.

But this seems like a worse design from a hardware perspective compared to the older behavior where the SMC could manage the target charge state... no idea what Apple was thinking.

aykevl commented 1 year ago

Just something I discovered while looking into Asahi Linux (source):

        /*
         * CH0I/CH0C are "hard" controls that will allow the battery to run down to 0.
         * CH0K/CH0B are "soft" controls that are reset to 0 when SOC drops below 50%;
         * we don't expose these yet.
         */

These could be useful for the bclm tool I guess.

actuallymentor commented 1 year ago

Oooh @aykevl that is super cool to know. I'm currently writing both values, but it sounds like CH0C is the "master switch" for practical purposes?

Also do you know by heart the function of CH0I and CH0K?

aykevl commented 1 year ago

I really don't know, I just looked through the Linux kernel source to make an AlDente equivalent for Linux (haven't done that yet, but I did find the values exposed in /sys so it should be simple). The Apple Silicon wizard here is @marcan.

hraban commented 1 year ago
while [ true ];
do
BATT_PERCENT=`pmset -g batt | tail -n1 | awk '{print $3}' | sed s:\%\;::`
echo Battery percent : $BATT_PERCENT
if [ $BATT_PERCENT -ge 43 ]; then
  ./smc -k CH0C -w 01
else
  ./smc -k CH0C -w 00
fi
sleep 60
done

Thanks for this tip, I've been using this manually to just toggle charging. It works well.

zackelia commented 1 year ago

It seems that for Apple Silicon chips, we are only able to enable/disable charging. A polling solution would be required which would be quite different to how bclm behaves currently. As well, ince other tooling seems to have been developed and optimized battery charging exists for Apple Silicon, it doesn't make sense to me to add it to bclm.

double-thinker commented 1 year ago

It seems that for Apple Silicon chips, we are only able to enable/disable charging. A polling solution would be required which would be quite different to how bclm behaves currently. As well, ince other tooling seems to have been developed and optimized battery charging exists for Apple Silicon, it doesn't make sense to me to add it to bclm.

Now there is a solution for Apple Silicon, see a naive implementation here using CHWA flag. It sets a 80% (fixed) limit managed by the firmware that is maintained while sleeping. Kudos to Asahi team

vosslab commented 1 year ago

See https://github.com/double-thinker/battery/

zackelia commented 10 months ago

Now there is a solution for Apple Silicon, see a naive implementation here using CHWA flag.

Thanks @double-thinker for calling my attention to this! While the key does not take a specified value like BCLM, I believe this is an acceptable compromise that fits well into the original model of bclm. This is a much better path forward for Apple Silicon and I am willing to implement this.

Since last update, I now have Apple Silicon hardware which will expedite development and testing. Expect updates in the coming days. Reopening this issue.

zackelia commented 10 months ago

Apple silicon support implemented via CHWA in release v0.1.0.

seamusdemora commented 10 months ago

"support for Apple ARM M1":

Would this by chance also support ARM M2?

zackelia commented 10 months ago

@seamusdemora While I have only personally tested it on an M1 chip, I see no reason why it wouldn't work on M2. If it doesn't work, please let know so I can document that!

seamusdemora commented 10 months ago

roger that!