1 WARNING: NO WARRANTIES, USE AT YOUR OWN RISK!
2 DONT SUE US WHEN YOUR BATTERY, MACBOOK, HOUSE OR ANYTHING ELSE
5 Potentially unbricking e.g. Apple MacBook batteries with
6 too low of a safety voltage and permanent failure bit set.
7 By unsealing and clearing the Permanent Failure Status bit!
9 1st: for this to have an effect and work you need to first
10 open the battery without damaging the cells, check that
11 they are ok, not expanded, burned, ... and pre-charge them
12 with a lab power supply until they have an initial high
13 enough minimal cell voltage.
15 Only then run this modifed ACPI code to reset the bit.
16 (if you run this without pre-charging the battery pack
17 will likely instantly go back into fail mode due to
18 stil being below the minimal safety voltage.
20 The pack I test this with did not instantly start charging,
21 but had to be ejected from the MacBook and inserted again.
23 Please let ms know if and when you try this and your result!
24 Outputs will be in the system dmesg.
26 Signed-off-by: René Rebe <rene@rebe.name>
28 --- linux-6.5/drivers/acpi/sbs.c.vanilla 2023-08-27 23:49:51.000000000 +0200
29 +++ linux-6.5/drivers/acpi/sbs.c 2023-10-18 13:20:03.766073598 +0200
31 module_param(cache_time, uint, 0644);
32 MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
34 +static bool unbrick_apple_pf = 0;
35 +module_param(unbrick_apple_pf, bool, 0644);
36 +MODULE_PARM_DESC(unbrick_apple_pf, "check permanent fault bit and try to unbrick Apple batteries");
39 #define ACPI_SBS_BLOCK_MAX 32
45 +static const char* mfg_status[16] = {
52 + "Charge termination",
54 + "Fault charge terminate",
55 + "Permanent failure",
64 +static const char* fet_status[4] = {
71 +static const char* pf_status[4] = {
72 + "Fuse blown (if failure)",
80 static int acpi_battery_add(struct acpi_sbs *sbs, int id)
83 pr_info("%s [%s]: Battery Slot [%s] (battery %s)\n",
84 ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device),
85 battery->name, battery->present ? "present" : "absent");
87 + /* test, unseal and unbrick Apple batteries? */
88 + if (unbrick_apple_pf) {
89 + u16 value = 0x0006; /* ManufacturerStatus */
90 + result = acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2);
92 + printk(KERN_INFO "error: failed to get ManufacturerStatus: %d\n", result);
94 + result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value);
95 + printk(KERN_INFO "result: %d %04x %s\n", result, value, mfg_status[(value >> 8) & 0xf]);
96 + printk(KERN_INFO "result: FETs: %s, PF: %s\n", fet_status[(value >> 14) & 0x3], pf_status[(value >> 12) & 0x3]);
97 + bool was_perm_fault = ((value >> 8) & 0xf) == 0x9;
100 + acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2);
101 + result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value);
102 + printk(KERN_INFO "PFStatus: %d %04x\n", result, value);
104 + acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2);
105 + result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value);
106 + printk(KERN_INFO "OpStatus: %d %04x\n", result, value);
108 + // was permanent fault? Unseal and reset:
109 + if (was_perm_fault) {
111 + result = acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2);
113 + result |= acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2);
115 + printk(KERN_INFO "Unseal: %d\n", result);
117 + /* clear permanent failure */
119 + result = acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2);
121 + result |= acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2);
123 + printk(KERN_INFO "Clear pf failed: %d\n", result);
127 + result = acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2);
129 + printk(KERN_INFO "Re-sealing failed: %d\n", result);