diff options
-rw-r--r-- | board/homestar/board.c | 1 | ||||
-rw-r--r-- | board/lazor/switchcap.c | 6 | ||||
-rw-r--r-- | driver/ln9310.c | 251 | ||||
-rw-r--r-- | include/driver/ln9310.h | 57 |
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); |