diff options
-rw-r--r-- | board/reef/board.h | 1 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 99 | ||||
-rw-r--r-- | driver/tcpm/anx74xx.c | 48 | ||||
-rw-r--r-- | driver/tcpm/tcpci.c | 59 | ||||
-rw-r--r-- | driver/tcpm/tcpci.h | 3 | ||||
-rw-r--r-- | driver/tcpm/tcpm.h | 13 | ||||
-rw-r--r-- | include/config.h | 3 | ||||
-rw-r--r-- | include/usb_pd.h | 5 | ||||
-rw-r--r-- | include/usb_pd_tcpm.h | 11 |
9 files changed, 208 insertions, 34 deletions
diff --git a/board/reef/board.h b/board/reef/board.h index 3f6464131c..95b0502d07 100644 --- a/board/reef/board.h +++ b/board/reef/board.h @@ -80,6 +80,7 @@ #define CONFIG_USB_PD_ALT_MODE_DFP #define CONFIG_USB_PD_CUSTOM_VDM #define CONFIG_USB_PD_DUAL_ROLE +#define CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE #define CONFIG_USB_PD_DISCHARGE #define CONFIG_USB_PD_DISCHARGE_TCPC #define CONFIG_USB_PD_LOGGING diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index 8580696f4e..96d9d9c8eb 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -189,6 +189,9 @@ static const char * const pd_state_names[] = { #endif /* CONFIG_USB_PD_DUAL_ROLE */ "SOFT_RESET", "HARD_RESET_SEND", "HARD_RESET_EXECUTE", "BIST_RX", "BIST_TX", +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + "DRP_AUTO_TOGGLE", +#endif }; BUILD_ASSERT(ARRAY_SIZE(pd_state_names) == PD_STATE_COUNT); #endif @@ -215,6 +218,11 @@ int pd_is_connected(int port) if (pd[port].task_state == PD_STATE_DISABLED) return 0; +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + if (pd[port].task_state == PD_STATE_DRP_AUTO_TOGGLE) + return 0; +#endif + return DUAL_ROLE_IF_ELSE(port, /* sink */ pd[port].task_state != PD_STATE_SNK_DISCONNECTED && @@ -294,16 +302,21 @@ static inline void set_state(int port, enum pd_states next_state) if (last_state == next_state) return; + #ifdef CONFIG_USB_PD_DUAL_ROLE +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + /* Clear flag to allow DRP auto toggle when possible */ + if (last_state != PD_STATE_DRP_AUTO_TOGGLE) + pd[port].flags &= ~PD_FLAGS_TCPC_DRP_TOGGLE; +#endif + /* Ignore dual-role toggling between sink and source */ if ((last_state == PD_STATE_SNK_DISCONNECTED && next_state == PD_STATE_SRC_DISCONNECTED) || (last_state == PD_STATE_SRC_DISCONNECTED && next_state == PD_STATE_SNK_DISCONNECTED)) return; -#endif -#ifdef CONFIG_USB_PD_DUAL_ROLE if (next_state == PD_STATE_SRC_DISCONNECTED || next_state == PD_STATE_SNK_DISCONNECTED) { /* Clear the input current limit */ @@ -1593,7 +1606,12 @@ void pd_task(void) CPRINTS("TCPC p%d reset!", port); if (tcpm_init(port) != EC_SUCCESS) CPRINTS("TCPC p%d init failed", port); +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + } + if ((evt & PD_EVENT_TCPC_RESET) && + (pd[port].task_state != PD_STATE_DRP_AUTO_TOGGLE)) { +#endif /* Ensure CC termination is default */ tcpm_set_cc(port, PD_ROLE_DEFAULT == PD_ROLE_SOURCE ? TYPEC_CC_RP : @@ -1644,6 +1662,21 @@ void pd_task(void) timeout = 10*MSEC; tcpm_get_cc(port, &cc1, &cc2); +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + /* + * Attempt TCPC auto DRP toggle if it is + * not already auto toggling and not try.src + */ + if (!(pd[port].flags & PD_FLAGS_TCPC_DRP_TOGGLE) && + !(pd[port].flags & PD_FLAGS_TRY_SRC) && + (cc1 == TYPEC_CC_VOLT_OPEN && + cc2 == TYPEC_CC_VOLT_OPEN)) { + set_state(port, PD_STATE_DRP_AUTO_TOGGLE); + timeout = 2*MSEC; + break; + } +#endif + /* Vnc monitoring */ if ((cc1 == TYPEC_CC_VOLT_RD || cc2 == TYPEC_CC_VOLT_RD) || @@ -2112,6 +2145,21 @@ void pd_task(void) #endif tcpm_get_cc(port, &cc1, &cc2); +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + /* + * Attempt TCPC auto DRP toggle if it is + * not already auto toggling and not try.src + */ + if (!(pd[port].flags & PD_FLAGS_TCPC_DRP_TOGGLE) && + !(pd[port].flags & PD_FLAGS_TRY_SRC) && + (cc1 == TYPEC_CC_VOLT_OPEN && + cc2 == TYPEC_CC_VOLT_OPEN)) { + set_state(port, PD_STATE_DRP_AUTO_TOGGLE); + timeout = 2*MSEC; + break; + } +#endif + /* Source connection monitoring */ if (cc1 != TYPEC_CC_VOLT_OPEN || cc2 != TYPEC_CC_VOLT_OPEN) { @@ -2710,6 +2758,53 @@ defined(CONFIG_CASE_CLOSED_DEBUG_EXTERNAL) PD_STATE_SRC_DISCONNECTED)); break; #endif +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + case PD_STATE_DRP_AUTO_TOGGLE: + { + enum pd_states next_state; + + /* Check for connection */ + tcpm_get_cc(port, &cc1, &cc2); + + /* Set to appropriate port state */ + if (cc1 == TYPEC_CC_VOLT_OPEN && + cc2 == TYPEC_CC_VOLT_OPEN) + /* nothing connected, keep toggling*/ + next_state = PD_STATE_DRP_AUTO_TOGGLE; + else if ((cc_is_rp(cc1) || cc_is_rp(cc2)) && + drp_state != PD_DRP_FORCE_SOURCE) + /* SNK allowed unless ForceSRC */ + next_state = PD_STATE_SNK_DISCONNECTED; + else if (((cc1 == TYPEC_CC_VOLT_RD || + cc2 == TYPEC_CC_VOLT_RD) || + (cc1 == TYPEC_CC_VOLT_RA && + cc2 == TYPEC_CC_VOLT_RA)) && + (drp_state != PD_DRP_TOGGLE_OFF && + drp_state != PD_DRP_FORCE_SINK)) + /* SRC allowed unless ForceSNK or Toggle Off */ + next_state = PD_STATE_SRC_DISCONNECTED; + else + /* Anything else, keep toggling */ + next_state = PD_STATE_DRP_AUTO_TOGGLE; + + if (next_state == PD_STATE_SNK_DISCONNECTED) { + tcpm_set_cc(port, TYPEC_CC_RD); + pd[port].power_role = PD_ROLE_SINK; + timeout = 2*MSEC; + } else if (next_state == PD_STATE_SRC_DISCONNECTED) { + tcpm_set_cc(port, TYPEC_CC_RP); + pd[port].power_role = PD_ROLE_SOURCE; + timeout = 2*MSEC; + } else { + tcpm_set_drp_toggle(port); + pd[port].flags |= PD_FLAGS_TCPC_DRP_TOGGLE; + timeout = -1; + } + set_state(port, next_state); + + break; + } +#endif default: break; } diff --git a/driver/tcpm/anx74xx.c b/driver/tcpm/anx74xx.c index c96cab36b0..5e3681e643 100644 --- a/driver/tcpm/anx74xx.c +++ b/driver/tcpm/anx74xx.c @@ -35,7 +35,7 @@ static struct anx_state anx[CONFIG_USB_PD_PORT_COUNT]; static int anx74xx_set_mux(int port, int polarity); /* Save the selected rp value */ -static int selected_rp = TYPEC_RP_USB; +static int selected_rp[CONFIG_USB_PD_PORT_COUNT]; static void anx74xx_tcpm_set_auto_good_crc(int port, int enable) { @@ -472,21 +472,36 @@ static int anx74xx_rp_control(int port, int rp) static int anx74xx_tcpm_select_rp_value(int port, int rp) { /* For ANX3429 cannot get cc correctly when Rp != USB_Default */ - selected_rp = rp; - return 1; + selected_rp[port] = rp; + return EC_SUCCESS; } -static int anx74xx_tcpm_set_cc(int port, int pull) + +static int anx74xx_cc_software_ctrl(int port, int enable) { - int rv = EC_SUCCESS; + int rv; int reg; - /* Enable CC software Control */ - rv |= tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, ®); + rv = tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, ®); if (rv) return EC_ERROR_UNKNOWN; - reg |= ANX74XX_REG_CC_SW_CTRL_ENABLE; + + if (enable) + reg |= ANX74XX_REG_CC_SW_CTRL_ENABLE; + else + reg &= ~ANX74XX_REG_CC_SW_CTRL_ENABLE; + rv |= tcpc_write(port, ANX74XX_REG_CC_SOFTWARE_CTRL, reg); + return rv; +} + +static int anx74xx_tcpm_set_cc(int port, int pull) +{ + int rv = EC_SUCCESS; + int reg; + + /* Enable CC software Control */ + rv = anx74xx_cc_software_ctrl(port, 1); if (rv) return EC_ERROR_UNKNOWN; @@ -515,6 +530,18 @@ static int anx74xx_tcpm_set_cc(int port, int pull) return rv; } +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE +static int anx74xx_tcpc_drp_toggle(int port) +{ + int rv; + + /* Disable CC software Control */ + rv = anx74xx_cc_software_ctrl(port, 0); + + return rv; +} +#endif + static int anx74xx_tcpm_set_polarity(int port, int polarity) { int reg, mux_state, rv = EC_SUCCESS; @@ -638,7 +665,7 @@ static int anx74xx_tcpm_set_rx_enable(int port, int enable) if (enable) { reg &= ~(ANX74XX_REG_IRQ_CC_MSG_INT); anx74xx_tcpm_set_auto_good_crc(port, 1); - anx74xx_rp_control(port, selected_rp); + anx74xx_rp_control(port, selected_rp[port]); } else { /* Disable RX message by masking interrupt */ reg |= (ANX74XX_REG_IRQ_CC_MSG_INT); @@ -879,6 +906,9 @@ const struct tcpm_drv anx74xx_tcpm_drv = { #ifdef CONFIG_USB_PD_DISCHARGE_TCPC .tcpc_discharge_vbus = &anx74xx_tcpc_discharge_vbus, #endif +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + .drp_toggle = &anx74xx_tcpc_drp_toggle, +#endif }; #ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC diff --git a/driver/tcpm/tcpci.c b/driver/tcpm/tcpci.c index f69e8a96d6..a9232e411c 100644 --- a/driver/tcpm/tcpci.c +++ b/driver/tcpm/tcpci.c @@ -17,6 +17,9 @@ static int tcpc_vbus[CONFIG_USB_PD_PORT_COUNT]; +/* Save the selected rp value */ +static int selected_rp[CONFIG_USB_PD_PORT_COUNT]; + static int init_alert_mask(int port) { uint16_t mask; @@ -65,9 +68,12 @@ int tcpci_tcpm_get_cc(int port, int *cc1, int *cc2) rv = tcpc_read(port, TCPC_REG_CC_STATUS, &status); - /* If tcpc read fails, return error */ - if (rv) + /* If tcpc read fails, return error and CC as open */ + if (rv) { + *cc1 = TYPEC_CC_VOLT_OPEN; + *cc2 = TYPEC_CC_VOLT_OPEN; return rv; + } *cc1 = TCPC_REG_CC_STATUS_CC1(status); *cc2 = TCPC_REG_CC_STATUS_CC2(status); @@ -91,15 +97,8 @@ static int tcpci_tcpm_get_power_status(int port, int *status) int tcpci_tcpm_select_rp_value(int port, int rp) { - int reg; - int rv; - - rv = tcpc_read(port, TCPC_REG_ROLE_CTRL, ®); - if (rv) - return rv; - reg = (reg & ~TCPC_REG_ROLE_CTRL_RP_MASK) - | ((rp << 4) & TCPC_REG_ROLE_CTRL_RP_MASK); - return tcpc_write(port, TCPC_REG_ROLE_CTRL, reg); + selected_rp[port] = rp; + return EC_SUCCESS; } #ifdef CONFIG_USB_PD_DISCHARGE_TCPC @@ -119,22 +118,33 @@ static void tcpci_tcpc_discharge_vbus(int port, int enable) } #endif +static int set_role_ctrl(int port, int toggle, int rp, int pull) +{ + return tcpc_write(port, TCPC_REG_ROLE_CTRL, + TCPC_REG_ROLE_CTRL_SET(toggle, rp, pull, pull)); +} + int tcpci_tcpm_set_cc(int port, int pull) { - int reg, rv; - uint8_t rp; + /* Set manual control, and set both CC lines to the same pull */ + return set_role_ctrl(port, 0, selected_rp[port], pull); +} - rv = tcpc_read(port, TCPC_REG_ROLE_CTRL, ®); - if (rv) - return rv; - rp = TCPC_REG_ROLE_CTRL_RP(reg); - /* - * Set manual control of Rp/Rd, and set both CC lines to the same - * pull. - */ - return tcpc_write(port, TCPC_REG_ROLE_CTRL, - TCPC_REG_ROLE_CTRL_SET(0, rp, pull, pull)); +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE +static int tcpci_tcpc_drp_toggle(int port) +{ + int rv; + + /* Set auto drp toggle */ + rv = set_role_ctrl(port, 1, TYPEC_RP_USB, TYPEC_CC_OPEN); + + /* Set Look4Connection command */ + rv |= tcpc_write(port, TCPC_REG_COMMAND, + TCPC_REG_COMMAND_LOOK4CONNECTION); + + return rv; } +#endif int tcpci_tcpm_set_polarity(int port, int polarity) { @@ -408,4 +418,7 @@ const struct tcpm_drv tcpci_tcpm_drv = { #ifdef CONFIG_USB_PD_DISCHARGE_TCPC .tcpc_discharge_vbus = &tcpci_tcpc_discharge_vbus, #endif +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + .drp_toggle = &tcpci_tcpc_drp_toggle, +#endif }; diff --git a/driver/tcpm/tcpci.h b/driver/tcpm/tcpci.h index 141d3daf14..165b206bf1 100644 --- a/driver/tcpm/tcpci.h +++ b/driver/tcpm/tcpci.h @@ -66,6 +66,7 @@ #define TCPC_REG_POWER_CTRL_VCONN(reg) ((reg) & 0x1) #define TCPC_REG_CC_STATUS 0x1d +#define TCPC_REG_CC_STATUS_LOOK4CONNECTION(reg) ((reg & 0x20) >> 5) #define TCPC_REG_CC_STATUS_SET(term, cc1, cc2) \ ((term) << 4 | ((cc2) & 0x3) << 2 | ((cc1) & 0x3)) #define TCPC_REG_CC_STATUS_TERM(reg) (((reg) & 0x10) >> 4) @@ -80,6 +81,8 @@ #define TCPC_REG_FAULT_STATUS 0x1f #define TCPC_REG_COMMAND 0x23 +#define TCPC_REG_COMMAND_LOOK4CONNECTION 0x99 + #define TCPC_REG_DEV_CAP_1 0x24 #define TCPC_REG_DEV_CAP_2 0x26 #define TCPC_REG_STD_INPUT_CAP 0x28 diff --git a/driver/tcpm/tcpm.h b/driver/tcpm/tcpm.h index 7903cccffd..57950dc34d 100644 --- a/driver/tcpm/tcpm.h +++ b/driver/tcpm/tcpm.h @@ -13,6 +13,12 @@ #include "i2c.h" #include "usb_pd_tcpm.h" +#if defined(CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE) && \ + !defined(CONFIG_USB_PD_DUAL_ROLE) +#error "DRP auto toggle requires board to have DRP support" +#error "Please upgrade your board configuration" +#endif + #ifndef CONFIG_USB_PD_TCPC extern const struct tcpc_config_t tcpc_config[]; @@ -130,6 +136,13 @@ static inline void tcpc_discharge_vbus(int port, int enable) tcpc_config[port].drv->tcpc_discharge_vbus(port, enable); } +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE +static inline int tcpm_set_drp_toggle(int port) +{ + return tcpc_config[port].drv->drp_toggle(port); +} +#endif + #ifdef CONFIG_CMD_I2C_STRESS_TEST_TCPC static inline int tcpc_i2c_read(const int port, const int addr, const int reg, int *data) diff --git a/include/config.h b/include/config.h index 33d440d31b..797340ede3 100644 --- a/include/config.h +++ b/include/config.h @@ -2037,6 +2037,9 @@ /* Define if this board can act as a dual-role PD port (source and sink) */ #undef CONFIG_USB_PD_DUAL_ROLE +/* Define if this board can used TCPC-controlled DRP toggle */ +#undef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + /* * Define if VBUS source GPIOs (GPIO_USB_C*_5V_EN) are active-low (and named * (..._L) rather than default active-high. diff --git a/include/usb_pd.h b/include/usb_pd.h index a6be237102..b2cba7542b 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -684,6 +684,9 @@ enum pd_states { PD_STATE_BIST_TX, #endif +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + PD_STATE_DRP_AUTO_TOGGLE, +#endif /* Number of states. Not an actual state. */ PD_STATE_COUNT, }; @@ -693,6 +696,7 @@ enum pd_states { #define PD_FLAGS_PARTNER_DR_DATA (1 << 2) /* port partner is dualrole data */ #define PD_FLAGS_DATA_SWAPPED (1 << 3) /* data swap complete */ #define PD_FLAGS_SNK_CAP_RECVD (1 << 4) /* sink capabilities received */ +#define PD_FLAGS_TCPC_DRP_TOGGLE (1 << 5) /* TCPC-controlled DRP toggling */ #define PD_FLAGS_EXPLICIT_CONTRACT (1 << 6) /* explicit pwr contract in place */ #define PD_FLAGS_VBUS_NEVER_LOW (1 << 7) /* VBUS input has never been low */ #define PD_FLAGS_PREVIOUS_PD_CONN (1 << 8) /* previously PD connected */ @@ -708,6 +712,7 @@ enum pd_states { PD_FLAGS_PARTNER_DR_DATA | \ PD_FLAGS_DATA_SWAPPED | \ PD_FLAGS_SNK_CAP_RECVD | \ + PD_FLAGS_TCPC_DRP_TOGGLE | \ PD_FLAGS_EXPLICIT_CONTRACT | \ PD_FLAGS_PREVIOUS_PD_CONN | \ PD_FLAGS_CHECK_PR_ROLE | \ diff --git a/include/usb_pd_tcpm.h b/include/usb_pd_tcpm.h index 912e3bbda2..5960d1b62e 100644 --- a/include/usb_pd_tcpm.h +++ b/include/usb_pd_tcpm.h @@ -184,6 +184,17 @@ struct tcpm_drv { * @param enable Discharge enable or disable */ void (*tcpc_discharge_vbus)(int port, int enable); + +#ifdef CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE + /** + * Enable TCPC auto DRP toggling. + * + * @param port Type-C port number + * + * @return EC_SUCCESS or error + */ + int (*drp_toggle)(int port); +#endif }; enum tcpc_alert_polarity { |