summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAyushee <ayushee.shah@intel.com>2019-06-20 22:53:18 -0700
committerCommit Bot <commit-bot@chromium.org>2019-08-16 06:32:49 +0000
commit91be7060d7694eedb53ce73b39bc7423a3814536 (patch)
tree4c236211331689195ee8c02854e6b0b6d63cbf83
parentd731e31bce43a8616bff235626bc69f71712bc7a (diff)
downloadchrome-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.c81
-rw-r--r--common/usb_pd_protocol.c47
-rw-r--r--driver/tcpm/nct38xx.c3
-rw-r--r--driver/tcpm/tcpci.c11
-rw-r--r--driver/tcpm/tcpci.h3
-rw-r--r--include/usb_pd.h47
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