summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorAseda Aboagye <aaboagye@google.com>2018-03-23 17:34:11 -0700
committerchrome-bot <chrome-bot@chromium.org>2018-04-25 23:00:01 -0700
commit36980ec169795231ba5ddaba86decb2e05512581 (patch)
tree8088d09341c695b3fa4e7b97d30d80457677f516 /common
parentd268930c7522f788efd5c507ad6dbcef2aab364a (diff)
downloadchrome-ec-36980ec169795231ba5ddaba86decb2e05512581.tar.gz
pd: Properly assign data role on reset
According to PD spec: - Data role shall not be reset on soft reset. - Data role shall be reset to power-role default on hard reset. Implement the above. Even if both ports follow spec, it's still possible for a data role conflict to occur if, for example, data role swap occurs (data role mismatches power role default) followed by a hardware reset of one port (such that data role gets reset to power role default). Handle such cases by taking error recovery actions. BUG=b:71333840,chromium:805040 TEST=Connect scarlet to powered Apple accessory, verify scarlet comes up in SNK-DFP after soft reset and issuing "reboot" on EC console. After issuing a hard reset, the port comes up in SNK-UFP (which is the power-role default). BRANCH=None Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org> Signed-off-by: Aseda Aboagye <aaboagye@google.com> Change-Id: I65139f277d59a0612f8323d711080f52425ff5e7 Reviewed-on: https://chromium-review.googlesource.com/885462 Commit-Ready: Aseda Aboagye <aaboagye@chromium.org> Tested-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Jett Rink <jettrink@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'common')
-rw-r--r--common/usb_pd_protocol.c86
1 files changed, 61 insertions, 25 deletions
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index fbb657cfa2..a02203c0c8 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -924,6 +924,34 @@ static void handle_vdm_request(int port, int cnt, uint32_t *payload)
port, PD_VDO_VID(payload[0]), payload[0] & 0xFFFF);
}
+static void pd_set_data_role(int port, int role)
+{
+ pd[port].data_role = role;
+#ifdef CONFIG_USB_PD_DUAL_ROLE
+ pd_update_saved_port_flags(port, PD_BBRMFLG_DATA_ROLE, role);
+#endif /* defined(CONFIG_USB_PD_DUAL_ROLE) */
+ pd_execute_data_swap(port, role);
+
+#ifdef CONFIG_USBC_SS_MUX
+#ifdef CONFIG_USBC_SS_MUX_DFP_ONLY
+ /*
+ * Need to connect SS mux for if new data role is DFP.
+ * If new data role is UFP, then disconnect the SS mux.
+ */
+ if (role == PD_ROLE_DFP)
+ usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT,
+ pd[port].polarity);
+ else
+ usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT,
+ pd[port].polarity);
+#else
+ usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT,
+ pd[port].polarity);
+#endif
+#endif
+ pd_update_roles(port);
+}
+
void pd_execute_hard_reset(int port)
{
if (pd[port].last_state == PD_STATE_HARD_RESET_SEND)
@@ -957,6 +985,8 @@ void pd_execute_hard_reset(int port)
pd_power_supply_reset(port);
}
+ /* Set initial data role (matching power role) */
+ pd_set_data_role(port, pd[port].power_role);
if (pd[port].power_role == PD_ROLE_SINK) {
/* Clear the input current limit */
pd_set_input_current_limit(port, 0, 0);
@@ -1277,31 +1307,6 @@ void pd_request_data_swap(int port)
task_wake(PD_PORT_TO_TASK_ID(port));
}
-static void pd_set_data_role(int port, int role)
-{
- pd[port].data_role = role;
- pd_execute_data_swap(port, role);
-
-#ifdef CONFIG_USBC_SS_MUX
-#ifdef CONFIG_USBC_SS_MUX_DFP_ONLY
- /*
- * Need to connect SS mux for if new data role is DFP.
- * If new data role is UFP, then disconnect the SS mux.
- */
- if (role == PD_ROLE_DFP)
- usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT,
- pd[port].polarity);
- else
- usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_DISCONNECT,
- pd[port].polarity);
-#else
- usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT,
- pd[port].polarity);
-#endif
-#endif
- pd_update_roles(port);
-}
-
static void pd_set_power_role(int port, int role)
{
pd[port].power_role = role;
@@ -1591,6 +1596,7 @@ static void handle_request(int port, uint16_t head,
uint32_t *payload)
{
int cnt = PD_HEADER_CNT(head);
+ int data_role = PD_HEADER_DROLE(head);
int p;
/* dump received packet content (only dump ping at debug level 3) */
@@ -1609,6 +1615,36 @@ static void handle_request(int port, uint16_t head,
if (!pd_is_connected(port))
set_state(port, PD_STATE_HARD_RESET_SEND);
+ /*
+ * When a data role conflict is detected, USB-C ErrorRecovery
+ * actions shall be performed, and transitioning to unattached state
+ * is one such legal action.
+ */
+ if (pd[port].data_role == data_role) {
+ CPRINTF("C%d DR conflict!\n", port);
+ /*
+ * If the port doesn't support removing the terminations, just
+ * go to the unattached state.
+ */
+ if (tcpm_set_cc(port, TYPEC_CC_OPEN) == EC_SUCCESS) {
+ /* Do not drive VBUS or VCONN. */
+ pd_power_supply_reset(port);
+#ifdef CONFIG_USBC_VCONN
+ set_vconn(port, 0);
+#endif /* defined(CONFIG_USBC_VCONN) */
+ usleep(PD_T_ERROR_RECOVERY);
+
+ /* Restore terminations. */
+ tcpm_set_cc(port, DUAL_ROLE_IF_ELSE(port, TYPEC_CC_RD,
+ TYPEC_CC_RP));
+ }
+ set_state(port,
+ DUAL_ROLE_IF_ELSE(port,
+ PD_STATE_SNK_DISCONNECTED,
+ PD_STATE_SRC_DISCONNECTED));
+ return;
+ }
+
#ifdef CONFIG_USB_PD_REV30
/* Check if this is an extended chunked data message. */
if (pd[port].rev == PD_REV30 && PD_HEADER_EXT(head)) {