staging: typec: tcpm: Prevent TCPM from looping in SRC_TRYWAIT
authorBadhri Jagan Sridharan <badhri@google.com>
Mon, 28 Aug 2017 17:23:14 +0000 (10:23 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 29 Aug 2017 06:21:10 +0000 (08:21 +0200)
According to the spec the following is the condition
for exiting TryWait.SRC:

"The port shall transition to Attached.SRC when V BUS is at vSafe0V
and the SRC.Rd state is detected on exactly one of the CC pins for at
least tCCDebounce. The port shall transition to Unattached.SNK after
tDRPTry if neither of the CC1 or CC2 pins are in the SRC.Rd state"

TCPM at present keeps re-entering the SRC_TRYWAIT and keeps restarting
tDRPTry if the CC presents Rp and disconnects within tCCDebounce.

For example:
[  447.164308] pending state change SRC_TRYWAIT -> SRC_ATTACHED @ 200 ms
[  447.164386] CC1: 2 -> 0, CC2: 0 -> 0 [state SRC_TRYWAIT, polarity 0, disconnected]
[  447.164406] state change SRC_TRYWAIT -> SRC_TRYWAIT
[  447.164573] cc:=3
[  447.191408] pending state change SRC_TRYWAIT -> SRC_TRYWAIT_UNATTACHED @ 100 ms
[  447.191478] CC1: 0 -> 0, CC2: 0 -> 0 [state SRC_TRYWAIT, polarity 0, disconnected]
[  447.207261] CC1: 0 -> 2, CC2: 0 -> 0 [state SRC_TRYWAIT, polarity 0, connected]
[  447.207306] state change SRC_TRYWAIT -> SRC_TRYWAIT
[  447.207485] cc:=3
[  447.237283] pending state change SRC_TRYWAIT -> SRC_ATTACHED @ 200 ms
[  447.237357] CC1: 2 -> 0, CC2: 0 -> 0 [state SRC_TRYWAIT, polarity 0, disconnected]
[  447.237379] state change SRC_TRYWAIT -> SRC_TRYWAIT
[  447.237532] cc:=3
[  447.263219] pending state change SRC_TRYWAIT -> SRC_TRYWAIT_UNATTACHED @ 100 ms
[  447.263289] CC1: 0 -> 0, CC2: 0 -> 0 [state SRC_TRYWAIT, polarity 0, disconnected]
[  447.280926] CC1: 0 -> 2, CC2: 0 -> 0 [state SRC_TRYWAIT, polarity 0, connected]
[  447.280970] state change SRC_TRYWAIT -> SRC_TRYWAIT
[  447.281158] cc:=3
[  447.307767] pending state change SRC_TRYWAIT -> SRC_ATTACHED @ 200 ms
[  447.307838] CC1: 2 -> 0, CC2: 0 -> 0 [state SRC_TRYWAIT, polarity 0, disconnected]
[  447.307858] state change SRC_TRYWAIT -> SRC_TRYWAIT

In TCPM, tDRPTry is set tp 100ms (min 75ms and max 150ms)
and tCCdebounce is set to 200ms (min 100ms and max 200ms).
To overcome the issue, record the time at which the port
enters TryWait.SRC(SRC_TRYWAIT) and re-enter SRC_TRYWAIT
only when CC keeps debouncing within tDRPTry.

Signed-off-by: Badhri Jagan Sridharan <Badhri@google.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/typec/tcpm.c

index 81b052384cd5cda9f3cab49261ec828e2db32f4e..a605e21110a71a22b7a1a62edf247f154d44b052 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/completion.h>
 #include <linux/debugfs.h>
 #include <linux/device.h>
+#include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
        S(SNK_TRY),                             \
        S(SNK_TRY_WAIT),                        \
        S(SRC_TRYWAIT),                         \
+       S(SRC_TRYWAIT_DEBOUNCE),                \
        S(SRC_TRYWAIT_UNATTACHED),              \
                                                \
        S(SRC_TRY),                             \
@@ -284,6 +286,9 @@ struct tcpm_port {
        struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX];
        struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX];
 
