summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Berg <alecaberg@chromium.org>2015-02-01 15:48:48 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-02-03 08:21:36 +0000
commitfd9dd63e5c9cb76692501280dcb09a31f3154b12 (patch)
treec64234a6d43b0d6c47b641eb8eb0c551dff49ef6
parent0cfc6a087f103dfa1a03d5e6671844ddd4ac8c14 (diff)
downloadchrome-ec-stabilize-6752.B.tar.gz
samus: automatically recover from charge circuit failuresstabilize-6752.B
Occasionally the charge circuit on samus gets wedged and will not charge. This change detects when the charge circuit has failed and automatically recovers from it. It uses the BQ PROCHOT warning to detect the failure by setting PROCHOT to trigger when the BQ thinks input current is higher than the input current limit. When the failure is detected, the EC disables charging and tells PD MCU to disable CHARGE_EN, then a couple seconds later, it re-enables charging. This CL also adds more communication between EC and PD for the EC to be able to set the charge state for the PD. Valid charge states are: No charging allowed, 5V charging only, and max charging. The EC uses this as such: - When the EC gets AC present interrupt, it sets off a deferred function to change charge state to max charging after some delay to give time for the charge circuit to settle down. - When the EC gets AC disconnect interrupt, it disables charging briefly, enables learn mode, and then sets 5V charging allowed. This allows for the same starting conditions in the charge circuit for every AC attach. - When the EC detects a wedged charge circuit, it disables charging and waits a few seconds before re-enabling 5V only charging. Additionally, this change moves the charging cutoff in S3/S5/G3 when the battery is full to the EC. With the added control for the EC to set the PD charging state, it is more convenient for the EC to manage cutting off charging when battery is full. BUG=chrome-os-partner:36081 BRANCH=samus TEST=test the basics: - connect/disconnect zinger a bunch of times - connect and disconnect two zingers in different order - connect two zingers and charge override between the two test the automatic charge wedge recover: - wedge the charge circuit by setting charger voltage under battery voltage: "charger voltage 7000" - wait a few seconds for the system to recover and check it is charging with "battery" command test full battery charge cutoff: - added console command to change battery soc in board/samus/extpower.c: static int cmd_battfake(int argc, char **argv) { char *e; battery_soc = strtoi(argv[1], &e, 10); batt_soc_change(); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(battfake, cmd_battfake, "", "", NULL); - in S0, tested "battfake 100" does not disable charging. - in G3, tested "battfake 100" disables charging and "battfake 99" re-enables charging. - set "battfake 100" and tested transitioning to S0 enables charging and transitioning to S5 disables charging. - attached two chargers and used charge override to select active port. then toggled "battfake 100" to "battfake 99" back and forth and verified charge override port is still the same. test third-party 12V charger: - plug in a bunch of times and make sure we stay at 5V for 500ms and then transition to 12V test with no battery: - tested five different units with no battery and just zinger. 3/5 boot, while the other 2 don't. But, the 2 that don't boot without battery also can't boot w/o battery when this CL is reverted, so I don't think this change is causing the problem, I think there is an electrical limitation. test with EVT zinger: - EVT zingers (P2 - C2) negotiate very quickly after connection, which can cause INA problems w/o this CL. Tested an EVT zinger with samus and did a bunch of connections and disconnections and verified that we always wait at 5V for 500ms and that we don't wedge the INA circuit on connect. test backwards compatibility: - test new PD with old EC and make sure we can charge with zinger. (note that if the charge circuit wedges, we won't be able to unwedge it). - test old PD with new EC and make sure we can charge with zinger. Change-Id: I7703b7a2ab1209d7f559b265b03517e79c74b16a Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/245253 Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
-rw-r--r--board/samus/board.c2
-rw-r--r--board/samus/extpower.c204
-rw-r--r--board/samus_pd/board.c167
-rw-r--r--board/samus_pd/board.h1
-rw-r--r--common/charge_state_v2.c3
-rw-r--r--common/host_command_pd.c20
-rw-r--r--common/usb_pd_protocol.c15
-rw-r--r--driver/charger/bq24773.c13
-rw-r--r--include/config.h15
-rw-r--r--include/ec_commands.h10
-rw-r--r--include/host_command.h4
-rw-r--r--include/usb_pd.h8
12 files changed, 332 insertions, 130 deletions
diff --git a/board/samus/board.c b/board/samus/board.c
index 7199d60c6b..7f606516b0 100644
--- a/board/samus/board.c
+++ b/board/samus/board.c
@@ -47,7 +47,7 @@
static void pd_mcu_interrupt(enum gpio_signal signal)
{
/* Exchange status with PD MCU. */
- host_command_pd_send_status();
+ host_command_pd_send_status(PD_CHARGE_NO_CHANGE);
}
#include "gpio_list.h"
diff --git a/board/samus/extpower.c b/board/samus/extpower.c
index 8eb2494795..51cf5454e8 100644
--- a/board/samus/extpower.c
+++ b/board/samus/extpower.c
@@ -8,6 +8,8 @@
* Drive high in S5-S0 when AC_PRESENT is high, otherwise drive low.
*/
+#include "bq24773.h"
+#include "charge_state.h"
#include "charger.h"
#include "chipset.h"
#include "common.h"
@@ -16,13 +18,32 @@
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
+#include "i2c.h"
#include "system.h"
#include "task.h"
#include "util.h"
+/* Console output macros */
+#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
+
+/* Max number of attempts to enable/disable NVDC charger */
+#define CHARGER_MODE_ATTEMPTS 3
+
/* Backboost has been detected */
static int bkboost_detected;
+/* Charging is disabled */
+static int charge_is_disabled;
+
+/*
+ * Charge circuit occasionally gets wedged and doesn't charge.
+ * This variable keeps track of the state of the circuit.
+ */
+static enum {
+ CHARGE_CIRCUIT_OK,
+ CHARGE_CIRCUIT_WEDGED,
+} charge_circuit_state = CHARGE_CIRCUIT_OK;
+
int extpower_is_present(void)
{
return gpio_get_level(GPIO_AC_PRESENT);
@@ -64,71 +85,188 @@ static void extpower_init(void)
}
DECLARE_HOOK(HOOK_INIT, extpower_init, HOOK_PRIO_DEFAULT);
-static void extpower_board_hacks(int extpower)
+/*
+ * Save power in S3/S5/G3 by disabling charging when the battery is
+ * full. Restore charging when battery is not full anymore. This saves
+ * power because our input AC path is inefficient.
+ */
+
+static void check_charging_cutoff(void)
{
- static int extpower_prev;
+ /* If battery is full disable charging */
+ if (charge_get_percent() == 100) {
+ charge_is_disabled = 1;
+ host_command_pd_send_status(PD_CHARGE_NONE);
+ }
+}
+DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, check_charging_cutoff, HOOK_PRIO_DEFAULT);
- /*
- * Use discharge_on_ac() to workaround hardware backboosting
- * charge circuit problems.
- */
- int use_bkboost_workaround = (system_get_board_version() < 1);
+static void cancel_charging_cutoff(void)
+{
+ /* If charging is disabled, enable it */
+ if (charge_is_disabled) {
+ charge_is_disabled = 0;
+ host_command_pd_send_status(PD_CHARGE_5V);
+ }
+}
+DECLARE_HOOK(HOOK_CHIPSET_RESUME, cancel_charging_cutoff, HOOK_PRIO_DEFAULT);
+
+static void batt_soc_change(void)
+{
+ /* If in S0, leave charging alone */
+ if (chipset_in_state(CHIPSET_STATE_ON))
+ return;
+
+ /* Check to disable or enable charging based on batt state of charge */
+ if (!charge_is_disabled && charge_get_percent() == 100) {
+ host_command_pd_send_status(PD_CHARGE_NONE);
+ charge_is_disabled = 1;
+ } else if (charge_is_disabled && charge_get_percent() < 100) {
+ charge_is_disabled = 0;
+ host_command_pd_send_status(PD_CHARGE_5V);
+ }
+}
+DECLARE_HOOK(HOOK_BATTERY_SOC_CHANGE, batt_soc_change, HOOK_PRIO_DEFAULT);
+
+/**
+ * Enable/disable NVDC charger to control AC to system and battery.
+ */
+static void charger_disable(int disable)
+{
+ int i, rv;
+
+ for (i = 0; i < CHARGER_MODE_ATTEMPTS; i++) {
+ rv = charger_discharge_on_ac(disable);
+ if (rv == EC_SUCCESS)
+ return;
+ }
+
+ CPRINTS("Setting learn mode %d failed!", disable);
+}
+
+static void allow_max_request(void)
+{
+ int prochot_status;
+ if (charge_circuit_state == CHARGE_CIRCUIT_WEDGED) {
+ /* Read PROCHOT status register to clear it */
+ i2c_read8(I2C_PORT_CHARGER, BQ24773_ADDR,
+ BQ24773_PROCHOT_STATUS, &prochot_status);
+ charge_circuit_state = CHARGE_CIRCUIT_OK;
+ }
+ host_command_pd_send_status(PD_CHARGE_MAX);
+}
+DECLARE_DEFERRED(allow_max_request);
+
+static void extpower_board_hacks(int extpower, int extpower_prev)
+{
+ /* Cancel deferred attempt to enable max charge request */
+ hook_call_deferred(allow_max_request, -1);
/*
+ * When AC is detected, delay briefly before allowing PD
+ * to negotiate up to the max voltage to give charge circuit
+ * time to settle down. When AC goes away, set PD to only allow
+ * 5V charging for the next time AC is connected.
+ *
+ * Use NVDC charger learn mode (charger_disable()) when AC
+ * is not present to avoid backboosting when AC is plugged in.
+ *
* When in G3, PP5000 needs to be enabled to accurately sense
* CC voltage when AC is attached. When AC is disconnceted
* it needs to be off to save power.
*/
if (extpower && !extpower_prev) {
- if (use_bkboost_workaround)
- charger_discharge_on_ac(0);
-
+ /* AC connected */
+ charger_disable(0);
+ hook_call_deferred(allow_max_request, 500*MSEC);
set_pp5000_in_g3(PP5000_IN_G3_AC, 1);
} else if (extpower && extpower_prev) {
- if (use_bkboost_workaround) {
- /*
- * Glitch on AC_PRESENT, attempt to recover from
- * backboost
- */
- charger_discharge_on_ac(1);
- charger_discharge_on_ac(0);
- }
+ /*
+ * Glitch on AC_PRESENT, attempt to recover from
+ * backboost
+ */
+ host_command_pd_send_status(PD_CHARGE_NONE);
} else {
- if (use_bkboost_workaround)
- charger_discharge_on_ac(1);
+ /* AC disconnected */
+ if (!charge_is_disabled &&
+ charge_circuit_state == CHARGE_CIRCUIT_OK)
+ host_command_pd_send_status(PD_CHARGE_NONE);
+
+ charger_disable(1);
+
+ if (!charge_is_disabled &&
+ charge_circuit_state == CHARGE_CIRCUIT_OK)
+ host_command_pd_send_status(PD_CHARGE_5V);
set_pp5000_in_g3(PP5000_IN_G3_AC, 0);
}
extpower_prev = extpower;
}
+static void check_charge_wedged(void)
+{
+ int rv, prochot_status;
+
+ if (charge_circuit_state == CHARGE_CIRCUIT_OK) {
+ /* Check PROCHOT warning */
+ rv = i2c_read8(I2C_PORT_CHARGER, BQ24773_ADDR,
+ BQ24773_PROCHOT_STATUS, &prochot_status);
+ if (rv)
+ return;
+
+ /*
+ * If PROCHOT is asserted, then charge circuit is wedged, turn
+ * on learn mode and notify PD to disable charging on all ports.
+ * Note: learn mode is critical here because when in this state
+ * backboosting causes >20V on boostin even after PD disables
+ * CHARGE_EN lines.
+ */
+ if (prochot_status) {
+ host_command_pd_send_status(PD_CHARGE_NONE);
+ charge_circuit_state = CHARGE_CIRCUIT_WEDGED;
+ CPRINTS("Charge circuit wedged!");
+ }
+ } else {
+ /*
+ * Charge circuit is wedged and we already disabled charging,
+ * Now start to recover from wedged state by allowing 5V.
+ */
+ host_command_pd_send_status(PD_CHARGE_5V);
+ }
+}
+
/**
* Task to handle external power change
*/
void extpower_task(void)
{
int extpower = extpower_is_present();
- extpower_board_hacks(extpower);
+ int extpower_prev = 0;
+
+ extpower_board_hacks(extpower, extpower_prev);
/* Enable backboost detection interrupt */
gpio_enable_interrupt(GPIO_BKBOOST_DET);
while (1) {
- /* Wait until next extpower interrupt */
- task_wait_event(-1);
-
- extpower = extpower_is_present();
+ if (task_wait_event(2*SECOND) == TASK_EVENT_TIMER) {
+ /* Periodically check if charge circuit is wedged */
+ check_charge_wedged();
+ } else {
+ /* Must have received power change interrupt */
+ extpower = extpower_is_present();
- /* Various board hacks to run on extpower change */
- extpower_board_hacks(extpower);
+ /* Various board hacks to run on extpower change */
+ extpower_board_hacks(extpower, extpower_prev);
+ extpower_prev = extpower;
- hook_notify(HOOK_AC_CHANGE);
+ hook_notify(HOOK_AC_CHANGE);
- /* Forward notification to host */
- if (extpower)
- host_set_single_event(EC_HOST_EVENT_AC_CONNECTED);
- else
- host_set_single_event(EC_HOST_EVENT_AC_DISCONNECTED);
+ /* Forward notification to host */
+ host_set_single_event(extpower ?
+ EC_HOST_EVENT_AC_CONNECTED :
+ EC_HOST_EVENT_AC_DISCONNECTED);
+ }
}
}
diff --git a/board/samus_pd/board.c b/board/samus_pd/board.c
index d89527dfc2..3fd3f0e219 100644
--- a/board/samus_pd/board.c
+++ b/board/samus_pd/board.c
@@ -37,11 +37,9 @@ static enum power_state ps;
/* Battery state of charge */
static int batt_soc;
-static int fake_state_of_charge = -1; /* use real soc by default */
-/* Last charge port override when charging turned off due to full battery */
-static int chg_override_port = OVERRIDE_OFF;
-static int chg_is_cutoff;
+/* Default to 5V charging allowed for dead battery case */
+enum pd_charge_state charge_state = PD_CHARGE_5V;
/* PD MCU status and host event status for host command */
static struct ec_response_pd_status pd_status;
@@ -266,31 +264,6 @@ void usb1_evt(enum gpio_signal signal)
wake_usb_charger_task(1);
}
-/* When battery is full, cutoff charging by disabling AC input current */
-static void check_charging_cutoff(void)
-{
- int port;
-
- /* Only check if charging needs to be turned off when not in S0 */
- if (ps == POWER_S0)
- return;
-
- port = charge_manager_get_active_charge_port();
-
- /*
- * If battery is full disable charging, if battery is not full, restore
- * charge port.
- */
- if (!chg_is_cutoff && port != CHARGE_PORT_NONE && batt_soc == 100) {
- charge_manager_set_override(OVERRIDE_DONT_CHARGE);
- chg_is_cutoff = 1;
- } else if (chg_is_cutoff && batt_soc < 100) {
- charge_manager_set_override(chg_override_port);
- chg_is_cutoff = 0;
- }
-}
-DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, check_charging_cutoff, HOOK_PRIO_DEFAULT);
-
static void chipset_s5_to_s3(void)
{
ps = POWER_S3;
@@ -301,9 +274,6 @@ static void chipset_s3_to_s0(void)
{
/* Disable deep sleep and restore charge override port */
disable_sleep(SLEEP_MASK_AP_RUN);
- charge_manager_set_override(chg_override_port);
- chg_is_cutoff = 0;
-
ps = POWER_S0;
hook_notify(HOOK_CHIPSET_RESUME);
}
@@ -318,8 +288,6 @@ static void chipset_s0_to_s3(void)
{
/* Enable deep sleep and store charge override port */
enable_sleep(SLEEP_MASK_AP_RUN);
- chg_override_port = charge_manager_get_override();
-
ps = POWER_S3;
hook_notify(HOOK_CHIPSET_SUSPEND);
}
@@ -438,6 +406,9 @@ static void board_init(void)
gpio_enable_interrupt(GPIO_PCH_SLP_S3_L);
gpio_enable_interrupt(GPIO_PCH_SLP_S5_L);
+ /* Initialize active charge port to none */
+ pd_status.active_charge_port = CHARGE_PORT_NONE;
+
/*
* Do not enable PD communication in RO as a security measure.
* We don't want to allow communication to outside world until
@@ -596,7 +567,6 @@ void board_flip_usb_mux(int port)
void board_update_battery_soc(int soc)
{
batt_soc = soc;
- check_charging_cutoff();
}
int board_get_battery_soc(void)
@@ -643,26 +613,42 @@ int board_set_active_charge_port(int charge_port)
return EC_ERROR_INVAL;
}
- pd_status.active_charge_port = charge_port;
- gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, !(charge_port == 0));
- gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, !(charge_port == 1));
+ CPRINTS("New chg p%d", charge_port);
/*
- * If new charge port when charge is cutoff, then user must have
- * plugged in a new dedicated charger. This resets the charge
- * override port and clears the charge cutoff flag.
+ * If charging and the active charge port is changed, then disable
+ * charging to guarantee charge circuit starts up cleanly.
*/
- if (chg_is_cutoff && is_real_port) {
- chg_override_port = OVERRIDE_OFF;
- chg_is_cutoff = 0;
+ if (pd_status.active_charge_port != CHARGE_PORT_NONE &&
+ (charge_port == CHARGE_PORT_NONE ||
+ charge_port != pd_status.active_charge_port)) {
+ gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, 1);
+ gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, 1);
+ charge_state = PD_CHARGE_NONE;
+ pd_status.active_charge_port = charge_port;
+ CPRINTS("Chg: None\n");
+ return EC_SUCCESS;
+ }
+
+ /* Save active charge port and enable charging if allowed */
+ pd_status.active_charge_port = charge_port;
+ if (charge_state != PD_CHARGE_NONE) {
+ gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, !(charge_port == 0));
+ gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, !(charge_port == 1));
}
- check_charging_cutoff();
- CPRINTS("New chg p%d", charge_port);
return EC_SUCCESS;
}
/**
+ * Return if max voltage charging is allowed.
+ */
+int pd_is_max_request_allowed(void)
+{
+ return charge_state == PD_CHARGE_MAX;
+}
+
+/**
* Set the charge limit based upon desired maximum.
*
* @param charge_ma Desired charge limit (mA).
@@ -732,36 +718,6 @@ DECLARE_CONSOLE_COMMAND(pdevent, command_pd_host_event,
"Send PD host event",
NULL);
-static int command_battfake(int argc, char **argv)
-{
- char *e;
- int v;
-
- if (argc == 2) {
- v = strtoi(argv[1], &e, 0);
- if (*e || v < -1 || v > 100)
- return EC_ERROR_PARAM1;
-
- fake_state_of_charge = v;
- }
-
- if (fake_state_of_charge < 0) {
- ccprintf("Using real batt level\n");
- } else {
- ccprintf("Using fake batt level %d%%\n",
- fake_state_of_charge);
- }
-
- /* Send EC int to get batt info from EC */
- pd_send_ec_int();
-
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(battfake, command_battfake,
- "percent (-1 = use real level)",
- "Set fake battery level",
- NULL);
-
/****************************************************************************/
/* Host commands */
static int ec_status_host_cmd(struct host_cmd_handler_args *args)
@@ -769,9 +725,60 @@ static int ec_status_host_cmd(struct host_cmd_handler_args *args)
const struct ec_params_pd_status *p = args->params;
struct ec_response_pd_status *r = args->response;
- /* if not using fake soc, then update battery soc */
- board_update_battery_soc(fake_state_of_charge < 0 ?
- p->batt_soc : fake_state_of_charge);
+ /* update battery soc */
+ board_update_battery_soc(p->batt_soc);
+
+ if (args->version == 1) {
+ if (p->charge_state != charge_state) {
+ switch (p->charge_state) {
+ case PD_CHARGE_NONE:
+ /*
+ * No current allowed in, set new power request
+ * so that PD negotiates down to vSafe5V.
+ */
+ charge_state = p->charge_state;
+ gpio_set_level(GPIO_USB_C0_CHARGE_EN_L, 1);
+ gpio_set_level(GPIO_USB_C1_CHARGE_EN_L, 1);
+ pd_set_new_power_request(
+ pd_status.active_charge_port);
+ CPRINTS("Chg: None");
+ break;
+ case PD_CHARGE_5V:
+ /* Allow current on the active charge port */
+ charge_state = p->charge_state;
+ gpio_set_level(GPIO_USB_C0_CHARGE_EN_L,
+ !(pd_status.active_charge_port == 0));
+ gpio_set_level(GPIO_USB_C1_CHARGE_EN_L,
+ !(pd_status.active_charge_port == 1));
+ CPRINTS("Chg: 5V");
+ break;
+ case PD_CHARGE_MAX:
+ /*
+ * Allow negotiation above vSafe5V. Should only
+ * ever get this command when 5V charging is
+ * already allowed.
+ */
+ if (charge_state == PD_CHARGE_5V) {
+ charge_state = p->charge_state;
+ pd_set_new_power_request(
+ pd_status.active_charge_port);
+ CPRINTS("Chg: Max");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ /*
+ * If the EC is using this command version, then it won't ever
+ * set charging allowed, so we should just assume charging at
+ * the max is allowed.
+ */
+ charge_state = PD_CHARGE_MAX;
+ pd_set_new_power_request(pd_status.active_charge_port);
+ CPRINTS("Chg: Max");
+ }
*r = pd_status;
@@ -783,7 +790,7 @@ static int ec_status_host_cmd(struct host_cmd_handler_args *args)
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_PD_EXCHANGE_STATUS, ec_status_host_cmd,
- EC_VER_MASK(0));
+ EC_VER_MASK(0) | EC_VER_MASK(1));
static int host_event_status_host_cmd(struct host_cmd_handler_args *args)
{
diff --git a/board/samus_pd/board.h b/board/samus_pd/board.h
index 702dfcc9f5..641f78ece1 100644
--- a/board/samus_pd/board.h
+++ b/board/samus_pd/board.h
@@ -40,6 +40,7 @@
#define CONFIG_USB_POWER_DELIVERY
#define CONFIG_USB_PD_ALT_MODE
#define CONFIG_USB_PD_ALT_MODE_DFP
+#define CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED
#undef CONFIG_USB_PD_COMM_ENABLED
#define CONFIG_USB_PD_COMM_ENABLED 0
#define CONFIG_USB_PD_CUSTOM_VDM
diff --git a/common/charge_state_v2.c b/common/charge_state_v2.c
index 2487f05fe5..445819fc4e 100644
--- a/common/charge_state_v2.c
+++ b/common/charge_state_v2.c
@@ -774,9 +774,6 @@ wait_for_it:
show_charging_progress();
prev_charge = curr.batt.state_of_charge;
hook_notify(HOOK_BATTERY_SOC_CHANGE);
-#ifdef HAS_TASK_PDCMD
- host_command_pd_send_status();
-#endif
}
prev_full = is_full;
diff --git a/common/host_command_pd.c b/common/host_command_pd.c
index 5e3494c46e..e9282abee4 100644
--- a/common/host_command_pd.c
+++ b/common/host_command_pd.c
@@ -17,8 +17,15 @@
#define TASK_EVENT_EXCHANGE_PD_STATUS TASK_EVENT_CUSTOM(1)
-void host_command_pd_send_status(void)
+/* By default allow 5V charging only for the dead battery case */
+static enum pd_charge_state charge_state = PD_CHARGE_5V;
+
+void host_command_pd_send_status(enum pd_charge_state new_chg_state)
{
+ /* Update PD MCU charge state if necessary */
+ if (new_chg_state != PD_CHARGE_NO_CHANGE)
+ charge_state = new_chg_state;
+ /* Wake PD HC task to send status */
task_set_event(TASK_ID_PDCMD, TASK_EVENT_EXCHANGE_PD_STATUS, 0);
}
@@ -31,13 +38,20 @@ void pd_exchange_status(int *charge_port)
};
int rv = 0;
- /* Send battery state of charge */
+ /* Send PD charge state and battery state of charge */
+ ec_status.charge_state = charge_state;
if (charge_get_flags() & CHARGE_FLAG_BATT_RESPONSIVE)
ec_status.batt_soc = charge_get_percent();
else
ec_status.batt_soc = -1;
- rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 0, &ec_status,
+ rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 1, &ec_status,
+ sizeof(struct ec_params_pd_status), &pd_status,
+ sizeof(struct ec_response_pd_status));
+
+ /* If PD doesn't support new command version, try old version */
+ if (rv == -EC_RES_INVALID_VERSION)
+ rv = pd_host_command(EC_CMD_PD_EXCHANGE_STATUS, 0, &ec_status,
sizeof(struct ec_params_pd_status), &pd_status,
sizeof(struct ec_response_pd_status));
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 0bad095dd5..8d168e1f1c 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -917,14 +917,25 @@ static void pd_send_request_msg(int port, int always_send_request)
#else
const int charging = 1;
#endif
+
+#ifdef CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED
+ int max_request_allowed = pd_is_max_request_allowed();
+#else
+ const int max_request_allowed = 1;
+#endif
+
/* Clear new power request */
pd[port].new_power_request = 0;
/* Build and send request RDO */
- /* If this port is not actively charging, select vSafe5V */
+ /*
+ * If this port is not actively charging or we are not allowed to
+ * request the max voltage, then select vSafe5V
+ */
res = pd_build_request(pd_src_cap_cnt[port], pd_src_caps[port],
&rdo, &curr_limit, &supply_voltage,
- charging ? PD_REQUEST_MAX : PD_REQUEST_VSAFE5V);
+ charging && max_request_allowed ?
+ PD_REQUEST_MAX : PD_REQUEST_VSAFE5V);
if (res != EC_SUCCESS)
/*
diff --git a/driver/charger/bq24773.c b/driver/charger/bq24773.c
index fc5e7c688c..becef6d378 100644
--- a/driver/charger/bq24773.c
+++ b/driver/charger/bq24773.c
@@ -184,9 +184,22 @@ int charger_post_init(void)
if (rv)
return rv;
+#ifndef BOARD_SAMUS
/* Turn off PROCHOT warning */
rv = i2c_write8(I2C_PORT_CHARGER, BQ24773_ADDR,
BQ24773_PROCHOT_OPTION1, 0);
+#else
+ /* On Samus, use PROCHOT warning to detect charging problems */
+ /* Turn on PROCHOT warning */
+ rv = i2c_write16(I2C_PORT_CHARGER, BQ24773_ADDR,
+ BQ24773_PROCHOT_OPTION1, 0x8120);
+ /* Set PROCHOT ICRIT warning when IADP is >120% of IDPM */
+ rv |= i2c_write16(I2C_PORT_CHARGER, BQ24773_ADDR,
+ BQ24773_PROCHOT_OPTION0, 0x1b54);
+#endif
+
+ if (rv)
+ return rv;
#ifdef CONFIG_CHARGER_ILIM_PIN_DISABLED
/* Read the external ILIM pin enabled flag. */
diff --git a/include/config.h b/include/config.h
index 435a8889d0..e3983eb064 100644
--- a/include/config.h
+++ b/include/config.h
@@ -1157,18 +1157,18 @@
/* Include all USB Power Delivery modules */
#undef CONFIG_USB_POWER_DELIVERY
-/* Alternative configuration keeping only the TX part of PHY */
-#undef CONFIG_USB_PD_TX_PHY_ONLY
-
-/* Default state of PD communication enabled flag */
-#define CONFIG_USB_PD_COMM_ENABLED 1
-
/* Support for USB PD alternate mode */
#undef CONFIG_USB_PD_ALT_MODE
/* Support for USB PD alternate mode of Downward Facing Port */
#undef CONFIG_USB_PD_ALT_MODE_DFP
+/* Check if max voltage request is allowed before each request */
+#undef CONIFG_USB_PD_CHECK_MAX_REQUEST_ALLOWED
+
+/* Default state of PD communication enabled flag */
+#define CONFIG_USB_PD_COMM_ENABLED 1
+
/* Respond to custom vendor-defined messages over PD */
#undef CONFIG_USB_PD_CUSTOM_VDM
@@ -1206,6 +1206,9 @@
/* Use comparator module for PD RX interrupt */
#define CONFIG_USB_PD_RX_COMP_IRQ
+/* Alternative configuration keeping only the TX part of PHY */
+#undef CONFIG_USB_PD_TX_PHY_ONLY
+
/* Support for USB type-c superspeed mux */
#undef CONFIG_USBC_SS_MUX
diff --git a/include/ec_commands.h b/include/ec_commands.h
index f42e525c4a..304cb9acda 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -2680,9 +2680,17 @@ struct ec_params_reboot_ec {
/* EC to PD MCU exchange status command */
#define EC_CMD_PD_EXCHANGE_STATUS 0x100
+enum pd_charge_state {
+ PD_CHARGE_NO_CHANGE = 0, /* Don't change charge state */
+ PD_CHARGE_NONE, /* No charging allowed */
+ PD_CHARGE_5V, /* 5V charging only */
+ PD_CHARGE_MAX /* Charge at max voltage */
+};
+
/* Status of EC being sent to PD */
struct ec_params_pd_status {
- int8_t batt_soc; /* battery state of charge */
+ int8_t batt_soc; /* battery state of charge */
+ uint8_t charge_state; /* charging state (from enum pd_charge_state) */
} __packed;
/* Status of PD being sent back to EC */
diff --git a/include/host_command.h b/include/host_command.h
index e833b96101..4661e59dd6 100644
--- a/include/host_command.h
+++ b/include/host_command.h
@@ -210,8 +210,10 @@ void host_throttle_cpu(int throttle);
/**
* Signal host command task to send status to PD MCU.
+ *
+ * @new_chg_state PD MCU charge state
*/
-void host_command_pd_send_status(void);
+void host_command_pd_send_status(enum pd_charge_state new_chg_state);
/**
* Ask the PD MCU for its status, obtaining the current charge_port as a
diff --git a/include/usb_pd.h b/include/usb_pd.h
index 543b939682..410251775f 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -759,6 +759,14 @@ int pd_build_request(int cnt, uint32_t *src_caps, uint32_t *rdo,
uint32_t *ma, uint32_t *mv, enum pd_request_type req_type);
/**
+ * Check if max voltage request is allowed (only used if
+ * CONFIG_USB_PD_CHECK_MAX_REQUEST_ALLOWED is defined).
+ *
+ * @return True if max voltage request allowed, False otherwise
+ */
+int pd_is_max_request_allowed(void);
+
+/**
* Process source capabilities packet
*
* @param port USB-C port number