diff options
author | Ayushee <ayushee.shah@intel.com> | 2019-06-20 22:53:18 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-08-16 06:32:49 +0000 |
commit | 91be7060d7694eedb53ce73b39bc7423a3814536 (patch) | |
tree | 4c236211331689195ee8c02854e6b0b6d63cbf83 | |
parent | d731e31bce43a8616bff235626bc69f71712bc7a (diff) | |
download | chrome-ec-91be7060d7694eedb53ce73b39bc7423a3814536.tar.gz |
usb_pd: Adding USB-C cable detection
When a discover identity command is sent with a SOP prime
packet, the cable plug of an emark cable responds with the
cable attribute.
Added a structure pd_cable that stores the cable type and
and resetting it when the cable is disconnected. Also added
console command that gives the type of cable connected.
Host(DFP) Cable Port-Partner(UFP)
-------------------------EXPLICIT CONTRACT------------------------
Discover Identity SOP ----------------------------->
request |
<------------------------- Discover Identity
response
Discover Identity SOP' --------->
request |
(If Emark Cable)
<------------ Discover Identity SOP'
| response
Store cable type |
(If Non-Emark Cable)
<-------------- No response
|
Discover SVID SOP
request ------------------------------------>
(Rest of the PD flow)
BUG=b:129990370
BRANCH=none
TEST=Verified on dragonegg, able to get cable response
Change-Id: I2536cf24d58f7ee5ff462b34fc32f69d7a200d41
Signed-off-by: Ayushee <ayushee.shah@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1707851
Reviewed-by: Vijay P Hiremath <vijay.p.hiremath@intel.com>
Reviewed-by: Daisuke Nojiri <dnojiri@chromium.org>
-rw-r--r-- | common/usb_pd_policy.c | 81 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 47 | ||||
-rw-r--r-- | driver/tcpm/nct38xx.c | 3 | ||||
-rw-r--r-- | driver/tcpm/tcpci.c | 11 | ||||
-rw-r--r-- | driver/tcpm/tcpci.h | 3 | ||||
-rw-r--r-- | include/usb_pd.h | 47 |
6 files changed, 177 insertions, 15 deletions
diff --git a/common/usb_pd_policy.c b/common/usb_pd_policy.c index 20fc8559af..ddfaa41d2a 100644 --- a/common/usb_pd_policy.c +++ b/common/usb_pd_policy.c @@ -288,12 +288,63 @@ int pd_charge_from_device(uint16_t vid, uint16_t pid) } #endif /* CONFIG_USB_PD_DUAL_ROLE */ +static struct pd_cable cable[CONFIG_USB_PD_PORT_COUNT]; + +static uint8_t is_transmit_msg_sop_prime(int port) +{ + if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) + return !!(cable[port].flags & CABLE_FLAGS_SOP_PRIME_ENABLE); + + return 0; +} + +uint8_t is_sop_prime_ready(int port, uint8_t data_role, uint32_t pd_flags) +{ + /* + * Ref: USB PD 3.0 sec 2.5.4: When an Explicit Contract is in place the + * VCONN Source (either the DFP or the UFP) can communicate with the + * Cable Plug(s) using SOP’/SOP’’ Packets + * + * Ref: USB PD 2.0 sec 2.4.4: When an Explicit Contract is in place the + * DFP (either the Source or the Sink) can communicate with the + * Cable Plug(s) using SOP’/SOP” Packets. + * Sec 3.6.11 : Before communicating with a Cable Plug a Port Should + * ensure that it is the Vconn Source + */ + if (pd_flags & PD_FLAGS_VCONN_ON && (IS_ENABLED(CONFIG_USB_PD_REV30) || + data_role == PD_ROLE_DFP)) + return is_transmit_msg_sop_prime(port); + + return 0; +} + +void reset_pd_cable(int port) +{ + if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) + memset(&cable[port], 0, sizeof(cable[port])); +} + #ifdef CONFIG_USB_PD_ALT_MODE #ifdef CONFIG_USB_PD_ALT_MODE_DFP static struct pd_policy pe[CONFIG_USB_PD_PORT_COUNT]; +static int is_vdo_present(int cnt, int index) +{ + return cnt > index; +} + +static void enable_transmit_sop_prime(int port) +{ + cable[port].flags |= CABLE_FLAGS_SOP_PRIME_ENABLE; +} + +static void disable_transmit_sop_prime(int port) +{ + cable[port].flags &= ~CABLE_FLAGS_SOP_PRIME_ENABLE; +} + void pd_dfp_pe_init(int port) { memset(&pe[port], 0, sizeof(struct pd_policy)); @@ -325,6 +376,18 @@ static void dfp_consume_identity(int port, int cnt, uint32_t *payload) } } +static void dfp_consume_cable_response(int port, int cnt, uint32_t *payload) +{ + if (is_vdo_present(cnt, VDO_INDEX_IDH)) + cable[port].type = PD_IDH_PTYPE(payload[VDO_INDEX_IDH]); +} + +static int dfp_discover_ident(int port, uint32_t *payload) +{ + payload[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_IDENT); + return 1; +} + static int dfp_discover_svids(int port, uint32_t *payload) { payload[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID); @@ -754,8 +817,22 @@ int pd_svdm(int port, int cnt, uint32_t *payload, uint32_t **rpayload) switch (cmd) { #ifdef CONFIG_USB_PD_ALT_MODE_DFP case CMD_DISCOVER_IDENT: - dfp_consume_identity(port, cnt, payload); - rsize = dfp_discover_svids(port, payload); + /* Received a SOP Prime Discover Ident msg */ + if (is_transmit_msg_sop_prime(port)) { + /* Store cable type */ + dfp_consume_cable_response(port, cnt, payload); + disable_transmit_sop_prime(port); + rsize = dfp_discover_svids(port, payload); + /* Received a SOP Discover Ident Message */ + } else if (IS_ENABLED(CONFIG_USB_PD_DECODE_SOP)) { + dfp_consume_identity(port, cnt, payload); + rsize = dfp_discover_ident(port, payload); + /* Send SOP' Discover Ident message */ + enable_transmit_sop_prime(port); + } else { + dfp_consume_identity(port, cnt, payload); + rsize = dfp_discover_svids(port, payload); + } #ifdef CONFIG_CHARGE_MANAGER if (pd_charge_from_device(pd_get_identity_vid(port), pd_get_identity_pid(port))) diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c index b72053e683..15798615bc 100644 --- a/common/usb_pd_protocol.c +++ b/common/usb_pd_protocol.c @@ -715,8 +715,10 @@ static inline void set_state(int port, enum pd_states next_state) * here are really the same states in the state diagram. */ if (last_state != PD_STATE_SNK_DISCONNECTED_DEBOUNCE && - last_state != PD_STATE_SRC_DISCONNECTED_DEBOUNCE) + last_state != PD_STATE_SRC_DISCONNECTED_DEBOUNCE) { pd[port].flags &= ~PD_FLAGS_RESET_ON_DISCONNECT_MASK; + reset_pd_cable(port); + } /* Clear the input current limit */ pd_set_input_current_limit(port, 0, 0); @@ -2141,8 +2143,40 @@ static void pd_vdm_send_state_machine(int port) pd[port].data_role, pd[port].msg_id, (int)pd[port].vdo_count, pd_get_rev(port), 0); - res = pd_transmit(port, TCPC_TX_SOP, header, - pd[port].vdo_data); + + /* + * To communicate with the cable plug, an explicit contract + * should be established, VCONN should be enabled and data role + * that can communicate with the cable plug should be in place. + * For USB3.0, UFP/DFP can communicate whereas in case of + * USB2.0 only DFP can talk to the cable plug. + * + * For communication between USB2.0 UFP and cable plug, + * data role swap takes place during source and sink + * negotiation and in case of failure, a soft reset is issued. + */ + if (is_sop_prime_ready(port, pd[port].data_role, + pd[port].flags)) { + res = pd_transmit(port, TCPC_TX_SOP_PRIME, header, + pd[port].vdo_data); + /* + * If there is no ack from the cable, its a non-emark + * cable and since, the pd flow should continue + * irrespective of cable response, sending + * discover_svid so the pd flow remains intact. + */ + if (res < 0) { + pd[port].vdo_data[0] = + VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID); + res = pd_transmit(port, TCPC_TX_SOP, header, + pd[port].vdo_data); + reset_pd_cable(port); + } + + } else + res = pd_transmit(port, TCPC_TX_SOP, header, + pd[port].vdo_data); + if (res < 0) { pd[port].vdm_state = VDM_STATE_ERR_SEND; } else { @@ -3446,6 +3480,8 @@ void pd_task(void *u) set_state(port, PD_STATE_SRC_STARTUP); break; case PD_STATE_SRC_STARTUP: + /* Reset cable attributes and flags */ + reset_pd_cable(port); /* Wait for power source to enable */ if (pd[port].last_state != pd[port].task_state) { pd[port].flags |= PD_FLAGS_CHECK_IDENTITY; @@ -3983,6 +4019,9 @@ void pd_task(void *u) pd[port].flags |= PD_FLAGS_CHECK_PR_ROLE | PD_FLAGS_CHECK_DR_ROLE | PD_FLAGS_CHECK_IDENTITY; + /* Reset cable attributes and flags */ + reset_pd_cable(port); + if (new_cc_state == PD_CC_DEBUG_ACC) pd[port].flags |= PD_FLAGS_TS_DTS_PARTNER; @@ -4685,6 +4724,8 @@ static void pd_chipset_startup(void) for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++) { pd_set_dual_role_no_wakeup(i, PD_DRP_TOGGLE_OFF); pd[i].flags |= PD_FLAGS_CHECK_IDENTITY; + /* Reset cable attributes and flags */ + reset_pd_cable(i); task_set_event(PD_PORT_TO_TASK_ID(i), PD_EVENT_POWER_STATE_CHANGE | PD_EVENT_UPDATE_DUAL_ROLE, diff --git a/driver/tcpm/nct38xx.c b/driver/tcpm/nct38xx.c index 42925910de..ee8aeb58e6 100644 --- a/driver/tcpm/nct38xx.c +++ b/driver/tcpm/nct38xx.c @@ -260,7 +260,8 @@ int tcpci_nct38xx_transmit(int port, enum tcpm_transmit_type type, rv = tcpc_write_block(port, TCPC_REG_TX_BYTE_CNT, (const uint8_t *)txBuf, txBuf[0] + 1); - rv = tcpc_write(port, TCPC_REG_TRANSMIT, TCPC_REG_TRANSMIT_SET(type)); + rv = tcpc_write(port, TCPC_REG_TRANSMIT, + TCPC_REG_TRANSMIT_SET_WITH_RETRY(type)); return rv; } diff --git a/driver/tcpm/tcpci.c b/driver/tcpm/tcpci.c index fb0faee0ce..712b2bea29 100644 --- a/driver/tcpm/tcpci.c +++ b/driver/tcpm/tcpci.c @@ -534,7 +534,16 @@ int tcpci_tcpm_transmit(int port, enum tcpm_transmit_type type, return rv; } - return tcpc_write(port, TCPC_REG_TRANSMIT, TCPC_REG_TRANSMIT_SET(type)); + /* + * On receiving a received message on SOP, protocol layer + * discards the pending SOP messages queued for transmission. + * But it doesn't do the same for SOP' message. So retry is + * assigned to 0 to avoid multiple transmission. + */ + return tcpc_write(port, TCPC_REG_TRANSMIT, + (type == TCPC_TX_SOP_PRIME) ? + TCPC_REG_TRANSMIT_SET_WITHOUT_RETRY(type) : + TCPC_REG_TRANSMIT_SET_WITH_RETRY(type)); } #ifndef CONFIG_USB_PD_TCPC_LOW_POWER diff --git a/driver/tcpm/tcpci.h b/driver/tcpm/tcpci.h index 9bec93e325..6db71e4161 100644 --- a/driver/tcpm/tcpci.h +++ b/driver/tcpm/tcpci.h @@ -116,8 +116,9 @@ #define TCPC_REG_RX_DATA 0x34 /* through 0x4f */ #define TCPC_REG_TRANSMIT 0x50 -#define TCPC_REG_TRANSMIT_SET(type) \ +#define TCPC_REG_TRANSMIT_SET_WITH_RETRY(type) \ (PD_RETRY_COUNT << 4 | (type)) +#define TCPC_REG_TRANSMIT_SET_WITHOUT_RETRY(type) (type) #define TCPC_REG_TRANSMIT_RETRY(reg) (((reg) & 0x30) >> 4) #define TCPC_REG_TRANSMIT_TYPE(reg) ((reg) & 0x7) diff --git a/include/usb_pd.h b/include/usb_pd.h index 3f693e40f0..2173c6d193 100644 --- a/include/usb_pd.h +++ b/include/usb_pd.h @@ -410,14 +410,17 @@ struct pd_policy { * <25:16> :: SBZ * <15:0> :: USB-IF assigned VID for this cable vendor */ -#define IDH_PTYPE_UNDEF 0 -#define IDH_PTYPE_HUB 1 -#define IDH_PTYPE_PERIPH 2 -#define IDH_PTYPE_PCABLE 3 -#define IDH_PTYPE_ACABLE 4 -#define IDH_PTYPE_AMA 5 -#define IDH_PTYPE_VPD 6 +enum idh_ptype { + IDH_PTYPE_UNDEF, + IDH_PTYPE_HUB, + IDH_PTYPE_PERIPH, + IDH_PTYPE_PCABLE, + IDH_PTYPE_ACABLE, + IDH_PTYPE_AMA, + IDH_PTYPE_VPD, + IDH_PTYPE_COUNT, +}; #define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \ ((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \ | (is_modal) << 26 | ((vid) & 0xffff)) @@ -480,6 +483,17 @@ struct pd_policy { | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \ | ((usbss) & 0x7)) +/* Cable structure for storing cable attributes */ +struct pd_cable { + /* Type of cable */ + enum idh_ptype type; + /* Cable flags. See CABLE_FLAGS_* */ + uint8_t flags; +}; + +/* Flag for sending SOP Prime packet */ +#define CABLE_FLAGS_SOP_PRIME_ENABLE BIT(0) + /* * AMA VDO * --------- @@ -1534,6 +1548,25 @@ uint16_t pd_get_identity_vid(int port); uint16_t pd_get_identity_pid(int port); /** + * Returns the status of cable flag - CABLE_FLAGS_SOP_PRIME_ENABLE + * + * @param port USB-C port number + * @param data_role current data role + * @param pd_flags current pd flags + * @return For rev3.0, true if vconn is on + * For rev2.0, true if vconn is on and data_role is dfp + * False otherwise + */ +uint8_t is_sop_prime_ready(int port, uint8_t data_role, uint32_t pd_flags); + +/** + * Reset Cable type, Cable attributes and cable flags + * + * @param port USB-C port number + */ +void reset_pd_cable(int port); + +/** * Store Device ID & RW hash of device * * @param port USB-C port number |