diff options
Diffstat (limited to 'driver')
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | driver/retimer/pi3dpx1207.c | 115 | ||||
-rw-r--r-- | driver/retimer/pi3dpx1207.h | 44 | ||||
-rw-r--r-- | driver/usb_mux/usb_mux.c | 38 |
4 files changed, 191 insertions, 7 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; |