summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);