Skip to content

AC power state bounces after plugging/unplugging AC power #88

Open

Description

@baodrate

system: FW 13 Pro w/ Ultra X7 358H (Sakura)

When I plug/unplug AC power, the state of /sys/class/power_supply/ACAD/online bounces for ~150ms before it settles back to the correct state.

This is causing issues for me with tuned-ppd which seems to be reading the wrong AC state and never correcting it.

e.g:

  1. AC is unplugged
  2. 0ms: kernel event on /devices/../power_supply/ACAD.
  3. 5ms: /sys/class/power_supply/ACAD/online reads 0
  4. 30ms: /sys/class/power_supply/ACAD/online reads 1
  5. 100ms: tuned (on udev event) reads AC state as online, changes to AC power profile.
  6. 150ms: /sys/class/power_supply/ACAD/online reads 0

It seems like tuned does not poll but only reacts to udev events, so it never changes to the correct power profile.

Activity

  1. baodrate commented on Sep 22, 2026

    @baodrate
    Author

    I'm not confident, but is the issue something like this?

    • charger_task() reads AC state in a loop, and reads the AC-state

    while (1) {
    /* Let's see what's going on... */
    curr.ts = get_time();
    sleep_usec = 0;
    problems_exist = 0;
    battery_critical = 0;
    check_extpower(chgnum);

    • then the charger is connected/disconnected and triggers the extpower_deferred() to be deferred

    static void extpower_deferred(void)
    {
    int extpower_presence = gpio_get_level(GPIO_AC_PRESENT);
    if (extpower_presence == debounced_extpower_presence)
    return;
    debounced_extpower_presence = extpower_presence;
    extpower_handle_update(extpower_presence);
    }
    DECLARE_DEFERRED(extpower_deferred);
    void extpower_interrupt(enum gpio_signal signal)
    {
    /* Trigger deferred notification of external power change */
    hook_call_deferred(&extpower_deferred_data,
    CONFIG_EXTPOWER_DEBOUNCE_MS * MSEC);
    }

    • charger_task() continues to charger_get_params() which reads from i2c and blocks/yields(?)1
    • extpower_deferred() runs, calling extpower_handle_update() which updates the mmap and notifies the system
    • charger_task() continues, hitting process_charge_state(), which updates the battery flags mmap with the now-outdated AC-state that was read at the beginning of the iteration
    • on the next iteration the updated state is read and is reverted

    Footnotes

    1. not very familiar with how zephyr scheduling works. this might be off the mark ↩

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions