summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/reef/board.h1
-rw-r--r--common/usb_pd_protocol.c99
-rw-r--r--driver/tcpm/anx74xx.c48
-rw-r--r--driver/tcpm/tcpci.c59
-rw-r--r--driver/tcpm/tcpci.h3
-rw-r--r--driver/tcpm/tcpm.h13
-rw-r--r--include/config.h3
-rw-r--r--include/usb_pd.h5
-rw-r--r--include/usb_pd_tcpm.h11
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, &reg);
+ rv = tcpc_read(port, ANX74XX_REG_CC_SOFTWARE_CTRL, &reg);
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, &reg);
- 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, &reg);
- 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 {