diff options
Diffstat (limited to 'power/common.c')
-rw-r--r-- | power/common.c | 1117 |
1 files changed, 0 insertions, 1117 deletions
diff --git a/power/common.c b/power/common.c deleted file mode 100644 index 0f83a2ce61..0000000000 --- a/power/common.c +++ /dev/null @@ -1,1117 +0,0 @@ -/* Copyright 2013 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* Common functionality across all chipsets */ - -#include "battery.h" -#include "charge_state.h" -#include "chipset.h" -#include "common.h" -#include "console.h" -#include "display_7seg.h" -#include "espi.h" -#include "extpower.h" -#include "gpio.h" -#include "hooks.h" -#include "host_command.h" -#include "lpc.h" -#include "power.h" -#include "power/intel_x86.h" -#include "power/qcom.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) -#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) - -/* - * Default timeout in us; if we've been waiting this long for an input - * transition, just jump to the next state. - */ -#define DEFAULT_TIMEOUT SECOND - -/* Timeout for dropping back from S5 to G3 in seconds */ -#ifdef CONFIG_CMD_S5_TIMEOUT -static int s5_inactivity_timeout = 10; -#else -static const int s5_inactivity_timeout = 10; -#endif - -static const char * const state_names[] = { - "G3", - "S5", - "S3", - "S0", -#ifdef CONFIG_POWER_S0IX - "S0ix", -#endif - "G3->S5", - "S5->S3", - "S3->S0", - "S0->S3", - "S3->S5", - "S5->G3", -#ifdef CONFIG_POWER_S0IX - "S0ix->S0", - "S0->S0ix", -#endif -}; - -static uint32_t in_signals; /* Current input signal states (IN_PGOOD_*) */ -static uint32_t in_want; /* Input signal state we're waiting for */ -static uint32_t in_debug; /* Signal values which print debug output */ - -static enum power_state state = POWER_G3; /* Current state */ -static int want_g3_exit; /* Should we exit the G3 state? */ -static uint64_t last_shutdown_time; /* When did we enter G3? */ - -#ifdef CONFIG_HIBERNATE -/* Delay before hibernating, in seconds */ -static uint32_t hibernate_delay = CONFIG_HIBERNATE_DELAY_SEC; -#endif - -#ifdef CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5 -/* Pause in S5 on shutdown? */ -static int pause_in_s5; -#endif - -static bool want_reboot_ap_at_g3;/* Want to reboot AP from G3? */ -/* Want to reboot AP from G3 with delay? */ -static uint64_t reboot_ap_at_g3_delay; - -static enum ec_status -host_command_reboot_ap_on_g3(struct host_cmd_handler_args *args) -{ - const struct ec_params_reboot_ap_on_g3_v1 *cmd = args->params; - - /* Store request for processing at g3 */ - want_reboot_ap_at_g3 = true; - - switch (args->version) { - case 0: - break; - case 1: - /* Store user specified delay to wait in G3 state */ - reboot_ap_at_g3_delay = cmd->reboot_ap_at_g3_delay; - break; - default: - return EC_RES_INVALID_PARAM; - } - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_REBOOT_AP_ON_G3, - host_command_reboot_ap_on_g3, - EC_VER_MASK(0) | EC_VER_MASK(1)); - -__overridable int power_signal_get_level(enum gpio_signal signal) -{ - if (IS_ENABLED(CONFIG_HOST_ESPI_VW_POWER_SIGNAL)) { - /* Check signal is from GPIOs or VWs */ - if (espi_signal_is_vw(signal)) - return espi_vw_get_wire(signal); - } - return gpio_get_level(signal); -} - -int power_signal_disable_interrupt(enum gpio_signal signal) -{ - if (IS_ENABLED(CONFIG_HOST_ESPI_VW_POWER_SIGNAL)) { - /* Check signal is from GPIOs or VWs */ - if (espi_signal_is_vw(signal)) - return espi_vw_disable_wire_int(signal); - } - return gpio_disable_interrupt(signal); -} - -int power_signal_enable_interrupt(enum gpio_signal signal) -{ - if (IS_ENABLED(CONFIG_HOST_ESPI_VW_POWER_SIGNAL)) { - /* Check signal is from GPIOs or VWs */ - if (espi_signal_is_vw(signal)) - return espi_vw_enable_wire_int(signal); - } - return gpio_enable_interrupt(signal); -} - -int power_signal_is_asserted(const struct power_signal_info *s) -{ - return power_signal_get_level(s->gpio) == - !!(s->flags & POWER_SIGNAL_ACTIVE_STATE); -} - -#ifdef CONFIG_BRINGUP -static const char *power_signal_get_name(enum gpio_signal signal) -{ - if (IS_ENABLED(CONFIG_HOSTCMD_ESPI)) { - /* Check signal is from GPIOs or VWs */ - if (espi_signal_is_vw(signal)) - return espi_vw_get_wire_name(signal); - } - return gpio_get_name(signal); -} -#endif - -/** - * Update input signals mask - */ -static void power_update_signals(void) -{ - uint32_t inew = 0; - const struct power_signal_info *s = power_signal_list; - int i; - - for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) { - if (power_signal_is_asserted(s)) - inew |= 1 << i; - } - - if ((in_signals & in_debug) != (inew & in_debug)) - CPRINTS("power in 0x%04x", inew); - - in_signals = inew; -} - -uint32_t power_get_signals(void) -{ - return in_signals; -} - -int power_has_signals(uint32_t want) -{ - if ((in_signals & want) == want) - return 1; - - CPRINTS("power lost input; wanted 0x%04x, got 0x%04x", - want, in_signals & want); - - return 0; -} - -int power_wait_signals(uint32_t want) -{ - int ret = power_wait_signals_timeout(want, DEFAULT_TIMEOUT); - - if (ret == EC_ERROR_TIMEOUT) - CPRINTS("power timeout on input; wanted 0x%04x, got 0x%04x", - want, in_signals & want); - return ret; -} - -int power_wait_signals_timeout(uint32_t want, int timeout) -{ - return power_wait_mask_signals_timeout(want, want, timeout); -} - -int power_wait_mask_signals_timeout(uint32_t want, uint32_t mask, int timeout) -{ - in_want = want; - if (!mask) - return EC_SUCCESS; - - while ((in_signals & mask) != in_want) { - if (task_wait_event(timeout) == TASK_EVENT_TIMER) { - power_update_signals(); - return EC_ERROR_TIMEOUT; - } - /* - * TODO(crosbug.com/p/23772): should really shrink the - * remaining timeout if we woke up but didn't have all the - * signals we wanted. Also need to handle aborts if we're no - * longer in the same state we were when we started waiting. - */ - } - return EC_SUCCESS; -} - -void power_set_state(enum power_state new_state) -{ - /* Record the time we go into G3 */ - if (new_state == POWER_G3) - last_shutdown_time = get_time().val; - - /* Print out the RTC value to help correlate EC and kernel logs. */ - print_system_rtc(CC_CHIPSET); - - state = new_state; - - /* - * Reset want_g3_exit flag here to prevent the situation that if the - * error handler in POWER_S5S3 decides to force shutdown the system and - * the flag is set, the system will go to G3 and then immediately exit - * G3 again. - */ - if (state == POWER_S5S3) - want_g3_exit = 0; -} - -enum power_state power_get_state(void) -{ - return state; -} - -#ifdef CONFIG_HOSTCMD_X86 - -/* If host doesn't program s0ix lazy wake mask, use default s0ix mask */ -#define DEFAULT_WAKE_MASK_S0IX (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) | \ - EC_HOST_EVENT_MASK(EC_HOST_EVENT_MODE_CHANGE)) - - /* - * Set the wake mask according to the current power state: - * 1. On transition to S0, wake mask is reset. - * 2. In non-S0 states, active mask set by host gets a higher preference. - * 3. If host has not set any active mask, then check if a lazy mask exists - * for the current power state. - * 4. If state is S0ix and no lazy or active wake mask is set, then use default - * S0ix mask to be compatible with older BIOS versions. - */ - -void power_update_wake_mask(void) -{ - host_event_t wake_mask; - enum power_state state; - - state = power_get_state(); - - if (state == POWER_S0) - wake_mask = 0; - else if (lpc_is_active_wm_set_by_host()) - return; - else if (get_lazy_wake_mask(state, &wake_mask)) - return; -#ifdef CONFIG_POWER_S0IX - if ((state == POWER_S0ix) && (wake_mask == 0)) - wake_mask = DEFAULT_WAKE_MASK_S0IX; -#endif - - lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, wake_mask); -} - /* - * Set wake mask after power state has stabilized, 5ms after power state - * change. The reason for making this a deferred call is to avoid race - * conditions occurring from S0ix periodic wakes on the SoC. - */ - -static void power_update_wake_mask_deferred(void); -DECLARE_DEFERRED(power_update_wake_mask_deferred); - -static void power_update_wake_mask_deferred(void) -{ - hook_call_deferred(&power_update_wake_mask_deferred_data, -1); - power_update_wake_mask(); -} - -static void power_set_active_wake_mask(void) -{ - /* - * Allow state machine to stabilize and update wake mask after 5msec. It - * was observed that on platforms where host wakes up periodically from - * S0ix for hardware book-keeping activities, there is a small window - * where host is not really up and running software, but still SLP_S0# - * is de-asserted and hence setting wake mask right away can cause user - * wake events to be missed. - * - * Time for deferred callback was chosen to be 5msec based on the fact - * that it takes ~2msec for the periodic wake cycle to complete on the - * host for KBL. - */ - hook_call_deferred(&power_update_wake_mask_deferred_data, - 5 * MSEC); -} - -#else -static void power_set_active_wake_mask(void) { } -#endif - -#ifdef CONFIG_HIBERNATE -#ifdef CONFIG_BATTERY -/* - * Smart discharge system - * - * EC controls how the system discharges differently depending on the remaining - * capacity and the expected hours to zero. - * - * 0 X1 X2 full - * |----------|-------------------|------------------------------------| - * cutoff stay-up safe - * - * EC cuts off the battery at X1 mAh and hibernates the system at X2 mAh. X1 and - * X2 are derived from the cutoff and hibernation discharge rate, respectively. - * - * TODO: Learn discharge rates dynamically. - * - * TODO: Save sdzone in non-volatile memory and restore it when waking up from - * cutoff or hibernation. - */ -static struct smart_discharge_zone sdzone; - -static enum ec_status hc_smart_discharge(struct host_cmd_handler_args *args) -{ - static uint16_t hours_to_zero; - static struct discharge_rate drate; - const struct ec_params_smart_discharge *p = args->params; - struct ec_response_smart_discharge *r = args->response; - - if (p->flags & EC_SMART_DISCHARGE_FLAGS_SET) { - int cap; - - if (battery_full_charge_capacity(&cap)) - return EC_RES_UNAVAILABLE; - - if (p->drate.hibern < p->drate.cutoff) - /* Hibernation discharge rate should be always higher */ - return EC_RES_INVALID_PARAM; - else if (p->drate.cutoff > 0 && p->drate.hibern > 0) - drate = p->drate; - else if (p->drate.cutoff == 0 && p->drate.hibern == 0) - ; /* no-op. use the current drate. */ - else - return EC_RES_INVALID_PARAM; - - /* Commit */ - hours_to_zero = p->hours_to_zero; - sdzone.stayup = MIN(hours_to_zero * drate.hibern / 1000, cap); - sdzone.cutoff = MIN(hours_to_zero * drate.cutoff / 1000, - sdzone.stayup); - } - - /* Return the effective values. */ - r->hours_to_zero = hours_to_zero; - r->dzone = sdzone; - r->drate = drate; - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_SMART_DISCHARGE, - hc_smart_discharge, - EC_VER_MASK(0)); - -__overridable enum critical_shutdown board_system_is_idle( - uint64_t last_shutdown_time, uint64_t *target, uint64_t now) -{ - int remain; - - if (now < *target) - return CRITICAL_SHUTDOWN_IGNORE; - - if (battery_remaining_capacity(&remain)) { - CPRINTS("SDC Failed to get remaining capacity"); - return CRITICAL_SHUTDOWN_HIBERNATE; - } - - if (remain < sdzone.cutoff) { - CPRINTS("SDC Cutoff"); - return CRITICAL_SHUTDOWN_CUTOFF; - } else if (remain < sdzone.stayup) { - CPRINTS("SDC Stay-up"); - return CRITICAL_SHUTDOWN_IGNORE; - } - - CPRINTS("SDC Safe"); - return CRITICAL_SHUTDOWN_HIBERNATE; -} -#else -/* Default implementation for battery-less systems */ -__overridable enum critical_shutdown board_system_is_idle( - uint64_t last_shutdown_time, uint64_t *target, uint64_t now) -{ - return now > *target ? - CRITICAL_SHUTDOWN_HIBERNATE : CRITICAL_SHUTDOWN_IGNORE; -} -#endif /* CONFIG_BATTERY */ -#endif /* CONFIG_HIBERNATE */ - -/** - * Common handler for steady states - * - * @param state Current power state - * @return Updated power state - */ -static enum power_state power_common_state(enum power_state state) -{ - switch (state) { - case POWER_G3: - if (want_g3_exit || want_reboot_ap_at_g3) { - uint64_t i; - - want_g3_exit = 0; - want_reboot_ap_at_g3 = false; - reboot_ap_at_g3_delay = reboot_ap_at_g3_delay * MSEC; - /* - * G3->S0 transition should happen only after the - * user specified delay. Hence, wait until the - * user specified delay times out. - */ - for (i = 0; i < reboot_ap_at_g3_delay; i += 100) - msleep(100); - reboot_ap_at_g3_delay = 0; - - return POWER_G3S5; - } - - in_want = 0; -#ifdef CONFIG_HIBERNATE - { - uint64_t target, now, wait; - if (extpower_is_present()) { - task_wait_event(-1); - break; - } - - now = get_time().val; - target = last_shutdown_time + - (uint64_t)hibernate_delay * SECOND; - switch (board_system_is_idle(last_shutdown_time, - &target, now)) { - case CRITICAL_SHUTDOWN_HIBERNATE: - CPRINTS("Hibernate due to G3 idle"); - system_hibernate(0, 0); - break; -#ifdef CONFIG_BATTERY_CUT_OFF - case CRITICAL_SHUTDOWN_CUTOFF: - CPRINTS("Cutoff due to G3 idle"); - /* Ensure logs are flushed. */ - cflush(); - board_cut_off_battery(); - break; -#endif - case CRITICAL_SHUTDOWN_IGNORE: - default: - break; - } - - wait = MIN(target - now, TASK_MAX_WAIT_US); - task_wait_event(wait); - } -#else /* !CONFIG_HIBERNATE */ - task_wait_event(-1); -#endif - break; - - case POWER_S5: - /* - * If the power button is pressed before S5 inactivity timer - * expires, the timer will be cancelled and the task of the - * power state machine will be back here again. Since we are - * here, which means the system has been waiting for CPU - * starting up, we don't need want_g3_exit flag to be set - * anymore. Therefore, we can reset the flag here to prevent - * the situation that the flag is still set after S5 inactivity - * timer expires, which can cause the system to exit G3 again. - */ - want_g3_exit = 0; - - power_wait_signals(0); - - /* Wait for inactivity timeout, if desired */ - if (s5_inactivity_timeout == 0) { - return POWER_S5G3; - } else if (s5_inactivity_timeout < 0) { - task_wait_event(-1); - } else if (task_wait_event(s5_inactivity_timeout * SECOND) == - TASK_EVENT_TIMER) { - /* Prepare to drop to G3; wake not requested yet */ - return POWER_S5G3; - } - break; - - case POWER_S3: - /* Wait for a message */ - power_wait_signals(0); - task_wait_event(-1); - break; - - case POWER_S0: - /* Wait for a message */ - power_wait_signals(0); - task_wait_event(-1); - break; -#ifdef CONFIG_POWER_S0IX - case POWER_S0ix: - /* Wait for a message */ - power_wait_signals(0); - task_wait_event(-1); - break; -#endif - default: - /* No common functionality for transition states */ - break; - } - - return state; -} - -/*****************************************************************************/ -/* Chipset interface */ - -int chipset_in_state(int state_mask) -{ - int need_mask = 0; - - /* - * TODO(crosbug.com/p/23773): what to do about state transitions? If - * the caller wants HARD_OFF|SOFT_OFF and we're in G3S5, we could still - * return non-zero. - */ - switch (state) { - case POWER_G3: - need_mask = CHIPSET_STATE_HARD_OFF; - break; - case POWER_G3S5: - case POWER_S5G3: - /* - * In between hard and soft off states. Match only if caller - * will accept both. - */ - need_mask = CHIPSET_STATE_HARD_OFF | CHIPSET_STATE_SOFT_OFF; - break; - case POWER_S5: - need_mask = CHIPSET_STATE_SOFT_OFF; - break; - case POWER_S5S3: - case POWER_S3S5: - need_mask = CHIPSET_STATE_SOFT_OFF | CHIPSET_STATE_SUSPEND; - break; - case POWER_S3: - need_mask = CHIPSET_STATE_SUSPEND; - break; - case POWER_S3S0: - case POWER_S0S3: - need_mask = CHIPSET_STATE_SUSPEND | CHIPSET_STATE_ON; - break; - case POWER_S0: - need_mask = CHIPSET_STATE_ON; - break; -#ifdef CONFIG_POWER_S0IX - case POWER_S0ixS0: - case POWER_S0S0ix: - need_mask = CHIPSET_STATE_ON | CHIPSET_STATE_STANDBY; - break; - case POWER_S0ix: - need_mask = CHIPSET_STATE_STANDBY; - break; -#endif - } - - /* Return non-zero if all needed bits are present */ - return (state_mask & need_mask) == need_mask; -} - -int chipset_in_or_transitioning_to_state(int state_mask) -{ - switch (state) { - case POWER_G3: - case POWER_S5G3: - return state_mask & CHIPSET_STATE_HARD_OFF; - case POWER_S5: - case POWER_G3S5: - case POWER_S3S5: - return state_mask & CHIPSET_STATE_SOFT_OFF; - case POWER_S3: - case POWER_S5S3: - case POWER_S0S3: - return state_mask & CHIPSET_STATE_SUSPEND; -#ifdef CONFIG_POWER_S0IX - case POWER_S0ix: - case POWER_S0S0ix: - return state_mask & CHIPSET_STATE_STANDBY; -#endif - case POWER_S0: - case POWER_S3S0: -#ifdef CONFIG_POWER_S0IX - case POWER_S0ixS0: -#endif - return state_mask & CHIPSET_STATE_ON; - } - - /* Unknown power state; return false. */ - return 0; -} - -void chipset_exit_hard_off(void) -{ - /* - * If not in the soft-off state, hard-off state, or headed there, - * nothing to do. - */ - if (state != POWER_G3 && state != POWER_S5G3 && state != POWER_S5) - return; - - /* - * Set a flag to leave G3, then wake the task. If the power state is - * POWER_S5G3, or is POWER_S5 but the S5 inactivity timer has - * expired, set this flag can let system go to G3 and then exit G3 - * immediately for powering on. - */ - want_g3_exit = 1; - - /* - * If the power state is in POWER_S5 and S5 inactivity timer is - * running, to wake the chipset task can cancel S5 inactivity timer and - * then restart the timer. This will give cpu a chance to start up if - * S5 inactivity timer is about to expire while power button is - * pressed. For other states here, to wake the chipset task to trigger - * the event for leaving G3 is necessary. - */ - task_wake(TASK_ID_CHIPSET); -} - -/*****************************************************************************/ -/* Task function */ - -void chipset_task(void *u) -{ - enum power_state new_state; - static enum power_state last_state; - uint32_t this_in_signals; - static uint32_t last_in_signals; - - while (1) { - /* - * In order to prevent repeated console spam, only print the - * current power state if something has actually changed. It's - * possible that one of the power signals goes away briefly and - * comes back by the time we update our in_signals. - */ - this_in_signals = in_signals; - if (this_in_signals != last_in_signals || state != last_state) { - CPRINTS("power state %d = %s, in 0x%04x", - state, state_names[state], this_in_signals); - if (IS_ENABLED(CONFIG_SEVEN_SEG_DISPLAY)) - display_7seg_write(SEVEN_SEG_EC_DISPLAY, state); - last_in_signals = this_in_signals; - last_state = state; - } - - /* Always let the specific chipset handle the state first */ - new_state = power_handle_state(state); - - /* - * If the state hasn't changed, run common steady-state - * handler. - */ - if (new_state == state) - new_state = power_common_state(state); - - /* Handle state changes */ - if (new_state != state) { - power_set_state(new_state); - power_set_active_wake_mask(); - - /* Call hooks before we enter G3 */ - if (new_state == POWER_G3) - hook_notify(HOOK_CHIPSET_HARD_OFF); - } - } -} - -/*****************************************************************************/ -/* Hooks */ - -static void power_common_init(void) -{ - const struct power_signal_info *s = power_signal_list; - int i; - - /* Update input state */ - power_update_signals(); - - /* Enable interrupts for input signals */ - for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) - if (s->flags & POWER_SIGNAL_DISABLE_AT_BOOT) - power_signal_disable_interrupt(s->gpio); - else - power_signal_enable_interrupt(s->gpio); - - /* Call chipset-specific init to set initial state */ - power_set_state(power_chipset_init()); - - /* - * Update input state again since there is a small window - * before GPIO is enabled. - */ - power_update_signals(); -} -DECLARE_HOOK(HOOK_INIT, power_common_init, HOOK_PRIO_INIT_CHIPSET); - -static void power_lid_change(void) -{ - /* Wake up the task to update power state */ - task_wake(TASK_ID_CHIPSET); -} -DECLARE_HOOK(HOOK_LID_CHANGE, power_lid_change, HOOK_PRIO_DEFAULT); - -#ifdef CONFIG_EXTPOWER -static void power_ac_change(void) -{ - if (extpower_is_present()) { - CPRINTS("AC on"); - } else { - CPRINTS("AC off"); - - if (state == POWER_G3) { - last_shutdown_time = get_time().val; - task_wake(TASK_ID_CHIPSET); - } - } -} -DECLARE_HOOK(HOOK_AC_CHANGE, power_ac_change, HOOK_PRIO_DEFAULT); -#endif - -/*****************************************************************************/ -/* Interrupts */ - -#ifdef CONFIG_BRINGUP -#define MAX_SIGLOG_ENTRIES 24 - -static unsigned int siglog_entries; -static unsigned int siglog_truncated; - -static struct { - timestamp_t time; - enum gpio_signal signal; - int level; -} siglog[MAX_SIGLOG_ENTRIES]; - -static void siglog_deferred(void) -{ - unsigned int i; - timestamp_t tdiff = {.val = 0}; - - /* Disable interrupts for input signals while we print stuff.*/ - for (i = 0; i < POWER_SIGNAL_COUNT; i++) - power_signal_disable_interrupt(power_signal_list[i].gpio); - - CPRINTF("%d signal changes:\n", siglog_entries); - for (i = 0; i < siglog_entries; i++) { - if (i) - tdiff.val = siglog[i].time.val - siglog[i-1].time.val; - CPRINTF(" %.6lld +%.6lld %s => %d\n", - siglog[i].time.val, tdiff.val, - power_signal_get_name(siglog[i].signal), - siglog[i].level); - } - if (siglog_truncated) - CPRINTF(" SIGNAL LOG TRUNCATED...\n"); - siglog_entries = siglog_truncated = 0; - - /* Okay, turn 'em on again. */ - for (i = 0; i < POWER_SIGNAL_COUNT; i++) - power_signal_enable_interrupt(power_signal_list[i].gpio); -} -DECLARE_DEFERRED(siglog_deferred); - -static void siglog_add(enum gpio_signal signal) -{ - if (siglog_entries >= MAX_SIGLOG_ENTRIES) { - siglog_truncated = 1; - return; - } - - siglog[siglog_entries].time = get_time(); - siglog[siglog_entries].signal = signal; - siglog[siglog_entries].level = power_signal_get_level(signal); - siglog_entries++; - - hook_call_deferred(&siglog_deferred_data, SECOND); -} - -#define SIGLOG(S) siglog_add(S) - -#else -#define SIGLOG(S) -#endif /* CONFIG_BRINGUP */ - -#ifdef CONFIG_POWER_SIGNAL_INTERRUPT_STORM_DETECT_THRESHOLD -/* - * Print an interrupt storm warning when we receive more than - * CONFIG_POWER_SIGNAL_INTERRUPT_STORM_DETECT_THRESHOLD interrupts of a - * single source within 1 second. - */ -static int power_signal_interrupt_count[POWER_SIGNAL_COUNT]; - -static void reset_power_signal_interrupt_count(void) -{ - int i; - - for (i = 0; i < POWER_SIGNAL_COUNT; ++i) - power_signal_interrupt_count[i] = 0; -} -DECLARE_HOOK(HOOK_SECOND, - reset_power_signal_interrupt_count, - HOOK_PRIO_DEFAULT); -#endif - -void power_signal_interrupt(enum gpio_signal signal) -{ -#ifdef CONFIG_POWER_SIGNAL_INTERRUPT_STORM_DETECT_THRESHOLD - int i; - - /* Tally our interrupts and print a warning if necessary. */ - for (i = 0; i < POWER_SIGNAL_COUNT; ++i) { - if (power_signal_list[i].gpio == signal) { - if (power_signal_interrupt_count[i]++ == - CONFIG_POWER_SIGNAL_INTERRUPT_STORM_DETECT_THRESHOLD) - CPRINTS("Interrupt storm! Signal %d", i); - break; - } - } -#endif - - SIGLOG(signal); - - /* Shadow signals and compare with our desired signal state. */ - power_update_signals(); - - /* Wake up the task */ - task_wake(TASK_ID_CHIPSET); -} - -#ifdef CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5 -inline int power_get_pause_in_s5(void) -{ - return pause_in_s5; -} - -inline void power_set_pause_in_s5(int pause) -{ - pause_in_s5 = pause; -} -#endif - -/*****************************************************************************/ -/* Console commands */ - -static int command_powerinfo(int argc, char **argv) -{ - /* - * Print power state in same format as state machine. This is - * used by FAFT tests, so must match exactly. - */ - ccprintf("power state %d = %s, in 0x%04x\n", - state, state_names[state], in_signals); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(powerinfo, command_powerinfo, - NULL, - "Show current power state"); - -#ifdef CONFIG_CMD_POWERINDEBUG -static int command_powerindebug(int argc, char **argv) -{ - const struct power_signal_info *s = power_signal_list; - int i; - char *e; - - /* If one arg, set the mask */ - if (argc == 2) { - int m = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - in_debug = m; - } - - /* Print the mask */ - ccprintf("power in: 0x%04x\n", in_signals); - ccprintf("debug mask: 0x%04x\n", in_debug); - - /* Print the decode */ - - ccprintf("bit meanings:\n"); - for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) { - int mask = 1 << i; - ccprintf(" 0x%04x %d %s\n", - mask, in_signals & mask ? 1 : 0, s->name); - } - - return EC_SUCCESS; -}; -DECLARE_CONSOLE_COMMAND(powerindebug, command_powerindebug, - "[mask]", - "Get/set power input debug mask"); -#endif - -#ifdef CONFIG_CMD_S5_TIMEOUT -/* Allow command-line access to configure our S5 delay for power testing */ -static int command_s5_timeout(int argc, char **argv) -{ - char *e; - - if (argc >= 2) { - uint32_t s = strtoi(argv[1], &e, 0); - - if (*e) - return EC_ERROR_PARAM1; - - s5_inactivity_timeout = s; - } - - /* Print the current setting */ - ccprintf("S5 inactivity timeout: %d s\n", s5_inactivity_timeout); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(s5_timeout, command_s5_timeout, - "[sec]", - "Set the timeout from S5 to G3 transition, " - "-1 to indicate no transition"); -#endif - -#ifdef CONFIG_HIBERNATE -static int command_hibernation_delay(int argc, char **argv) -{ - char *e; - uint32_t time_g3 = ((uint32_t)(get_time().val - last_shutdown_time)) - / SECOND; - - if (argc >= 2) { - uint32_t s = strtoi(argv[1], &e, 0); - if (*e) - return EC_ERROR_PARAM1; - - hibernate_delay = s; - } - - /* Print the current setting */ - ccprintf("Hibernation delay: %d s\n", hibernate_delay); - if (state == POWER_G3 && !extpower_is_present()) { - ccprintf("Time G3: %d s\n", time_g3); - ccprintf("Time left: %d s\n", hibernate_delay - time_g3); - } - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(hibdelay, command_hibernation_delay, - "[sec]", - "Set the delay before going into hibernation"); - -static enum ec_status -host_command_hibernation_delay(struct host_cmd_handler_args *args) -{ - const struct ec_params_hibernation_delay *p = args->params; - struct ec_response_hibernation_delay *r = args->response; - - uint32_t time_g3; - uint64_t t = get_time().val - last_shutdown_time; - - uint64divmod(&t, SECOND); - time_g3 = (uint32_t)t; - - /* Only change the hibernation delay if seconds is non-zero. */ - if (p->seconds) - hibernate_delay = p->seconds; - - if (state == POWER_G3 && !extpower_is_present()) - r->time_g3 = time_g3; - else - r->time_g3 = 0; - - if ((time_g3 != 0) && (time_g3 > hibernate_delay)) - r->time_remaining = 0; - else - r->time_remaining = hibernate_delay - time_g3; - r->hibernate_delay = hibernate_delay; - - args->response_size = sizeof(struct ec_response_hibernation_delay); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_HIBERNATION_DELAY, - host_command_hibernation_delay, - EC_VER_MASK(0)); -#endif /* CONFIG_HIBERNATE */ - -#ifdef CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5 -static enum ec_status -host_command_pause_in_s5(struct host_cmd_handler_args *args) -{ - const struct ec_params_get_set_value *p = args->params; - struct ec_response_get_set_value *r = args->response; - - if (p->flags & EC_GSV_SET) - pause_in_s5 = p->value; - - r->value = pause_in_s5; - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_GSV_PAUSE_IN_S5, - host_command_pause_in_s5, - EC_VER_MASK(0)); - -static int command_pause_in_s5(int argc, char **argv) -{ - if (argc > 1 && !parse_bool(argv[1], &pause_in_s5)) - return EC_ERROR_INVAL; - - ccprintf("pause_in_s5 = %s\n", pause_in_s5 ? "on" : "off"); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(pause_in_s5, command_pause_in_s5, - "[on|off]", - "Should the AP pause in S5 during shutdown?"); -#endif /* CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5 */ - -#ifdef CONFIG_POWER_PP5000_CONTROL -__overridable void board_power_5v_enable(int enable) -{ - if (enable) - gpio_set_level(GPIO_EN_PP5000, 1); - else - gpio_set_level(GPIO_EN_PP5000, 0); -} - -/* 5V enable request bitmask from various tasks. */ -static uint32_t pwr_5v_en_req; -K_MUTEX_DEFINE(pwr_5v_ctl_mtx); - -void power_5v_enable(task_id_t tid, int enable) -{ - mutex_lock(&pwr_5v_ctl_mtx); - - if (enable) /* Set the bit indicating the request. */ - pwr_5v_en_req |= 1 << tid; - else /* Clear the task's request bit. */ - pwr_5v_en_req &= ~(1 << tid); - - /* - * If there are any outstanding requests for the rail to be enabled, - * turn on the rail. Otherwise, turn it off. - */ - board_power_5v_enable(pwr_5v_en_req); - mutex_unlock(&pwr_5v_ctl_mtx); -} - -#define P5_SYSJUMP_TAG 0x5005 /* "P5" */ -static void restore_enable_5v_state(void) -{ - const uint32_t *state; - int size; - - state = (const uint32_t *) system_get_jump_tag(P5_SYSJUMP_TAG, 0, - &size); - if (state && size == sizeof(pwr_5v_en_req)) { - mutex_lock(&pwr_5v_ctl_mtx); - pwr_5v_en_req |= *state; - mutex_unlock(&pwr_5v_ctl_mtx); - } -} -DECLARE_HOOK(HOOK_INIT, restore_enable_5v_state, HOOK_PRIO_FIRST); - -static void preserve_enable_5v_state(void) -{ - mutex_lock(&pwr_5v_ctl_mtx); - system_add_jump_tag(P5_SYSJUMP_TAG, 0, sizeof(pwr_5v_en_req), - &pwr_5v_en_req); - mutex_unlock(&pwr_5v_ctl_mtx); -} -DECLARE_HOOK(HOOK_SYSJUMP, preserve_enable_5v_state, HOOK_PRIO_DEFAULT); -#endif /* defined(CONFIG_POWER_PP5000_CONTROL) */ |