diff options
Diffstat (limited to 'driver/retimer')
-rw-r--r-- | driver/retimer/anx7452.c | 164 | ||||
-rw-r--r-- | driver/retimer/anx7452.h | 76 |
2 files changed, 240 insertions, 0 deletions
diff --git a/driver/retimer/anx7452.c b/driver/retimer/anx7452.c new file mode 100644 index 0000000000..4693c0d5bb --- /dev/null +++ b/driver/retimer/anx7452.c @@ -0,0 +1,164 @@ +/* Copyright 2023 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * ANX7452: Active redriver with linear equalisation + */ + +#include "anx7452.h" +#include "chipset.h" +#include "common.h" +#include "console.h" +#include "gpio.h" +#include "i2c.h" +#include "retimer/anx7452_public.h" +#include "timer.h" +#include "usb_mux.h" +#include "util.h" + +#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ##args) +#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ##args) + +static int anx7452_read(const struct usb_mux *me, uint8_t reg, int *val) +{ + return i2c_read8(me->i2c_port, me->i2c_addr_flags, reg, val); +} + +static int anx7452_write(const struct usb_mux *me, uint8_t reg, uint8_t val) +{ + return i2c_write8(me->i2c_port, me->i2c_addr_flags, reg, val); +} + +static int anx7452_ctltop_update(const struct usb_mux *me, uint8_t reg, + uint8_t mask, uint8_t val) +{ + int reg_val = 0; + int rv; + + RETURN_ERROR(i2c_read8(me->i2c_port, ANX7452_I2C_ADDR_CTLTOP_FLAGS, reg, + ®_val)); + reg_val = (reg_val & ~mask) | (val & mask); + rv = i2c_write8(me->i2c_port, ANX7452_I2C_ADDR_CTLTOP_FLAGS, reg, + reg_val); + if (rv) { + CPRINTS("ANX7452: Failed to write to ctltop register %x rv:%d", + reg, rv); + return EC_ERROR_TIMEOUT; + } + return EC_SUCCESS; +} + +static int anx7452_ctltop_update_all(const struct usb_mux *me, uint8_t cfg0_val, + uint8_t cfg1_val, uint8_t cfg2_val) +{ + RETURN_ERROR(anx7452_ctltop_update(me, ANX7452_CTLTOP_CFG0_REG, + ANX7452_CTLTOP_CFG0_REG_BIT_MASK, + cfg0_val)); + RETURN_ERROR(anx7452_ctltop_update(me, ANX7452_CTLTOP_CFG1_REG, + ANX7452_CTLTOP_CFG1_REG_BIT_MASK, + cfg1_val)); + RETURN_ERROR(anx7452_ctltop_update(me, ANX7452_CTLTOP_CFG2_REG, + ANX7452_CTLTOP_CFG2_REG_BIT_MASK, + cfg2_val)); + + return EC_SUCCESS; +} + +static int anx7452_init(const struct usb_mux *me) +{ + int usb_enable; + timestamp_t start; + int val; + int rv; + + usb_enable = anx7452_controls[me->usb_port].usb_enable_gpio; + gpio_set_level(usb_enable, 1); + + /* Keep reading control register until mux wakes up or times out */ + start = get_time(); + do { + rv = anx7452_read(me, ANX7452_TOP_STATUS_REG, &val); + if (!rv) + break; + usleep(ANX7452_I2C_WAKE_RETRY_DELAY_US); + } while (time_since32(start) < ANX7452_I2C_WAKE_TIMEOUT_MS * MSEC); + if (rv) { + CPRINTS("ANX7452: Failed to wake mux rv:%d", rv); + return EC_ERROR_TIMEOUT; + } + + /* Configure for i2c control */ + val = ANX7452_TOP_REG_EN; + RETURN_ERROR(anx7452_write(me, ANX7452_TOP_STATUS_REG, val)); + + return EC_SUCCESS; +} + +static int anx7452_set(const struct usb_mux *me, mux_state_t mux_state, + bool *ack_required) +{ + int cfg0_val = 0; + int cfg1_val = 0; + int cfg2_val = 0; + + /* This driver does not use host command ACKs */ + *ack_required = false; + + /* Apply CC polarity settings */ + if (mux_state & USB_PD_MUX_POLARITY_INVERTED) { + cfg0_val |= ANX7452_CTLTOP_CFG0_FLIP_EN; + } + + /* Apply DP enable settings */ + if (mux_state & USB_PD_MUX_DP_ENABLED) { + cfg1_val |= ANX7452_CTLTOP_CFG1_DP_EN; + } + + /* Apply USB3 enable settings */ + if (mux_state & USB_PD_MUX_USB_ENABLED) { + cfg0_val |= ANX7452_CTLTOP_CFG0_USB3_EN; + } + + /* Apply USB4 enable settings */ + if (mux_state & USB_PD_MUX_USB4_ENABLED) { + cfg2_val |= ANX7452_CTLTOP_CFG2_USB4_EN; + } + + /* Apply TBT compatible enable settings */ + if (mux_state & USB_PD_MUX_TBT_COMPAT_ENABLED) { + cfg2_val |= ANX7452_CTLTOP_CFG2_TBT_EN; + } + + return anx7452_ctltop_update_all(me, cfg0_val, cfg1_val, cfg2_val); +} + +static int anx7452_get(const struct usb_mux *me, mux_state_t *mux_state) +{ + int reg = 0; + + *mux_state = 0; + RETURN_ERROR(anx7452_read(me, ANX7452_TOP_STATUS_REG, ®)); + if (reg & ANX7452_TOP_FLIP_INFO) { + *mux_state |= USB_PD_MUX_POLARITY_INVERTED; + } + if (reg & ANX7452_TOP_DP_INFO) { + *mux_state |= USB_PD_MUX_DP_ENABLED; + } + if (reg & ANX7452_TOP_TBT_INFO) { + *mux_state |= USB_PD_MUX_TBT_COMPAT_ENABLED; + } + if (reg & ANX7452_TOP_USB3_INFO) { + *mux_state |= USB_PD_MUX_USB_ENABLED; + } + if (reg & ANX7452_TOP_USB4_INFO) { + *mux_state |= USB_PD_MUX_USB4_ENABLED; + } + + return EC_SUCCESS; +} + +const struct usb_mux_driver anx7452_usb_retimer_driver = { + .init = anx7452_init, + .set = anx7452_set, + .get = anx7452_get, +}; diff --git a/driver/retimer/anx7452.h b/driver/retimer/anx7452.h new file mode 100644 index 0000000000..e0a3878500 --- /dev/null +++ b/driver/retimer/anx7452.h @@ -0,0 +1,76 @@ +/* Copyright 2023 The ChromiumOS Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * ANX7452: 2-Lane USB4 Retimer MUX driver + */ + +#ifndef __CROS_EC_USB_RETIMER_ANX7452_H +#define __CROS_EC_USB_RETIMER_ANX7452_H + +/* + * Programming guide specifies it may be as much as 30-50 ms after chip power on + * before it's ready for i2c + */ +#define ANX7452_I2C_WAKE_TIMEOUT_MS 30 +#define ANX7452_I2C_WAKE_RETRY_DELAY_US 3000 + +/* + * CTLTOP I2C address (7 bit) + */ +#define ANX7452_I2C_ADDR_CTLTOP_FLAGS 0x20 + +/* + * TOP Status register + * + * 7 EN (0: Config info from pins, 1: Config info from registers) + * 6 Reserved + * 5 SWAP (0: host side, 1: device side) + * 4 FLIP info (Read only use) + * 3 USB4 info (Read only use) + * 2 TBT info (Read only use) + * 1 DP info (Read only use) + * 0 USB3 info (Read only use) + */ +#define ANX7452_TOP_STATUS_REG 0xF8 +#define ANX7452_TOP_REG_EN BIT(7) +#define ANX7452_TOP_SWAP_EN BIT(5) +#define ANX7452_TOP_FLIP_INFO BIT(4) +#define ANX7452_TOP_USB4_INFO BIT(3) +#define ANX7452_TOP_TBT_INFO BIT(2) +#define ANX7452_TOP_DP_INFO BIT(1) +#define ANX7452_TOP_USB3_INFO BIT(0) + +/* + * CTLTOP - 0 register + * + * 5 USB3 info (To set Bit 0 of TOP Status register indirectly) + * 1 FLIP info (To set BIT 4 of TOP Status register indirectly) + */ +#define ANX7452_CTLTOP_CFG0_REG 0x04 +#define ANX7452_CTLTOP_CFG0_USB3_EN BIT(5) +#define ANX7452_CTLTOP_CFG0_FLIP_EN BIT(1) +#define ANX7452_CTLTOP_CFG0_REG_BIT_MASK \ + (ANX7452_CTLTOP_CFG0_USB3_EN | ANX7452_CTLTOP_CFG0_FLIP_EN) + +/* + * CTLTOP - 1 register + * + * 0 DP info (To set Bit 1 of TOP Status register indirectly) + */ +#define ANX7452_CTLTOP_CFG1_REG 0x05 +#define ANX7452_CTLTOP_CFG1_DP_EN BIT(0) +#define ANX7452_CTLTOP_CFG1_REG_BIT_MASK ANX7452_CTLTOP_CFG1_DP_EN + +/* + * CTLTOP - 2 register + * + * 7 USB4 info (To set Bit 3 of TOP Status register indirectly) + * 0 TBT info (To set BIT 2 of TOP Status register indirectly) + */ +#define ANX7452_CTLTOP_CFG2_REG 0x06 +#define ANX7452_CTLTOP_CFG2_USB4_EN BIT(7) +#define ANX7452_CTLTOP_CFG2_TBT_EN BIT(0) +#define ANX7452_CTLTOP_CFG2_REG_BIT_MASK \ + (ANX7452_CTLTOP_CFG2_USB4_EN | ANX7452_CTLTOP_CFG2_TBT_EN) +#endif /* __CROS_EC_USB_RETIMER_ANX7452_H */ |