summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Berg <alecaberg@chromium.org>2014-12-31 14:18:32 -0800
committerAlec Berg <alecaberg@chromium.org>2015-01-10 23:43:25 +0000
commitd83bd6b892e696c13f3584b5d3e1e0f51aafe710 (patch)
tree366798000953d830de5ed6c5902a8c76e570cdde
parent193e23509f8b9df44852b4581c357f2d7caed0aa (diff)
downloadchrome-ec-d83bd6b892e696c13f3584b5d3e1e0f51aafe710.tar.gz
pd: implement new type-C connect state machine
Implement the new type-C connect state machine which removes lock and hold times and adds a debounce time to make sure CC lines settle before going into the attached state. This also adds detection of accessories, but doesn't do anything when an accessory is detected. BUG=chrome-os-partner:33680 BRANCH=samus TEST=test samus connected zinger and samus connected to samus. make sure that the connection is always formed. also tested with a third party with old state machine implementation and formed a connection every time. Change-Id: I91a7a6031bc35082cc19d7697142e4aa92ef46f2 Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/238210 Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--board/dingdong/usb_pd_config.h3
-rw-r--r--board/hoho/usb_pd_config.h3
-rw-r--r--board/zinger/usb_pd_config.h4
-rw-r--r--common/usb_pd_protocol.c338
-rw-r--r--include/usb_pd.h21
-rw-r--r--test/usb_pd.c39
6 files changed, 279 insertions, 129 deletions
diff --git a/board/dingdong/usb_pd_config.h b/board/dingdong/usb_pd_config.h
index ba6e545a3f..db46d83b73 100644
--- a/board/dingdong/usb_pd_config.h
+++ b/board/dingdong/usb_pd_config.h
@@ -120,7 +120,8 @@ static inline void pd_set_host_mode(int port, int enable) {}
static inline int pd_adc_read(int port, int cc)
{
- return adc_read_channel(ADC_CH_CC1_PD);
+ /* only one CC line, assume other one is always low */
+ return (cc == 0) ? adc_read_channel(ADC_CH_CC1_PD) : 0;
}
static inline int pd_snk_is_vbus_provided(int port)
diff --git a/board/hoho/usb_pd_config.h b/board/hoho/usb_pd_config.h
index ba6e545a3f..db46d83b73 100644
--- a/board/hoho/usb_pd_config.h
+++ b/board/hoho/usb_pd_config.h
@@ -120,7 +120,8 @@ static inline void pd_set_host_mode(int port, int enable) {}
static inline int pd_adc_read(int port, int cc)
{
- return adc_read_channel(ADC_CH_CC1_PD);
+ /* only one CC line, assume other one is always low */
+ return (cc == 0) ? adc_read_channel(ADC_CH_CC1_PD) : 0;
}
static inline int pd_snk_is_vbus_provided(int port)
diff --git a/board/zinger/usb_pd_config.h b/board/zinger/usb_pd_config.h
index c22e68858d..0456a24652 100644
--- a/board/zinger/usb_pd_config.h
+++ b/board/zinger/usb_pd_config.h
@@ -107,8 +107,8 @@ static inline void pd_tx_init(void)
static inline int pd_adc_read(int port, int cc)
{
- /* only one CC line */
- return adc_read_channel(ADC_CH_CC1_PD);
+ /* only one CC line, assume other one is always high */
+ return (cc == 0) ? adc_read_channel(ADC_CH_CC1_PD) : 4096;
}
/* 3.0A DFP : no-connect voltage is 2.45V */
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index fdacae05f9..9db9b8bd75 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -132,7 +132,8 @@ static const uint8_t dec4b5b[] = {
#define PD_HARD_RESET (PD_RST1 | (PD_RST1 << 5) |\
(PD_RST1 << 10) | (PD_RST2 << 15))
-/* Polarity is based 'DFP Perspective' (see table USB Type-C Cable and Connector
+/*
+ * Polarity is based 'DFP Perspective' (see table USB Type-C Cable and Connector
* Specification)
*
* CC1 CC2 STATE POSITION
@@ -140,25 +141,35 @@ static const uint8_t dec4b5b[] = {
* open open NC N/A
* Rd open UFP attached 1
* open Rd UFP attached 2
- * open Ra pwr cable no UFP 1
- * Ra open pwr cable no UFP 2
+ * open Ra pwr cable no UFP N/A
+ * Ra open pwr cable no UFP N/A
* Rd Ra pwr cable & UFP 1
* Ra Rd pwr cable & UFP 2
* Rd Rd dbg accessory N/A
* Ra Ra audio accessory N/A
*
* Note, V(Rd) > V(Ra)
- * TODO(crosbug.com/p/31197): Need to identify necessary polarity switching for
- * debug dongle.
- *
*/
#ifndef PD_SRC_RD_THRESHOLD
#define PD_SRC_RD_THRESHOLD 200 /* mV */
#endif
#define CC_RA(cc) (cc < PD_SRC_RD_THRESHOLD)
-#define CC_RD(cc) ((cc > PD_SRC_RD_THRESHOLD) && (cc < PD_SRC_VNC))
-#define GET_POLARITY(cc1, cc2) (CC_RD(cc2) || CC_RA(cc1))
-#define IS_CABLE(cc1, cc2) (CC_RD(cc1) || CC_RD(cc2))
+#define CC_RD(cc) ((cc >= PD_SRC_RD_THRESHOLD) && (cc < PD_SRC_VNC))
+#define CC_NC(cc) (cc >= PD_SRC_VNC)
+#define DFP_GET_POLARITY(cc1, cc2) (CC_RD(cc2))
+
+/*
+ * Polarity based on 'UFP Perspective'.
+ *
+ * CC1 CC2 STATE POSITION
+ * ----------------------------------------
+ * open open NC N/A
+ * Rp open DFP attached 1
+ * open Rp DFP attached 2
+ * Rp Rp Accessory attached N/A
+ */
+#define CC_RP(cc) (cc >= PD_SNK_VA)
+#define UFP_GET_POLARITY(cc1, cc2) (CC_RP(cc2))
/*
* Type C power source charge current limits are identified by their cc
@@ -214,8 +225,9 @@ static int pd_src_cap_cnt[PD_PORT_COUNT];
#define PD_FLAGS_EXPLICIT_CONTRACT (1 << 6) /* explicit pwr contract in place */
#define PD_FLAGS_SFT_RST_DIS_COMM (1 << 7) /* disable comms after soft reset */
#define PD_FLAGS_PREVIOUS_PD_CONN (1 << 8) /* previously PD connected */
-#define PD_FLAGS_CHECK_PR_ROLE (1 << 9)/* check power role in READY */
-#define PD_FLAGS_CHECK_DR_ROLE (1 << 10) /* check data role in READY */
+#define PD_FLAGS_CHECK_PR_ROLE (1 << 9) /* check power role in READY */
+#define PD_FLAGS_CHECK_DR_ROLE (1 << 10)/* check data role in READY */
+#define PD_FLAGS_CURR_LIM_INIT (1 << 11)/* input curr limit initialized */
/* Flags to clear on a disconnect */
#define PD_FLAGS_RESET_ON_DISCONNECT_MASK (PD_FLAGS_PARTNER_DR_POWER | \
PD_FLAGS_PARTNER_DR_DATA | \
@@ -248,6 +260,10 @@ static struct pd_protocol {
uint64_t timeout;
/* Time for source recovery after hard reset */
uint64_t src_recover;
+ /* Time for CC debounce end */
+ uint64_t cc_debounce;
+ /* The cc state */
+ enum pd_cc_states cc_state;
/* Error sending message and message was dropped */
int8_t send_error;
@@ -292,12 +308,14 @@ struct mutex pd_crc_lock;
static const char * const pd_state_names[] = {
"DISABLED",
#ifdef CONFIG_USB_PD_DUAL_ROLE
- "SUSPENDED", "SNK_DISCONNECTED", "SNK_HARD_RESET_RECOVER",
+ "SUSPENDED", "SNK_DISCONNECTED", "SNK_DISCONNECTED_DEBOUNCE",
+ "SNK_HARD_RESET_RECOVER",
"SNK_DISCOVERY", "SNK_REQUESTED", "SNK_TRANSITION", "SNK_READY",
"SNK_DR_SWAP", "SNK_SWAP_INIT", "SNK_SWAP_SNK_DISABLE",
"SNK_SWAP_SRC_DISABLE", "SNK_SWAP_STANDBY", "SNK_SWAP_COMPLETE",
#endif /* CONFIG_USB_PD_DUAL_ROLE */
- "SRC_DISCONNECTED", "SRC_HARD_RESET_RECOVER", "SRC_STARTUP",
+ "SRC_DISCONNECTED", "SRC_DISCONNECTED_DEBOUNCE", "SRC_ACCESSORY",
+ "SRC_HARD_RESET_RECOVER", "SRC_STARTUP",
"SRC_DISCOVERY", "SRC_NEGOCIATE", "SRC_ACCEPTED", "SRC_POWERED",
"SRC_TRANSITION", "SRC_READY", "SRC_GET_SNK_CAP", "SRC_DR_SWAP",
#ifdef CONFIG_USB_PD_DUAL_ROLE
@@ -334,10 +352,13 @@ int pd_is_connected(int port)
#ifdef CONFIG_USB_PD_DUAL_ROLE
/* Check if sink is connected */
if (pd[port].power_role == PD_ROLE_SINK)
- return pd[port].task_state != PD_STATE_SNK_DISCONNECTED;
+ return pd[port].task_state != PD_STATE_SNK_DISCONNECTED &&
+ pd[port].task_state != PD_STATE_SNK_DISCONNECTED_DEBOUNCE;
#endif
/* Must be a source */
- return pd[port].task_state != PD_STATE_SRC_DISCONNECTED;
+ return pd[port].task_state != PD_STATE_SRC_DISCONNECTED &&
+ pd[port].task_state != PD_STATE_SRC_DISCONNECTED_DEBOUNCE &&
+ pd[port].task_state != PD_STATE_SRC_ACCESSORY;
}
static inline void set_state(int port, enum pd_states next_state)
@@ -1649,13 +1670,13 @@ void pd_task(void)
int snk_hard_reset_vbus_off = 0;
#endif
#ifdef CONFIG_CHARGE_MANAGER
- static int initialized[PD_PORT_COUNT];
int typec_curr = 0, typec_curr_change = 0;
#endif /* CONFIG_CHARGE_MANAGER */
#endif /* CONFIG_USB_PD_DUAL_ROLE */
enum pd_states this_state;
+ enum pd_cc_states new_cc_state;
timestamp_t now;
- int caps_count = 0, src_connected = 0, hard_reset_sent = 0;
+ int caps_count = 0, hard_reset_sent = 0;
/* Initialize TX pins and put them in Hi-Z */
pd_tx_init();
@@ -1735,35 +1756,29 @@ void pd_task(void)
/* Vnc monitoring */
cc1_volt = pd_adc_read(port, 0);
cc2_volt = pd_adc_read(port, 1);
- if ((cc1_volt < PD_SRC_VNC) ||
- (cc2_volt < PD_SRC_VNC)) {
- pd[port].polarity =
- GET_POLARITY(cc1_volt, cc2_volt);
- pd_select_polarity(port, pd[port].polarity);
- /* initial data role for source is DFP */
- pd_set_data_role(port, PD_ROLE_DFP);
- /* Set to USB SS initially */
+ if (!CC_NC(cc1_volt) || !CC_NC(cc2_volt)) {
#ifdef CONFIG_USBC_SS_MUX
+ /*
+ * Set to USB SS based on current plarity
+ * (might change after the debounce).
+ */
board_set_usb_mux(port, TYPEC_MUX_USB,
- pd[port].polarity);
+ DFP_GET_POLARITY(cc1_volt,
+ cc2_volt));
#endif
/* Enable VBUS */
if (pd_set_power_supply_ready(port)) {
#ifdef CONFIG_USBC_SS_MUX
board_set_usb_mux(port, TYPEC_MUX_NONE,
- pd[port].polarity);
+ DFP_GET_POLARITY(
+ cc1_volt,
+ cc2_volt));
#endif
break;
}
-
-#ifdef CONFIG_USBC_VCONN
- pd_set_vconn(port, pd[port].polarity, 1);
-#endif
-
- pd[port].flags |= PD_FLAGS_CHECK_PR_ROLE |
- PD_FLAGS_CHECK_DR_ROLE;
- hard_reset_count = 0;
- set_state(port, PD_STATE_SRC_STARTUP);
+ pd[port].cc_state = PD_CC_NONE;
+ set_state(port,
+ PD_STATE_SRC_DISCONNECTED_DEBOUNCE);
}
#ifdef CONFIG_USB_PD_DUAL_ROLE
/* Swap roles if time expired or VBUS is present */
@@ -1780,6 +1795,88 @@ void pd_task(void)
}
#endif
break;
+ case PD_STATE_SRC_DISCONNECTED_DEBOUNCE:
+ timeout = 20*MSEC;
+ cc1_volt = pd_adc_read(port, 0);
+ cc2_volt = pd_adc_read(port, 1);
+
+ if (CC_NC(cc1_volt) && CC_NC(cc2_volt)) {
+ /* No connection any more, remove VBUS */
+ pd_power_supply_reset(port);
+ set_state(port, PD_STATE_SRC_DISCONNECTED);
+ timeout = 5*MSEC;
+ break;
+ } else if (CC_RA(cc1_volt) && CC_RA(cc2_volt)) {
+ /* Audio accessory */
+ new_cc_state = PD_CC_AUDIO_ACC;
+ } else if (CC_RD(cc1_volt) && CC_RD(cc2_volt)) {
+ /* Debug accessory */
+ new_cc_state = PD_CC_DEBUG_ACC;
+ } else if (CC_RD(cc1_volt) || CC_RD(cc2_volt)) {
+ /* UFP attached */
+ new_cc_state = PD_CC_UFP_ATTACHED;
+ } else {
+ /* Powered cable, no UFP */
+ new_cc_state = PD_CC_NO_UFP;
+ }
+
+ /* Debounce the cc state */
+ if (new_cc_state != pd[port].cc_state) {
+ pd[port].cc_debounce = get_time().val +
+ PD_T_CC_DEBOUNCE;
+ pd[port].cc_state = new_cc_state;
+ break;
+ } else if (get_time().val < pd[port].cc_debounce) {
+ break;
+ }
+
+ /* Debounce complete */
+ /* UFP is attached */
+ if (new_cc_state == PD_CC_UFP_ATTACHED) {
+ pd[port].polarity =
+ DFP_GET_POLARITY(cc1_volt, cc2_volt);
+ pd_select_polarity(port, pd[port].polarity);
+#ifdef CONFIG_USBC_SS_MUX
+ board_set_usb_mux(port, TYPEC_MUX_USB,
+ pd[port].polarity);
+#endif
+ /* initial data role for source is DFP */
+ pd_set_data_role(port, PD_ROLE_DFP);
+
+#ifdef CONFIG_USBC_VCONN
+ pd_set_vconn(port, pd[port].polarity, 1);
+#endif
+
+ pd[port].flags |= PD_FLAGS_CHECK_PR_ROLE |
+ PD_FLAGS_CHECK_DR_ROLE;
+ hard_reset_count = 0;
+ timeout = 5*MSEC;
+ set_state(port, PD_STATE_SRC_STARTUP);
+ }
+ /* Accessory is attached */
+ else if (new_cc_state == PD_CC_AUDIO_ACC ||
+ new_cc_state == PD_CC_DEBUG_ACC) {
+ /* Remove VBUS */
+ pd_power_supply_reset(port);
+ set_state(port, PD_STATE_SRC_ACCESSORY);
+ }
+ break;
+ case PD_STATE_SRC_ACCESSORY:
+ /* Combined audio / debug accessory state */
+ timeout = 100*MSEC;
+
+ cc1_volt = pd_adc_read(port, 0);
+ cc2_volt = pd_adc_read(port, 1);
+
+ /* If accessory becomes detached */
+ if ((pd[port].cc_state == PD_CC_AUDIO_ACC &&
+ (!CC_RA(cc1_volt) || !CC_RA(cc2_volt))) ||
+ (pd[port].cc_state == PD_CC_DEBUG_ACC &&
+ (!CC_RD(cc1_volt) || !CC_RD(cc2_volt)))) {
+ set_state(port, PD_STATE_SRC_DISCONNECTED);
+ timeout = 10*MSEC;
+ }
+ break;
case PD_STATE_SRC_HARD_RESET_RECOVER:
/* Do not continue until hard reset recovery time */
if (get_time().val < pd[port].src_recover) {
@@ -1805,21 +1902,25 @@ void pd_task(void)
pd[port].flags |= PD_FLAGS_DATA_SWAPPED;
/* reset various counters */
caps_count = 0;
- src_connected = 0;
pd[port].msg_id = 0;
set_state_timeout(
port,
+ /*
+ * delay for power supply to start up.
+ * subtract out debounce time if coming
+ * from debounce state since vbus is
+ * on during debounce.
+ */
get_time().val +
- PD_POWER_SUPPLY_TRANSITION_DELAY,
+ PD_POWER_SUPPLY_TRANSITION_DELAY -
+ (pd[port].last_state ==
+ PD_STATE_SRC_DISCONNECTED_DEBOUNCE
+ ? PD_T_CC_DEBOUNCE : 0),
PD_STATE_SRC_DISCOVERY);
}
break;
case PD_STATE_SRC_DISCOVERY:
if (pd[port].last_state != pd[port].task_state) {
-#ifdef CONFIG_USB_PD_DUAL_ROLE
- /* Keep VBUS up for the hold period */
- next_role_swap = get_time().val + PD_T_DRP_HOLD;
-#endif
/*
* If we have had PD connection with this port
* partner, then start NoResponseTimer.
@@ -1834,21 +1935,6 @@ void pd_task(void)
PD_STATE_SRC_DISCONNECTED);
}
- /*
- * While we were enabling VBUS, other side could have
- * toggled roles, so now wait until other side settles
- * on UFP, before sending source cap.
- */
- if (!src_connected) {
- cc1_volt = pd_adc_read(port, pd[port].polarity);
- if (cc1_volt < PD_SRC_VNC) {
- src_connected = 1;
- } else {
- timeout = 10*MSEC;
- break;
- }
- }
-
/* Send source cap some minimum number of times */
if (caps_count < PD_CAPS_COUNT) {
/* Query capabilites of the other side */
@@ -2096,59 +2182,41 @@ void pd_task(void)
case PD_STATE_SNK_DISCONNECTED:
timeout = 10*MSEC;
- /* Source connection monitoring */
+ /*
+ * Source connection monitoring
+ * Note, if we just turned VBUS off, we may still see
+ * VBUS present, but that should be safe because we
+ * will wait in the debounce state until vbus is
+ * removed.
+ */
if (pd_snk_is_vbus_provided(port)) {
- cc1_volt = pd_adc_read(port, 0);
- cc2_volt = pd_adc_read(port, 1);
- if ((cc1_volt >= PD_SNK_VA) ||
- (cc2_volt >= PD_SNK_VA)) {
- pd[port].polarity =
- GET_POLARITY(cc1_volt,
- cc2_volt);
- pd_select_polarity(port,
- pd[port].polarity);
- /* reset message ID on connection */
- pd[port].msg_id = 0;
- /* initial data role for sink is UFP */
- pd_set_data_role(port, PD_ROLE_UFP);
-#ifdef CONFIG_CHARGE_MANAGER
- initialized[port] = 1;
- typec_curr = get_typec_current_limit(
- pd[port].polarity ? cc2_volt :
- cc1_volt);
- typec_set_input_current_limit(
- port, typec_curr, TYPE_C_VOLTAGE);
-#endif
- hard_reset_count = 0;
- pd[port].flags |=
- PD_FLAGS_CHECK_PR_ROLE |
- PD_FLAGS_CHECK_DR_ROLE;
- set_state(port, PD_STATE_SNK_DISCOVERY);
- timeout = 10*MSEC;
- hook_call_deferred(
- pd_usb_billboard_deferred,
- PD_T_AME);
- break;
- }
+ pd[port].cc_state = PD_CC_NONE;
+ hard_reset_count = 0;
+ set_state(port,
+ PD_STATE_SNK_DISCONNECTED_DEBOUNCE);
+ break;
}
#ifdef CONFIG_CHARGE_MANAGER
/*
- * Set charge limit to zero if we have yet to set
- * a limit for this port.
+ * If no VBUS, set charge limit to zero if we have
+ * yet to set a limit for this port.
*/
- if (!initialized[port]) {
- initialized[port] = 1;
+ if (!(pd[port].flags & PD_FLAGS_CURR_LIM_INIT)) {
+ pd[port].flags |= PD_FLAGS_CURR_LIM_INIT;
typec_set_input_current_limit(port, 0, 0);
pd_set_input_current_limit(port, 0, 0);
}
#endif
/*
- * If no source detected, reset hard reset counter and
- * check for role swap
+ * If no source detected, check for role toggle.
+ * Do not role toggle if Rp is detected.
*/
+ cc1_volt = pd_adc_read(port, 0);
+ cc2_volt = pd_adc_read(port, 1);
if (drp_state == PD_DRP_TOGGLE_ON &&
- get_time().val >= next_role_swap) {
+ get_time().val >= next_role_swap &&
+ !CC_RP(cc1_volt) && !CC_RP(cc2_volt)) {
/* Swap roles to source */
pd[port].power_role = PD_ROLE_SOURCE;
set_state(port, PD_STATE_SRC_DISCONNECTED);
@@ -2159,6 +2227,60 @@ void pd_task(void)
timeout = 2*MSEC;
}
break;
+ case PD_STATE_SNK_DISCONNECTED_DEBOUNCE:
+ if (!pd_snk_is_vbus_provided(port)) {
+ /* No connection any more */
+ set_state(port, PD_STATE_SNK_DISCONNECTED);
+ timeout = 5*MSEC;
+ break;
+ }
+
+ timeout = 20*MSEC;
+ cc1_volt = pd_adc_read(port, 0);
+ cc2_volt = pd_adc_read(port, 1);
+ if (!CC_RP(cc1_volt) && !CC_RP(cc2_volt))
+ /* Accessory */
+ new_cc_state = PD_CC_ACC_PRESENT;
+ else
+ /* DFP attached */
+ new_cc_state = PD_CC_DFP_ATTACHED;
+
+ /* Debounce the cc state */
+ if (new_cc_state != pd[port].cc_state) {
+ pd[port].cc_debounce = get_time().val +
+ PD_T_CC_DEBOUNCE;
+ pd[port].cc_state = new_cc_state;
+ break;
+ } else if (get_time().val < pd[port].cc_debounce) {
+ break;
+ }
+
+ /* Debounce complete */
+ if (new_cc_state == PD_CC_DFP_ATTACHED) {
+ pd[port].polarity =
+ UFP_GET_POLARITY(cc1_volt, cc2_volt);
+ pd_select_polarity(port, pd[port].polarity);
+ /* reset message ID on connection */
+ pd[port].msg_id = 0;
+ /* initial data role for sink is UFP */
+ pd_set_data_role(port, PD_ROLE_UFP);
+#ifdef CONFIG_CHARGE_MANAGER
+ pd[port].flags |= PD_FLAGS_CURR_LIM_INIT;
+ typec_curr = get_typec_current_limit(
+ pd[port].polarity ? cc2_volt :
+ cc1_volt);
+ typec_set_input_current_limit(
+ port, typec_curr, TYPE_C_VOLTAGE);
+#endif
+ pd[port].flags |= PD_FLAGS_CHECK_PR_ROLE |
+ PD_FLAGS_CHECK_DR_ROLE;
+ set_state(port, PD_STATE_SNK_DISCOVERY);
+ timeout = 10*MSEC;
+ hook_call_deferred(
+ pd_usb_billboard_deferred,
+ PD_T_AME);
+ }
+ break;
case PD_STATE_SNK_HARD_RESET_RECOVER:
if (pd[port].last_state != pd[port].task_state)
pd[port].flags |= PD_FLAGS_DATA_SWAPPED;
@@ -2245,7 +2367,7 @@ void pd_task(void)
* we guarantee this is revised below.
*/
if (pd[port].last_state !=
- PD_STATE_SNK_DISCONNECTED)
+ PD_STATE_SNK_DISCONNECTED_DEBOUNCE)
typec_curr = 0;
#endif
}
@@ -2533,22 +2655,14 @@ void pd_task(void)
if (!pd_is_connected(port) || pd_is_power_swapping(port))
continue;
#endif
- if (pd[port].power_role == PD_ROLE_SOURCE &&
- pd[port].task_state != PD_STATE_SRC_STARTUP) {
+ if (pd[port].power_role == PD_ROLE_SOURCE) {
/* Source: detect disconnect by monitoring CC */
cc1_volt = pd_adc_read(port, pd[port].polarity);
-#ifdef CONFIG_USB_PD_DUAL_ROLE
- if (cc1_volt > PD_SRC_VNC &&
- get_time().val >= next_role_swap) {
- /* Stay a source port for lock period */
- next_role_swap = get_time().val + PD_T_DRP_LOCK;
-#else
- if (cc1_volt > PD_SRC_VNC) {
-#endif
+ if (CC_NC(cc1_volt)) {
pd_power_supply_reset(port);
set_state(port, PD_STATE_SRC_DISCONNECTED);
/* Debouncing */
- timeout = 50*MSEC;
+ timeout = 10*MSEC;
}
}
#ifdef CONFIG_USB_PD_DUAL_ROLE
@@ -2586,7 +2700,7 @@ static void dual_role_on(void)
int i;
pd_set_dual_role(PD_DRP_TOGGLE_ON);
- CPRINTS("chipset -> S0, enable dual-role toggling");
+ CPRINTS("chipset -> S0");
for (i = 0; i < PD_PORT_COUNT; i++) {
#ifdef CONFIG_CHARGE_MANAGER
@@ -2601,7 +2715,7 @@ DECLARE_HOOK(HOOK_CHIPSET_RESUME, dual_role_on, HOOK_PRIO_DEFAULT);
static void dual_role_off(void)
{
pd_set_dual_role(PD_DRP_TOGGLE_OFF);
- CPRINTS("chipset -> S3, disable dual-role toggling");
+ CPRINTS("chipset -> S3");
}
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, dual_role_off, HOOK_PRIO_DEFAULT);
DECLARE_HOOK(HOOK_CHIPSET_STARTUP, dual_role_off, HOOK_PRIO_DEFAULT);
@@ -2609,7 +2723,7 @@ DECLARE_HOOK(HOOK_CHIPSET_STARTUP, dual_role_off, HOOK_PRIO_DEFAULT);
static void dual_role_force_sink(void)
{
pd_set_dual_role(PD_DRP_FORCE_SINK);
- CPRINTS("chipset -> S5, force dual-role port to sink");
+ CPRINTS("chipset -> S5");
}
DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, dual_role_force_sink, HOOK_PRIO_DEFAULT);
diff --git a/include/usb_pd.h b/include/usb_pd.h
index 7220f74d7d..67c049dcf0 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -122,8 +122,8 @@ enum pd_errors {
#define PD_T_PS_SOURCE_ON (480*MSEC) /* between 390ms and 480ms */
#define PD_T_PS_SOURCE_OFF (920*MSEC) /* between 750ms and 920ms */
#define PD_T_PS_HARD_RESET (15*MSEC) /* between 10ms and 20ms */
-#define PD_T_DRP_HOLD (120*MSEC) /* between 100ms and 150ms */
-#define PD_T_DRP_LOCK (120*MSEC) /* between 100ms and 150ms */
+#define PD_T_ERROR_RECOVERY (25*MSEC) /* 25ms */
+#define PD_T_CC_DEBOUNCE (50*MSEC) /* between ??ms and ??ms */
/* DRP_SNK + DRP_SRC must be between 50ms and 100ms with 30%-70% duty cycle */
#define PD_T_DRP_SNK (40*MSEC) /* toggle time for sink DRP */
#define PD_T_DRP_SRC (30*MSEC) /* toggle time for source DRP */
@@ -569,6 +569,7 @@ enum pd_states {
#ifdef CONFIG_USB_PD_DUAL_ROLE
PD_STATE_SUSPENDED,
PD_STATE_SNK_DISCONNECTED,
+ PD_STATE_SNK_DISCONNECTED_DEBOUNCE,
PD_STATE_SNK_HARD_RESET_RECOVER,
PD_STATE_SNK_DISCOVERY,
PD_STATE_SNK_REQUESTED,
@@ -584,6 +585,8 @@ enum pd_states {
#endif /* CONFIG_USB_PD_DUAL_ROLE */
PD_STATE_SRC_DISCONNECTED,
+ PD_STATE_SRC_DISCONNECTED_DEBOUNCE,
+ PD_STATE_SRC_ACCESSORY,
PD_STATE_SRC_HARD_RESET_RECOVER,
PD_STATE_SRC_STARTUP,
PD_STATE_SRC_DISCOVERY,
@@ -612,6 +615,20 @@ enum pd_states {
PD_STATE_COUNT,
};
+enum pd_cc_states {
+ PD_CC_NONE,
+
+ /* From DFP perspective */
+ PD_CC_NO_UFP,
+ PD_CC_AUDIO_ACC,
+ PD_CC_DEBUG_ACC,
+ PD_CC_UFP_ATTACHED,
+
+ /* From UFP perspective */
+ PD_CC_ACC_PRESENT,
+ PD_CC_DFP_ATTACHED
+};
+
#ifdef CONFIG_USB_PD_DUAL_ROLE
enum pd_dual_role_states {
PD_DRP_TOGGLE_ON,
diff --git a/test/usb_pd.c b/test/usb_pd.c
index 9da5bfc85c..4fb38d1916 100644
--- a/test/usb_pd.c
+++ b/test/usb_pd.c
@@ -17,21 +17,37 @@
struct pd_port_t {
int host_mode;
- int cc_volt[2]; /* -1 for Hi-Z */
int has_vbus;
int msg_tx_id;
int msg_rx_id;
int polarity;
+ int partner_role; /* -1 for none */
+ int partner_polarity;
} pd_port[PD_PORT_COUNT];
/* Mock functions */
int pd_adc_read(int port, int cc)
{
- int val = pd_port[port].cc_volt[cc];
- if (val == -1)
- return pd_port[port].host_mode ? 3000 : 0;
- return val;
+ if (pd_port[port].host_mode &&
+ pd_port[port].partner_role == PD_ROLE_SINK)
+ /* we are source connected to sink, return Rd/Open */
+ return (pd_port[port].partner_polarity == cc) ? 400 : 3000;
+ else if (!pd_port[port].host_mode &&
+ pd_port[port].partner_role == PD_ROLE_SOURCE)
+ /* we are sink connected to source, return Rp/Open */
+ return (pd_port[port].partner_polarity == cc) ? 1700 : 0;
+ else if (pd_port[port].host_mode &&
+ pd_port[port].partner_role == PD_ROLE_SINK)
+ /* both sources */
+ return 3000;
+ else if (!pd_port[port].host_mode &&
+ pd_port[port].partner_role == PD_ROLE_SOURCE)
+ /* both sinks */
+ return 0;
+
+ /* should never get here */
+ return 0;
}
int pd_snk_is_vbus_provided(int port)
@@ -72,7 +88,7 @@ static void init_ports(void)
for (i = 0; i < PD_PORT_COUNT; ++i) {
pd_port[i].host_mode = 0;
- pd_port[i].cc_volt[0] = pd_port[i].cc_volt[1] = -1;
+ pd_port[i].partner_role = -1;
pd_port[i].has_vbus = 0;
}
}
@@ -126,20 +142,21 @@ static int verify_goodcrc(int port, int role, int id)
static void plug_in_source(int port, int polarity)
{
pd_port[port].has_vbus = 1;
- pd_port[port].cc_volt[polarity] = 3000;
+ pd_port[port].partner_role = PD_ROLE_SOURCE;
+ pd_port[port].partner_polarity = polarity;
}
static void plug_in_sink(int port, int polarity)
{
pd_port[port].has_vbus = 0;
- pd_port[port].cc_volt[polarity] = 400; /* V_rd */
+ pd_port[port].partner_role = PD_ROLE_SINK;
+ pd_port[port].partner_polarity = polarity;
}
static void unplug(int port)
{
pd_port[port].has_vbus = 0;
- pd_port[port].cc_volt[0] = -1;
- pd_port[port].cc_volt[1] = -1;
+ pd_port[port].partner_role = -1;
task_wake(PORT_TO_TASK_ID(port));
usleep(30 * MSEC);
}
@@ -150,7 +167,7 @@ static int test_request(void)
plug_in_source(0, 0);
task_wake(PORT_TO_TASK_ID(0));
- task_wait_event(100 * MSEC);
+ task_wait_event(2 * PD_T_CC_DEBOUNCE + 100 * MSEC);
TEST_ASSERT(pd_port[0].polarity == 0);
/* We're in SNK_DISCOVERY now. Let's send the source cap. */