summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Brockus <dbrockus@chromium.org>2020-01-16 11:29:41 -0700
committerCommit Bot <commit-bot@chromium.org>2020-01-23 22:43:12 +0000
commit21945692c2c1bb464fdc51a7b003ee630b72bdb1 (patch)
treed324ff38f7192c2a1884f1044e2ab6c484ef6bad
parent495f0f2d2f170f9763a11cfe2630f8d3bec9c17e (diff)
downloadchrome-ec-21945692c2c1bb464fdc51a7b003ee630b72bdb1.tar.gz
zork: PS8802/PS8818 dynamic detection cleanup
Changed the method for detection to a non-destructive mechanism so it could be called from any of the interfaces. Added in switching to direct calling the detected driver once the hardware has been determined. The 8802 is the main mux when it is present and the fp5 mux should only select the lanes to send. Flip should not be sent to the FP5. It is a specialized form of a secondary MUX and currently being placed in as a retimer. In the future the retimers will all become MUXes and they will chain... just not today. BUG=b:147428570 BRANCH=none TEST=verify USB-C1 device with AP running Change-Id: I6b0eedd1dfcc91c3114f8dc481f5ca2841eb9e85 Signed-off-by: Denis Brockus <dbrockus@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2014311 Reviewed-by: Edward Hill <ecgh@chromium.org>
-rw-r--r--baseboard/zork/baseboard.c197
-rw-r--r--driver/retimer/ps8802.c81
-rw-r--r--driver/retimer/ps8802.h3
-rw-r--r--driver/retimer/ps8818.c28
-rw-r--r--driver/retimer/ps8818.h2
-rw-r--r--driver/usb_mux/amd_fp5.c11
-rw-r--r--driver/usb_mux/amd_fp5.h3
7 files changed, 239 insertions, 86 deletions
diff --git a/baseboard/zork/baseboard.c b/baseboard/zork/baseboard.c
index 3ecddadd42..b1c8662a07 100644
--- a/baseboard/zork/baseboard.c
+++ b/baseboard/zork/baseboard.c
@@ -463,128 +463,166 @@ void bc12_interrupt(enum gpio_signal signal)
}
/*****************************************************************************
- * Custom Zork USB-C1 Retimer driver
- *
+ * Custom Zork USB-C1 Retimer/MUX driver
+ */
+
+/*
+ * FP5 is a true MUX but being used as a secondary MUX. Don't want to
+ * send FLIP or this will cause a double flip
+ */
+static int zork_c1_retimer_set_mux(int port, mux_state_t mux_state)
+{
+ return amd_fp5_usb_retimer.set(port,
+ mux_state & ~MUX_POLARITY_INVERTED);
+}
+
+const struct usb_retimer_driver zork_c1_usb_retimer = {
+ /* Secondary MUX/Retimer only needs the set mux interface */
+ .set = zork_c1_retimer_set_mux,
+};
+
+struct usb_retimer usb_retimers[] = {
+ [USBC_PORT_C0] = {
+ .driver = &pi3dpx1207_usb_retimer,
+ .i2c_port = I2C_PORT_TCPC0,
+ .i2c_addr_flags = PI3DPX1207_I2C_ADDR_FLAGS,
+ },
+ [USBC_PORT_C1] = {
+ /*
+ * The driver is left off until we detect the
+ * hardware present. Once the hardware has been
+ * detected, the driver will be set to the
+ * detected hardware driver table.
+ */
+ .i2c_port = I2C_PORT_TCPC1,
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(usb_retimers) == USBC_PORT_COUNT);
+
+/*
* To support both OPT1 DB with PS8818 retimer, and OPT3 DB with PS8802
* retimer, Try both, and remember the first one that succeeds.
*
- * TODO(b:147593165, b:147593660) Cleanup of retimers as muxes in a more
+ * TODO(b:147593660) Cleanup of retimers as muxes in a more
* generalized mechanism
*/
enum zork_c1_retimer zork_c1_retimer = C1_RETIMER_UNKNOWN;
-
-static int zork_c1_set_retimer_mux(int port, mux_state_t mux_state)
+static int zork_c1_detect(int port, int err_if_power_off)
{
- int rv = EC_ERROR_UNKNOWN;
+ 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 (mux_state == TYPEC_MUX_NONE)
- ? EC_SUCCESS
- : EC_ERROR_NOT_POWERED;
+ return (err_if_power_off) ? EC_ERROR_NOT_POWERED
+ : EC_SUCCESS;
/*
* Identifying a PS8818 is faster than the PS8802,
* so do it first.
*/
- if (zork_c1_retimer != C1_RETIMER_PS8802) {
- usb_retimers[USBC_PORT_C1].i2c_addr_flags
- = PS8818_I2C_ADDR_FLAGS;
- rv = ps8818_usb_retimer.set(port, mux_state);
- if (rv == EC_SUCCESS && zork_c1_retimer != C1_RETIMER_PS8818) {
- zork_c1_retimer = C1_RETIMER_PS8818;
- ccprints("C1 PS8818 detected");
- }
+ usb_retimers[port].i2c_addr_flags = PS8818_I2C_ADDR_FLAGS;
+ rv = ps8818_detect(port);
+ if (rv == EC_SUCCESS) {
+ zork_c1_retimer = C1_RETIMER_PS8818;
+ ccprints("C1 PS8818 detected");
+
+ /* Main MUX is FP5, secondary MUX is PS8818 */
+ usb_muxes[USBC_PORT_C1].driver = &amd_fp5_usb_mux_driver;
+ usb_retimers[USBC_PORT_C1].driver = &ps8818_usb_retimer;
+ return rv;
}
- if (zork_c1_retimer != C1_RETIMER_PS8818) {
- usb_retimers[USBC_PORT_C1].i2c_addr_flags
- = PS8802_I2C_ADDR_FLAGS;
- rv = ps8802_usb_retimer.set(port, mux_state);
- if (rv == EC_SUCCESS && zork_c1_retimer != C1_RETIMER_PS8802) {
- zork_c1_retimer = C1_RETIMER_PS8802;
- ccprints("C1 PS8802 detected");
- }
+ usb_retimers[port].i2c_addr_flags = PS8802_I2C_ADDR_FLAGS;
+ rv = ps8802_detect(port);
+ if (rv == EC_SUCCESS) {
+ zork_c1_retimer = C1_RETIMER_PS8802;
+ ccprints("C1 PS8802 detected");
+
+ /* 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;
}
return rv;
}
-const struct usb_retimer_driver zork_c1_usb_retimer = {
- .set = zork_c1_set_retimer_mux,
-};
-
-const struct pi3dpx1207_usb_control pi3dpx1207_controls[] = {
- [USBC_PORT_C0] = {
- .enable_gpio = IOEX_USB_C0_DATA_EN,
- .dp_enable_gpio = GPIO_USB_C0_IN_HPD,
- },
- [USBC_PORT_C1] = {
- },
-};
-BUILD_ASSERT(ARRAY_SIZE(pi3dpx1207_controls) == USBC_PORT_COUNT);
-
-struct usb_retimer usb_retimers[] = {
- [USBC_PORT_C0] = {
- .driver = &pi3dpx1207_usb_retimer,
- .i2c_port = I2C_PORT_TCPC0,
- .i2c_addr_flags = PI3DPX1207_I2C_ADDR_FLAGS,
- },
- [USBC_PORT_C1] = {
- .driver = &zork_c1_usb_retimer,
- .i2c_port = I2C_PORT_TCPC1,
- },
-};
-BUILD_ASSERT(ARRAY_SIZE(usb_retimers) == USBC_PORT_COUNT);
-
-/*****************************************************************************
- * Custom Zork USB-C1 MUX driver
- *
- * USB MUX is AMD FP5 for both ports. The C1 port may not need
- * to do flip if this is a PS8802 retimer. I will save the
- * polarity state so we can reproduce it on the mux_get
+/*
+ * 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.
*/
-static mux_state_t zork_c1_saved_polarity_state = TYPEC_MUX_NONE;
-
static int zork_c1_init_mux(int port)
{
- return amd_fp5_usb_mux_driver.init(port);
+ /* Try to detect, but don't give an error if no power */
+ return zork_c1_detect(port, 0);
}
static int zork_c1_set_mux(int port, mux_state_t mux_state)
{
- /* If we have not detected the retimer yet, go do it */
- if (zork_c1_retimer == C1_RETIMER_UNKNOWN) {
- int rv;
+ int rv;
- rv = zork_c1_set_retimer_mux(port, TYPEC_MUX_NONE);
- if (rv)
- return 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(port, mux_state != TYPEC_MUX_NONE);
+ if (rv)
+ return rv;
- /* PS8802 requires us not to flip in AMD_FP5 mux */
- zork_c1_saved_polarity_state = TYPEC_MUX_NONE;
- if (zork_c1_retimer == C1_RETIMER_PS8802) {
- zork_c1_saved_polarity_state = MUX_POLARITY_INVERTED;
- mux_state &= ~MUX_POLARITY_INVERTED;
- }
+ /*
+ * 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)
+ rv = usb_muxes[port].driver->set(port, mux_state);
- return amd_fp5_usb_mux_driver.set(port, mux_state);
+ return rv;
}
static int zork_c1_get_mux(int port, mux_state_t *mux_state)
{
int rv;
- rv = amd_fp5_usb_mux_driver.get(port, mux_state);
- *mux_state |= zork_c1_saved_polarity_state;
+ /* Try to detect the hardware */
+ rv = zork_c1_detect(port, 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 = TYPEC_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)
+ rv = usb_muxes[port].driver->get(port, mux_state);
return rv;
}
+const struct pi3dpx1207_usb_control pi3dpx1207_controls[] = {
+ [USBC_PORT_C0] = {
+ .enable_gpio = IOEX_USB_C0_DATA_EN,
+ .dp_enable_gpio = GPIO_USB_C0_IN_HPD,
+ },
+ [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,
@@ -596,6 +634,11 @@ struct usb_mux usb_muxes[] = {
.driver = &amd_fp5_usb_mux_driver,
},
[USBC_PORT_C1] = {
+ /*
+ * This is the detection driver. Once the hardware
+ * has been detected, the driver will change to the
+ * detected hardware driver table.
+ */
.driver = &zork_c1_usb_mux_driver,
},
};
diff --git a/driver/retimer/ps8802.c b/driver/retimer/ps8802.c
index 1fda5526be..5b9736dba4 100644
--- a/driver/retimer/ps8802.c
+++ b/driver/retimer/ps8802.c
@@ -5,6 +5,7 @@
* PS8802 retimer.
*/
+#include "chipset.h"
#include "common.h"
#include "console.h"
#include "i2c.h"
@@ -14,6 +15,20 @@
#define PS8802_I2C_WAKE_DELAY 500
+static int ps8802_i2c_read(int port, int offset, int *data)
+{
+ return i2c_read8(usb_retimers[port].i2c_port,
+ usb_retimers[port].i2c_addr_flags,
+ offset, data);
+}
+
+static int ps8802_i2c_write(int port, int offset, int data)
+{
+ return i2c_write8(usb_retimers[port].i2c_port,
+ usb_retimers[port].i2c_addr_flags,
+ offset, data);
+}
+
/*
* 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
@@ -26,9 +41,7 @@ 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 = i2c_read8(usb_retimers[port].i2c_port,
- usb_retimers[port].i2c_addr_flags,
- PS8802_REG_MODE, &data);
+ rv = ps8802_i2c_read(port, PS8802_REG_MODE, &data);
if (rv == EC_SUCCESS)
return rv;
@@ -38,11 +51,20 @@ static int ps8802_i2c_wake(int port)
return rv;
}
-static int ps8802_i2c_write(int port, int offset, int data)
+int ps8802_detect(int port)
{
- return i2c_write8(usb_retimers[port].i2c_port,
- usb_retimers[port].i2c_addr_flags,
- offset, data);
+ int rv = EC_ERROR_NOT_POWERED;
+
+ /* Detected if we are powered and can read the device */
+ if (!chipset_in_state(CHIPSET_STATE_HARD_OFF))
+ rv = ps8802_i2c_wake(port);
+
+ return rv;
+}
+
+static int ps8802_init(int port)
+{
+ return EC_SUCCESS;
}
static int ps8802_set_mux(int port, mux_state_t mux_state)
@@ -52,6 +74,10 @@ static int ps8802_set_mux(int port, mux_state_t mux_state)
| PS8802_MODE_FLIP_REG_CONTROL);
int rv;
+ if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
+ return (mux_state == TYPEC_MUX_NONE) ? EC_SUCCESS
+ : EC_ERROR_NOT_POWERED;
+
rv = ps8802_i2c_wake(port);
if (rv)
return rv;
@@ -66,6 +92,47 @@ static int ps8802_set_mux(int port, mux_state_t mux_state)
return ps8802_i2c_write(port, PS8802_REG_MODE, val);
}
+static int ps8802_get_mux(int port, mux_state_t *mux_state)
+{
+ int rv;
+ int val;
+
+ *mux_state = TYPEC_MUX_NONE;
+
+ if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
+ return EC_ERROR_NOT_POWERED;
+
+ rv = ps8802_i2c_wake(port);
+ if (rv)
+ return rv;
+
+ rv = ps8802_i2c_read(port, PS8802_REG_MODE, &val);
+ if (rv)
+ return rv;
+
+ if (val & PS8802_MODE_USB_ENABLE)
+ *mux_state |= MUX_USB_ENABLED;
+ if (val & PS8802_MODE_DP_ENABLE)
+ *mux_state |= MUX_DP_ENABLED;
+ if (val & PS8802_MODE_FLIP_ENABLE)
+ *mux_state |= MUX_POLARITY_INVERTED;
+
+ return rv;
+}
+
+/*
+ * PS8802 can look like a retimer or a MUX. So create both tables
+ * and use them as needed, until retimers become a type of MUX and
+ * then we will only need one of these tables.
+ *
+ * TODO(b:147593660) Cleanup of retimers as muxes in a more
+ * generalized mechanism
+ */
const struct usb_retimer_driver ps8802_usb_retimer = {
.set = ps8802_set_mux,
};
+const struct usb_mux_driver ps8802_usb_mux_driver = {
+ .init = ps8802_init,
+ .set = ps8802_set_mux,
+ .get = ps8802_get_mux,
+};
diff --git a/driver/retimer/ps8802.h b/driver/retimer/ps8802.h
index 12f6f3ccb8..f7f30d3be9 100644
--- a/driver/retimer/ps8802.h
+++ b/driver/retimer/ps8802.h
@@ -25,6 +25,9 @@
#define PS8802_MODE_IN_HPD_REG_CONTROL BIT(1)
#define PS8802_MODE_IN_HPD_ENABLE BIT(0)
+extern const struct usb_mux_driver ps8802_usb_mux_driver;
extern const struct usb_retimer_driver ps8802_usb_retimer;
+int ps8802_detect(int port);
+
#endif /* __CROS_EC_USB_RETIMER_PS8802_H */
diff --git a/driver/retimer/ps8818.c b/driver/retimer/ps8818.c
index d58d9966e2..c542152a16 100644
--- a/driver/retimer/ps8818.c
+++ b/driver/retimer/ps8818.c
@@ -5,14 +5,22 @@
* PS8818 retimer.
*/
-#include "ps8818.h"
+#include "chipset.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "i2c.h"
#include "ioexpander.h"
+#include "ps8818.h"
#include "usb_mux.h"
+static int ps8818_i2c_read(int port, int offset, int *data)
+{
+ return i2c_read8(usb_retimers[port].i2c_port,
+ usb_retimers[port].i2c_addr_flags,
+ offset, data);
+}
+
static int ps8818_i2c_write(int port, int offset, int data)
{
return i2c_write8(usb_retimers[port].i2c_port,
@@ -20,10 +28,26 @@ static int ps8818_i2c_write(int port, int offset, int data)
offset, data);
}
+int ps8818_detect(int port)
+{
+ int rv = EC_ERROR_NOT_POWERED;
+ int val;
+
+ /* Detected if we are powered and can read the device */
+ if (!chipset_in_state(CHIPSET_STATE_HARD_OFF))
+ rv = ps8818_i2c_read(port, PS8818_REG_FLIP, &val);
+
+ return rv;
+}
+
static int ps8818_set_mux(int port, mux_state_t mux_state)
{
- int val = 0;
int rv;
+ int val = 0;
+
+ if (chipset_in_state(CHIPSET_STATE_HARD_OFF))
+ return (mux_state == TYPEC_MUX_NONE) ? EC_SUCCESS
+ : EC_ERROR_NOT_POWERED;
if (mux_state & MUX_USB_ENABLED)
val |= PS8818_MODE_USB_ENABLE;
diff --git a/driver/retimer/ps8818.h b/driver/retimer/ps8818.h
index 93481d3e4b..39e6f04b13 100644
--- a/driver/retimer/ps8818.h
+++ b/driver/retimer/ps8818.h
@@ -19,4 +19,6 @@
extern const struct usb_retimer_driver ps8818_usb_retimer;
+int ps8818_detect(int port);
+
#endif /* __CROS_EC_USB_RETIMER_PS8818_H */
diff --git a/driver/usb_mux/amd_fp5.c b/driver/usb_mux/amd_fp5.c
index a912d03365..a2c9ad68fb 100644
--- a/driver/usb_mux/amd_fp5.c
+++ b/driver/usb_mux/amd_fp5.c
@@ -112,6 +112,17 @@ static int amd_fp5_get_mux(int port, mux_state_t *mux_state)
return EC_SUCCESS;
}
+/*
+ * The FP5 MUX can look like a retimer or a MUX. So create both tables
+ * and use them as needed, until retimers become a type of MUX and
+ * then we will only need one of these tables.
+ *
+ * TODO(b:147593660) Cleanup of retimers as muxes in a more
+ * generalized mechanism
+ */
+const struct usb_retimer_driver amd_fp5_usb_retimer = {
+ .set = amd_fp5_set_mux,
+};
const struct usb_mux_driver amd_fp5_usb_mux_driver = {
.init = amd_fp5_init,
.set = amd_fp5_set_mux,
diff --git a/driver/usb_mux/amd_fp5.h b/driver/usb_mux/amd_fp5.h
index 7534ea0d8a..ac1a7ce27e 100644
--- a/driver/usb_mux/amd_fp5.h
+++ b/driver/usb_mux/amd_fp5.h
@@ -18,4 +18,7 @@
#define AMD_FP5_MUX_DP 0x0C
#define AMD_FP5_MUX_DP_INVERTED 0x1C
+extern const struct usb_mux_driver amd_fp5_usb_mux_driver;
+extern const struct usb_retimer_driver amd_fp5_usb_retimer;
+
#endif /* __CROS_EC_USB_MUX_AMD_FP5_H */