summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Brockus <dbrockus@chromium.org>2019-11-05 14:51:56 -0700
committerCommit Bot <commit-bot@chromium.org>2019-11-14 00:12:44 +0000
commita4972e187c6ce582aa54dbfce6039fd2239e4bbd (patch)
tree2dfffcb6b3b457b8abdbc6f4a0acc267cb9209d2
parentf5633029509350cf7254ab14c440d14e8e73f729 (diff)
downloadchrome-ec-a4972e187c6ce582aa54dbfce6039fd2239e4bbd.tar.gz
usbc: retimer pi3dpx1207
BUG=b:139428185 BRANCH=none TEST=verify mode is set correctly when switching devices Change-Id: Ic9d460a94bb8007f17168ac5237a4dcbc24cfb2b Signed-off-by: Denis Brockus <dbrockus@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1900123 Reviewed-by: Edward Hill <ecgh@chromium.org>
-rw-r--r--driver/build.mk1
-rw-r--r--driver/retimer/pi3dpx1207.c115
-rw-r--r--driver/retimer/pi3dpx1207.h44
-rw-r--r--driver/usb_mux/usb_mux.c38
-rw-r--r--include/config.h7
-rw-r--r--include/usb_mux.h56
6 files changed, 253 insertions, 8 deletions
diff --git a/driver/build.mk b/driver/build.mk
index edd97a09a9..e912c3d33a 100644
--- a/driver/build.mk
+++ b/driver/build.mk
@@ -139,6 +139,7 @@ driver-$(CONFIG_USB_PD_TCPM_NCT38XX)+=tcpm/nct38xx.o
# Type-C Retimer drivers
driver-$(CONFIG_USBC_RETIMER_INTEL_BB)+=retimer/bb_retimer.o
+driver-$(CONFIG_USBC_RETIMER_PI3DPX1207)+=retimer/pi3dpx1207.o
# USB mux high-level driver
driver-$(CONFIG_USBC_SS_MUX)+=usb_mux/usb_mux.o
diff --git a/driver/retimer/pi3dpx1207.c b/driver/retimer/pi3dpx1207.c
new file mode 100644
index 0000000000..f1f1e19b54
--- /dev/null
+++ b/driver/retimer/pi3dpx1207.c
@@ -0,0 +1,115 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * PI3DPX1207 retimer.
+ */
+
+#include "pi3dpx1207.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "i2c.h"
+#include "ioexpander.h"
+#include "usb_mux.h"
+
+#define I2C_MAX_RETRIES 2
+
+/* Stack space is limited, so put the buffer somewhere else */
+static uint8_t buf[PI3DPX1207_NUM_REGISTERS];
+
+static int pi3dpx1207_i2c_write(int i2c_port,
+ uint16_t addr_flags,
+ uint8_t offset,
+ uint8_t val)
+{
+ int rv = EC_SUCCESS;
+ int attempt;
+
+ if (offset >= PI3DPX1207_NUM_REGISTERS)
+ return EC_ERROR_INVAL;
+
+ /*
+ * PI3DPX1207 does not support device register offset in
+ * the typical I2C sense. Have to read the values starting
+ * from 0, modify the byte and then write the block.
+ *
+ * NOTE: The device may not respond correctly if it was
+ * just powered or has gone to sleep. Allow for retries
+ * in case this happens.
+ */
+ if (offset > 0) {
+ attempt = 0;
+ do {
+ attempt++;
+ rv = i2c_xfer(i2c_port, addr_flags,
+ NULL, 0, buf, offset);
+ } while ((rv != EC_SUCCESS) && (attempt < I2C_MAX_RETRIES));
+ }
+
+ if (rv == EC_SUCCESS) {
+ buf[offset] = val;
+
+ attempt = 0;
+ do {
+ attempt++;
+ rv = i2c_xfer(i2c_port, addr_flags,
+ buf, offset + 1, NULL, 0);
+ } while ((rv != EC_SUCCESS) && (attempt < I2C_MAX_RETRIES));
+ }
+ return rv;
+}
+
+static int pi3dpx1207_set_mux(int port, mux_state_t mux_state)
+{
+ int rv = EC_SUCCESS;
+ uint8_t mode_val = PI3DPX1207_MODE_WATCHDOG_EN;
+
+ const int i2c_port = usb_retimers[port].i2c_port;
+ const uint16_t i2c_addr_flags = usb_retimers[port].i2c_addr_flags;
+ const int gpio_enable = usb_retimers[port].gpio_enable;
+ const int gpio_dp_enable = usb_retimers[port].gpio_dp_enable;
+
+ /* USB */
+ if (mux_state & MUX_USB_ENABLED) {
+ gpio_or_ioex_set_level(gpio_enable, 1);
+ /* USB with DP */
+ if (mux_state & MUX_DP_ENABLED) {
+ gpio_or_ioex_set_level(gpio_dp_enable, 1);
+ mode_val |= (mux_state & MUX_POLARITY_INVERTED)
+ ? PI3DPX1207_MODE_CONF_USB_DP_FLIP
+ : PI3DPX1207_MODE_CONF_USB_DP;
+ }
+ /* USB without DP */
+ else {
+ gpio_or_ioex_set_level(gpio_dp_enable, 0);
+ mode_val |= (mux_state & MUX_POLARITY_INVERTED)
+ ? PI3DPX1207_MODE_CONF_USB_FLIP
+ : PI3DPX1207_MODE_CONF_USB;
+ }
+ }
+ /* DP without USB */
+ else if (mux_state & MUX_DP_ENABLED) {
+ gpio_or_ioex_set_level(gpio_enable, 1);
+ gpio_or_ioex_set_level(gpio_dp_enable, 1);
+ mode_val |= (mux_state & MUX_POLARITY_INVERTED)
+ ? PI3DPX1207_MODE_CONF_DP_FLIP
+ : PI3DPX1207_MODE_CONF_DP;
+ }
+ /* Nothing enabled, power down the retimer */
+ else {
+ gpio_or_ioex_set_level(gpio_enable, 0);
+ gpio_or_ioex_set_level(gpio_dp_enable, 0);
+ return EC_SUCCESS;
+ }
+
+ /* Write the retimer config byte */
+ rv = pi3dpx1207_i2c_write(i2c_port, i2c_addr_flags,
+ PI3DPX1207_MODE_OFFSET,
+ mode_val);
+ return rv;
+}
+
+const struct usb_retimer_driver pi3dpx1207_usb_retimer = {
+ .set = pi3dpx1207_set_mux,
+};
diff --git a/driver/retimer/pi3dpx1207.h b/driver/retimer/pi3dpx1207.h
new file mode 100644
index 0000000000..7b3c3047f2
--- /dev/null
+++ b/driver/retimer/pi3dpx1207.h
@@ -0,0 +1,44 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * PI3DPX1207 retimer.
+ */
+
+#ifndef __CROS_EC_USB_RETIMER_PI3PDX1207_H
+#define __CROS_EC_USB_RETIMER_PI3PDX1207_H
+
+#define PI3DPX1207_I2C_ADDR_FLAGS 0x57
+#define PI3DPX1207_NUM_REGISTERS 32
+
+/* Register Offset 0 - Revision and Vendor ID */
+#define PI3DPX1207_VID_OFFSET 0
+
+#define PI3DPX1207B_VID 0x03
+#define PI3DPX1207C_VID 0x13
+
+/* Register Offset 1 - Device Type/ID */
+#define PI3DPX1207_DID_OFFSET 1
+
+#define PI3DPX1207_DID_ACTIVE_MUX 0x11
+
+/* Register Offset 3 - Mode Control */
+#define PI3DPX1207_MODE_OFFSET 3
+
+#define PI3DPX1207_MODE_WATCHDOG_EN 0x02
+
+#define PI3DPX1207B_MODE_GEN_APP_EN 0x08
+
+#define PI3DPX1207_MODE_CONF_SAFE 0x00
+#define PI3DPX1207_MODE_CONF_DP 0x20
+#define PI3DPX1207_MODE_CONF_DP_FLIP 0x30
+#define PI3DPX1207_MODE_CONF_USB 0x40
+#define PI3DPX1207_MODE_CONF_USB_FLIP 0x50
+#define PI3DPX1207_MODE_CONF_USB_DP 0x60
+#define PI3DPX1207_MODE_CONF_USB_DP_FLIP 0x70
+#define PI3DPX1207_MODE_CONF_USB_SUPER 0xC0
+
+/* Supported USB retimer drivers */
+extern const struct usb_retimer_driver pi3dpx1207_usb_retimer;
+
+#endif /* __CROS_EC_USB_RETIMER_PI3PDX1207_H */
diff --git a/driver/usb_mux/usb_mux.c b/driver/usb_mux/usb_mux.c
index 122f71d773..be150c8741 100644
--- a/driver/usb_mux/usb_mux.c
+++ b/driver/usb_mux/usb_mux.c
@@ -68,6 +68,19 @@ void usb_mux_init(int port)
return;
}
+ if (IS_ENABLED(CONFIG_USBC_MUX_RETIMER)) {
+ const struct usb_retimer *retimer = &usb_retimers[port];
+
+ if (retimer->driver && retimer->driver->init) {
+ res = retimer->driver->init(port);
+ if (res) {
+ CPRINTS("Err: init retimer port(%d): %d",
+ port, res);
+ return;
+ }
+ }
+ }
+
/* Device is always out of LPM after initialization. */
flags[port] &= ~USB_MUX_FLAG_IN_LPM;
@@ -93,10 +106,9 @@ void usb_mux_set(int port, enum typec_mux mux_mode,
const int should_enter_low_power_mode =
mux_mode == TYPEC_MUX_NONE && usb_mode == USB_SWITCH_DISCONNECT;
-#ifdef CONFIG_USB_CHARGER
/* Configure USB2.0 */
- usb_charger_set_switches(port, usb_mode);
-#endif
+ if (IS_ENABLED(CONFIG_USB_CHARGER))
+ usb_charger_set_switches(port, usb_mode);
/*
* Don't wake device up just to put it back to sleep. Low power mode
@@ -116,6 +128,19 @@ void usb_mux_set(int port, enum typec_mux mux_mode,
return;
}
+ if (IS_ENABLED(CONFIG_USBC_MUX_RETIMER)) {
+ const struct usb_retimer *retimer = &usb_retimers[port];
+
+ if (retimer->driver && retimer->driver->set) {
+ res = retimer->driver->set(port, mux_state);
+ if (res) {
+ CPRINTS("Err: set retimer port(%d): %d",
+ port, res);
+ return;
+ }
+ }
+ }
+
if (enable_debug_prints)
CPRINTS(
"usb/dp mux: port(%d) typec_mux(%d) usb2(%d) polarity(%d)",
@@ -241,12 +266,11 @@ static enum ec_status hc_usb_pd_mux_info(struct host_cmd_handler_args *args)
if (mux->driver->get(port, &r->flags) != EC_SUCCESS)
return EC_RES_ERROR;
-#ifdef CONFIG_USB_MUX_VIRTUAL
/* Clear HPD IRQ event since we're about to inform host of it. */
- if ((r->flags & USB_PD_MUX_HPD_IRQ) &&
- mux->hpd_update == &virtual_hpd_update)
+ if (IS_ENABLED(CONFIG_USB_MUX_VIRTUAL) &&
+ (r->flags & USB_PD_MUX_HPD_IRQ) &&
+ (mux->hpd_update == &virtual_hpd_update))
mux->hpd_update(port, r->flags & USB_PD_MUX_HPD_LVL, 0);
-#endif
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
diff --git a/include/config.h b/include/config.h
index 60f8768c12..f7b2ee83e5 100644
--- a/include/config.h
+++ b/include/config.h
@@ -3808,9 +3808,10 @@
#undef CONFIG_USBC_MUX_RETIMER
/*
- * Type-C retimer drivers to be used in on-board applications.
+ * Type-C retimer drivers to be used.
*/
#undef CONFIG_USBC_RETIMER_INTEL_BB
+#undef CONFIG_USBC_RETIMER_PI3DPX1207
/*
* Adds an EC console command to erase the ANX7447 OCM flash.
@@ -4698,6 +4699,10 @@
#define CONFIG_USBC_VIRTUAL_MUX_RETIMER
#endif
+#ifdef CONFIG_USBC_RETIMER_PI3DPX1207
+#define CONFIG_USBC_MUX_RETIMER
+#endif
+
/*****************************************************************************/
/*
* Define CONFIG_LIBCRYPTOC if a board needs to read secret data from the
diff --git a/include/usb_mux.h b/include/usb_mux.h
index 88fb10b549..126e7da4fa 100644
--- a/include/usb_mux.h
+++ b/include/usb_mux.h
@@ -139,6 +139,62 @@ void virtual_hpd_update(int port, int hpd_lvl, int hpd_irq);
extern struct usb_mux usb_muxes[];
/*
+ * Retimer driver function pointers
+ *
+ * The retimer driver is driven by calls to the MUX API. These are not
+ * called directly anywhere else in the code.
+ */
+struct usb_retimer_driver {
+ /**
+ * Initialize USB retimer. This is called every time the MUX is
+ * access after being put in a fully disconnected state (low power
+ * mode).
+ *
+ * @param port usb port of redriver (not port_addr)
+ * @return EC_SUCCESS on success, non-zero error code on failure.
+ */
+ int (*init)(int port);
+
+ /**
+ * Set USB retimer state.
+ *
+ * @param port usb port of retimer (not port_addr)
+ * @param mux_state State to set retimer mode to.
+ * @return EC_SUCCESS on success, non-zero error code on failure.
+ */
+ int (*set)(int port, mux_state_t mux_state);
+};
+
+/* Describes a USB retimer present in the system */
+struct usb_retimer {
+ /*
+ * All of the fields are provided on an as needed basis.
+ * If your retimer does not perform I2C operations, then
+ * values would not be set. This includes the driver
+ * field, which would indicate no retimer driver is to
+ * be called. Values that are not specifically popuplated
+ * shall be 0/NULL.
+ */
+
+ /* I2C port and slave address */
+ const int i2c_port;
+ const uint16_t i2c_addr_flags;
+
+ /* GPIOs for enabling the retimer and DP mode */
+ const int gpio_enable;
+ const int gpio_dp_enable;
+
+ /* Driver interfaces for this retimer */
+ const struct usb_retimer_driver *driver;
+};
+
+/*
+ * USB retimers present in system, ordered by PD port #, defined at
+ * board-level
+ */
+extern struct usb_retimer usb_retimers[];
+
+/*
* Helper methods that either use tcpc communication or direct i2c
* communication depending on how the TCPC/MUX device is configured.
*/