diff options
-rw-r--r-- | board/cr50/board.c | 126 | ||||
-rw-r--r-- | chip/g/system.c | 9 | ||||
-rw-r--r-- | include/system.h | 7 |
3 files changed, 140 insertions, 2 deletions
diff --git a/board/cr50/board.c b/board/cr50/board.c index fe6a29a26f..6e0726bdfe 100644 --- a/board/cr50/board.c +++ b/board/cr50/board.c @@ -59,6 +59,8 @@ #undef SHA_DIGEST_SIZE #include "Implementation.h" +#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) + #define NVMEM_CR50_SIZE 300 #define NVMEM_TPM_SIZE ((sizeof((struct nvmem_partition *)0)->buffer) \ - NVMEM_CR50_SIZE) @@ -119,6 +121,124 @@ void post_reboot_request(void) reboot_request_posted = 1; } +/*****************************************************************************/ +/* */ + +/* + * Battery cutoff monitor is needed on the devices where hardware alone does + * not provide proper battery cutoff functionality. + * + * The sequence is as follows: set up an interrupt to react to the charger + * disconnect event. When the interrupt happens observe status of the buttons + * connected to PWRB_IN and KEY0_IN. + * + * If both are pressed, start the 5 second timeout, while keeping monitoring + * the charger connection state. If it remains disconnected for the entire + * duration - generate 5 second pulses on EC_RST_L and BAT_EN outputs. + * + * In reality the BAT_EN output pulse will cause the complete power cut off, + * so strictly speaking the code does not need to do anything once BAT_EN + * output is deasserted. + */ + +/* Time to wait before initiating battery cutoff procedure. */ +#define CUTOFF_TIMEOUT_US (5 * SECOND) + +/* A timeout hook to run in the end of the 5 s interval. */ +static void ac_stayed_disconnected(void) +{ + uint32_t saved_override_state; + + CPRINTS("%s", __func__); + + /* assert EC_RST_L and deassert BAT_EN */ + GREG32(RBOX, ASSERT_EC_RST) = 1; + + /* + * BAT_EN needs to use the RBOX override ability, bit 1 is battery + * disable bit. + */ + saved_override_state = GREG32(RBOX, OVERRIDE_OUTPUT); + GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, VAL, 0); /* Setting it to zero. */ + GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, OEN, 1); + GWRITE_FIELD(RBOX, OVERRIDE_OUTPUT, EN, 1); + + + msleep(5000); + + /* + * The system was supposed to be shut down the moment battery + * disconnect was asserted, but if we made it here we might as well + * restore the original state. + */ + GREG32(RBOX, OVERRIDE_OUTPUT) = saved_override_state; + GREG32(RBOX, ASSERT_EC_RST) = 0; +} +DECLARE_DEFERRED(ac_stayed_disconnected); + +/* + * Just a shortcut to make use of these AC power interrupt states better + * readable. RED means rising edge and FED means falling edge. + */ +enum { + ac_pres_red = GC_RBOX_INT_STATE_INTR_AC_PRESENT_RED_MASK, + ac_pres_fed = GC_RBOX_INT_STATE_INTR_AC_PRESENT_FED_MASK, + buttons_not_pressed = GC_RBOX_CHECK_INPUT_KEY0_IN_MASK | + GC_RBOX_CHECK_INPUT_PWRB_IN_MASK +}; + +/* + * ISR reacting to both falling and raising edges of the AC_PRESENT signal. + * Falling edge indicates pulling out of the charger cable and vice versa. + */ +static void ac_power_state_changed(void) +{ + uint32_t req; + + /* Get current status and clear it. */ + req = GREG32(RBOX, INT_STATE) & (ac_pres_red | ac_pres_fed); + GREG32(RBOX, INT_STATE) = req; + + CPRINTS("%s: status 0x%x", __func__, req); + + /* Raising edge gets priority, stop timeout timer and go. */ + if (req & ac_pres_red) { + hook_call_deferred(&ac_stayed_disconnected_data, -1); + return; + } + + /* + * If this is not a falling edge, or either of the buttons is not + * pressed - bail out. + */ + if (!(req & ac_pres_fed) || + (GREG32(RBOX, CHECK_INPUT) & buttons_not_pressed)) + return; + + /* + * Charger cable was yanked while the power and key0 buttons were kept + * pressed - user wants a battery cut off. + */ + hook_call_deferred(&ac_stayed_disconnected_data, CUTOFF_TIMEOUT_US); +} +DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_RED_INT, ac_power_state_changed, 1); +DECLARE_IRQ(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_FED_INT, ac_power_state_changed, 1); + +/* Enable interrupts on plugging in and yanking out of the charger cable. */ +static void set_up_battery_cutoff_monitor(void) +{ + /* It is set in idle.c also. */ + GWRITE_FIELD(RBOX, WAKEUP, ENABLE, 1); + + GWRITE_FIELD(RBOX, INT_ENABLE, INTR_AC_PRESENT_RED, 1); + GWRITE_FIELD(RBOX, INT_ENABLE, INTR_AC_PRESENT_FED, 1); + + task_enable_irq(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_RED_INT); + task_enable_irq(GC_IRQNUM_RBOX0_INTR_AC_PRESENT_FED_INT); +} +/* */ +/*****************************************************************************/ + /* * There's no way to trigger on both rising and falling edges, so force a * compiler error if we try. The workaround is to use the pinmux to connect @@ -316,6 +436,10 @@ static void board_init(void) /* Indication that firmware is running, for debug purposes. */ GREG32(PMU, PWRDN_SCRATCH16) = 0xCAFECAFE; + + /* Enable battery cutoff software support on detachable devices. */ + if (system_battery_cutoff_support_required()) + set_up_battery_cutoff_monitor(); } DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT); @@ -387,8 +511,6 @@ int flash_regions_to_enable(struct g_flash_region *regions, return 3; } -#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) - /* This is the interrupt handler to react to SYS_RST_L_IN */ void sys_rst_asserted(enum gpio_signal signal) { diff --git a/chip/g/system.c b/chip/g/system.c index 8bc24040d1..ad553168c5 100644 --- a/chip/g/system.c +++ b/chip/g/system.c @@ -213,6 +213,15 @@ const char *system_get_chip_revision(void) return revision_str; } +int system_battery_cutoff_support_required(void) +{ + switch (get_fuse_set_id()) + case 0xc00059: + return 1; + + return 0; +} + /* TODO(crosbug.com/p/33822): Where can we store stuff persistently? */ int system_get_vbnvcontext(uint8_t *block) { diff --git a/include/system.h b/include/system.h index 4df0fb1bad..1854e7373a 100644 --- a/include/system.h +++ b/include/system.h @@ -494,4 +494,11 @@ int system_rolling_reboot_suspected(void); * @return a boolean, set to True if a rollback is detected. */ int system_rollback_detected(void); + +/** + * Returns non-zero value when firmware is expected to be abe to detect user + * request to cut off battery supply. + */ +int system_battery_cutoff_support_required(void); + #endif /* __CROS_EC_SYSTEM_H */ |