summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2022-03-09 18:16:08 +0000
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-04-21 17:34:45 +0000
commit4b672f3e938678ec184735fa05958e68de20ed2f (patch)
treee20f32f57daca1cdc534c94276c5c819ab423fd2
parentb8bd278b08f566d1e6347953c42c93f48290511b (diff)
downloadchrome-ec-4b672f3e938678ec184735fa05958e68de20ed2f.tar.gz
chgstv2: Add bypass mode for ISL9241
This patch adds bypass mode support to the charger interface and implement it for ISL9241. The bypass mode routes the input current from AC directly to the system power rail for efficiency. Enabling or disabling is done by the charger task. BUG=b:214057333, b:216206104 BRANCH=None TEST=On Agah. Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Change-Id: I951393b0e0026de795ce930e8fdeb9018d57b84e Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3519879 Tested-by: Zick Wei <zick.wei@quanta.corp-partner.google.com> Reviewed-by: Zhuohao Lee <zhuohao@chromium.org>
-rw-r--r--common/charger.c7
-rw-r--r--driver/charger/isl9241.c324
-rw-r--r--driver/charger/isl9241.h15
-rw-r--r--include/charger.h20
4 files changed, 348 insertions, 18 deletions
diff --git a/common/charger.c b/common/charger.c
index 38eb91faf5..8dae2e71e8 100644
--- a/common/charger.c
+++ b/common/charger.c
@@ -493,6 +493,13 @@ enum ec_error_list charger_discharge_on_ac(int enable)
return rv;
}
+enum ec_error_list charger_enable_bypass_mode(int chgnum, int enable)
+{
+ if (!chg_chips[chgnum].drv->enable_bypass_mode)
+ return EC_ERROR_UNIMPLEMENTED;
+ return chg_chips[chgnum].drv->enable_bypass_mode(chgnum, enable);
+}
+
enum ec_error_list charger_get_vbus_voltage(int port, int *voltage)
{
int chgnum = 0;
diff --git a/driver/charger/isl9241.c b/driver/charger/isl9241.c
index b2a061d696..bd84d30755 100644
--- a/driver/charger/isl9241.c
+++ b/driver/charger/isl9241.c
@@ -12,6 +12,7 @@
#include "battery.h"
#include "battery_smart.h"
#include "charger.h"
+#include "charge_manager.h"
#include "charge_state.h"
#include "console.h"
#include "common.h"
@@ -41,7 +42,7 @@
#define AC_CURRENT_TO_REG(CUR) (((CUR) * BOARD_RS1) / ISL9241_DEFAULT_RS1)
/* Console output macros */
-#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args)
+#define CPRINTS(format, args...) cprints(CC_CHARGER, format, ## args)
static int learn_mode;
@@ -70,26 +71,38 @@ static enum ec_error_list isl9241_discharge_on_ac_weak_disable(int chgnum);
static inline enum ec_error_list isl9241_read(int chgnum, int offset,
int *value)
{
- return i2c_read16(chg_chips[chgnum].i2c_port,
- chg_chips[chgnum].i2c_addr_flags,
- offset, value);
+ int rv = i2c_read16(chg_chips[chgnum].i2c_port,
+ chg_chips[chgnum].i2c_addr_flags,
+ offset, value);
+ if (rv)
+ CPRINTS("%s failed (%d)", __func__, rv);
+
+ return rv;
}
static inline enum ec_error_list isl9241_write(int chgnum, int offset,
int value)
{
- return i2c_write16(chg_chips[chgnum].i2c_port,
- chg_chips[chgnum].i2c_addr_flags,
- offset, value);
+ int rv = i2c_write16(chg_chips[chgnum].i2c_port,
+ chg_chips[chgnum].i2c_addr_flags,
+ offset, value);
+ if (rv)
+ CPRINTS("%s failed (%d)", __func__, rv);
+
+ return rv;
}
static inline enum ec_error_list isl9241_update(int chgnum, int offset,
uint16_t mask,
enum mask_update_action action)
{
- return i2c_update16(chg_chips[chgnum].i2c_port,
- chg_chips[chgnum].i2c_addr_flags,
- offset, mask, action);
+ int rv = i2c_update16(chg_chips[chgnum].i2c_port,
+ chg_chips[chgnum].i2c_addr_flags,
+ offset, mask, action);
+ if (rv)
+ CPRINTS("%s failed (%d)", __func__, rv);
+
+ return rv;
}
/* chip specific interfaces */
@@ -299,7 +312,7 @@ error_restore_ctl3:
error:
if (rv)
- CPRINTF("Could not read VBUS ADC! Error: %d\n", rv);
+ CPRINTS("Could not read VBUS ADC! Error: %d", rv);
return rv;
}
@@ -368,7 +381,7 @@ int isl9241_set_ac_prochot(int chgnum, int ma)
rv = isl9241_write(chgnum, ISL9241_REG_AC_PROCHOT, reg);
if (rv)
- CPRINTF("set_ac_prochot failed (%d)\n", rv);
+ CPRINTS("set_ac_prochot failed (%d)", rv);
return rv;
}
@@ -389,7 +402,283 @@ int isl9241_set_dc_prochot(int chgnum, int ma)
rv = isl9241_write(chgnum, ISL9241_REG_DC_PROCHOT, ma);
if (rv)
- CPRINTF("set_dc_prochot failed (%d)\n", rv);
+ CPRINTS("set_dc_prochot failed (%d)", rv);
+
+ return rv;
+}
+
+static bool isl9241_is_ac_present(int chgnum)
+{
+ static bool ac_is_present;
+ int reg;
+ int rv;
+
+ rv = isl9241_read(chgnum, ISL9241_REG_INFORMATION2, &reg);
+ if (rv == EC_SUCCESS)
+ ac_is_present = !!(reg & ISL9241_INFORMATION2_ACOK_PIN);
+
+ return ac_is_present;
+}
+
+/*
+ * Check whether ISL9241 is in any CHRG state, including NVDC+CHRG, Bypass+CHRG,
+ * RTB+CHRG.
+ */
+static bool isl9241_is_in_chrg(int chgnum)
+{
+ static bool trickle_charge_enabled, fast_charge_enabled;
+ int reg;
+ int rv;
+
+ rv = isl9241_read(chgnum, ISL9241_REG_MIN_SYSTEM_VOLTAGE, &reg);
+ if (rv == EC_SUCCESS)
+ trickle_charge_enabled = reg > 0;
+
+ rv = isl9241_read(chgnum, ISL9241_REG_CHG_CURRENT_LIMIT, &reg);
+ if (rv == EC_SUCCESS)
+ fast_charge_enabled = reg > 0;
+
+ return trickle_charge_enabled || fast_charge_enabled;
+}
+
+/*
+ * Transition from Bypass to BAT.
+ */
+static enum ec_error_list isl9241_bypass_to_bat(int chgnum)
+{
+ const struct battery_info *bi = battery_get_info();
+
+ /* 1: Disable force forward buck/reverse boost. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL4,
+ ISL9241_CONTROL4_FORCE_BUCK_MODE, MASK_CLR);
+ /*
+ * 2: Turn off BYPSG, turn on NGATE, disable charge pump 100%, disable
+ * Vin<Vout comparator.
+ */
+ isl9241_write(chgnum, ISL9241_REG_CONTROL0, 0);
+ /* 3: Set MaxSysVoltage to full charge. */
+ isl9241_write(chgnum, ISL9241_REG_MAX_SYSTEM_VOLTAGE, bi->voltage_max);
+ /* 4: Disable ADC. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL3,
+ ISL9241_CONTROL3_ENABLE_ADC, MASK_CLR);
+ /* 5: Set BGATE to normal operation. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL1,
+ ISL9241_CONTROL1_BGATE_OFF, MASK_CLR);
+ /* 6: Set ACOK reference to normal value. TODO: Revisit. */
+ isl9241_write(chgnum, ISL9241_REG_ACOK_REFERENCE,
+ ISL9241_MV_TO_ACOK_REFERENCE(
+ ISL9241_ACOK_REF_LOW_VOLTAGE_ADAPTER_MV));
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Transition from Bypass+CHRG to BAT (M).
+ */
+static enum ec_error_list isl9241_bypass_chrg_to_bat(int chgnum)
+{
+ /* 1: Disable force forward buck/reverse boost. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL4,
+ ISL9241_CONTROL4_FORCE_BUCK_MODE, MASK_CLR);
+ /* 2: Disable fast charge. */
+ isl9241_write(chgnum, ISL9241_REG_CHG_CURRENT_LIMIT, 0);
+ /* 3: Disable trickle charge. */
+ isl9241_write(chgnum, ISL9241_REG_MIN_SYSTEM_VOLTAGE, 0);
+ /*
+ * 4: Turn off BYPSG, turn on NGATE, disable charge pump 100%, disable
+ * Vin<Vout comparator.
+ */
+ isl9241_write(chgnum, ISL9241_REG_CONTROL0, 0);
+ /* 5: Disable ADC. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL3,
+ ISL9241_CONTROL3_ENABLE_ADC, MASK_CLR);
+ /* 6: Set BGATE to normal operation. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL1,
+ ISL9241_CONTROL1_BGATE_OFF, MASK_CLR);
+ /* 7: Set ACOK reference to normal value. TODO: Revisit. */
+ isl9241_write(chgnum, ISL9241_REG_ACOK_REFERENCE,
+ ISL9241_MV_TO_ACOK_REFERENCE(3600));
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Transition from NVDC+CHRG to NVDC (L).
+ */
+static enum ec_error_list isl9241_nvdc_chrg_to_nvdc(int chgnum)
+{
+ enum ec_error_list rv;
+
+ /* L: If we're in NVDC+Chg, first transition to NVDC. */
+ /* 1: Disable fast charge. */
+ rv = isl9241_set_current(chgnum, 0);
+ if (rv)
+ return rv;
+
+ /* 2: Disable trickle charge. */
+ rv = isl9241_write(chgnum, ISL9241_REG_MIN_SYSTEM_VOLTAGE, 0);
+ if (rv)
+ return rv;
+
+ return EC_SUCCESS;
+}
+
+static enum ec_error_list isl9241_enable_bypass_mode(int chgnum, bool enable);
+
+/*
+ * Transition from NVDC to Bypass (A).
+ */
+static enum ec_error_list isl9241_nvdc_to_bypass(int chgnum)
+{
+ int voltage;
+
+ /* 1: Set adapter current limit. */
+ isl9241_set_input_current_limit(
+ chgnum, charge_manager_get_charger_current());
+ /* 2: Set charge pumps to 100%. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL0,
+ ISL9241_CONTROL0_EN_CHARGE_PUMPS, MASK_SET);
+ /* 3: Enable ADC. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL3,
+ ISL9241_CONTROL3_ENABLE_ADC, MASK_SET);
+ /* 4: Turn on Vin/Vout comparator. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL0,
+ ISL9241_CONTROL0_EN_VIN_VOUT_COMP, MASK_SET);
+ /* 5: Set ACOK reference higher than battery full voltage.
+ isl9241_write(chgnum, ISL9241_REG_ACOK_REFERENCE,
+ ISL9241_MV_TO_ACOK_REFERENCE(
+ battery_full_voltage_mv + 800));
+ */
+ /* 6*: Reduce system load below ACLIM. */
+ /* 7: Turn off BGATE */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL1,
+ ISL9241_CONTROL1_BGATE_OFF, MASK_SET);
+ /* 8*: Set MaxSysVoltage to VADP. */
+ isl9241_get_vbus_voltage(chgnum, 0, &voltage);
+ isl9241_write(chgnum, ISL9241_REG_MAX_SYSTEM_VOLTAGE, voltage - 256);
+ /* 9*: Wait until VSYS == MaxSysVoltage. */
+ /* 10*: Turn on Bypass gate */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL0,
+ ISL9241_CONTROL0_EN_BYPASS_GATE, MASK_SET);
+ /* 11: Wait 1 ms. */
+ msleep(1);
+ /* 12*: Turn off NGATE. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL0,
+ ISL9241_CONTROL0_NGATE_OFF, MASK_SET);
+ /* 14*: Stop switching. */
+ isl9241_write(chgnum, ISL9241_REG_MAX_SYSTEM_VOLTAGE, 0);
+ /* 15: Set BGATE to normal operation. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL1,
+ ISL9241_CONTROL1_BGATE_OFF, MASK_CLR);
+ /*
+ * Suggestion-1: If ACOK goes low before step A16, stop here
+ * then execute the steps for Bypass to BAT to abort.
+ */
+ if (!isl9241_is_ac_present(chgnum))
+ return isl9241_enable_bypass_mode(chgnum, false);
+ /* 16: Enable 10 mA discharge on CSOP. */
+ /* 17: Read diode emulation active bit. */
+ /* 18: Disable 10mA discharge on CSOP. */
+ /* 19*: Force forward buck/reverse boost mode. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL4,
+ ISL9241_CONTROL4_FORCE_BUCK_MODE, MASK_SET);
+
+ /*
+ * Suggestion-2 and 3: If AC is removed on or after A16,
+ * complete all steps then execute Bypass to BAT to revert.
+ */
+ if (!isl9241_is_ac_present(chgnum))
+ return isl9241_enable_bypass_mode(chgnum, false);
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Transition from Bypass + CHRG to Bypass (J).
+ */
+static enum ec_error_list isl9241_bypass_chrg_to_bypass(int chgnum)
+{
+ /* 1: Stop switching. */
+ isl9241_write(chgnum, ISL9241_REG_MAX_SYSTEM_VOLTAGE, 0);
+ /* 2: Disable fast charge. */
+ isl9241_write(chgnum, ISL9241_REG_CHG_CURRENT_LIMIT, 0);
+ /* 3: Disable trickle charge. */
+ isl9241_write(chgnum, ISL9241_REG_MIN_SYSTEM_VOLTAGE, 0);
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Transition from Bypass to NVDC (B).
+ */
+static enum ec_error_list isl9241_bypass_to_nvdc(int chgnum)
+{
+ const struct battery_info *bi = battery_get_info();
+ int voltage;
+
+ /* 1*: Reduce system load below ACLIM. */
+ /* 3*: Disable force forward buck/reverse boost. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL4,
+ ISL9241_CONTROL4_FORCE_BUCK_MODE, MASK_CLR);
+ /* 6*: Set MaxSysVoltage to VADP. */
+ isl9241_get_vbus_voltage(chgnum, 0, &voltage);
+ isl9241_write(chgnum, ISL9241_REG_MAX_SYSTEM_VOLTAGE, voltage - 256);
+ /* 7*: Wait until VSYS == MaxSysVoltage. */
+ msleep(1);
+ /* 8*: Turn on NGATE. */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL0,
+ ISL9241_CONTROL0_NGATE_OFF, MASK_CLR);
+ /* 10*: Turn off Bypass gate */
+ isl9241_update(chgnum, ISL9241_REG_CONTROL0,
+ ISL9241_CONTROL0_EN_BYPASS_GATE, MASK_CLR);
+ /* 12*: Set MaxSysVoltage to full charge. */
+ isl9241_write(chgnum, ISL9241_REG_MAX_SYSTEM_VOLTAGE, bi->voltage_max);
+
+ return EC_SUCCESS;
+}
+
+static enum ec_error_list isl9241_enable_bypass_mode(int chgnum, bool enable)
+{
+ enum ec_error_list rv = EC_ERROR_UNKNOWN;
+
+ if (enable) {
+ /* We should be already in NVDC. */
+ if (isl9241_is_in_chrg(chgnum)) {
+ /* (Optional) L (then A) */
+ rv = isl9241_nvdc_chrg_to_nvdc(chgnum);
+ CPRINTS("%s nvdc_chrg -> nvdc",
+ rv ? "Failed" : "Succeeded");
+ }
+ /* A */
+ rv = isl9241_nvdc_to_bypass(chgnum);
+ CPRINTS("%s nvdc -> bypass", rv ? "Failed" : "Succeeded");
+ return rv;
+ } else if (isl9241_is_ac_present(chgnum)) {
+ /* Switch to NVDC (e.g. BJ -> Type-C) */
+ if (isl9241_is_in_chrg(chgnum)) {
+ /* J (then B) */
+ rv = isl9241_bypass_chrg_to_bypass(chgnum);
+ CPRINTS("%s bypass_chrg -> bypass",
+ rv ? "Failed" : "Succeeded");
+ }
+ /* B */
+ rv = isl9241_bypass_to_nvdc(chgnum);
+ CPRINTS("%s bypass -> nvdc", rv ? "Failed" : "Succeeded");
+ return rv;
+ } else {
+ /* AC removal */
+ if (isl9241_is_in_chrg(chgnum)) {
+ /* M */
+ rv = isl9241_bypass_chrg_to_bat(chgnum);
+ CPRINTS("%s bypass_chrg -> bat",
+ rv ? "Failed" : "Succeeded");
+ return rv;
+ }
+ /* M */
+ rv = isl9241_bypass_to_bat(chgnum);
+ CPRINTS("%s bypass -> bat", rv ? "Failed" : "Succeeded");
+ return rv;
+ }
return rv;
}
@@ -482,7 +771,7 @@ static void isl9241_init(int chgnum)
return;
init_fail:
- CPRINTF("ISL9241_init failed!\n");
+ CPRINTS("Init failed!");
}
/*****************************************************************************/
@@ -561,12 +850,12 @@ static void dump_reg_range(int chgnum, int low, int high)
int rv;
for (reg = low; reg <= high; reg++) {
- CPRINTF("[%Xh] = ", reg);
+ ccprintf("[%Xh] = ", reg);
rv = isl9241_read(chgnum, reg, &regval);
if (!rv)
- CPRINTF("0x%04x\n", regval);
+ ccprintf("0x%04x\n", regval);
else
- CPRINTF("ERR (%d)\n", rv);
+ ccprintf("ERR (%d)\n", rv);
cflush();
}
}
@@ -607,6 +896,7 @@ const struct charger_drv isl9241_drv = {
.ramp_is_detected = &isl9241_ramp_is_detected,
.ramp_get_current_limit = &isl9241_ramp_get_current_limit,
#endif
+ .enable_bypass_mode = isl9241_enable_bypass_mode,
#ifdef CONFIG_CMD_CHARGER_DUMP
.dump_registers = &command_isl9241_dump,
#endif
diff --git a/driver/charger/isl9241.h b/driver/charger/isl9241.h
index f8d0b18784..f6f7844ef5 100644
--- a/driver/charger/isl9241.h
+++ b/driver/charger/isl9241.h
@@ -43,7 +43,10 @@
#define ISL9241_REG_CONTROL0 0x39
/* 2: Input Voltage Regulation (0 = Enable (default), 1 = Disable) */
#define ISL9241_CONTROL0_INPUT_VTG_REGULATION BIT(2)
-
+#define ISL9241_CONTROL0_EN_VIN_VOUT_COMP BIT(5)
+#define ISL9241_CONTROL0_EN_CHARGE_PUMPS BIT(6)
+#define ISL9241_CONTROL0_EN_BYPASS_GATE BIT(11)
+#define ISL9241_CONTROL0_NGATE_OFF BIT(12)
#define ISL9241_REG_INFORMATION1 0x3A
#define ISL9241_REG_ADAPTER_CUR_LIMIT2 0x3B
@@ -51,6 +54,7 @@
/* Configures various charger options */
#define ISL9241_REG_CONTROL1 0x3C
#define ISL9241_CONTROL1_PSYS BIT(3)
+#define ISL9241_CONTROL1_BGATE_OFF BIT(6)
#define ISL9241_CONTROL1_LEARN_MODE BIT(12)
/*
* 9:7 - Switching Frequency
@@ -97,6 +101,8 @@
#define ISL9241_REG_OTG_VOLTAGE 0x49
#define ISL9241_REG_OTG_CURRENT 0x4A
+#define ISL9241_MV_TO_ACOK_REFERENCE(mv) (((mv) / 96) << 6)
+
/* VIN Voltage (ADP Min Voltage) (default 4.096V) */
#define ISL9241_REG_VIN_VOLTAGE 0x4B
@@ -119,6 +125,7 @@
#define ISL9241_INFORMATION2_ACOK_PIN BIT(14)
#define ISL9241_REG_CONTROL4 0x4E
+#define ISL9241_CONTROL4_FORCE_BUCK_MODE BIT(10)
/* 11: Rsense (Rs1:Rs2) ratio for PSYS (0 - 2:1, 1 - 1:1) */
#define ISL9241_CONTROL4_PSYS_RSENSE_RATIO BIT(11)
/* 13: Enable VSYS slew rate control (0 - disable, 1 - enable) */
@@ -144,4 +151,10 @@
#define ISL9241_VIN_ADC_BIT_OFFSET 6
#define ISL9241_VIN_ADC_STEP_MV 96
+/*
+ * Used to reset ACOKref register to normal value to detect low voltage (5V or
+ * 9V) adapter during next plug in event
+ */
+#define ISL9241_ACOK_REF_LOW_VOLTAGE_ADAPTER_MV 3600
+
#endif /* __CROS_EC_ISL9241_H */
diff --git a/include/charger.h b/include/charger.h
index bc2b66eac2..66130ce3a5 100644
--- a/include/charger.h
+++ b/include/charger.h
@@ -136,6 +136,14 @@ struct charger_drv {
/* Enable/disable linear charging */
enum ec_error_list (*enable_linear_charge)(int chgnum, bool enable);
+ /*
+ * Enable/disable bypass mode
+ *
+ * Callers are responsible for checking required conditions beforehand.
+ * (e.g supplier == CHARGE_SUPPLIER_DEDICATED, 20 V < input_voltage)
+ */
+ enum ec_error_list (*enable_bypass_mode)(int chgnum, bool enable);
+
/* Dumps charger registers */
void (*dump_registers)(int chgnum);
};
@@ -364,6 +372,18 @@ enum ec_error_list charger_is_icl_reached(int chgnum, bool *reached);
*/
enum ec_error_list charger_enable_linear_charge(int chgnum, bool enable);
+/**
+ * Enable/disable bypass mode
+ *
+ * Bypass mode allows AC power to be supplied directly to the system rail
+ * instead of going through the charger.
+ *
+ * @param chgnum: Active charge port
+ * @param enable: Whether to enable or disable bypass mode.
+ * @return EC_SUCCESS on success, error otherwise.
+ */
+enum ec_error_list charger_enable_bypass_mode(int chgnum, int enable);
+
/*
* Print all charger info for debugging purposes
* @param chgnum: charger IC index.