/* Copyright 2013 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* rk3399 chipset power control module for Chrome EC */ /* * The description of each CONFIG_CHIPSET_POWER_SEQ_VERSION: * * Version 0: Initial/default revision for clamshell / convertible. * Version 1: Simplified power tree for tablet / detachable. */ #include "builtin/assert.h" #include "charge_state.h" #include "chipset.h" #include "common.h" #include "console.h" #include "ec_commands.h" #include "gpio.h" #include "hooks.h" #include "lid_switch.h" #include "power.h" #include "power_button.h" #include "system.h" #include "task.h" #include "timer.h" #include "usb_charge.h" #include "util.h" /* Console output macros */ #define CPUTS(outstr) cputs(CC_CHIPSET, outstr) #define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ##args) /* Input state flags */ #if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 #define IN_PGOOD_PP1250_S3 POWER_SIGNAL_MASK(PP1250_S3_PWR_GOOD) #define IN_PGOOD_PP900_S0 POWER_SIGNAL_MASK(PP900_S0_PWR_GOOD) #else #define IN_PGOOD_PP5000 POWER_SIGNAL_MASK(PP5000_PWR_GOOD) #define IN_PGOOD_SYS POWER_SIGNAL_MASK(SYS_PWR_GOOD) #endif #define IN_PGOOD_AP POWER_SIGNAL_MASK(AP_PWR_GOOD) #define IN_SUSPEND_DEASSERTED POWER_SIGNAL_MASK(SUSPEND_DEASSERTED) /* Rails requires for S3 and S0 */ #if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 #define IN_PGOOD_S3 (IN_PGOOD_PP1250_S3) #define IN_PGOOD_S0 (IN_PGOOD_S3 | IN_PGOOD_PP900_S0 | IN_PGOOD_AP) /* This board can optionally wake-on-USB in S3 */ #define S3_USB_WAKE /* This board has non-INT power signal pins */ #define POWER_SIGNAL_POLLING /* This board supports CR50 deep sleep mode */ #define CR50_DEEP_SLEEP /* * If AP_PWR_GOOD assertion does not trigger an interrupt, poll the * signal every 5ms, up to 200 times (~ 1 second timeout). */ #define PGOOD_S0_POLL_TIMEOUT (5 * MSEC) #define PGOOD_S0_POLL_TRIES 200 #else #define IN_PGOOD_S3 (IN_PGOOD_PP5000) #define IN_PGOOD_S0 (IN_PGOOD_S3 | IN_PGOOD_AP | IN_PGOOD_SYS) #endif /* All inputs in the right state for S0 */ #define IN_ALL_S0 (IN_PGOOD_S0 | IN_SUSPEND_DEASSERTED) /* Long power key press to force shutdown in S0 */ #define FORCED_SHUTDOWN_DELAY (8 * SECOND) #define CHARGER_INITIALIZED_DELAY_MS 100 #define CHARGER_INITIALIZED_TRIES 40 /* Data structure for a GPIO operation for power sequencing */ struct power_seq_op { /* enum gpio_signal in 8 bits */ uint8_t signal; uint8_t level; /* Number of milliseconds to delay after setting signal to level */ uint8_t delay; }; BUILD_ASSERT(GPIO_COUNT < 256); /* * This is the power sequence for POWER_S5S3. * The entries in the table are handled sequentially from the top * to the bottom. */ #if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 static const struct power_seq_op s5s3_power_seq[] = { { GPIO_PP900_S3_EN, 1, 2 }, { GPIO_PP3300_S3_EN, 1, 2 }, { GPIO_PP1800_S3_EN, 1, 2 }, { GPIO_PP1250_S3_EN, 1, 2 }, }; #else static const struct power_seq_op s5s3_power_seq[] = { { GPIO_PPVAR_LOGIC_EN, 1, 0 }, { GPIO_PP900_AP_EN, 1, 0 }, { GPIO_PP900_PCIE_EN, 1, 2 }, { GPIO_PP900_PMU_EN, 1, 0 }, { GPIO_PP900_PLL_EN, 1, 0 }, { GPIO_PP900_USB_EN, 1, 2 }, { GPIO_SYS_RST_L, 0, 0 }, { GPIO_PP1800_PMU_EN_L, 0, 2 }, { GPIO_LPDDR_PWR_EN, 1, 2 }, { GPIO_PP1800_USB_EN_L, 0, 2 }, { GPIO_PP3300_USB_EN_L, 0, 0 }, { GPIO_PP5000_EN, 1, 0 }, { GPIO_PP3300_TRACKPAD_EN_L, 0, 1 }, { GPIO_PP1800_LID_EN_L, 0, 0 }, { GPIO_PP1800_SIXAXIS_EN_L, 0, 2 }, { GPIO_PP1800_SENSOR_EN_L, 0, 0 }, }; #endif /* The power sequence for POWER_S3S0 */ #if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 static const struct power_seq_op s3s0_power_seq[] = { { GPIO_AP_CORE_EN, 1, 2 }, { GPIO_PP1800_S0_EN, 1, 0 }, }; #else static const struct power_seq_op s3s0_power_seq[] = { { GPIO_PPVAR_CLOGIC_EN, 1, 2 }, { GPIO_PP900_DDRPLL_EN, 1, 2 }, { GPIO_PP1800_AP_AVDD_EN_L, 0, 2 }, { GPIO_AP_CORE_EN, 1, 2 }, { GPIO_PP1800_S0_EN_L, 0, 2 }, { GPIO_PP3300_S0_EN_L, 0, 0 }, }; #endif #ifdef S3_USB_WAKE /* Sigs that may already be on in S3, if we need to wake-on-USB */ static const struct power_seq_op s3s0_usb_wake_power_seq[] = { { GPIO_PP900_S0_EN, 1, 2 }, { GPIO_PP1800_USB_EN, 1, 2 }, { GPIO_PP3300_S0_EN, 1, 2 }, }; #endif /* The power sequence for POWER_S0S3 */ #if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 static const struct power_seq_op s0s3_power_seq[] = { { GPIO_AP_CORE_EN, 0, 20 }, }; #else static const struct power_seq_op s0s3_power_seq[] = { { GPIO_PP3300_S0_EN_L, 1, 20 }, { GPIO_PP1800_S0_EN_L, 1, 1 }, { GPIO_AP_CORE_EN, 0, 20 }, { GPIO_PP1800_AP_AVDD_EN_L, 1, 1 }, { GPIO_PP900_DDRPLL_EN, 0, 1 }, { GPIO_PPVAR_CLOGIC_EN, 0, 0 }, }; #endif #ifdef S3_USB_WAKE /* Sigs that need to be left on in S3, if we need to wake-on-USB */ static const struct power_seq_op s0s3_usb_wake_power_seq[] = { { GPIO_PP3300_S0_EN, 0, 20 }, { GPIO_PP1800_S0_EN, 0, 1 }, { GPIO_PP1800_USB_EN, 0, 1 }, { GPIO_PP900_S0_EN, 0, 0 }, }; #endif /* The power sequence for POWER_S3S5 */ #if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 static const struct power_seq_op s3s5_power_seq[] = { { GPIO_SYS_RST_L, 0, 0 }, { GPIO_PP1250_S3_EN, 0, 2 }, { GPIO_PP1800_S3_EN, 0, 2 }, { GPIO_PP3300_S3_EN, 0, 2 }, { GPIO_PP900_S3_EN, 0, 0 }, }; #else static const struct power_seq_op s3s5_power_seq[] = { { GPIO_PP1800_SENSOR_EN_L, 1, 0 }, { GPIO_PP1800_SIXAXIS_EN_L, 1, 0 }, { GPIO_PP1800_LID_EN_L, 1, 0 }, { GPIO_PP3300_TRACKPAD_EN_L, 1, 0 }, { GPIO_PP5000_EN, 0, 0 }, { GPIO_PP3300_USB_EN_L, 1, 20 }, { GPIO_PP1800_USB_EN_L, 1, 10 }, { GPIO_LPDDR_PWR_EN, 0, 20 }, { GPIO_PP1800_PMU_EN_L, 1, 2 }, { GPIO_PP900_PLL_EN, 0, 0 }, { GPIO_PP900_PMU_EN, 0, 0 }, { GPIO_PP900_USB_EN, 0, 6 }, { GPIO_PP900_PCIE_EN, 0, 0 }, { GPIO_PP900_AP_EN, 0, 0 }, { GPIO_PPVAR_LOGIC_EN, 0, 0 }, }; #endif static int forcing_shutdown; void chipset_force_shutdown(enum chipset_shutdown_reason reason) { CPRINTS("%s(%d)", __func__, reason); report_ap_reset(reason); /* * Force power off. This condition will reset once the state machine * transitions to G3. */ forcing_shutdown = 1; task_wake(TASK_ID_CHIPSET); } #define SYS_RST_HOLD_US (1 * MSEC) void chipset_reset(enum chipset_shutdown_reason reason) { #ifdef CONFIG_CMD_RTC /* Print out the RTC to help correlate resets in logs. */ print_system_rtc(CC_CHIPSET); #endif CPRINTS("%s(%d)", __func__, reason); report_ap_reset(reason); /* Pulse SYS_RST */ gpio_set_level(GPIO_SYS_RST_L, 0); if (in_interrupt_context()) udelay(SYS_RST_HOLD_US); else usleep(SYS_RST_HOLD_US); gpio_set_level(GPIO_SYS_RST_L, 1); } enum power_state power_chipset_init(void) { if (system_jumped_to_this_image()) { if ((power_get_signals() & IN_ALL_S0) == IN_ALL_S0) { disable_sleep(SLEEP_MASK_AP_RUN); CPRINTS("already in S0"); return POWER_S0; } } else if (!(system_get_reset_flags() & EC_RESET_FLAG_AP_OFF)) /* Auto-power on */ chipset_exit_hard_off(); return POWER_G3; } static void force_shutdown(void) { forcing_shutdown = 1; task_wake(TASK_ID_CHIPSET); } DECLARE_DEFERRED(force_shutdown); /* * Debounce PGOOD_AP if we lose it suddenly during S0, since output voltage * transitions may cause spurious pulses. */ #define PGOOD_AP_DEBOUNCE_TIMEOUT (100 * MSEC) /* * The AP informs the EC of its S0 / S3 state through IN_SUSPEND_DEASSERTED / * AP_EC_S3_S0_L. Latency between deassertion and power rails coming up must * be minimized, so check for deassertion at various stages of our suspend * power sequencing, and immediately transition out of suspend if necessary. */ #define SLEEP_INTERVAL_MS 5 #define MSLEEP_CHECK_ABORTED_SUSPEND(msec) \ do { \ int sleep_remain = msec; \ do { \ msleep(MIN(sleep_remain, SLEEP_INTERVAL_MS)); \ sleep_remain -= SLEEP_INTERVAL_MS; \ if (!forcing_shutdown && \ power_get_signals() & IN_SUSPEND_DEASSERTED) { \ CPRINTS("suspend aborted"); \ return POWER_S3S0; \ } \ } while (sleep_remain > 0); \ } while (0) BUILD_ASSERT(POWER_S3S0 != 0); /** * Step through the power sequence table and do corresponding GPIO operations. * * @param power_seq_ops The pointer to the power sequence table. * @param op_count The number of entries of power_seq_ops. * @return non-zero if suspend aborted during POWER_S0S3, 0 otherwise. */ static int power_seq_run(const struct power_seq_op *power_seq_ops, int op_count) { int i; for (i = 0; i < op_count; i++) { gpio_set_level(power_seq_ops[i].signal, power_seq_ops[i].level); if (!power_seq_ops[i].delay) continue; if ((power_seq_ops == s0s3_power_seq) #ifdef S3_USB_WAKE || (power_seq_ops == s0s3_usb_wake_power_seq) #endif ) MSLEEP_CHECK_ABORTED_SUSPEND(power_seq_ops[i].delay); else msleep(power_seq_ops[i].delay); } return 0; } enum power_state power_handle_state(enum power_state state) { #ifndef CR50_DEEP_SLEEP static int sys_reset_asserted; #endif #ifdef S3_USB_WAKE static int usb_wake_enabled; #endif int tries = 0; switch (state) { case POWER_G3: break; case POWER_S5: if (forcing_shutdown) return POWER_S5G3; else return POWER_S5S3; break; case POWER_S3: if (!power_has_signals(IN_PGOOD_S3) || forcing_shutdown) return POWER_S3S5; else if (power_get_signals() & IN_SUSPEND_DEASSERTED) return POWER_S3S0; break; case POWER_S0: if (!power_has_signals(IN_PGOOD_S3) || forcing_shutdown || !(power_get_signals() & IN_SUSPEND_DEASSERTED)) return POWER_S0S3; #if CONFIG_CHIPSET_POWER_SEQ_VERSION != 1 /* * Wait up to PGOOD_AP_DEBOUNCE_TIMEOUT for IN_PGOOD_AP to * come back before transitioning back to S3. PGOOD_SYS can * also glitch, with a glitch duration < 1ms, so debounce * it here as well. */ if (power_wait_signals_timeout(IN_PGOOD_AP | IN_PGOOD_SYS, PGOOD_AP_DEBOUNCE_TIMEOUT) == EC_ERROR_TIMEOUT) return POWER_S0S3; /* * power_wait_signals_timeout() can block and consume task * wake events, so re-verify the state of the world. */ if (!power_has_signals(IN_PGOOD_S3) || forcing_shutdown || !(power_get_signals() & IN_SUSPEND_DEASSERTED)) return POWER_S0S3; #endif break; case POWER_G3S5: forcing_shutdown = 0; /* * Allow time for charger to be initialized, in case we're * trying to boot the AP with no battery. */ while (charge_prevent_power_on(0) && tries++ < CHARGER_INITIALIZED_TRIES) { msleep(CHARGER_INITIALIZED_DELAY_MS); } /* Return to G3 if battery level is too low. */ if (charge_want_shutdown() || tries > CHARGER_INITIALIZED_TRIES) { CPRINTS("power-up inhibited"); chipset_force_shutdown( CHIPSET_SHUTDOWN_BATTERY_INHIBIT); return POWER_G3; } /* Power up to next state */ return POWER_S5; case POWER_S5S3: power_seq_run(s5s3_power_seq, ARRAY_SIZE(s5s3_power_seq)); #ifndef CR50_DEEP_SLEEP /* * Assert SYS_RST now, to be released in S3S0, to avoid * resetting the TPM soon after power-on. */ sys_reset_asserted = 1; #endif if (power_wait_signals(IN_PGOOD_S3)) { chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT); return POWER_S3S5; } /* Call hooks now that rails are up */ hook_notify(HOOK_CHIPSET_STARTUP); /* Power up to next state */ return POWER_S3; case POWER_S3S0: #ifdef S3_USB_WAKE /* Bring up S3 USB wake rails, if they are down */ if (!usb_wake_enabled) power_seq_run(s3s0_usb_wake_power_seq, ARRAY_SIZE(s3s0_usb_wake_power_seq)); usb_wake_enabled = 0; #endif power_seq_run(s3s0_power_seq, ARRAY_SIZE(s3s0_power_seq)); #ifndef CR50_DEEP_SLEEP /* Release SYS_RST if we came from S5 */ if (sys_reset_asserted) { #endif msleep(10); gpio_set_level(GPIO_SYS_RST_L, 1); #ifndef CR50_DEEP_SLEEP sys_reset_asserted = 0; } #endif #ifdef POWER_SIGNAL_POLLING /* * Poll power signals every PGOOD_S0_POLL_TIMEOUT us, since * AP_PWR_GOOD assertion doesn't trigger a power signal * interrupt. */ while (power_wait_signals_timeout(IN_PGOOD_S0, PGOOD_S0_POLL_TIMEOUT) == EC_ERROR_TIMEOUT && ++tries < PGOOD_S0_POLL_TRIES) ; if (tries >= PGOOD_S0_POLL_TRIES) { CPRINTS("power timeout on input; " "wanted 0x%04x, got 0x%04x", IN_PGOOD_S0, power_get_signals() & IN_PGOOD_S0); #else if (power_wait_signals(IN_PGOOD_S0)) { #endif /* POWER_SIGNAL_POLLING */ chipset_force_shutdown(CHIPSET_SHUTDOWN_WAIT); return POWER_S0S3; } /* Call hooks now that rails are up */ hook_notify(HOOK_CHIPSET_RESUME); /* * Disable idle task deep sleep. This means that the low * power idle task will not go into deep sleep while in S0. */ disable_sleep(SLEEP_MASK_AP_RUN); /* Power up to next state */ return POWER_S0; case POWER_S0S3: /* Call hooks before we remove power rails */ hook_notify(HOOK_CHIPSET_SUSPEND); MSLEEP_CHECK_ABORTED_SUSPEND(20); if (power_seq_run(s0s3_power_seq, ARRAY_SIZE(s0s3_power_seq))) return POWER_S3S0; #ifdef S3_USB_WAKE /* Leave up rails needed for S3 USB wake, if requested */ usb_wake_enabled = (power_get_host_sleep_state() == HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND); if (!usb_wake_enabled && power_seq_run(s0s3_usb_wake_power_seq, ARRAY_SIZE(s0s3_usb_wake_power_seq))) return POWER_S3S0; #endif /* * Enable idle task deep sleep. Allow the low power idle task * to go into deep sleep in S3 or lower. */ enable_sleep(SLEEP_MASK_AP_RUN); /* * In case the power button is held awaiting power-off timeout, * power off immediately now that we're entering S3. */ if (power_button_is_pressed()) { forcing_shutdown = 1; hook_call_deferred(&force_shutdown_data, -1); } return POWER_S3; case POWER_S3S5: #ifdef S3_USB_WAKE /* Make sure all S3 rails are off */ if (usb_wake_enabled) { power_seq_run(s0s3_usb_wake_power_seq, ARRAY_SIZE(s0s3_usb_wake_power_seq)); usb_wake_enabled = 0; } #endif /* Call hooks before we remove power rails */ hook_notify(HOOK_CHIPSET_SHUTDOWN); power_seq_run(s3s5_power_seq, ARRAY_SIZE(s3s5_power_seq)); /* Call hooks after we remove power rails */ hook_notify(HOOK_CHIPSET_SHUTDOWN_COMPLETE); /* Start shutting down */ return POWER_S5; case POWER_S5G3: return POWER_G3; default: CPRINTS("Unexpected power state %d", state); ASSERT(0); break; } return state; } static void power_button_changed(void) { static uint8_t tablet_boot_on_button_release; if (power_button_is_pressed()) { if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) { #if CONFIG_CHIPSET_POWER_SEQ_VERSION != 1 /* Power up from off */ chipset_exit_hard_off(); #else tablet_boot_on_button_release = 1; #endif } /* Delayed power down from S0/S3, cancel on PB release */ hook_call_deferred(&force_shutdown_data, FORCED_SHUTDOWN_DELAY); } else { #if CONFIG_CHIPSET_POWER_SEQ_VERSION == 1 if (tablet_boot_on_button_release) { /* Power up from off */ chipset_exit_hard_off(); tablet_boot_on_button_release = 0; } #endif /* Power button released, cancel deferred shutdown */ hook_call_deferred(&force_shutdown_data, -1); } } DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, power_button_changed, HOOK_PRIO_DEFAULT); #ifdef CONFIG_LID_SWITCH static void lid_changed(void) { /* Power-up from off on lid open */ if (lid_is_open() && chipset_in_state(CHIPSET_STATE_ANY_OFF)) chipset_exit_hard_off(); } DECLARE_HOOK(HOOK_LID_CHANGE, lid_changed, HOOK_PRIO_DEFAULT); #endif #ifdef POWER_SIGNAL_POLLING /* * Polling for non-INT power signal pins. * Call power_signal_interrupt() when the GPIO status of those pins changes. */ static void power_signal_changed(void) { static uint8_t in_signals; /* Current power signal status */ uint8_t inew = 0; const struct power_signal_info *s = power_signal_list; int i; BUILD_ASSERT(POWER_SIGNAL_COUNT <= 8); for (i = 0; i < POWER_SIGNAL_COUNT; i++, s++) { /* Skip if this is an INT pin. */ if (s->gpio < GPIO_IH_COUNT) continue; if (power_signal_is_asserted(s)) inew |= 1 << i; } if (inew != in_signals) { /* * Pass a fake power gpio_signal to power_signal_interrupt(). * Note that here we make power_signal_interrupt() reentrant. */ power_signal_interrupt(GPIO_COUNT); in_signals = inew; } } DECLARE_HOOK(HOOK_TICK, power_signal_changed, HOOK_PRIO_DEFAULT); #endif