From 1fa485defa9cf6d663afdc7a44f924e97148b4ac Mon Sep 17 00:00:00 2001 From: Marco Chen Date: Fri, 17 Jul 2020 14:50:42 +0800 Subject: ps8xxx: support multiple chips in the runtime BUG=b:159082424 BRANCH=none TEST=verify the same EC image can work on devices with PS8751 or PS8755 for src / snk roles. Signed-off-by: Marco Chen Change-Id: I3a743666a4ccbcae37ecb6f0d6657122cf9c5a69 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2304237 Reviewed-by: Jett Rink (cherry picked from commit d4013027f0f1c95397363b21761085c0d08a6825) Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2373850 Reviewed-by: Henry Sun Commit-Queue: Jett Rink --- driver/tcpm/ps8xxx.c | 308 ++++++++++++++++++++++++++++++++++++++------------- driver/tcpm/ps8xxx.h | 56 +++++----- include/config.h | 37 ++++++- 3 files changed, 290 insertions(+), 111 deletions(-) diff --git a/driver/tcpm/ps8xxx.c b/driver/tcpm/ps8xxx.c index e8cdde9fde..da008f0833 100644 --- a/driver/tcpm/ps8xxx.c +++ b/driver/tcpm/ps8xxx.c @@ -15,6 +15,7 @@ */ #include "common.h" +#include "console.h" #include "ps8xxx.h" #include "tcpci.h" #include "tcpm.h" @@ -38,12 +39,188 @@ #endif +#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args) +#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args) + +/* + * The product_id per ports here is expected to be set in callback function - + * .init of tcpm_drv by calling board_get_ps8xxx_product_id(). + * + * In case of CONFIG_USB_PD_TCPM_MULTI_PS8XXX enabled, board code should + * override the board_get_ps8xxx_product_id() for getting the correct id. + */ +static uint16_t product_id[CONFIG_USB_PD_PORT_MAX_COUNT]; + /* * timestamp of the next possible toggle to ensure the 2-ms spacing * between IRQ_HPD. */ static uint64_t hpd_deadline[CONFIG_USB_PD_PORT_MAX_COUNT]; +#if defined(CONFIG_USB_PD_TCPM_PS8705) || \ + defined(CONFIG_USB_PD_TCPM_PS8751) || \ + defined(CONFIG_USB_PD_TCPM_PS8755) || \ + defined(CONFIG_USB_PD_TCPM_PS8805) +/* + * DCI is enabled by default and burns about 40 mW when the port is in + * USB2 mode or when a C-to-A dongle is attached, so force it off. + */ + +static int ps8xxx_addr_dci_disable(int port, int i2c_addr, int i2c_reg) +{ + int status; + int dci; + + status = tcpc_addr_read(port, i2c_addr, i2c_reg, &dci); + if (status != EC_SUCCESS) + return status; + if ((dci & PS8XXX_REG_MUX_USB_DCI_CFG_MODE_MASK) != + PS8XXX_REG_MUX_USB_DCI_CFG_MODE_OFF) { + dci &= ~PS8XXX_REG_MUX_USB_DCI_CFG_MODE_MASK; + dci |= PS8XXX_REG_MUX_USB_DCI_CFG_MODE_OFF; + if (tcpc_addr_write(port, i2c_addr, i2c_reg, dci) != EC_SUCCESS) + return status; + } + return EC_SUCCESS; +} +#endif /* CONFIG_USB_PD_TCPM_PS875[15] || CONFIG_USB_PD_TCPM_PS8[78]05 */ + +#if defined(CONFIG_USB_PD_TCPM_PS8705) || \ + defined(CONFIG_USB_PD_TCPM_PS8755) || \ + defined(CONFIG_USB_PD_TCPM_PS8805) +static int ps8705_dci_disable(int port) +{ + int p1_addr; + int p3_addr; + int regval; + int rv; + + /* Enable access to debug pages. */ + p3_addr = tcpc_config[port].i2c_info.addr; + rv = tcpc_addr_read(port, p3_addr, PS8XXX_REG_I2C_DEBUGGING_ENABLE, + ®val); + if (rv) + return rv; + + rv = tcpc_addr_write(port, p3_addr, PS8XXX_REG_I2C_DEBUGGING_ENABLE, + PS8XXX_REG_I2C_DEBUGGING_ENABLE_ON); + + /* Disable Auto DCI */ + p1_addr = PS8751_P3_TO_P1(p3_addr); + rv = ps8xxx_addr_dci_disable(port, p1_addr, + PS8XXX_P1_REG_MUX_USB_DCI_CFG); + + /* + * PS8705/PS8755/PS8805 will automatically re-assert bit:0 on the + * PS8XXX_REG_I2C_DEBUGGING_ENABLE register. + */ + return rv; +} +#endif /* CONFIG_USB_PD_TCPM_PS8755 || CONFIG_USB_PD_TCPM_PS8[78]05 */ + +#ifdef CONFIG_USB_PD_TCPM_PS8751 +static int ps8751_dci_disable(int port) +{ + int p3_addr; + + p3_addr = tcpc_config[port].i2c_info.addr; + return ps8xxx_addr_dci_disable(port, p3_addr, + PS8751_REG_MUX_USB_DCI_CFG); +} +#endif /* CONFIG_USB_PD_TCPM_PS8751 */ + +#ifdef CONFIG_USB_PD_TCPM_PS8815 +static int ps8815_dci_disable(int port) +{ + /* DCI is disabled on the ps8815 */ + return EC_SUCCESS; +} +#endif /* CONFIG_USB_PD_TCPM_PS8815 */ + +enum ps8xxx_variant_regs { + REG_FIRST_INDEX = 0, + /* NOTE: The rev will read as 0x00 if the FW has malfunctioned. */ + REG_FW_VER = REG_FIRST_INDEX, + REG_MAX_COUNT, +}; + +struct ps8xxx_variant_map { + int product_id; + int (*dci_disable_ptr)(int port); + int reg_map[REG_MAX_COUNT]; +}; + +/* + * variant_map here is leveraged to lookup from ps8xxx_variant_regs to i2c + * register and corresponding dci_disable function based on product_id. + */ +static struct ps8xxx_variant_map variant_map[] = { +#ifdef CONFIG_USB_PD_TCPM_PS8705 + { + PS8705_PRODUCT_ID, + ps8705_dci_disable, + { + [REG_FW_VER] = 0x82, + } + }, +#endif +#ifdef CONFIG_USB_PD_TCPM_PS8751 + { + PS8751_PRODUCT_ID, + ps8751_dci_disable, + { + [REG_FW_VER] = 0x90, + } + }, +#endif +#ifdef CONFIG_USB_PD_TCPM_PS8755 + { + PS8755_PRODUCT_ID, + ps8705_dci_disable, + { + [REG_FW_VER] = 0x82, + } + }, +#endif +#ifdef CONFIG_USB_PD_TCPM_PS8805 + { + PS8805_PRODUCT_ID, + ps8705_dci_disable, + { + [REG_FW_VER] = 0x82, + } + }, +#endif +#ifdef CONFIG_USB_PD_TCPM_PS8815 + { + PS8815_PRODUCT_ID, + ps8815_dci_disable, + { + [REG_FW_VER] = 0x82, + } + }, +#endif +}; + +static int get_reg_by_product(const int port, + const enum ps8xxx_variant_regs reg) +{ + int i; + + if (reg < REG_FIRST_INDEX || reg >= REG_MAX_COUNT) + return INT32_MAX; + + for (i = 0; i < ARRAY_SIZE(variant_map); i++) { + if (product_id[port] == + variant_map[i].product_id) { + return variant_map[i].reg_map[reg]; + } + } + + CPRINTS("%s: failed to get register number by product_id.", __func__); + return INT32_MAX; +} + static int dp_set_hpd(int port, int enable) { int reg; @@ -75,6 +252,33 @@ static int dp_set_irq(int port, int enable) return mux_write(port, MUX_IN_HPD_ASSERTION_REG, reg); } +__overridable +uint16_t board_get_ps8xxx_product_id(int port) +{ + /* Board supporting multiple chip sources in ps8xxx.c MUST override this + * function to judge the real chip source for this board. For example, + * SKU ID / strappings / provisioning in the factory can be the ways. + */ + + if (IS_ENABLED(CONFIG_USB_PD_TCPM_MULTI_PS8XXX)) { + CPRINTS("%s: board should override this function.", __func__); + return 0; + } else if (IS_ENABLED(CONFIG_USB_PD_TCPM_PS8705)) { + return PS8705_PRODUCT_ID; + } else if (IS_ENABLED(CONFIG_USB_PD_TCPM_PS8751)) { + return PS8751_PRODUCT_ID; + } else if (IS_ENABLED(CONFIG_USB_PD_TCPM_PS8755)) { + return PS8755_PRODUCT_ID; + } else if (IS_ENABLED(CONFIG_USB_PD_TCPM_PS8805)) { + return PS8805_PRODUCT_ID; + } else if (IS_ENABLED(CONFIG_USB_PD_TCPM_PS8815)) { + return PS8815_PRODUCT_ID; + } + + CPRINTS("%s: Any new product id is not defined here?", __func__); + return 0; +} + void ps8xxx_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq) { dp_set_hpd(port, hpd_lvl); @@ -127,8 +331,9 @@ static int ps8xxx_tcpm_release(int port) { int version; int status; + int reg = get_reg_by_product(port, REG_FW_VER); - status = tcpc_read(port, FW_VER_REG, &version); + status = tcpc_read(port, reg, &version); if (status != 0) { /* wait for chip to wake up */ msleep(10); @@ -141,26 +346,35 @@ static int ps8xxx_get_chip_info(int port, int live, struct ec_response_pd_chip_info_v1 **chip_info) { int val; + int reg; int rv = tcpci_get_chip_info(port, live, chip_info); if (rv) return rv; if (!live) { + product_id[port] = board_get_ps8xxx_product_id(port); (*chip_info)->vendor_id = PS8XXX_VENDOR_ID; - (*chip_info)->product_id = PS8XXX_PRODUCT_ID; + (*chip_info)->product_id = product_id[port]; } if ((*chip_info)->fw_version_number == 0 || (*chip_info)->fw_version_number == -1 || live) { - rv = tcpc_read(port, FW_VER_REG, &val); - - if (rv) + reg = get_reg_by_product(port, REG_FW_VER); + rv = tcpc_read(port, reg, &val); + if (rv != EC_SUCCESS) return rv; (*chip_info)->fw_version_number = val; } + /* Treat unexpected values as error (FW not initiated from reset) */ + if (live && ( + (*chip_info)->vendor_id != PS8XXX_VENDOR_ID || + (*chip_info)->product_id != board_get_ps8xxx_product_id(port) || + (*chip_info)->fw_version_number == 0)) + return EC_ERROR_UNKNOWN; + #if defined(CONFIG_USB_PD_TCPM_PS8751) && \ defined(CONFIG_USB_PD_VBUS_DETECT_TCPC) /* @@ -173,91 +387,25 @@ static int ps8xxx_get_chip_info(int port, int live, return rv; } - -#if defined(CONFIG_USB_PD_TCPM_PS8705) || \ - defined(CONFIG_USB_PD_TCPM_PS8751) || \ - defined(CONFIG_USB_PD_TCPM_PS8755) || \ - defined(CONFIG_USB_PD_TCPM_PS8805) -/* - * DCI is enabled by default and burns about 40 mW when the port is in - * USB2 mode or when a C-to-A dongle is attached, so force it off. - */ - -static int ps8xxx_addr_dci_disable(int port, int i2c_addr, int i2c_reg) -{ - int status; - int dci; - - status = tcpc_addr_read(port, i2c_addr, i2c_reg, &dci); - if (status != EC_SUCCESS) - return status; - if ((dci & PS8XXX_REG_MUX_USB_DCI_CFG_MODE_MASK) != - PS8XXX_REG_MUX_USB_DCI_CFG_MODE_OFF) { - dci &= ~PS8XXX_REG_MUX_USB_DCI_CFG_MODE_MASK; - dci |= PS8XXX_REG_MUX_USB_DCI_CFG_MODE_OFF; - if (tcpc_addr_write(port, i2c_addr, i2c_reg, dci) != EC_SUCCESS) - return status; - } - return EC_SUCCESS; -} -#endif /* CONFIG_USB_PD_TCPM_PS875[15] || CONFIG_USB_PD_TCPM_PS8[78]05 */ - -#if defined(CONFIG_USB_PD_TCPM_PS8705) || \ - defined(CONFIG_USB_PD_TCPM_PS8755) || \ - defined(CONFIG_USB_PD_TCPM_PS8805) -static int ps8xxx_dci_disable(int port) -{ - int p1_addr; - int p3_addr; - int regval; - int rv; - - /* Enable access to debug pages. */ - p3_addr = tcpc_config[port].i2c_info.addr; - rv = tcpc_addr_read(port, p3_addr, PS8XXX_REG_I2C_DEBUGGING_ENABLE, - ®val); - if (rv) - return rv; - - rv = tcpc_addr_write(port, p3_addr, PS8XXX_REG_I2C_DEBUGGING_ENABLE, - PS8XXX_REG_I2C_DEBUGGING_ENABLE_ON); - - /* Disable Auto DCI */ - p1_addr = PS8751_P3_TO_P1(p3_addr); - rv = ps8xxx_addr_dci_disable(port, p1_addr, - PS8XXX_P1_REG_MUX_USB_DCI_CFG); - - /* - * PS8705/PS8755/PS8805 will automatically re-assert bit:0 on the - * PS8XXX_REG_I2C_DEBUGGING_ENABLE register. - */ - return rv; -} -#endif /* CONFIG_USB_PD_TCPM_PS8755 || CONFIG_USB_PD_TCPM_PS8[78]05 */ - -#ifdef CONFIG_USB_PD_TCPM_PS8751 static int ps8xxx_dci_disable(int port) { - int p3_addr; + int i; - p3_addr = tcpc_config[port].i2c_info.addr; - return ps8xxx_addr_dci_disable(port, p3_addr, - PS8751_REG_MUX_USB_DCI_CFG); -} -#endif /* CONFIG_USB_PD_TCPM_PS8751 */ + for (i = 0; i < ARRAY_SIZE(variant_map); i++) { + if (product_id[port] == variant_map[i].product_id) + return variant_map[i].dci_disable_ptr(port); + } -#ifdef CONFIG_USB_PD_TCPM_PS8815 -static int ps8xxx_dci_disable(int port) -{ - /* DCI is disabled on the ps8815 */ - return EC_SUCCESS; + CPRINTS("%s: failed to get dci_disable function pointers.", __func__); + return EC_ERROR_INVAL; } -#endif /* CONFIG_USB_PD_TCPM_PS8815 */ static int ps8xxx_tcpm_init(int port) { int status; + product_id[port] = board_get_ps8xxx_product_id(port); + status = tcpci_tcpm_init(port); if (status != EC_SUCCESS) return status; diff --git a/driver/tcpm/ps8xxx.h b/driver/tcpm/ps8xxx.h index aa03118aa2..d120fad1a5 100644 --- a/driver/tcpm/ps8xxx.h +++ b/driver/tcpm/ps8xxx.h @@ -49,11 +49,17 @@ #define PS8XXX_P1_REG_MUX_USB_DCI_CFG 0x4B +/* NOTE: The Product ID will read as 0x8803 if the firmware has malfunctioned in + * 8705, 8755 and 8805. + */ +#define PS8705_PRODUCT_ID 0x8705 +#define PS8751_PRODUCT_ID 0x8751 +#define PS8755_PRODUCT_ID 0x8755 +#define PS8805_PRODUCT_ID 0x8805 +#define PS8815_PRODUCT_ID 0x8815 + #if defined(CONFIG_USB_PD_TCPM_PS8751) /* Vendor defined registers */ -#define PS8XXX_PRODUCT_ID 0x8751 - -#define FW_VER_REG 0x90 #define PS8XXX_REG_VENDOR_ID_L 0x00 #define PS8XXX_REG_VENDOR_ID_H 0x01 #define PS8XXX_REG_MUX_DP_EQ_CONFIGURATION 0xD3 @@ -61,40 +67,32 @@ #define PS8XXX_REG_MUX_USB_C2SS_EQ 0xE7 #define PS8XXX_REG_MUX_USB_C2SS_HS_THRESHOLD 0xE8 #define PS8751_REG_MUX_USB_DCI_CFG 0xED +#endif -#elif defined(CONFIG_USB_PD_TCPM_PS8755) -/* Vendor defined registers */ -/* NOTE: The Product ID will read as 0x8803 if the firmware has malfunctioned */ -#define PS8XXX_PRODUCT_ID 0x8755 - -#define FW_VER_REG 0x82 - -#elif defined(CONFIG_USB_PD_TCPM_PS8705) -/* Vendor defined registers */ -/* NOTE: The Product ID will read as 0x8803 if the firmware has malfunctioned */ -#define PS8XXX_PRODUCT_ID 0x8705 - -/* NOTE: The revision will read as 0x00 if the firmware has malfunctioned. */ -#define FW_VER_REG 0x82 - -#elif defined(CONFIG_USB_PD_TCPM_PS8805) -/* Vendor defined registers */ -/* NOTE: The Product ID will read as 0x8803 if the firmware has malfunctioned */ -#define PS8XXX_PRODUCT_ID 0x8805 - -#define FW_VER_REG 0x82 - -#elif defined(CONFIG_USB_PD_TCPM_PS8815) +#if defined(CONFIG_USB_PD_TCPM_PS8815) /* Vendor defined registers */ -#define PS8XXX_PRODUCT_ID 0x8815 - -#define FW_VER_REG 0x82 +#define PS8815_P1_REG_HW_REVISION 0xF0 #endif extern const struct tcpm_drv ps8xxx_tcpm_drv; void ps8xxx_tcpc_update_hpd_status(int port, int hpd_lvl, int hpd_irq); +/** + * Board specific callback to judge and provide which chip source of PS8XXX + * series supported by this driver per specific port. + * + * If the board supports only one single source then there is no nencessary to + * provide the __override version. + * + * If board supports two sources or above (with CONFIG_USB_PD_TCPM_MULTI_PS8XXX) + * then the __override version is mandatory. + * + * @param port TCPC port number. + */ +__override_proto +uint16_t board_get_ps8xxx_product_id(int port); + #ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC extern struct i2c_stress_test_dev ps8xxx_i2c_stress_test_dev; #endif /* defined(CONFIG_CMD_I2C_STRESS_TEST_TCPC) */ diff --git a/include/config.h b/include/config.h index e33657f32b..141799dac9 100644 --- a/include/config.h +++ b/include/config.h @@ -3299,13 +3299,24 @@ #undef CONFIG_USB_PD_TCPM_ANX741X #undef CONFIG_USB_PD_TCPM_ANX7447 #undef CONFIG_USB_PD_TCPM_ANX7688 +#undef CONFIG_USB_PD_TCPM_MT6370 +#undef CONFIG_USB_PD_TCPM_TUSB422 + +/* PS8XXX series are all supported by a single driver with a build time config + * listed below (CONFIG_USB_PD_TCPM_PS*) defined to enable the specific product. + * + * If a board with the same EC FW is expected to support multiple products here + * then CONFIG_USB_PD_TCPM_MULTI_PS8XXX MUST be defined then we can enable more + * than one product config to support them in the runtime. In this case, board + * is responsible to override function of board_get_ps8xxx_product_id in order + * to provide the product id per port. + */ +#undef CONFIG_USB_PD_TCPM_MULTI_PS8XXX #undef CONFIG_USB_PD_TCPM_PS8751 #undef CONFIG_USB_PD_TCPM_PS8755 #undef CONFIG_USB_PD_TCPM_PS8705 #undef CONFIG_USB_PD_TCPM_PS8805 #undef CONFIG_USB_PD_TCPM_PS8815 -#undef CONFIG_USB_PD_TCPM_MT6370 -#undef CONFIG_USB_PD_TCPM_TUSB422 /* * Adds an EC console command to erase the ANX7447 OCM flash. @@ -4226,5 +4237,27 @@ #error "CONFIG_DPTF_MULTI_PROFILE can be set only when CONFIG_DPTF is set." #endif /* CONFIG_DPTF_MULTI_PROFILE && !CONFIG_DPTF */ +#if defined(CONFIG_USB_PD_TCPM_MULTI_PS8XXX) +#if defined(CONFIG_USB_PD_TCPM_PS8705) + \ + defined(CONFIG_USB_PD_TCPM_PS8751) + \ + defined(CONFIG_USB_PD_TCPM_PS8755) + \ + defined(CONFIG_USB_PD_TCPM_PS8805) + \ + defined(CONFIG_USB_PD_TCPM_PS8815) < 2 +#error "Must select 2 CONFIG_USB_PD_TCPM_PS8* or above if " \ + "CONFIG_USB_PD_TCPM_MULTI_PS8XXX is defined." +#endif +#endif /* CONFIG_USB_PD_TCPM_MULTI_PS8XXX */ + +#if defined(CONFIG_USB_PD_TCPM_PS8705) + \ + defined(CONFIG_USB_PD_TCPM_PS8751) + \ + defined(CONFIG_USB_PD_TCPM_PS8755) + \ + defined(CONFIG_USB_PD_TCPM_PS8805) + \ + defined(CONFIG_USB_PD_TCPM_PS8815) > 1 +#if !defined(CONFIG_USB_PD_TCPM_MULTI_PS8XXX) +#error "CONFIG_USB_PD_TCPM_MULTI_PS8XXX MUST be defined if more than one " \ + "CONFIG_USB_PD_TCPM_PS8* are intended to support in a board." +#endif +#endif /* defined(CONFIG_USB_PD_TCPM_PS8705) + ... */ + #endif /* __CROS_EC_CONFIG_H */ -- cgit v1.2.1