summaryrefslogtreecommitdiff
path: root/drivers/usb/pd
diff options
context:
space:
mode:
authorJack Pham <jackp@codeaurora.org>2016-11-16 11:30:30 -0800
committerJack Pham <jackp@codeaurora.org>2016-11-23 15:03:14 -0800
commit8f5b031e11be5bfc3bd58a79a27ac25c8988ddaf (patch)
tree0daad963ba42e05c596c8b701474e498ad3f984d /drivers/usb/pd
parent9febdfdde008c8d7875b5186a4a37cb515cb924d (diff)
usb: pd: Fix sink hard reset timing
The current hard reset handling (most recently fixed with commit 172cec3a2c76 ("usb: pd: Improve sink hard reset handling")) is based on a maximum timeout for the longest possible (according to spec) duration that VBUS may turn off and back on again, just under 2s, before re-starting the sink and waiting for capabilities again. However, this method is prone to timing errors, particularly tTypeCSinkWaitCap, which should be based on when VBUS turns on. Fix this by making use of the VBUS presence notification from the charger (PROP_PRESENT). Keep track of this in the psy_changed() callback and use the falling notification to determine when to transition out of PE_SNK_TRANSITION_TO_DEFAULT into PE_SNK_STARTUP. Bring back PE_SNK_DISCOVERY which is now used as a waiting state until the VBUS rising notification comes and transition to PE_SNK_WAIT_FOR_CAPABILITIES. And move setting of PROP_VOLTAGE_MAX to PE_SNK_STARTUP after VBUS has turned off to avoid tripping the charger's overvoltage detection. Also since aforementioned commit, the CC HW is now able to handle VBUS going away and not treating it as a false disconnect, so we can remove the workaround as a disconnect can now be treated as a disconnect. Change-Id: I885f3f4c219e102758fd09c8aae9257d093ebb72 Signed-off-by: Jack Pham <jackp@codeaurora.org>
Diffstat (limited to 'drivers/usb/pd')
-rw-r--r--drivers/usb/pd/policy_engine.c96
1 files changed, 44 insertions, 52 deletions
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index aa2e1d9f3c70..0c9fe4131943 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -163,7 +163,7 @@ static void *usbpd_ipc_log;
/* Timeouts (in ms) */
#define ERROR_RECOVERY_TIME 25
#define SENDER_RESPONSE_TIME 26
-#define SINK_WAIT_CAP_TIME 620
+#define SINK_WAIT_CAP_TIME 500
#define PS_TRANSITION_TIME 450
#define SRC_CAP_TIME 120
#define SRC_TRANSITION_TIME 25
@@ -175,8 +175,11 @@ static void *usbpd_ipc_log;
#define VDM_BUSY_TIME 50
#define VCONN_ON_TIME 100
-/* tPSHardReset + tSafe0V + tSrcRecover + tSrcTurnOn */
-#define SNK_HARD_RESET_RECOVER_TIME (35 + 650 + 1000 + 275)
+/* tPSHardReset + tSafe0V */
+#define SNK_HARD_RESET_VBUS_OFF_TIME (35 + 650)
+
+/* tSrcRecover + tSrcTurnOn */
+#define SNK_HARD_RESET_VBUS_ON_TIME (1000 + 275)
#define PD_CAPS_COUNT 50
@@ -837,7 +840,17 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->pd_phy_opened = true;
}
- pd->current_voltage = 5000000;
+ pd->current_voltage = pd->requested_voltage = 5000000;
+ val.intval = pd->requested_voltage; /* set max range to 5V */
+ power_supply_set_property(pd->usb_psy,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);
+
+ if (!pd->vbus_present) {
+ pd->current_state = PE_SNK_DISCOVERY;
+ /* max time for hard reset to turn vbus back on */
+ kick_sm(pd, SNK_HARD_RESET_VBUS_ON_TIME);
+ break;
+ }
pd->current_state = PE_SNK_WAIT_FOR_CAPABILITIES;
/* fall-through */
@@ -901,13 +914,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->vconn_enabled = false;
}
- val.intval = pd->requested_voltage; /* set range back to 5V */
- power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);
- pd->current_voltage = pd->requested_voltage;
-
- /* max time for hard reset to toggle vbus off/on */
- kick_sm(pd, SNK_HARD_RESET_RECOVER_TIME);
+ /* max time for hard reset to turn vbus off */
+ kick_sm(pd, SNK_HARD_RESET_VBUS_OFF_TIME);
break;
case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
@@ -1665,6 +1673,15 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SNK_STARTUP);
break;
+ case PE_SNK_DISCOVERY:
+ if (!rx_msg) {
+ if (pd->vbus_present)
+ usbpd_set_state(pd,
+ PE_SNK_WAIT_FOR_CAPABILITIES);
+ break;
+ }
+ /* else fall-through */
+
case PE_SNK_WAIT_FOR_CAPABILITIES:
if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) {
val.intval = 0;
@@ -1872,21 +1889,7 @@ static void usbpd_sm(struct work_struct *w)
break;
case PE_SNK_TRANSITION_TO_DEFAULT:
- val.intval = 0;
- power_supply_set_property(pd->usb_psy,
- POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
-
- if (pd->vbus_present) {
- usbpd_set_state(pd, PE_SNK_STARTUP);
- } else {
- /* Hard reset and VBUS didn't come back? */
- power_supply_get_property(pd->usb_psy,
- POWER_SUPPLY_PROP_TYPEC_MODE, &val);
- if (val.intval == POWER_SUPPLY_TYPEC_NONE) {
- pd->typec_mode = POWER_SUPPLY_TYPEC_NONE;
- kick_sm(pd, 0);
- }
- }
+ usbpd_set_state(pd, PE_SNK_STARTUP);
break;
case PE_SRC_SOFT_RESET:
@@ -2134,6 +2137,21 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
pd->psy_type = val.intval;
+ /*
+ * For sink hard reset, state machine needs to know when VBUS changes
+ * - when in PE_SNK_TRANSITION_TO_DEFAULT, notify when VBUS falls
+ * - when in PE_SNK_DISCOVERY, notify when VBUS rises
+ */
+ if (typec_mode && ((!pd->vbus_present &&
+ pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) ||
+ (pd->vbus_present && pd->current_state == PE_SNK_DISCOVERY))) {
+ usbpd_dbg(&pd->dev, "hard reset: typec mode:%d present:%d\n",
+ typec_mode, pd->vbus_present);
+ pd->typec_mode = typec_mode;
+ kick_sm(pd, 0);
+ return 0;
+ }
+
if (pd->typec_mode == typec_mode)
return 0;
@@ -2151,32 +2169,6 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
return 0;
}
- /*
- * Workaround for PMIC HW bug.
- *
- * During hard reset when VBUS goes to 0 the CC logic
- * will report this as a disconnection. In those cases
- * it can be ignored, however the downside is that
- * we can also happen to be in the SNK_Transition_to_default
- * state due to a hard reset attempt even with a non-PD
- * capable source, in which a physical disconnect may get
- * masked. In that case, allow for the common case of
- * disconnecting from an SDP.
- *
- * The less common case is a PD-capable SDP which will
- * result in a hard reset getting treated like a
- * disconnect. We can live with this until the HW bug
- * is fixed: in which disconnection won't be reported
- * on VBUS loss alone unless pullup is also removed
- * from CC.
- */
- if (pd->psy_type != POWER_SUPPLY_TYPE_USB &&
- pd->current_state ==
- PE_SNK_TRANSITION_TO_DEFAULT) {
- usbpd_dbg(&pd->dev, "Ignoring disconnect due to hard reset\n");
- return 0;
- }
-
break;
/* Sink states */