WARNING: NO WARRANTIES, USE AT YOUR OWN RISK! DONT SUE US WHEN YOUR BATTERY, MACBOOK, HOUSE OR ANYTHING ELSE BURNS DOWN !!! Potentially unbricking e.g. Apple MacBook batteries with too low of a safety voltage and permanent failure bit set. By unsealing and clearing the Permanent Failure Status bit! 1st: for this to have an effect and work you need to first open the battery without damaging the cells, check that they are ok, not expanded, burned, ... and pre-charge them with a lab power supply until they have an initial high enough minimal cell voltage. Only then run this modifed ACPI code to reset the bit. (if you run this without pre-charging the battery pack will likely instantly go back into fail mode due to stil being below the minimal safety voltage. The pack I test this with did not instantly start charging, but had to be ejected from the MacBook and inserted again. Please let ms know if and when you try this and your result! Outputs will be in the system dmesg. Signed-off-by: René Rebe --- linux-6.5/drivers/acpi/sbs.c.vanilla 2023-08-27 23:49:51.000000000 +0200 +++ linux-6.5/drivers/acpi/sbs.c 2023-10-18 13:20:03.766073598 +0200 @@ -42,6 +42,10 @@ module_param(cache_time, uint, 0644); MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); +static bool unbrick_apple_pf = 0; +module_param(unbrick_apple_pf, bool, 0644); +MODULE_PARM_DESC(unbrick_apple_pf, "check permanent fault bit and try to unbrick Apple batteries"); + #define MAX_SBS_BAT 4 #define ACPI_SBS_BLOCK_MAX 32 @@ -514,6 +518,40 @@ return result; } +static const char* mfg_status[16] = { + "Wake up", + "Normal discharge", + "", + "Precharge", + "", + "Charge", + "Charge termination", + "", + "Fault charge terminate", + "Permanent failure", + "Overcurrent", + "Overtemperature", + "Battery failure", + "Sleep", + "Reserved", + "Battery removed", +}; + +static const char* fet_status[4] = { + "both on", + "CHG off, DSG on", + "both off", + "CHG on, DSG off", +}; + +static const char* pf_status[4] = { + "Fuse blown (if failure)", + "Cell imbalance", + "Safety Volt fail", + "FET fail", +}; + + /* Smart Battery */ static int acpi_battery_add(struct acpi_sbs *sbs, int id) { @@ -556,6 +594,55 @@ pr_info("%s [%s]: Battery Slot [%s] (battery %s)\n", ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), battery->name, battery->present ? "present" : "absent"); + + /* test, unseal and unbrick Apple batteries? */ + if (unbrick_apple_pf) { + u16 value = 0x0006; /* ManufacturerStatus */ + result = acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2); + if (result) { + printk(KERN_INFO "error: failed to get ManufacturerStatus: %d\n", result); + } else { + result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value); + printk(KERN_INFO "result: %d %04x %s\n", result, value, mfg_status[(value >> 8) & 0xf]); + printk(KERN_INFO "result: FETs: %s, PF: %s\n", fet_status[(value >> 14) & 0x3], pf_status[(value >> 12) & 0x3]); + bool was_perm_fault = ((value >> 8) & 0xf) == 0x9; + + value = 0x53; + acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2); + result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value); + printk(KERN_INFO "PFStatus: %d %04x\n", result, value); + value = 0x54; + acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2); + result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value); + printk(KERN_INFO "OpStatus: %d %04x\n", result, value); + + // was permanent fault? Unseal and reset: + if (was_perm_fault) { + value = 0x0414; + result = acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2); + value = 0x3672; + result |= acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2); + if (result) { + printk(KERN_INFO "Unseal: %d\n", result); + } else { + /* clear permanent failure */ + value = 0x2673; + result = acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2); + value = 0x1712; + result |= acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2); + if (result) + printk(KERN_INFO "Clear pf failed: %d\n", result); + + /* seal */ + value = 0x20; + result = acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD, ACPI_SBS_BATTERY, 0x0, (u8 *)&value, 2); + if (result) + printk(KERN_INFO "Re-sealing failed: %d\n", result); + } + } + } + } + return result; }