Skip to content

Commit

Permalink
ACPI: SBS: Fix handling of Smart Battery Selectors
Browse files Browse the repository at this point in the history
The "Smart Battery Selector" standard says that when writing
SelectorState (0x1), the nibbles which should not be modified
need to be masked with 0xff. This is necessary since in contrast
to a "Smart Battery Manager", the last three nibbles are writable.

Failing to do so might trigger the following cycle:
1. Host accidentally changes power source of the system (3rd nibble)
   when selecting a battery.
2. Power source is invalid, Selector changes to another power source.
3. Selector notifies host that it changed the power source.
4. Host re-reads some batteries.
5. goto 1 for each re-read battery.

This loop might also be entered when a battery which is not present
is selected for SMBus access. In the end some workqueues fill up,
which causes the system to lockup upon suspend/shutdown.

Fix this by correctly masking the value to be written, and avoid
selecting batteries which are absent.

Tested on a Acer Travelmate 4002WLMi.

Signed-off-by: Armin Wolf <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
  • Loading branch information
Wer-Wolf authored and rafaeljw committed Mar 30, 2023
1 parent e5b492c commit 3bd554e
Showing 1 changed file with 18 additions and 9 deletions.
27 changes: 18 additions & 9 deletions drivers/acpi/sbs.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,23 +473,32 @@ static const struct device_attribute alarm_attr = {
-------------------------------------------------------------------------- */
static int acpi_battery_read(struct acpi_battery *battery)
{
int result = 0, saved_present = battery->present;
int result, saved_present = battery->present;
u16 state;

if (battery->sbs->manager_present) {
result = acpi_smbus_read(battery->sbs->hc, SMBUS_READ_WORD,
ACPI_SBS_MANAGER, 0x01, (u8 *)&state);
if (!result)
battery->present = state & (1 << battery->id);
state &= 0x0fff;
if (result)
return result;

battery->present = state & (1 << battery->id);
if (!battery->present)
return 0;

/* Masking necessary for Smart Battery Selectors */
state = 0x0fff;
state |= 1 << (battery->id + 12);
acpi_smbus_write(battery->sbs->hc, SMBUS_WRITE_WORD,
ACPI_SBS_MANAGER, 0x01, (u8 *)&state, 2);
} else if (battery->id == 0)
battery->present = 1;

if (result || !battery->present)
return result;
} else {
if (battery->id == 0) {
battery->present = 1;
} else {
if (!battery->present)
return 0;
}
}

if (saved_present != battery->present) {
battery->update_time = 0;
Expand Down

0 comments on commit 3bd554e

Please sign in to comment.