summaryrefslogtreecommitdiff
path: root/power/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'power/common.c')
-rw-r--r--power/common.c1117
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) */