diff options
-rw-r--r-- | driver/usb_mux/anx3443.c | 74 | ||||
-rw-r--r-- | driver/usb_mux/anx3443.h | 5 |
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 |