+       /* Deadline in jiffies to exit src_try_wait state */
+       unsigned long max_wait;
+
 #ifdef CONFIG_DEBUG_FS
        struct dentry *dentry;
        struct mutex logbuffer_lock;    /* log buffer access lock */
@@ -2209,6 +2214,7 @@ static void run_state_machine(struct tcpm_port *port)
                if (!tcpm_port_is_sink(port)) {
                        tcpm_set_state(port, SRC_TRYWAIT,
                                       PD_T_PD_DEBOUNCE);
+                       port->max_wait = 0;
                        break;
                }
                /* No vbus, cc state is sink or open */
@@ -2216,11 +2222,22 @@ static void run_state_machine(struct tcpm_port *port)
                break;
        case SRC_TRYWAIT:
                tcpm_set_cc(port, tcpm_rp_cc(port));
-               if (!port->vbus_present && tcpm_port_is_source(port))
-                       tcpm_set_state(port, SRC_ATTACHED, PD_T_CC_DEBOUNCE);
-               else
+               if (port->max_wait == 0) {
+                       port->max_wait = jiffies +
+                                        msecs_to_jiffies(PD_T_DRP_TRY);
                        tcpm_set_state(port, SRC_TRYWAIT_UNATTACHED,
                                       PD_T_DRP_TRY);
+               } else {
+                       if (time_is_after_jiffies(port->max_wait))
+                               tcpm_set_state(port, SRC_TRYWAIT_UNATTACHED,
+                                              jiffies_to_msecs(port->max_wait -
+                                                               jiffies));
+                       else
+                               tcpm_set_state(port, SNK_UNATTACHED, 0);
+               }
+               break;
+       case SRC_TRYWAIT_DEBOUNCE:
+               tcpm_set_state(port, SRC_ATTACHED, PD_T_CC_DEBOUNCE);
                break;
        case SRC_TRYWAIT_UNATTACHED:
                tcpm_set_state(port, SNK_UNATTACHED, 0);
@@ -2903,11 +2920,10 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
        case SRC_TRYWAIT:
                /* Hand over to state machine if needed */
                if (!port->vbus_present && tcpm_port_is_source(port))
-                       new_state = SRC_ATTACHED;
-               else
-                       new_state = SRC_TRYWAIT_UNATTACHED;
-
-               if (new_state != port->delayed_state)
+                       tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0);
+               break;
+       case SRC_TRYWAIT_DEBOUNCE:
+               if (port->vbus_present || !tcpm_port_is_source(port))
                        tcpm_set_state(port, SRC_TRYWAIT, 0);
                break;
        case SNK_TRY_WAIT:
@@ -2995,9 +3011,10 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port)
                /* Do nothing, waiting for timeout */
                break;
        case SRC_TRYWAIT:
-               /* Hand over to state machine if needed */
-               if (port->delayed_state != SRC_TRYWAIT_UNATTACHED)
-                       tcpm_set_state(port, SRC_TRYWAIT, 0);
+               /* Do nothing, Waiting for Rd to be detected */
+               break;
+       case SRC_TRYWAIT_DEBOUNCE:
+               tcpm_set_state(port, SRC_TRYWAIT, 0);
                break;
        case SNK_TRY_WAIT:
                if (tcpm_port_is_sink(port)) {
@@ -3044,11 +3061,7 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port)
        case SRC_TRYWAIT:
                /* Hand over to state machine if needed */
                if (tcpm_port_is_source(port))
-                       new_state = SRC_ATTACHED;
-               else
-                       new_state = SRC_TRYWAIT_UNATTACHED;
-               if (new_state != port->delayed_state)
-                       tcpm_set_state(port, SRC_TRYWAIT, 0);
+                       tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0);
                break;
        case SNK_TRY_WAIT:
                if (!tcpm_port_is_sink(port))