/** * Check for some event triggering the shutdown. * * It can be either a long power button press or a shutdown triggered from the * AP and detected by reading POWER_GOOD. * * @return non-zero if a shutdown should happen, 0 if not */ static int check_for_power_off_event(void) { timestamp_t now; int pressed = 0; int ret = 0; /* * Check for power button press. */ if (power_button_is_pressed()) { pressed = 1; } else if (power_request == POWER_REQ_OFF) { power_request = POWER_REQ_NONE; return 4; /* return non-zero for shudown down */ } #ifdef HAS_TASK_KEYSCAN /* Dis/Enable keyboard scanning when the power button state changes */ if (!pressed || pressed != power_button_was_pressed) keyboard_scan_enable(!pressed, KB_SCAN_DISABLE_POWER_BUTTON); #endif now = get_time(); if (pressed) { if (!power_button_was_pressed) { power_off_deadline.val = now.val + DELAY_FORCE_SHUTDOWN; CPRINTS("power waiting for long press %u", power_off_deadline.le.lo); /* Ensure we will wake up to check the power key */ timer_arm(power_off_deadline, TASK_ID_CHIPSET); } else if (timestamp_expired(power_off_deadline, &now)) { power_off_deadline.val = 0; CPRINTS("power off after long press now=%u, %u", now.le.lo, power_off_deadline.le.lo); return 2; } } else if (power_button_was_pressed) { CPRINTS("power off cancel"); timer_cancel(TASK_ID_CHIPSET); } /* POWER_GOOD released by AP : shutdown immediately */ if (!power_has_signals(IN_POWER_GOOD)) { if (power_button_was_pressed) timer_cancel(TASK_ID_CHIPSET); ret = 3; } power_button_was_pressed = pressed; return ret; }
/** * Update status of non-debounced switches. * * Note that deferred functions are called in the same context as lid and * power button changes, so we don't need a mutex. */ static void switch_update(void) { static uint8_t prev; /* Make sure this is safe to call before power_button_init() */ if (!memmap_switches) return; prev = *memmap_switches; if (power_button_is_pressed()) *memmap_switches |= EC_SWITCH_POWER_BUTTON_PRESSED; else *memmap_switches &= ~EC_SWITCH_POWER_BUTTON_PRESSED; if (lid_is_open()) *memmap_switches |= EC_SWITCH_LID_OPEN; else *memmap_switches &= ~EC_SWITCH_LID_OPEN; if ((flash_get_protect() & EC_FLASH_PROTECT_GPIO_ASSERTED) == 0) *memmap_switches |= EC_SWITCH_WRITE_PROTECT_DISABLED; else *memmap_switches &= ~EC_SWITCH_WRITE_PROTECT_DISABLED; #ifdef CONFIG_SWITCH_DEDICATED_RECOVERY if (gpio_get_level(GPIO_RECOVERY_L) == 0) *memmap_switches |= EC_SWITCH_DEDICATED_RECOVERY; else *memmap_switches &= ~EC_SWITCH_DEDICATED_RECOVERY; #endif if (prev != *memmap_switches) CPRINTS("SW 0x%02x", *memmap_switches); }
/** * Handle debounced power button changing state. */ static void powerbtn_x86_changed(void) { if (pwrbtn_state == PWRBTN_STATE_BOOT_KB_RESET || pwrbtn_state == PWRBTN_STATE_INIT_ON || pwrbtn_state == PWRBTN_STATE_LID_OPEN || pwrbtn_state == PWRBTN_STATE_WAS_OFF) { /* Ignore all power button changes during an initial pulse */ CPRINTS("PB ignoring change"); return; } if (power_button_is_pressed()) { /* Power button pressed */ power_button_pressed(get_time().val); } else { /* Power button released */ if (pwrbtn_state == PWRBTN_STATE_EAT_RELEASE) { /* * Ignore the first power button release if we already * told the PCH the power button was released. */ CPRINTS("PB ignoring release"); pwrbtn_state = PWRBTN_STATE_IDLE; return; } power_button_released(get_time().val); } /* Wake the power button task */ task_wake(TASK_ID_POWERBTN); }
static void fake_hibernate_power_button_hook(void) { if (fake_hibernate && lid_is_open() && !power_button_is_pressed()) { ccprints("%s() resets EC", __func__); cflush(); system_reset(SYSTEM_RESET_HARD); } }
static void hang_detect_power_button(void) { if (power_button_is_pressed()) { if (hdparams.flags & EC_HANG_START_ON_POWER_PRESS) hang_detect_start("power button"); } else { if (hdparams.flags & EC_HANG_STOP_ON_POWER_RELEASE) hang_detect_stop("power button"); } }
void power_button_pch_release(void) { CPRINTS("PB PCH force release"); /* Deassert power button signal to PCH */ set_pwrbtn_to_pch(1); /* * If power button is actually pressed, eat the next release so we * don't send an extra release. */ if (power_button_is_pressed()) pwrbtn_state = PWRBTN_STATE_EAT_RELEASE; else pwrbtn_state = PWRBTN_STATE_IDLE; }
/** * Check if there has been a power-on event * * This checks all power-on event signals and returns non-zero if any have been * triggered (with debounce taken into account). * * @return non-zero if there has been a power-on event, 0 if not. */ static int check_for_power_on_event(void) { int ap_off_flag; ap_off_flag = system_get_reset_flags() & RESET_FLAG_AP_OFF; system_clear_reset_flags(RESET_FLAG_AP_OFF); /* check if system is already ON */ if (power_get_signals() & IN_POWER_GOOD) { if (ap_off_flag) { CPRINTS( "system is on, but " "RESET_FLAG_AP_OFF is on"); return 0; } else { CPRINTS( "system is on, thus clear " "auto_power_on"); /* no need to arrange another power on */ auto_power_on = 0; return 1; } } /* power on requested at EC startup for recovery */ if (auto_power_on) { auto_power_on = 0; return 2; } /* Check lid open */ if (lid_opened) { lid_opened = 0; return 3; } /* check for power button press */ if (power_button_is_pressed()) return 4; if (power_request == POWER_REQ_ON) { power_request = POWER_REQ_NONE; return 5; } return 0; }
/** * Set initial power button state. */ static void set_initial_pwrbtn_state(void) { uint32_t reset_flags = system_get_reset_flags(); if (system_jumped_to_this_image() && chipset_in_state(CHIPSET_STATE_ON)) { /* * Jumped to this image while the chipset was already on, so * simply reflect the actual power button state. */ if (power_button_is_pressed()) { CPRINTS("PB init-jumped-held"); set_pwrbtn_to_pch(0); } else { CPRINTS("PB init-jumped"); } } else if ((reset_flags & RESET_FLAG_AP_OFF) || (keyboard_scan_get_boot_key() == BOOT_KEY_DOWN_ARROW)) { /* * Reset triggered by keyboard-controlled reset, and down-arrow * was held down. Or reset flags request AP off. * * Leave the main processor off. This is a fail-safe * combination for debugging failures booting the main * processor. * * Don't let the PCH see that the power button was pressed. * Otherwise, it might power on. */ CPRINTS("PB init-off"); power_button_pch_release(); } else { /* * All other EC reset conditions power on the main processor so * it can verify the EC. */ #ifdef CONFIG_BRINGUP CPRINTS("PB idle"); pwrbtn_state = PWRBTN_STATE_IDLE; #else CPRINTS("PB init-on"); pwrbtn_state = PWRBTN_STATE_INIT_ON; #endif } }
/** * Wait for the power button to be released * * @param timeout_us Timeout in microseconds, or -1 to wait forever * @return EC_SUCCESS if ok, or * EC_ERROR_TIMEOUT if power button failed to release */ int power_button_wait_for_release(int timeout_us) { timestamp_t deadline; timestamp_t now = get_time(); deadline.val = now.val + timeout_us; while (!power_button_is_stable || power_button_is_pressed()) { now = get_time(); if (timeout_us < 0) { task_wait_event(-1); } else if (timestamp_expired(deadline, &now) || (task_wait_event(deadline.val - now.val) == TASK_EVENT_TIMER)) { CPRINTS("power button not released in time"); return EC_ERROR_TIMEOUT; } } CPRINTS("power button released in time"); return EC_SUCCESS; }
/** * Power button state machine. * * @param tnow Current time from usec counter */ static void state_machine(uint64_t tnow) { /* Not the time to move onto next state */ if (tnow < tnext_state) return; /* States last forever unless otherwise specified */ tnext_state = 0; switch (pwrbtn_state) { case PWRBTN_STATE_PRESSED: if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { /* * Chipset is off, so wake the chipset and send it a * long enough pulse to wake up. After that we'll * reflect the true power button state. If we don't * stretch the pulse here, the user may release the * power button before the chipset finishes waking from * hard off state. */ chipset_exit_hard_off(); tnext_state = tnow + PWRBTN_INITIAL_US; pwrbtn_state = PWRBTN_STATE_WAS_OFF; } else { /* Chipset is on, so send the chipset a pulse */ tnext_state = tnow + PWRBTN_DELAY_T0; pwrbtn_state = PWRBTN_STATE_T0; } set_pwrbtn_to_pch(0); break; case PWRBTN_STATE_T0: tnext_state = tnow + PWRBTN_DELAY_T1; pwrbtn_state = PWRBTN_STATE_T1; set_pwrbtn_to_pch(1); break; case PWRBTN_STATE_T1: /* * If the chipset is already off, don't tell it the power * button is down; it'll just cause the chipset to turn on * again. */ if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) CPRINTS("PB chipset already off"); else set_pwrbtn_to_pch(0); pwrbtn_state = PWRBTN_STATE_HELD; break; case PWRBTN_STATE_RELEASED: case PWRBTN_STATE_LID_OPEN: set_pwrbtn_to_pch(1); pwrbtn_state = PWRBTN_STATE_IDLE; break; case PWRBTN_STATE_INIT_ON: /* * Don't do anything until the charger knows the battery level. * Otherwise we could power on the AP only to shut it right * back down due to insufficient battery. */ #ifdef HAS_TASK_CHARGER if (charge_get_state() == PWR_STATE_INIT) break; #endif /* * Power the system on if possible. Gating due to insufficient * battery is handled inside set_pwrbtn_to_pch(). */ chipset_exit_hard_off(); set_pwrbtn_to_pch(0); tnext_state = get_time().val + PWRBTN_INITIAL_US; if (power_button_is_pressed()) { if (system_get_reset_flags() & RESET_FLAG_RESET_PIN) pwrbtn_state = PWRBTN_STATE_BOOT_KB_RESET; else pwrbtn_state = PWRBTN_STATE_WAS_OFF; } else { pwrbtn_state = PWRBTN_STATE_RELEASED; } break; case PWRBTN_STATE_BOOT_KB_RESET: /* Initial forced pulse is done. Ignore the actual power * button until it's released, so that holding down the * recovery combination doesn't cause the chipset to shut back * down. */ set_pwrbtn_to_pch(1); if (power_button_is_pressed()) pwrbtn_state = PWRBTN_STATE_EAT_RELEASE; else pwrbtn_state = PWRBTN_STATE_IDLE; break; case PWRBTN_STATE_WAS_OFF: /* Done stretching initial power button signal, so show the * true power button state to the PCH. */ if (power_button_is_pressed()) { /* User is still holding the power button */ pwrbtn_state = PWRBTN_STATE_HELD; } else { /* Stop stretching the power button press */ power_button_released(tnow); } break; case PWRBTN_STATE_IDLE: case PWRBTN_STATE_HELD: case PWRBTN_STATE_EAT_RELEASE: /* Do nothing */ break; } }