summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTing Shen <phoenixshen@google.com>2022-01-17 17:06:43 +0800
committerCommit Bot <commit-bot@chromium.org>2022-01-21 14:21:46 +0000
commita1ef248358601d2e994e41692f11fd2bebe6f24c (patch)
tree1f039215113ba6609fdf7078b0768ec73499ed5c
parente3f58ee21296a509e26355ca8e0dbad1231f4951 (diff)
downloadchrome-ec-a1ef248358601d2e994e41692f11fd2bebe6f24c.tar.gz
anx3443: power down in S3 if the mux is in usb2 only mode
To save S3 power consumption when USB2 device (e.g. Gnubby) attached, add a mechanism to detect if the device is USB2 or USB3. If it's USB2 only, cache the mux state when entering S3, and restore when back to S0. Also removed the "Mux is not powered in Z1" related code since it's not true. BUG=b:209533566 TEST=see b:209533566 comment8 BRANCH=cherry Signed-off-by: Ting Shen <phoenixshen@google.com> Change-Id: Ib05dfe7868e57d3507c95f0020f94fd352d0034b Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3394666 Reviewed-by: Eric Yilun Lin <yllin@google.com> Commit-Queue: Ting Shen <phoenixshen@chromium.org> Tested-by: Ting Shen <phoenixshen@chromium.org>
-rw-r--r--driver/usb_mux/anx3443.c74
-rw-r--r--driver/usb_mux/anx3443.h5
2 files changed, 72 insertions, 7 deletions
diff --git a/driver/usb_mux/anx3443.c b/driver/usb_mux/anx3443.c
index 2e57f4d30c..c7158b645c 100644
--- a/driver/usb_mux/anx3443.c
+++ b/driver/usb_mux/anx3443.c
@@ -10,6 +10,7 @@
#include "chipset.h"
#include "common.h"
#include "console.h"
+#include "hooks.h"
#include "i2c.h"
#include "time.h"
#include "usb_mux.h"
@@ -25,6 +26,11 @@
#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
+static struct {
+ mux_state_t mux_state;
+ bool awake;
+} saved_mux_state[CONFIG_USB_PD_PORT_MAX_COUNT];
+
static inline int anx3443_read(const struct usb_mux *me,
uint8_t reg, int *val)
{
@@ -39,11 +45,20 @@ static inline int anx3443_write(const struct usb_mux *me,
static int anx3443_power_off(const struct usb_mux *me)
{
+ /**
+ * No-op if the mux is already down.
+ *
+ * Writing or reading any register wakes the mux up.
+ */
+ if (!saved_mux_state[me->usb_port].awake)
+ return EC_SUCCESS;
+
/*
* The mux will not send an acknowledgment when powered off, so ignore
* response and always return success.
*/
anx3443_write(me, ANX3443_REG_POWER_CNTRL, ANX3443_POWER_CNTRL_OFF);
+ saved_mux_state[me->usb_port].awake = false;
return EC_SUCCESS;
}
@@ -53,9 +68,6 @@ static int anx3443_wake_up(const struct usb_mux *me)
int rv;
int val;
- if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
- return EC_ERROR_NOT_POWERED;
-
/* Keep reading top register until mux wakes up or timesout */
start = get_time();
do {
@@ -72,6 +84,7 @@ static int anx3443_wake_up(const struct usb_mux *me)
/* ULTRA_LOW_POWER must always be disabled (Fig 2-2) */
RETURN_ERROR(anx3443_write(me, ANX3443_REG_ULTRA_LOW_POWER,
ANX3443_ULTRA_LOW_POWER_DIS));
+ saved_mux_state[me->usb_port].awake = true;
return EC_SUCCESS;
}
@@ -84,15 +97,19 @@ static int anx3443_set_mux(const struct usb_mux *me, mux_state_t mux_state,
/* This driver does not use host command ACKs */
*ack_required = false;
- /* Mux is not powered in Z1 */
- if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
- return (mux_state == USB_PD_MUX_NONE) ? EC_SUCCESS
- : EC_ERROR_NOT_POWERED;
+ saved_mux_state[me->usb_port].mux_state = mux_state;
/* To disable both DP and USB the mux must be powered off. */
if (!(mux_state & (USB_PD_MUX_USB_ENABLED | USB_PD_MUX_DP_ENABLED)))
return anx3443_power_off(me);
+ /**
+ * If the request state is not NONE, process it after we back to
+ * S0.
+ */
+ if (chipset_in_state(CHIPSET_STATE_ANY_SUSPEND))
+ return EC_SUCCESS;
+
RETURN_ERROR(anx3443_wake_up(me));
/* ULP_CFG_MODE_EN overrides pin control. Always set it */
@@ -160,3 +177,46 @@ const struct usb_mux_driver anx3443_usb_mux_driver = {
.set = anx3443_set_mux,
.get = anx3443_get_mux,
};
+
+static bool anx3443_port_is_usb2_only(const struct usb_mux *me)
+{
+ int val;
+ int port = me->usb_port;
+
+ if (!(saved_mux_state[port].mux_state & USB_PD_MUX_USB_ENABLED))
+ return false;
+
+ if (anx3443_read(me, ANX3443_REG_USB_STATUS, &val))
+ return false;
+
+ return !(val & ANX3443_UP_EN_RTERM_ST);
+}
+
+static void anx3443_suspend(void)
+{
+ for (int i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ const struct usb_mux *mux = &usb_muxes[i];
+
+ if (mux->driver != &anx3443_usb_mux_driver)
+ continue;
+
+ if (anx3443_port_is_usb2_only(mux))
+ anx3443_power_off(mux);
+ }
+}
+DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, anx3443_suspend, HOOK_PRIO_DEFAULT);
+
+static void anx3443_resume(void)
+{
+ for (int i = 0; i < CONFIG_USB_PD_PORT_MAX_COUNT; i++) {
+ int port = usb_muxes[i].usb_port;
+ bool ack_required;
+
+ if (usb_muxes[i].driver != &anx3443_usb_mux_driver)
+ continue;
+
+ anx3443_set_mux(&usb_muxes[i], saved_mux_state[port].mux_state,
+ &ack_required);
+ }
+}
+DECLARE_HOOK(HOOK_CHIPSET_RESUME, anx3443_resume, HOOK_PRIO_DEFAULT);
diff --git a/driver/usb_mux/anx3443.h b/driver/usb_mux/anx3443.h
index b505f993ea..a8e84d5e5e 100644
--- a/driver/usb_mux/anx3443.h
+++ b/driver/usb_mux/anx3443.h
@@ -21,6 +21,11 @@
#define ANX3443_REG_POWER_CNTRL 0x2B
#define ANX3443_POWER_CNTRL_OFF 0xFF
+#define ANX3443_REG_USB_STATUS 0xD7
+/* status of downstream RX term */
+#define ANX3443_DN_EN_RTERM_ST BIT(7)
+/* status of upstream RX term */
+#define ANX3443_UP_EN_RTERM_ST BIT(6)
/* Ultra low power control register */
#define ANX3443_REG_ULTRA_LOW_POWER 0xE6