summaryrefslogtreecommitdiff
path: root/baseboard/zork/variant_trembyle.c
diff options
context:
space:
mode:
authorEdward Hill <ecgh@chromium.org>2020-02-28 12:14:49 -0700
committerCommit Bot <commit-bot@chromium.org>2020-02-29 19:54:19 +0000
commit7345fbc90603f8d0d9161dc18e5579487b346593 (patch)
tree995d69d0be91684d43a5ff6ffe11ccb38b9b9e92 /baseboard/zork/variant_trembyle.c
parent0eb3e2a0c7e71e461b2ee0375652bb61b28c1cb7 (diff)
downloadchrome-ec-7345fbc90603f8d0d9161dc18e5579487b346593.tar.gz
zork: Split mux/retimer for trembyle/dalboz
BUG=b:150099043 b:150384642 BRANCH=none TEST=none Signed-off-by: Edward Hill <ecgh@chromium.org> Change-Id: Ica9eda3f9d6a1332319b5c7ba56c0881d05eeebd Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2079353 Reviewed-by: Denis Brockus <dbrockus@chromium.org>
Diffstat (limited to 'baseboard/zork/variant_trembyle.c')
-rw-r--r--baseboard/zork/variant_trembyle.c346
1 files changed, 346 insertions, 0 deletions
diff --git a/baseboard/zork/variant_trembyle.c b/baseboard/zork/variant_trembyle.c
index 2b6e520136..1df084e5df 100644
--- a/baseboard/zork/variant_trembyle.c
+++ b/baseboard/zork/variant_trembyle.c
@@ -3,9 +3,14 @@
* found in the LICENSE file.
*/
+#include "chipset.h"
#include "common.h"
#include "console.h"
+#include "driver/retimer/pi3dpx1207.h"
+#include "driver/retimer/ps8802.h"
#include "driver/retimer/ps8811.h"
+#include "driver/retimer/ps8818.h"
+#include "driver/usb_mux/amd_fp5.h"
#include "fan.h"
#include "fan_chip.h"
#include "gpio.h"
@@ -13,6 +18,7 @@
#include "i2c.h"
#include "ioexpander.h"
#include "timer.h"
+#include "usb_mux.h"
#define CPRINTSUSB(format, args...) cprints(CC_USBCHARGE, format, ## args)
#define CPRINTFUSB(format, args...) cprintf(CC_USBCHARGE, format, ## args)
@@ -150,3 +156,343 @@ void mst_hpd_interrupt(enum ioex_signal signal)
/* Debounce for 2 msec. */
hook_call_deferred(&mst_hpd_handler_data, (2 * MSEC));
}
+
+/*****************************************************************************
+ * Custom Zork USB-C1 Retimer/MUX driver
+ */
+
+/*
+ * PS8802 set mux tuning.
+ * Adds in board specific gain and DP lane count configuration
+ */
+static int ps8802_mux_set(const struct usb_mux *me, mux_state_t mux_state)
+{
+ int rv = EC_SUCCESS;
+
+ /* Make sure the PS8802 is awake */
+ rv = ps8802_i2c_wake(me);
+ if (rv)
+ return rv;
+
+ /* USB specific config */
+ if (mux_state & USB_PD_MUX_USB_ENABLED) {
+ /* Boost the USB gain */
+ rv = ps8802_i2c_field_update16(me,
+ PS8802_REG_PAGE2,
+ PS8802_REG2_USB_SSEQ_LEVEL,
+ PS8802_USBEQ_LEVEL_UP_MASK,
+ PS8802_USBEQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+ }
+
+ /* DP specific config */
+ if (mux_state & USB_PD_MUX_DP_ENABLED) {
+ /* Boost the DP gain */
+ rv = ps8802_i2c_field_update8(me,
+ PS8802_REG_PAGE2,
+ PS8802_REG2_DPEQ_LEVEL,
+ PS8802_DPEQ_LEVEL_UP_MASK,
+ PS8802_DPEQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ /* Enable IN_HPD on the DB */
+ ioex_set_level(IOEX_USB_C1_HPD_IN_DB, 1);
+ } else {
+ /* Disable IN_HPD on the DB */
+ ioex_set_level(IOEX_USB_C1_HPD_IN_DB, 0);
+ }
+
+ return rv;
+}
+
+/*
+ * PS8818 set mux tuning.
+ * Adds in board specific gain and DP lane count configuration
+ */
+static int ps8818_mux_set(const struct usb_mux *me, 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 = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_APTX1EQ_10G_LEVEL,
+ PS8818_EQ_LEVEL_UP_MASK,
+ PS8818_EQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_APTX2EQ_10G_LEVEL,
+ PS8818_EQ_LEVEL_UP_MASK,
+ PS8818_EQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_APTX1EQ_5G_LEVEL,
+ PS8818_EQ_LEVEL_UP_MASK,
+ PS8818_EQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_APTX2EQ_5G_LEVEL,
+ PS8818_EQ_LEVEL_UP_MASK,
+ PS8818_EQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+ }
+
+ /* DP specific config */
+ if (mux_state & USB_PD_MUX_DP_ENABLED) {
+ /* Boost the DP gain */
+ rv = ps8818_i2c_field_update8(me,
+ PS8818_REG_PAGE1,
+ PS8818_REG1_DPEQ_LEVEL,
+ PS8818_DPEQ_LEVEL_UP_MASK,
+ PS8818_DPEQ_LEVEL_UP_19DB);
+ if (rv)
+ return rv;
+
+ /* Enable IN_HPD on the DB */
+ ioex_set_level(IOEX_USB_C1_HPD_IN_DB, 1);
+ } else {
+ /* Disable IN_HPD on the DB */
+ ioex_set_level(IOEX_USB_C1_HPD_IN_DB, 0);
+ }
+
+ return rv;
+}
+
+/*
+ * To support both OPT1 DB with PS8818 retimer, and OPT3 DB with PS8802
+ * retimer, Try both, and remember the first one that succeeds.
+ */
+const struct usb_mux usbc1_ps8802;
+const struct usb_mux usbc1_ps8818;
+struct usb_mux usbc1_amd_fp5_usb_mux;
+
+enum zork_c1_retimer zork_c1_retimer = C1_RETIMER_UNKNOWN;
+static int zork_c1_detect(const struct usb_mux *me, int err_if_power_off)
+{
+ int rv;
+
+ /*
+ * Retimers are not powered in G3 so return success if setting mux to
+ * none and error otherwise.
+ */
+ if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
+ return (err_if_power_off) ? EC_ERROR_NOT_POWERED
+ : EC_SUCCESS;
+
+ /*
+ * Identifying a PS8818 is faster than the PS8802,
+ * so do it first.
+ */
+ rv = ps8818_detect(&usbc1_ps8818);
+ if (rv == EC_SUCCESS) {
+ zork_c1_retimer = C1_RETIMER_PS8818;
+ ccprints("C1 PS8818 detected");
+
+ /* Main MUX is FP5, secondary MUX is PS8818 */
+ memcpy(&usb_muxes[USBC_PORT_C1],
+ &usbc1_amd_fp5_usb_mux,
+ sizeof(struct usb_mux));
+ usb_muxes[USBC_PORT_C1].next_mux = &usbc1_ps8818;
+ return rv;
+ }
+
+ rv = ps8802_detect(&usbc1_ps8802);
+ if (rv == EC_SUCCESS) {
+ zork_c1_retimer = C1_RETIMER_PS8802;
+ ccprints("C1 PS8802 detected");
+
+ /* Main MUX is PS8802, secondary MUX is modified FP5 */
+ memcpy(&usb_muxes[USBC_PORT_C1],
+ &usbc1_ps8802,
+ sizeof(struct usb_mux));
+ usb_muxes[USBC_PORT_C1].next_mux = &usbc1_amd_fp5_usb_mux;
+ usbc1_amd_fp5_usb_mux.flags = USB_MUX_FLAG_SET_WITHOUT_FLIP;
+ }
+
+ return rv;
+}
+
+/*
+ * We start off not sure which configuration we are using. We set
+ * the interface to be this special primary MUX driver in order to
+ * determine the actual hardware and then we patch the jump tables
+ * to go to the actual drivers instead.
+ *
+ * "me" will always point to usb_muxes[0]. If detection is made
+ * on the real device, then detect will change the tables so the
+ * content of me is the real driver configuration and will setup
+ * next_mux appropriately. So all we have to do on detection is
+ * perform the actual call for this entry and then let the generic
+ * chain traverse mechanism in usb_mux.c do any following calls.
+ */
+static int zork_c1_init_mux(const struct usb_mux *me)
+{
+ int rv;
+
+ /* Try to detect, but don't give an error if no power */
+ rv = zork_c1_detect(me, 0);
+ if (rv)
+ return rv;
+
+ /*
+ * If we detected the hardware, then call the real routine.
+ * We only do this one time, after that time we will go direct
+ * and avoid this special driver.
+ */
+ if (zork_c1_retimer != C1_RETIMER_UNKNOWN)
+ if (me->driver && me->driver->init)
+ rv = me->driver->init(me);
+
+ return rv;
+}
+
+static int zork_c1_set_mux(const struct usb_mux *me, mux_state_t mux_state)
+{
+ int rv;
+
+ /*
+ * Try to detect, give an error if we are setting to a
+ * MUX value that is not NONE when we have no power.
+ */
+ rv = zork_c1_detect(me, mux_state != USB_PD_MUX_NONE);
+ if (rv)
+ return rv;
+
+ /*
+ * If we detected the hardware, then call the real routine.
+ * We only do this one time, after that time we will go direct
+ * and avoid this special driver.
+ */
+ if (zork_c1_retimer != C1_RETIMER_UNKNOWN) {
+ const struct usb_mux_driver *drv = me->driver;
+
+ if (drv && drv->set) {
+ mux_state_t state = mux_state;
+
+ if (me->flags & USB_MUX_FLAG_SET_WITHOUT_FLIP)
+ state &= ~USB_PD_MUX_POLARITY_INVERTED;
+
+ /* Apply Driver generic settings */
+ rv = drv->set(me, state);
+ if (rv)
+ return rv;
+
+ /* Apply Board specific settings */
+ if (me->board_set)
+ rv = me->board_set(me, state);
+ }
+ }
+
+ return rv;
+}
+
+static int zork_c1_get_mux(const struct usb_mux *me, mux_state_t *mux_state)
+{
+ int rv;
+
+ /* Try to detect the hardware */
+ rv = zork_c1_detect(me, 1);
+ if (rv) {
+ /*
+ * Not powered is MUX_NONE, so change the values
+ * and make it a good status
+ */
+ if (rv == EC_ERROR_NOT_POWERED) {
+ *mux_state = USB_PD_MUX_NONE;
+ rv = EC_SUCCESS;
+ }
+ return rv;
+ }
+
+ /*
+ * If we detected the hardware, then call the real routine.
+ * We only do this one time, after that time we will go direct
+ * and avoid this special driver.
+ */
+ if (zork_c1_retimer != C1_RETIMER_UNKNOWN)
+ if (me->driver && me->driver->get)
+ rv = me->driver->get(me, mux_state);
+
+ return rv;
+}
+
+const struct pi3dpx1207_usb_control pi3dpx1207_controls[] = {
+ [USBC_PORT_C0] = {
+#ifdef VARIANT_ZORK_TREMBYLE
+ .enable_gpio = IOEX_USB_C0_DATA_EN,
+ .dp_enable_gpio = GPIO_USB_C0_IN_HPD,
+#endif
+ },
+ [USBC_PORT_C1] = {
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(pi3dpx1207_controls) == USBC_PORT_COUNT);
+
+const struct usb_mux_driver zork_c1_usb_mux_driver = {
+ .init = zork_c1_init_mux,
+ .set = zork_c1_set_mux,
+ .get = zork_c1_get_mux,
+};
+
+const struct usb_mux usbc0_pi3dpx1207_usb_retimer = {
+ .usb_port = USBC_PORT_C0,
+ .i2c_port = I2C_PORT_TCPC0,
+ .i2c_addr_flags = PI3DPX1207_I2C_ADDR_FLAGS,
+ .driver = &pi3dpx1207_usb_retimer,
+};
+
+const struct usb_mux usbc1_ps8802 = {
+ .usb_port = USBC_PORT_C1,
+ .i2c_port = I2C_PORT_TCPC1,
+ .i2c_addr_flags = PS8802_I2C_ADDR_FLAGS,
+ .driver = &ps8802_usb_mux_driver,
+ .board_set = &ps8802_mux_set,
+};
+const struct usb_mux usbc1_ps8818 = {
+ .usb_port = USBC_PORT_C1,
+ .i2c_port = I2C_PORT_TCPC1,
+ .i2c_addr_flags = PS8818_I2C_ADDR_FLAGS,
+ .driver = &ps8818_usb_retimer_driver,
+ .board_set = &ps8818_mux_set,
+};
+struct usb_mux usbc1_amd_fp5_usb_mux = {
+ .usb_port = USBC_PORT_C1,
+ .i2c_port = I2C_PORT_USB_AP_MUX,
+ .i2c_addr_flags = AMD_FP5_MUX_I2C_ADDR_FLAGS,
+ .driver = &amd_fp5_usb_mux_driver,
+};
+
+struct usb_mux usb_muxes[] = {
+ [USBC_PORT_C0] = {
+ .usb_port = USBC_PORT_C0,
+ .i2c_port = I2C_PORT_USB_AP_MUX,
+ .i2c_addr_flags = AMD_FP5_MUX_I2C_ADDR_FLAGS,
+ .driver = &amd_fp5_usb_mux_driver,
+ .next_mux = &usbc0_pi3dpx1207_usb_retimer,
+ },
+ [USBC_PORT_C1] = {
+ /*
+ * This is the detection driver. Once the hardware
+ * has been detected, the driver will change to the
+ * detected hardware driver table.
+ */
+ .usb_port = USBC_PORT_C1,
+ .i2c_port = I2C_PORT_TCPC1,
+ .driver = &zork_c1_usb_mux_driver,
+ }
+};
+BUILD_ASSERT(ARRAY_SIZE(usb_muxes) == USBC_PORT_COUNT);