summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Crossley <crossley@lionsemi.corp-partner.google.com>2021-04-19 21:27:03 +0000
committerCommit Bot <commit-bot@chromium.org>2021-05-06 04:22:42 +0000
commit64be980319e9e7c8f132bfda349e8ebaefb780f1 (patch)
treea1d153fd6328179e1ca37df10159a88cb239e3e2
parent0a9949d1b059698247bde17a34c9c8caace4d48d (diff)
downloadchrome-ec-64be980319e9e7c8f132bfda349e8ebaefb780f1.tar.gz
ln9310: Stronger workaround to prevent SCOUT overvoltage at startup
This improved workaround (compared to the previous 2520279) moves the CFLY precharge step out of the ln9310's internal startup sequence and implements it using I2C commands sent by the EC just before the I2C command that triggers the ln9310 startup. The workaround additionally modifies the ln9310's internal startup sequence to use the precharged CFLY capacitors as decoupling of an internal node during the startup sequence which should help prevent an OV glitch from appearing on the LN9310 output even if the internal level shifter on SW1 glitches and pulls up on the C1PA/B nodes. BRANCH=Trogdor BUG=b:185308433 TEST=Should not break the current boot flow, i.e. power-up and power-down (using long-press of the power button) should function similar to before this change. Testing the efficacy at preventing the SCOUT overvoltage glitch must be done with LN9310 parts screened out at wafer test that have a higher likelihood of exhibiting the glitch at the SCOUT output. With these parts, startup should be observed with a scope probe monitoring LN9310 SCOUT for overvoltagee. Change-Id: I216991f950196225cabbbfdaa2333f1650f7f4fa Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2837531 Reviewed-by: Alexandru M Stan <amstan@chromium.org> Reviewed-by: Wai-Hong Tam <waihong@google.com> Tested-by: John Crossley <crossley@lionsemi.corp-partner.google.com> Commit-Queue: John Crossley <crossley@lionsemi.corp-partner.google.com>
-rw-r--r--board/homestar/board.c1
-rw-r--r--board/lazor/switchcap.c6
-rw-r--r--driver/ln9310.c251
-rw-r--r--include/driver/ln9310.h57
4 files changed, 277 insertions, 38 deletions
diff --git a/board/homestar/board.c b/board/homestar/board.c
index ce13ee1076..657b547c5f 100644
--- a/board/homestar/board.c
+++ b/board/homestar/board.c
@@ -439,6 +439,7 @@ DECLARE_HOOK(HOOK_CHIPSET_RESUME, board_chipset_resume, HOOK_PRIO_DEFAULT);
void board_set_switchcap_power(int enable)
{
gpio_set_level(GPIO_SWITCHCAP_ON_L, !enable);
+ ln9310_software_enable(enable);
}
int board_is_switchcap_enabled(void)
diff --git a/board/lazor/switchcap.c b/board/lazor/switchcap.c
index 3d5255791c..6cb5106efb 100644
--- a/board/lazor/switchcap.c
+++ b/board/lazor/switchcap.c
@@ -105,10 +105,12 @@ DECLARE_HOOK(HOOK_INIT, switchcap_init, HOOK_PRIO_DEFAULT);
void board_set_switchcap_power(int enable)
{
- if (board_has_ln9310())
+ if (board_has_ln9310()) {
gpio_set_level(GPIO_SWITCHCAP_ON_L, !enable);
- else
+ ln9310_software_enable(enable);
+ } else {
gpio_set_level(GPIO_SWITCHCAP_ON, enable);
+ }
}
int board_is_switchcap_enabled(void)
diff --git a/driver/ln9310.c b/driver/ln9310.c
index 31c6dbca7a..d339c228a2 100644
--- a/driver/ln9310.c
+++ b/driver/ln9310.c
@@ -18,6 +18,7 @@
#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)
static int power_good;
+static int startup_workaround_required;
int ln9310_power_good(void)
{
@@ -119,22 +120,25 @@ static int ln9310_update_startup_seq(void)
{
CPRINTS("LN9310 update startup sequence");
- /* Startup sequence instruction swap */
+ /*
+ * Startup sequence instruction swap to hold Cfly
+ * bottom plate low during startup
+ */
field_update8(LN9310_REG_LION_CTRL,
LN9310_LION_CTRL_MASK,
- LN9310_LION_CTRL_UNLOCK);
+ LN9310_LION_CTRL_UNLOCK_AND_EN_TM);
field_update8(LN9310_REG_SWAP_CTRL_0,
0xff,
- 0x3f);
+ 0x52);
field_update8(LN9310_REG_SWAP_CTRL_1,
0xff,
- 0x51);
+ 0x54);
field_update8(LN9310_REG_SWAP_CTRL_2,
0xff,
- 0x19);
+ 0xCC);
field_update8(LN9310_REG_SWAP_CTRL_3,
0xff,
@@ -180,7 +184,7 @@ static int ln9310_init_3to1(void)
LN9310_PWR_OP_MODE_MASK,
LN9310_PWR_OP_MODE_SWITCH31);
- /* 3S lower bounde delta configurations */
+ /* 3S lower bound delta configurations */
field_update8(LN9310_REG_LB_CTRL,
LN9310_LB_DELTA_MASK,
LN9310_LB_DELTA_3S);
@@ -217,7 +221,7 @@ static int ln9310_init_2to1(void)
LN9310_PWR_OP_MODE_MASK,
LN9310_PWR_OP_MODE_SWITCH21);
- /* 2S lower bounde delta configurations */
+ /* 2S lower bound delta configurations */
field_update8(LN9310_REG_LB_CTRL,
LN9310_LB_DELTA_MASK,
LN9310_LB_DELTA_2S);
@@ -240,7 +244,7 @@ static int ln9310_update_infet(void)
field_update8(LN9310_REG_LION_CTRL,
LN9310_LION_CTRL_MASK,
- LN9310_LION_CTRL_UNLOCK);
+ LN9310_LION_CTRL_UNLOCK_AND_EN_TM);
/* Update Infet register settings */
field_update8(LN9310_REG_CFG_5,
@@ -268,11 +272,107 @@ static int ln9310_update_infet(void)
return EC_SUCCESS;
}
+static int ln9310_precharge_cfly(uint64_t *precharge_timeout)
+{
+ int status = 0;
+ CPRINTS("LN9310 precharge cfly");
+
+ /* Unlock registers and enable test mode */
+ status |= field_update8(LN9310_REG_LION_CTRL,
+ LN9310_LION_CTRL_MASK,
+ LN9310_LION_CTRL_UNLOCK_AND_EN_TM);
+
+ /* disable test mode overrides */
+ status |= field_update8(LN9310_REG_FORCE_SC21_CTRL_2,
+ LN9310_FORCE_SC21_CTRL_2_FORCE_SW_CTRL_REQ_MASK,
+ LN9310_FORCE_SC21_CTRL_2_FORCE_SW_CTRL_REQ_OFF);
+
+ /* Configure test mode target values for precharge ckts. */
+ status |= field_update8(LN9310_REG_FORCE_SC21_CTRL_1,
+ LN9310_FORCE_SC21_CTRL_1_TM_SC_OUT_CFLY_PRECHARGE_MASK,
+ LN9310_FORCE_SC21_CTRL_1_TM_SC_OUT_CFLY_PRECHARGE_ON);
+
+ /* Force SCOUT precharge/predischarge overrides */
+ status |= field_update8(LN9310_REG_TEST_MODE_CTRL,
+ LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PRECHARGE_MASK |
+ LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PREDISCHARGE_MASK,
+ LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PRECHARGE_ON |
+ LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PREDISCHARGE_ON);
+
+ /* Force enable CFLY precharge overrides */
+ status |= field_update8(LN9310_REG_FORCE_SC21_CTRL_2,
+ LN9310_FORCE_SC21_CTRL_2_FORCE_SW_CTRL_REQ_MASK,
+ LN9310_FORCE_SC21_CTRL_2_FORCE_SW_CTRL_REQ_ON);
+
+ /* delay long enough to ensure CFLY has time to fully precharge */
+ usleep(LN9310_CFLY_PRECHARGE_DELAY);
+
+ /* locking and leaving test mode will stop CFLY precharge */
+ *precharge_timeout = get_time().val + LN9310_CFLY_PRECHARGE_TIMEOUT;
+ status |= field_update8(LN9310_REG_LION_CTRL,
+ LN9310_LION_CTRL_MASK,
+ LN9310_LION_CTRL_LOCK);
+
+ return status;
+}
+
+static int ln9310_precharge_cfly_reset(void)
+{
+ int status = 0;
+ CPRINTS("LN9310 precharge cfly reset");
+
+ /* set known initial state for config bits related to cfly precharge */
+ status |= field_update8(LN9310_REG_LION_CTRL,
+ LN9310_LION_CTRL_MASK,
+ LN9310_LION_CTRL_UNLOCK);
+
+ /* Force off SCOUT precharge/predischarge overrides */
+ status |= field_update8(LN9310_REG_TEST_MODE_CTRL,
+ LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PRECHARGE_MASK |
+ LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PREDISCHARGE_MASK,
+ LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PRECHARGE_OFF |
+ LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PREDISCHARGE_OFF);
+
+ /* disable test mode overrides */
+ status |= field_update8(LN9310_REG_FORCE_SC21_CTRL_2,
+ LN9310_FORCE_SC21_CTRL_2_FORCE_SW_CTRL_REQ_MASK,
+ LN9310_FORCE_SC21_CTRL_2_FORCE_SW_CTRL_REQ_OFF);
+
+ /* disable CFLY and SC_OUT precharge control */
+ status |= field_update8(LN9310_REG_FORCE_SC21_CTRL_1,
+ LN9310_FORCE_SC21_CTRL_1_TM_SC_OUT_CFLY_PRECHARGE_MASK,
+ LN9310_FORCE_SC21_CTRL_1_TM_SC_OUT_CFLY_PRECHARGE_OFF);
+
+ status |= field_update8(LN9310_REG_LION_CTRL,
+ LN9310_LION_CTRL_MASK,
+ LN9310_LION_CTRL_LOCK);
+
+ return status;
+}
+
void ln9310_init(void)
{
- int status, val;
+ int status, val, chip_revision;
enum battery_cell_type batt;
+ /* Make sure initial state of LN9310 is STANDBY (i.e. output is off) */
+ field_update8(LN9310_REG_STARTUP_CTRL,
+ LN9310_STARTUP_STANDBY_EN,
+ 1);
+
+ /*
+ * LN9310 software startup is only required for earlier silicon revs.
+ * LN9310 hardware revisions after LN9310_BC_STS_C_CHIP_REV_FIXED
+ * should not use the software startup sequence.
+ */
+ status = raw_read8(LN9310_REG_BC_STS_C, &val);
+ if (status) {
+ CPRINTS("LN9310 reading BC_STS_C failed");
+ return;
+ }
+ chip_revision = val && LN9310_BC_STS_C_CHIP_REV_MASK;
+ startup_workaround_required = chip_revision < LN9310_BC_STS_C_CHIP_REV_FIXED;
+
/* Update INFET configuration */
status = ln9310_update_infet();
@@ -312,11 +412,12 @@ void ln9310_init(void)
usleep(LN9310_CDC_DELAY);
CPRINTS("LN9310 OP_MODE Update method: Self-sync");
- /* Update Startup sequence */
- status = ln9310_update_startup_seq();
-
- if (status != EC_SUCCESS)
- return;
+ if (startup_workaround_required) {
+ /* Update Startup sequence */
+ status = ln9310_update_startup_seq();
+ if (status != EC_SUCCESS)
+ return;
+ }
batt = board_get_battery_cell_type();
if (batt == BATTERY_CELL_TYPE_3S) {
@@ -333,20 +434,124 @@ void ln9310_init(void)
/* Unmask the MODE change interrupt */
field_update8(LN9310_REG_INT1_MSK,
- LN9310_INT1_MODE,
- 0);
+ LN9310_INT1_MODE,
+ 0);
+}
+void ln9310_software_enable(int enable)
+{
+ int status, val, retry_count;
+ uint64_t precharge_timeout;
+ bool ln9310_init_completed = false;
+
+ /*
+ * LN9310 startup requires (nEN=0 AND STANDBY_EN=0) where nEN is a pin
+ * and STANDBY_EN is a register bit. Previous EC firmware set
+ * STANDBY_EN=1 in ln9310_init and toggled nEN to startup/shutdown. This
+ * function implements an alternate software (i.e. controlled by EC
+ * through I2C commands) startup sequence so one option is to set nEN=1
+ * and just used ln9310_software_enable to startup/shutdown.
+ * ln9310_software_enable can also be used in conjunction w/ the nEN pin
+ * (in case nEN pin is desired as visible signal ) as follows:
+ *
+ * Initial battery insertion:
+ * nEN=1
+ * ln9310_init() - initial condition is STANDBY_EN=1
+ *
+ * Power up LN9310:
+ * nEN=0 - STANDBY_EN should be 1 so this doesn't trigger startup
+ * ln9310_software_enable(1) - triggers alternate software-based startup
+ *
+ * Power down LN9310:
+ * nEN=1 - shutdown LN9310 (shutdown seq. does not require modification)
+ * ln9310_software_enable(0) - reset LN9310 register to state necessary
+ * for subsequent startups
+ */
/* Dummy clear all interrupts */
status = raw_read8(LN9310_REG_INT1, &val);
if (status) {
CPRINTS("LN9310 reading INT1 failed");
return;
}
-
- /* Clear the STANDBY_EN bit */
- field_update8(LN9310_REG_STARTUP_CTRL,
- LN9310_STARTUP_STANDBY_EN,
- 0);
-
CPRINTS("LN9310 cleared interrupts: 0x%x", val);
-}
+
+ if (startup_workaround_required) {
+ if (enable) {
+ /*
+ * Software modification of LN9310 startup sequence w/ retry
+ * loop.
+ *
+ * (1) Clear interrupts
+ * (2) Precharge Cfly w/ overrides of internal LN9310 signals
+ * (3) disable overrides -> stop precharging Cfly
+ * (4.1) if < 100 ms elapsed since (2) -> trigger LN9310 internal
+ * startup seq.
+ * (4.2) else -> abort and optionally retry from step 2
+ */
+ retry_count = 0;
+ while (!ln9310_init_completed && retry_count < LN9310_INIT_RETRY_COUNT) {
+ /* Precharge CFLY before starting up */
+ status = ln9310_precharge_cfly(&precharge_timeout);
+ if (status != EC_SUCCESS) {
+ CPRINTS("LN9310 failed to run Cfly precharge sequence");
+ status = ln9310_precharge_cfly_reset();
+ retry_count++;
+ continue;
+ }
+
+ /*
+ * Only start the SC if the cfly precharge
+ * hasn't timed out (i.e. ended too long ago)
+ */
+ if (get_time().val < precharge_timeout) {
+ /* Clear the STANDBY_EN bit to enable the SC */
+ field_update8(LN9310_REG_STARTUP_CTRL,
+ LN9310_STARTUP_STANDBY_EN,
+ 0);
+ if (get_time().val > precharge_timeout ) {
+ /*
+ * if timed out during previous I2C command, abort
+ * startup attempt
+ */
+ field_update8(LN9310_REG_STARTUP_CTRL,
+ LN9310_STARTUP_STANDBY_EN,
+ 1);
+ } else {
+ /* all other paths should reattempt startup */
+ ln9310_init_completed = true;
+ }
+ }
+ /* Reset to known state for config bits related to cfly precharge */
+ ln9310_precharge_cfly_reset();
+ retry_count++;
+ }
+
+ if (!ln9310_init_completed) {
+ CPRINTS("LN9310 failed to start after %d retry attempts",
+ retry_count);
+ }
+ } else {
+ /*
+ * Internal LN9310 shutdown sequence is ok as is, so just reset
+ * the state to prepare for subsequent startup sequences.
+ *
+ * (1) set STANDBY_EN=1 to be sure the part turns off even if nEN=0
+ * (2) reset cfly precharge related registers to known initial state
+ */
+ field_update8(LN9310_REG_STARTUP_CTRL,
+ LN9310_STARTUP_STANDBY_EN,
+ 1);
+
+ ln9310_precharge_cfly_reset();
+ }
+ } else {
+ /*
+ * for newer LN9310 revsisions, the startup workaround is not required
+ * so the STANDBY_EN bit can just be set directly
+ */
+ field_update8(LN9310_REG_STARTUP_CTRL,
+ LN9310_STARTUP_STANDBY_EN,
+ !enable);
+ }
+ return;
+} \ No newline at end of file
diff --git a/include/driver/ln9310.h b/include/driver/ln9310.h
index 07cff8ba97..e4bd0bc7e0 100644
--- a/include/driver/ln9310.h
+++ b/include/driver/ln9310.h
@@ -66,14 +66,14 @@
#define LN9310_REG_SYS_CTRL 0x1e
#define LN9310_REG_STARTUP_CTRL 0x1f
-#define LN9310_STARTUP_STANDBY_EN BIT(0)
+#define LN9310_STARTUP_STANDBY_EN BIT(0)
#define LN9310_STARTUP_SELECT_EXT_5V_FOR_VDR BIT(3)
#define LN9310_REG_IIN_CTRL 0x20
#define LN9310_REG_VIN_CTRL 0x21
#define LN9310_REG_TRACK_CTRL 0x22
-#define LN9310_TRACK_INFET_OUT_SWITCH_OK_EN BIT(7)
+#define LN9310_TRACK_INFET_OUT_SWITCH_OK_EN BIT(7)
#define LN9310_TRACK_INFET_OUT_SWITCH_OK_CFG2 BIT(6)
#define LN9310_TRACK_INFET_OUT_SWITCH_OK_CFG1 BIT(5)
#define LN9310_TRACK_INFET_OUT_SWITCH_OK_CFG0 BIT(4)
@@ -93,8 +93,8 @@
#define LN9310_REG_RECOVERY_CTRL 0x25
#define LN9310_REG_LB_CTRL 0x26
-#define LN9310_LB_MIN_FREQ_EN BIT(2)
-#define LN9310_LB_DELTA_MASK 0x38
+#define LN9310_LB_MIN_FREQ_EN BIT(2)
+#define LN9310_LB_DELTA_MASK 0x38
#define LN9310_LB_DELTA_2S 0x20
#define LN9310_LB_DELTA_3S 0x20
@@ -130,23 +130,24 @@
#define LN9310_REG_SC_DITHER_CTRL 0x2f
-#define LN9310_REG_LION_CTRL 0x30
-#define LN9310_LION_CTRL_MASK 0xFF
-#define LN9310_LION_CTRL_UNLOCK 0xAA
-#define LN9310_LION_CTRL_LOCK 0x00
+#define LN9310_REG_LION_CTRL 0x30
+#define LN9310_LION_CTRL_MASK 0xFF
+#define LN9310_LION_CTRL_UNLOCK_AND_EN_TM 0xAA
+#define LN9310_LION_CTRL_UNLOCK 0x5B
+#define LN9310_LION_CTRL_LOCK 0x00
#define LN9310_REG_CFG_0 0x3C
-#define LN9310_CFG_0_LS_HELPER_IDLE_MSK_MASK 0x20
+#define LN9310_CFG_0_LS_HELPER_IDLE_MSK_MASK 0x20
#define LN9310_CFG_0_LS_HELPER_IDLE_MSK_ON 0x20
#define LN9310_REG_CFG_4 0x40
#define LN9310_CFG_4_SC_OUT_PRECHARGE_EN_TIME_CFG BIT(2)
-#define LN9310_CFG_4_SW1_VGS_SHORT_EN_MSK BIT(3)
+#define LN9310_CFG_4_SW1_VGS_SHORT_EN_MSK BIT(3)
#define LN9310_CFG_4_SC_OUT_PRECHARGE_EN_TIME_CFG_MASK 0x04
-#define LN9310_CFG_4_SW1_VGS_SHORT_EN_MSK_MASK 0x08
+#define LN9310_CFG_4_SW1_VGS_SHORT_EN_MSK_MASK 0x08
#define LN9310_CFG_4_BSTH_BSTL_HIGH_ROUT_CFG_MASK 0xC0
#define LN9310_CFG_4_SC_OUT_PRECHARGE_EN_TIME_CFG_ON 0x04
-#define LN9310_CFG_4_SW1_VGS_SHORT_EN_MSK_OFF 0x00
+#define LN9310_CFG_4_SW1_VGS_SHORT_EN_MSK_OFF 0x00
#define LN9310_CFG_4_BSTH_BSTL_HIGH_ROUT_CFG_LOWEST 0x00
#define LN9310_REG_CFG_5 0x41
@@ -155,6 +156,24 @@
#define LN9310_CFG_5_INFET_CP_PD_BIAS_CFG_MASK 0x30
#define LN9310_CFG_5_INFET_CP_PD_BIAS_CFG_LOWEST 0x00
+#define LN9310_REG_TEST_MODE_CTRL 0x46
+#define LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PRECHARGE_MASK 0x40
+#define LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PRECHARGE_ON 0x40
+#define LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PRECHARGE_OFF 0x00
+#define LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PREDISCHARGE_MASK 0x20
+#define LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PREDISCHARGE_ON 0x20
+#define LN9310_TEST_MODE_CTRL_FORCE_SC_OUT_PREDISCHARGE_OFF 0x00
+
+#define LN9310_REG_FORCE_SC21_CTRL_1 0x49
+#define LN9310_FORCE_SC21_CTRL_1_TM_SC_OUT_CFLY_PRECHARGE_MASK 0xFF
+#define LN9310_FORCE_SC21_CTRL_1_TM_SC_OUT_CFLY_PRECHARGE_ON 0x59
+#define LN9310_FORCE_SC21_CTRL_1_TM_SC_OUT_CFLY_PRECHARGE_OFF 0x40
+
+#define LN9310_REG_FORCE_SC21_CTRL_2 0x4A
+#define LN9310_FORCE_SC21_CTRL_2_FORCE_SW_CTRL_REQ_MASK 0x80
+#define LN9310_FORCE_SC21_CTRL_2_FORCE_SW_CTRL_REQ_ON 0x80
+#define LN9310_FORCE_SC21_CTRL_2_FORCE_SW_CTRL_REQ_OFF 0x00
+
#define LN9310_REG_SWAP_CTRL_0 0x58
#define LN9310_REG_SWAP_CTRL_1 0x59
#define LN9310_REG_SWAP_CTRL_2 0x5A
@@ -164,8 +183,17 @@
#define LN9310_BC_STS_B_INFET_OUT_SWITCH_OK BIT(5)
#define LN9310_BC_STS_B_INFET_OUT_SWITCH_OK_MASK 0x20
+#define LN9310_REG_BC_STS_C 0x52
+#define LN9310_BC_STS_C_CHIP_REV_MASK 0xF0
+#define LN9310_BC_STS_C_CHIP_REV_FIXED 0x40
+
/* LN9310 Timing definition */
-#define LN9310_CDC_DELAY 120 /* 120us */
+#define LN9310_CDC_DELAY 120 /* 120us */
+#define LN9310_CFLY_PRECHARGE_DELAY (12*MSEC)
+#define LN9310_CFLY_PRECHARGE_TIMEOUT (100*MSEC)
+
+/* LN9310 Driver Configuration */
+#define LN9310_INIT_RETRY_COUNT 3
/* Define configuration of LN9310 part */
struct ln9310_config_t {
@@ -179,6 +207,9 @@ extern const struct ln9310_config_t ln9310_config;
/* Init the driver */
void ln9310_init(void);
+/* Enable/disable the ln9310 output */
+void ln9310_software_enable(int enable);
+
/* Interrupt handler */
void ln9310_interrupt(enum gpio_signal signal);