diff options
-rw-r--r-- | common/usb_pd_protocol.c | 86 | ||||
-rw-r--r-- | include/usb_pd.h | 12 |
2 files changed, 68 insertions, 30 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)) { diff --git a/include/usb_pd.h b/include/usb_pd.h index 33a8c79121..acad78fb42 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -737,6 +737,7 @@ enum pd_states { /* Per-port battery backed RAM flags */ #define PD_BBRMFLG_EXPLICIT_CONTRACT (1 << 0) #define PD_BBRMFLG_POWER_ROLE (1 << 1) +#define PD_BBRMFLG_DATA_ROLE (1 << 2) enum pd_cc_states { PD_CC_NONE, @@ -915,11 +916,12 @@ enum pd_data_msg_type { ((id) << 9) | ((cnt) << 12) | ((ext) << 15)) /* Used for processing pd header */ -#define PD_HEADER_EXT(header) (((header) >> 15) & 1) -#define PD_HEADER_CNT(header) (((header) >> 12) & 7) -#define PD_HEADER_TYPE(header) ((header) & 0xF) -#define PD_HEADER_ID(header) (((header) >> 9) & 7) -#define PD_HEADER_REV(header) (((header) >> 6) & 3) +#define PD_HEADER_EXT(header) (((header) >> 15) & 1) +#define PD_HEADER_CNT(header) (((header) >> 12) & 7) +#define PD_HEADER_TYPE(header) ((header) & 0xF) +#define PD_HEADER_ID(header) (((header) >> 9) & 7) +#define PD_HEADER_REV(header) (((header) >> 6) & 3) +#define PD_HEADER_DROLE(header) (((header) >> 5) & 1) /* Used for processing pd extended header */ #define PD_EXT_HEADER_CHUNKED(header) (((header) >> 15) & 1) |