summaryrefslogtreecommitdiff
path: root/driver/usb_mux
diff options
context:
space:
mode:
authorTing Shen <phoenixshen@google.com>2021-07-02 13:08:50 +0800
committerCommit Bot <commit-bot@chromium.org>2021-07-07 04:57:04 +0000
commit7dd1f09fab19c2505c420df1339bd57400e10da1 (patch)
tree91d868f4415cd0725376beb20002c77bf298c72c /driver/usb_mux
parent9c219a90250c3899ea649164a18544d5d24577a3 (diff)
downloadchrome-ec-7dd1f09fab19c2505c420df1339bd57400e10da1.tar.gz
anx3443: enable power saving mode
Ultra low power mode is not low enough, vendor recommends that power saving mode is more suitable for us. BUG=b:192426163 TEST=measure power BRANCH=main Signed-off-by: Ting Shen <phoenixshen@google.com> Change-Id: I788e2ed3b2a9deef5ed1dae4061ec5536783ad33 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3002707 Commit-Queue: Ting Shen <phoenixshen@chromium.org> Tested-by: Ting Shen <phoenixshen@chromium.org> Reviewed-by: Eric Yilun Lin <yllin@google.com>
Diffstat (limited to 'driver/usb_mux')
-rw-r--r--driver/usb_mux/anx3443.c90
-rw-r--r--driver/usb_mux/anx3443.h4
2 files changed, 72 insertions, 22 deletions
diff --git a/driver/usb_mux/anx3443.c b/driver/usb_mux/anx3443.c
index 27eb731ce6..dc7fd3a3c0 100644
--- a/driver/usb_mux/anx3443.c
+++ b/driver/usb_mux/anx3443.c
@@ -7,6 +7,7 @@
*/
#include "anx3443.h"
+#include "chipset.h"
#include "common.h"
#include "console.h"
#include "i2c.h"
@@ -14,6 +15,13 @@
#include "usb_mux.h"
#include "util.h"
+/*
+ * Empirical testing found it takes ~12ms to wake mux.
+ * Setting timeout to 20ms for some buffer.
+ */
+#define ANX3443_I2C_WAKE_TIMEOUT_MS 20
+#define ANX3443_I2C_WAKE_RETRY_DELAY_US 500
+
#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
@@ -29,24 +37,66 @@ static inline int anx3443_write(const struct usb_mux *me,
return i2c_write8(me->i2c_port, me->i2c_addr_flags, reg, val);
}
+static int anx3443_power_off(const struct usb_mux *me)
+{
+ /*
+ * 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);
+ return EC_SUCCESS;
+}
+
+static int anx3443_wake_up(const struct usb_mux *me)
+{
+ timestamp_t start;
+ 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 {
+ rv = anx3443_read(me, 0x0, &val);
+ if (!rv)
+ break;
+ usleep(ANX3443_I2C_WAKE_RETRY_DELAY_US);
+ } while (time_since32(start) < ANX3443_I2C_WAKE_TIMEOUT_MS * MSEC);
+ if (rv) {
+ CPRINTS("ANX3443: Failed to wake mux rv:%d", rv);
+ return EC_ERROR_TIMEOUT;
+ }
+
+ /* 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));
+
+ return EC_SUCCESS;
+}
+
static int anx3443_set_mux(const struct usb_mux *me, mux_state_t mux_state)
{
int reg;
+ /* 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;
+
+ /* 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);
+
+ RETURN_ERROR(anx3443_wake_up(me));
+
/* ULP_CFG_MODE_EN overrides pin control. Always set it */
reg = ANX3443_ULP_CFG_MODE_EN;
-
- /*
- * NOTE: This mux does not have a "none" state, so USB_PD_MUX_NONE
- * is mapped to ANX3443_ULP_CFG_MODE_USB_EN here.
- */
- if ((mux_state & USB_PD_MUX_DOCK) == USB_PD_MUX_DOCK)
- reg |= ANX3443_ULP_CFG_MODE_USB_EN | ANX3443_ULP_CFG_MODE_DP_EN;
- else if (mux_state & USB_PD_MUX_DP_ENABLED)
- reg |= ANX3443_ULP_CFG_MODE_DP_EN;
- else
+ if (mux_state & USB_PD_MUX_USB_ENABLED)
reg |= ANX3443_ULP_CFG_MODE_USB_EN;
-
+ if (mux_state & USB_PD_MUX_DP_ENABLED)
+ reg |= ANX3443_ULP_CFG_MODE_DP_EN;
if (mux_state & USB_PD_MUX_POLARITY_INVERTED)
reg |= ANX3443_ULP_CFG_MODE_FLIP;
@@ -57,6 +107,12 @@ static int anx3443_get_mux(const struct usb_mux *me, mux_state_t *mux_state)
{
int reg;
+ /* Mux is not powered in Z1 */
+ if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
+ return USB_PD_MUX_NONE;
+
+ RETURN_ERROR(anx3443_wake_up(me));
+
*mux_state = 0;
RETURN_ERROR(anx3443_read(me, ANX3443_REG_ULP_CFG_MODE, &reg));
@@ -82,9 +138,7 @@ static int anx3443_init(const struct usb_mux *me)
if (now < ANX3443_I2C_READY_DELAY)
usleep(ANX3443_I2C_READY_DELAY - now);
- /* Disable ultra-low power mode */
- RETURN_ERROR(anx3443_write(me, ANX3443_REG_ULTRA_LOW_POWER,
- ANX3443_ULTRA_LOW_POWER_DIS));
+ RETURN_ERROR(anx3443_wake_up(me));
/* Default to USB mode */
RETURN_ERROR(anx3443_set_mux(me, USB_PD_MUX_USB_ENABLED));
@@ -92,16 +146,8 @@ static int anx3443_init(const struct usb_mux *me)
return EC_SUCCESS;
}
-static int anx3443_enter_low_power_mode(const struct usb_mux *me)
-{
- /* Enable vendor-defined ultra-low power mode */
- return anx3443_write(me, ANX3443_REG_ULTRA_LOW_POWER,
- ANX3443_ULTRA_LOW_POWER_EN);
-}
-
const struct usb_mux_driver anx3443_usb_mux_driver = {
.init = anx3443_init,
.set = anx3443_set_mux,
.get = anx3443_get_mux,
- .enter_low_power_mode = anx3443_enter_low_power_mode,
};
diff --git a/driver/usb_mux/anx3443.h b/driver/usb_mux/anx3443.h
index e60ffe2e25..b505f993ea 100644
--- a/driver/usb_mux/anx3443.h
+++ b/driver/usb_mux/anx3443.h
@@ -17,6 +17,10 @@
#define ANX3443_I2C_ADDR2_FLAGS 0x16
#define ANX3443_I2C_ADDR3_FLAGS 0x11
+/* This register is not documented in datasheet. */
+#define ANX3443_REG_POWER_CNTRL 0x2B
+#define ANX3443_POWER_CNTRL_OFF 0xFF
+
/* Ultra low power control register */
#define ANX3443_REG_ULTRA_LOW_POWER 0xE6