summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Brockus <dbrockus@chromium.org>2020-01-29 09:30:33 -0700
committerCommit Bot <commit-bot@chromium.org>2020-01-31 19:49:22 +0000
commita0d6b40c49c7615cddded5e6811a550468eba7b2 (patch)
tree366c24995d70676a58aa1668d121016588649889
parentd1824490d69fb45ad00856496e06d8f253778a71 (diff)
downloadchrome-ec-a0d6b40c49c7615cddded5e6811a550468eba7b2.tar.gz
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 <dbrockus@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2031646 Reviewed-by: Edward Hill <ecgh@chromium.org>
-rw-r--r--baseboard/zork/baseboard.c59
-rw-r--r--driver/retimer/ps8802.c145
-rw-r--r--driver/retimer/ps8802.h88
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
@@ -467,6 +467,64 @@ void bc12_interrupt(enum gpio_signal signal)
*/
/*
+ * 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 */