diff options
Diffstat (limited to 'driver')
-rw-r--r-- | driver/usb_mux/anx7451.c | 62 | ||||
-rw-r--r-- | driver/usb_mux/anx7451.h | 4 |
2 files changed, 56 insertions, 10 deletions
diff --git a/driver/usb_mux/anx7451.c b/driver/usb_mux/anx7451.c index 38d56e8fcb..c46a78acf8 100644 --- a/driver/usb_mux/anx7451.c +++ b/driver/usb_mux/anx7451.c @@ -15,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 ANX7451_I2C_WAKE_TIMEOUT_MS 20 +#define ANX7451_I2C_WAKE_RETRY_DELAY_US 500 + #define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) #define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args) @@ -30,6 +37,45 @@ static inline int anx7451_write(const struct usb_mux *me, return i2c_write8(me->i2c_port, me->i2c_addr_flags, reg, val); } +static int anx7451_power_off(const struct usb_mux *me) +{ + /* + * The mux will not send an acknowledgment when powered off, so ignore + * response and always return success. + */ + anx7451_write(me, ANX7451_REG_POWER_CNTRL, ANX7451_POWER_CNTRL_OFF); + return EC_SUCCESS; +} + +static int anx7451_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 = anx7451_read(me, 0x0, &val); + if (!rv) + break; + usleep(ANX7451_I2C_WAKE_RETRY_DELAY_US); + } while (time_since32(start) < ANX7451_I2C_WAKE_TIMEOUT_MS * MSEC); + if (rv) { + CPRINTS("ANX7451: Failed to wake mux rv:%d", rv); + return EC_ERROR_TIMEOUT; + } + + /* ULTRA_LOW_POWER must always be disabled (Fig 2-2) */ + RETURN_ERROR(anx7451_write(me, ANX7451_REG_ULTRA_LOW_POWER, + ANX7451_ULTRA_LOW_POWER_DIS)); + + return EC_SUCCESS; +} + static int anx7451_set_mux(const struct usb_mux *me, mux_state_t mux_state) { int reg; @@ -39,17 +85,11 @@ static int anx7451_set_mux(const struct usb_mux *me, mux_state_t mux_state) return (mux_state == USB_PD_MUX_NONE) ? EC_SUCCESS : EC_ERROR_NOT_POWERED; - /* ULTRA_LOW_POWER must always be disabled (Fig 2-2) */ - RETURN_ERROR(anx7451_write(me, ANX7451_REG_ULTRA_LOW_POWER, - ANX7451_ULTRA_LOW_POWER_DIS)); + /* 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 anx7451_power_off(me); - - /* b/184907521: If both DP and USB disabled, mux will fail */ - if (!(mux_state & (USB_PD_MUX_USB_ENABLED | USB_PD_MUX_DP_ENABLED))) { - CPRINTS("ANX7451 requires USB or DP to be set, " - "forcing USB enabled"); - mux_state |= USB_PD_MUX_USB_ENABLED; - } + RETURN_ERROR(anx7451_wake_up(me)); /* ULP_CFG_MODE_EN overrides pin control. Always set it */ reg = ANX7451_ULP_CFG_MODE_EN; @@ -71,6 +111,8 @@ static int anx7451_get_mux(const struct usb_mux *me, mux_state_t *mux_state) if (chipset_in_state(CHIPSET_STATE_HARD_OFF)) return USB_PD_MUX_NONE; + RETURN_ERROR(anx7451_wake_up(me)); + *mux_state = 0; RETURN_ERROR(anx7451_read(me, ANX7451_REG_ULP_CFG_MODE, ®)); diff --git a/driver/usb_mux/anx7451.h b/driver/usb_mux/anx7451.h index c8d302f5c9..cf7a701f6c 100644 --- a/driver/usb_mux/anx7451.h +++ b/driver/usb_mux/anx7451.h @@ -15,6 +15,10 @@ #define ANX7451_I2C_ADDR2_FLAGS 0x16 #define ANX7451_I2C_ADDR3_FLAGS 0x11 +/* This register is not documented in datasheet. */ +#define ANX7451_REG_POWER_CNTRL 0x2B +#define ANX7451_POWER_CNTRL_OFF 0xFF + /* * Ultra low power control register. * On ANX7451, this register should always be 0 (disabled). |