summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin K Wong <kevin.k.wong@intel.com>2016-09-21 08:57:40 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-11-02 06:42:04 -0700
commit180ac77e16e1e5a57769a2a20411b6e37d649107 (patch)
tree7b9d5cb676836a9e368c36aabd004734c8b55f59
parentdd652accd0c26d56af3511d178507af738912885 (diff)
downloadchrome-ec-180ac77e16e1e5a57769a2a20411b6e37d649107.tar.gz
reef: enable tcpc-controlled drp toggle
BUG=chrome-os-partner:54668 BRANCH=none TEST=Verified SNK is detected in S0 (toggle on), S3 (toggle off), and S5 (force sink). SRC is detect in S0 only, stays detected when entered S3, but unplug/plug while in S3 will not re-detect until system back in S0. When go to S5, SRC will get disconnected until back in S0, and hotplug SRC in S5 will not get detected. Checked power role swap with another chromebook in the above scenario also. Change-Id: I2a487fca5cb04c45524aa3efde84fcd10ff0579e Signed-off-by: Kevin K Wong <kevin.k.wong@intel.com> Reviewed-on: https://chromium-review.googlesource.com/396918 Reviewed-by: Aaron Durbin <adurbin@chromium.org>
-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 {