From a0d6b40c49c7615cddded5e6811a550468eba7b2 Mon Sep 17 00:00:00 2001 From: Denis Brockus Date: Wed, 29 Jan 2020 09:30:33 -0700 Subject: ps8802: fix redriver configuration in driver Added software IN_HPD control Added compile time optional debug in board specific tune function in usb_retimer Added gain control Added display lane control NOTE: PS8802 has reserved register bits that are being used internally, so be cautious just hitting these with 0, i.e. use field update to set a value to retain the old reserved fields BUG=b:146394157 BRANCH=none TEST=verify USB-C1 DP and USB connections Change-Id: I0b539df15fade509058492d6ab73a7b3ca9181df Signed-off-by: Denis Brockus Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2031646 Reviewed-by: Edward Hill --- baseboard/zork/baseboard.c | 59 ++++++++++++++++++ driver/retimer/ps8802.c | 145 +++++++++++++++++++++++++++++++++++++++++---- driver/retimer/ps8802.h | 88 +++++++++++++++++++++++---- 3 files changed, 269 insertions(+), 23 deletions(-) diff --git a/baseboard/zork/baseboard.c b/baseboard/zork/baseboard.c index ef55d12138..8f2a324185 100644 --- a/baseboard/zork/baseboard.c +++ b/baseboard/zork/baseboard.c @@ -466,6 +466,64 @@ void bc12_interrupt(enum gpio_signal signal) * Custom Zork USB-C1 Retimer/MUX driver */ +/* + * PS8802 set mux tuning. + * Adds in board specific gain and DP lane count configuration + */ +static int ps8802_tune_mux(int port, mux_state_t mux_state) +{ + int rv = EC_SUCCESS; + + /* USB specific config */ + if (mux_state & USB_PD_MUX_USB_ENABLED) { + /* Boost the USB gain */ + rv = ps8802_i2c_field_update16(port, + PS8802_REG_PAGE2, + PS8802_REG2_USB_SSEQ_LEVEL, + PS8802_USBEQ_LEVEL_UP_MASK, + PS8802_USBEQ_LEVEL_UP_20DB); + if (rv) + return rv; + + rv = ps8802_i2c_field_update16(port, + PS8802_REG_PAGE2, + PS8802_REG2_USB_CEQ_LEVEL, + PS8802_USBEQ_LEVEL_UP_MASK, + PS8802_USBEQ_LEVEL_UP_20DB); + if (rv) + return rv; + } + + /* DP specific config */ + if (mux_state & USB_PD_MUX_DP_ENABLED) { + int val; + + /* Boost the DP gain */ + rv = ps8802_i2c_field_update8(port, + PS8802_REG_PAGE2, + PS8802_REG2_DPEQ_LEVEL, + PS8802_DPEQ_LEVEL_UP_MASK, + PS8802_DPEQ_LEVEL_UP_20DB); + if (rv) + return rv; + + /* Set DP lane count */ + val = (mux_state & USB_PD_MUX_USB_ENABLED) + ? PS8802_LANE_COUNT_SET_2_LANE + : PS8802_LANE_COUNT_SET_4_LANE; + + rv = ps8802_i2c_field_update8(port, + PS8802_REG_PAGE1, + PS8802_REG1_LANE_COUNT_SET, + PS8802_LANE_COUNT_SET_MASK, + val); + if (rv) + return rv; + } + + return rv; +} + /* * PS8818 set mux tuning. * Adds in board specific gain and DP lane count configuration @@ -641,6 +699,7 @@ static int zork_c1_detect(int port, int err_if_power_off) /* Main MUX is PS8802, secondary MUX is modified FP5 */ usb_muxes[USBC_PORT_C1].driver = &ps8802_usb_mux_driver; usb_retimers[USBC_PORT_C1].driver = &zork_c1_usb_retimer; + usb_retimers[USBC_PORT_C1].tune = &ps8802_tune_mux; } return rv; diff --git a/driver/retimer/ps8802.c b/driver/retimer/ps8802.c index cb8b179bc0..6c7f2b5244 100644 --- a/driver/retimer/ps8802.c +++ b/driver/retimer/ps8802.c @@ -13,22 +13,84 @@ #include "timer.h" #include "usb_mux.h" +#define PS8802_DEBUG 0 #define PS8802_I2C_WAKE_DELAY 500 -static int ps8802_i2c_read(int port, int offset, int *data) +int ps8802_i2c_read(int port, int page, int offset, int *data) { - return i2c_read8(usb_retimers[port].i2c_port, - usb_retimers[port].i2c_addr_flags, - offset, data); + int rv; + + rv = i2c_read8(usb_retimers[port].i2c_port, + usb_retimers[port].i2c_addr_flags + page, + offset, data); + + if (PS8802_DEBUG) + ccprintf("%s(%d:0x%02X, 0x%02X) => 0x%02X\n", __func__, + usb_retimers[port].i2c_port, + usb_retimers[port].i2c_addr_flags + page, + offset, *data); + + return rv; } -static int ps8802_i2c_write(int port, int offset, int data) +int ps8802_i2c_write(int port, int page, int offset, int data) { + if (PS8802_DEBUG) + ccprintf("%s(%d:0x%02X, 0x%02X, 0x%02X)\n", __func__, + usb_retimers[port].i2c_port, + usb_retimers[port].i2c_addr_flags + page, + offset, data); + return i2c_write8(usb_retimers[port].i2c_port, - usb_retimers[port].i2c_addr_flags, + usb_retimers[port].i2c_addr_flags + page, offset, data); } +int ps8802_i2c_write16(int port, int page, int offset, int data) +{ + if (PS8802_DEBUG) + ccprintf("%s(%d:0x%02X, 0x%02X, 0x%04X)\n", __func__, + usb_retimers[port].i2c_port, + usb_retimers[port].i2c_addr_flags + page, + offset, data); + + return i2c_write16(usb_retimers[port].i2c_port, + usb_retimers[port].i2c_addr_flags + page, + offset, data); +} + +int ps8802_i2c_field_update8(int port, int page, int offset, + uint8_t field_mask, uint8_t set_value) +{ + if (PS8802_DEBUG) + ccprintf("%s(%d:0x%02X, 0x%02X, 0x%02X, 0x%02X)\n", __func__, + usb_retimers[port].i2c_port, + usb_retimers[port].i2c_addr_flags + page, + offset, field_mask, set_value); + + return i2c_field_update8(usb_retimers[port].i2c_port, + usb_retimers[port].i2c_addr_flags + page, + offset, + field_mask, + set_value); +} + +int ps8802_i2c_field_update16(int port, int page, int offset, + uint16_t field_mask, uint16_t set_value) +{ + if (PS8802_DEBUG) + ccprintf("%s(%d:0x%02X, 0x%02X, 0x%04X, 0x%04X)\n", __func__, + usb_retimers[port].i2c_port, + usb_retimers[port].i2c_addr_flags + page, + offset, field_mask, set_value); + + return i2c_field_update16(usb_retimers[port].i2c_port, + usb_retimers[port].i2c_addr_flags + page, + offset, + field_mask, + set_value); +} + /* * If PS8802 is in I2C standby mode, wake it up by reading PS8802_REG_MODE. * From Application Note: 1) Activate by reading any Page 2 register. 2) Wait @@ -41,7 +103,10 @@ static int ps8802_i2c_wake(int port) /* If in standby, first read will fail, second should succeed. */ for (int i = 0; i < 2; i++) { - rv = ps8802_i2c_read(port, PS8802_REG_MODE, &data); + rv = ps8802_i2c_read(port, + PS8802_REG_PAGE2, + PS8802_REG2_MODE, + &data); if (rv == EC_SUCCESS) return rv; @@ -71,25 +136,80 @@ static int ps8802_set_mux(int port, mux_state_t mux_state) { int val = (PS8802_MODE_DP_REG_CONTROL | PS8802_MODE_USB_REG_CONTROL - | PS8802_MODE_FLIP_REG_CONTROL); + | PS8802_MODE_FLIP_REG_CONTROL + | PS8802_MODE_IN_HPD_REG_CONTROL); int rv; if (chipset_in_state(CHIPSET_STATE_HARD_OFF)) return (mux_state == USB_PD_MUX_NONE) ? EC_SUCCESS : EC_ERROR_NOT_POWERED; + /* Make sure the PS8802 is awake */ rv = ps8802_i2c_wake(port); if (rv) return rv; + if (PS8802_DEBUG) + ccprintf("%s(%d, 0x%02X) %s %s %s\n", + __func__, port, mux_state, + (mux_state & USB_PD_MUX_USB_ENABLED) ? "USB" : "", + (mux_state & USB_PD_MUX_DP_ENABLED) ? "DP" : "", + (mux_state & USB_PD_MUX_POLARITY_INVERTED) + ? "FLIP" : ""); + + /* Set the mode and flip */ if (mux_state & USB_PD_MUX_USB_ENABLED) val |= PS8802_MODE_USB_ENABLE; if (mux_state & USB_PD_MUX_DP_ENABLED) - val |= PS8802_MODE_DP_ENABLE; + val |= PS8802_MODE_DP_ENABLE | PS8802_MODE_IN_HPD_ENABLE; if (mux_state & USB_PD_MUX_POLARITY_INVERTED) val |= PS8802_MODE_FLIP_ENABLE; - return ps8802_i2c_write(port, PS8802_REG_MODE, val); + rv = ps8802_i2c_write(port, + PS8802_REG_PAGE2, + PS8802_REG2_MODE, + val); + if (rv) + return rv; + + /* Board specific retimer mux tuning */ + if (usb_retimers[port].tune) { + rv = usb_retimers[port].tune(port, mux_state); + if (rv) + return rv; + } + + if (PS8802_DEBUG) { + int tx_status; + int rx_status; + + rv = ps8802_i2c_read(port, + PS8802_REG_PAGE0, + PS8802_REG0_TX_STATUS, + &tx_status); + if (rv) + return rv; + + rv = ps8802_i2c_read(port, + PS8802_REG_PAGE0, + PS8802_REG0_RX_STATUS, + &rx_status); + if (rv) + return rv; + + ccprintf("%s: tx:channel %snormal %s10Gbps\n", + __func__, + (tx_status & PS8802_STATUS_NORMAL_OPERATION) + ? "" : "NOT-", + (tx_status & PS8802_STATUS_10_GBPS) ? "" : "NON-"); + ccprintf("%s: rx:channel %snormal %s10Gbps\n", + __func__, + (rx_status & PS8802_STATUS_NORMAL_OPERATION) + ? "" : "NOT-", + (rx_status & PS8802_STATUS_10_GBPS) ? "" : "NON-"); + } + + return rv; } static int ps8802_get_mux(int port, mux_state_t *mux_state) @@ -106,7 +226,10 @@ static int ps8802_get_mux(int port, mux_state_t *mux_state) if (rv) return rv; - rv = ps8802_i2c_read(port, PS8802_REG_MODE, &val); + rv = ps8802_i2c_read(port, + PS8802_REG_PAGE2, + PS8802_REG2_MODE, + &val); if (rv) return rv; diff --git a/driver/retimer/ps8802.h b/driver/retimer/ps8802.h index f7f30d3be9..a54e714b9b 100644 --- a/driver/retimer/ps8802.h +++ b/driver/retimer/ps8802.h @@ -11,23 +11,87 @@ /* * PS8802 uses 7-bit I2C addresses 0x08 to 0x17 (ADDR=L). * Page 0 = 0x08, Page 1 = 0x09, Page 2 = 0x0A. - * We only need to read and write the Mode Selection register in Page 2. */ -#define PS8802_I2C_ADDR_FLAGS 0x0A - -#define PS8802_REG_MODE 0x06 -#define PS8802_MODE_DP_REG_CONTROL BIT(7) -#define PS8802_MODE_DP_ENABLE BIT(6) -#define PS8802_MODE_USB_REG_CONTROL BIT(5) -#define PS8802_MODE_USB_ENABLE BIT(4) -#define PS8802_MODE_FLIP_REG_CONTROL BIT(3) -#define PS8802_MODE_FLIP_ENABLE BIT(2) -#define PS8802_MODE_IN_HPD_REG_CONTROL BIT(1) -#define PS8802_MODE_IN_HPD_ENABLE BIT(0) +#define PS8802_I2C_ADDR_FLAGS 0x08 + +/* + * PAGE 0 Register Definitions + */ +#define PS8802_REG_PAGE0 0x00 + +#define PS8802_REG0_TX_STATUS 0x72 +#define PS8802_REG0_RX_STATUS 0x76 +#define PS8802_STATUS_NORMAL_OPERATION BIT(7) +#define PS8802_STATUS_10_GBPS BIT(5) + +/* + * PAGE 1 Register Definitions + */ +#define PS8802_REG_PAGE1 0x01 + +#define PS8802_REG1_LINK_BW_SET 0x10 +#define PS8802_LINK_BW_SET_1_62_GBPS 0x06 +#define PS8802_LINK_BW_SET_2_7_GBPS 0x0A +#define PS8802_LINK_BW_SET_5_4_GBPS 0x14 +#define PS8802_LINK_BW_SET_8_1_GBPS 0x1E + +#define PS8802_REG1_LANE_COUNT_SET 0x11 +#define PS8802_LANE_COUNT_SET_1_LANE 0x01 +#define PS8802_LANE_COUNT_SET_2_LANE 0x02 +#define PS8802_LANE_COUNT_SET_4_LANE 0x04 +#define PS8802_LANE_COUNT_SET_MASK 0x1F + +/* + * PAGE 2 Register Definitions + */ +#define PS8802_REG_PAGE2 0x02 + +#define PS8802_REG2_USB_SSEQ_LEVEL 0x02 +#define PS8802_REG2_USB_CEQ_LEVEL 0x04 +#define PS8802_USBEQ_LEVEL_UP_12DB (0x0000 | 0x0003) +#define PS8802_USBEQ_LEVEL_UP_13DB (0x0400 | 0x0007) +#define PS8802_USBEQ_LEVEL_UP_16DB (0x0C00 | 0x000F) +#define PS8802_USBEQ_LEVEL_UP_17DB (0x1C00 | 0x001F) +#define PS8802_USBEQ_LEVEL_UP_18DB (0x3C00 | 0x003F) +#define PS8802_USBEQ_LEVEL_UP_19DB (0x7C00 | 0x007F) +#define PS8802_USBEQ_LEVEL_UP_20DB (0xFC00 | 0x00FF) +#define PS8802_USBEQ_LEVEL_UP_23DB (0xFD00 | 0x01FF) +#define PS8802_USBEQ_LEVEL_UP_MASK 0xFDFF + +#define PS8802_REG2_MODE 0x06 +#define PS8802_MODE_DP_REG_CONTROL BIT(7) +#define PS8802_MODE_DP_ENABLE BIT(6) +#define PS8802_MODE_USB_REG_CONTROL BIT(5) +#define PS8802_MODE_USB_ENABLE BIT(4) +#define PS8802_MODE_FLIP_REG_CONTROL BIT(3) +#define PS8802_MODE_FLIP_ENABLE BIT(2) +#define PS8802_MODE_IN_HPD_REG_CONTROL BIT(1) +#define PS8802_MODE_IN_HPD_ENABLE BIT(0) + +#define PS8802_REG2_DPEQ_LEVEL 0x07 +#define PS8802_DPEQ_LEVEL_UP_9DB 0x00 +#define PS8802_DPEQ_LEVEL_UP_11DB 0x01 +#define PS8802_DPEQ_LEVEL_UP_12DB 0x02 +#define PS8802_DPEQ_LEVEL_UP_14DB 0x03 +#define PS8802_DPEQ_LEVEL_UP_17DB 0x04 +#define PS8802_DPEQ_LEVEL_UP_18DB 0x05 +#define PS8802_DPEQ_LEVEL_UP_19DB 0x06 +#define PS8802_DPEQ_LEVEL_UP_20DB 0x07 +#define PS8802_DPEQ_LEVEL_UP_21DB 0x08 +#define PS8802_DPEQ_LEVEL_UP_MASK 0x0F + extern const struct usb_mux_driver ps8802_usb_mux_driver; extern const struct usb_retimer_driver ps8802_usb_retimer; int ps8802_detect(int port); +int ps8802_i2c_read(int port, int page, int offset, int *data); +int ps8802_i2c_write(int port, int page, int offset, int data); +int ps8802_i2c_write16(int port, int page, int offset, int data); +int ps8802_i2c_field_update8(int port, int page, int offset, + uint8_t field_mask, uint8_t set_value); +int ps8802_i2c_field_update16(int port, int page, int offset, + uint16_t field_mask, uint16_t set_value); + #endif /* __CROS_EC_USB_RETIMER_PS8802_H */ -- cgit v1.2.1