summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2017-01-05 21:54:51 -0800
committerchrome-bot <chrome-bot@chromium.org>2017-01-12 19:40:07 -0800
commitea5d8066b6c82cfc1d050e6e9803b99f3443f73b (patch)
treed05c97fd0c9d96edbb4fb330ac4ad75a6d21cbce
parent65370849cb8391680bb451950948cd1369c80c7f (diff)
downloadchrome-ec-ea5d8066b6c82cfc1d050e6e9803b99f3443f73b.tar.gz
cr50: help battery disconnect to work on detachable devices
Detachable devices need firmware help to process battery disconnect requests promptly. The request happens when the user keeps pressed both power and "volume up" buttons and yanks the charger cable. Once this condition is detected a 5 s timeout is started, and if the charger cable is not plugged back in during this interval, the code initiates a low polarity pulse on both EC_RST_L and BAT_EN outputs. Lowering BAT_EN level will cause the battery cut off which is supposed to cause an immediate system power down. BRANCH=none BUG=chrome-os-partner:59833 TEST=verified desired behavior on an H1 dev board with a H1B2-D chip. Change-Id: Iecdcc93e228f4bc18734569bd896b0afa4bb752a Signed-off-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/426345 Reviewed-by: Marius Schilder <mschilder@chromium.org>
-rw-r--r--board/cr50/board.c126
-rw-r--r--chip/g/system.c9
-rw-r--r--include/system.h7
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 */