summaryrefslogtreecommitdiff
path: root/drivers/usb/pd
diff options
context:
space:
mode:
authorJack Pham <jackp@codeaurora.org>2016-10-11 14:48:40 -0700
committerJack Pham <jackp@codeaurora.org>2016-10-14 17:51:45 -0700
commit18da08334eb3355158544a02db35d48761584a22 (patch)
tree9b293c193b99763d474c3b450dc52a579b17161f /drivers/usb/pd
parent5b100713ecb922b373c0334cf69b5dfae9f3a5c1 (diff)
usb: pd: Handle PD_ALLOWED within state machine
Charger may be able to detect a legacy cable connection in which PD is not supported however the state machine still needs to run in order to send the EXTCON_USB notification to start the USB controller in peripheral mode (assuming type is SDP or CDP). Move checking of PD_ALLOWED to the state machine which can be used to determine if moving beyond PE_SNK_STARTUP is allowed or not. The psy_changed() callback should only queue the work when either typec_mode or pd_allowed states have changed. Also move handling of the in_pr_swap flag to this function by absorbing the disconnect callback and clearing the flag when CC state indicates it is reconnected. Change-Id: I8879699531a49915e7286e4a166944709076fbd1 Signed-off-by: Jack Pham <jackp@codeaurora.org>
Diffstat (limited to 'drivers/usb/pd')
-rw-r--r--drivers/usb/pd/policy_engine.c176
1 files changed, 94 insertions, 82 deletions
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 7a1174b32a38..ccf71dc4f624 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -299,6 +299,7 @@ struct usbpd {
enum power_supply_typec_mode typec_mode;
enum power_supply_type psy_type;
bool vbus_present;
+ bool pd_allowed;
enum data_role current_dr;
enum power_role current_pr;
@@ -625,7 +626,10 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
case PE_SRC_STARTUP:
if (pd->current_dr == DR_NONE) {
pd->current_dr = DR_DFP;
- /* Defer starting USB host mode until after PD */
+ /*
+ * Defer starting USB host mode until PE_SRC_READY or
+ * when PE_SRC_SEND_CAPABILITIES fails
+ */
}
/* Set CC back to DRP toggle for the next disconnect */
@@ -660,7 +664,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->current_state = PE_SRC_SEND_CAPABILITIES;
if (pd->in_pr_swap) {
- pd->in_pr_swap = false;
kick_sm(pd, SWAP_SOURCE_START_TIME);
break;
}
@@ -820,6 +823,9 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
}
}
+ if (!pd->pd_allowed)
+ break;
+
/* Reset protocol layer */
pd->tx_msgid = 0;
pd->rx_msgid = -1;
@@ -847,7 +853,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
pd->pd_phy_opened = true;
}
- pd->in_pr_swap = false;
pd->current_voltage = 5000000;
pd->current_state = PE_SNK_WAIT_FOR_CAPABILITIES;
@@ -1373,11 +1378,11 @@ static void usbpd_sm(struct work_struct *w)
ctrl_recvd = pd->rx_msg_type;
/* Disconnect? */
- if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
+ if (pd->typec_mode == POWER_SUPPLY_TYPEC_NONE && !pd->in_pr_swap) {
if (pd->current_state == PE_UNKNOWN)
goto sm_done;
- usbpd_info(&pd->dev, "USB PD disconnect\n");
+ usbpd_info(&pd->dev, "USB Type-C disconnect\n");
if (pd->pd_phy_opened) {
pd_phy_close();
@@ -1484,6 +1489,10 @@ static void usbpd_sm(struct work_struct *w)
}
break;
+ case PE_SRC_STARTUP:
+ usbpd_set_state(pd, PE_SRC_STARTUP);
+ break;
+
case PE_SRC_SEND_CAPABILITIES:
ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps,
ARRAY_SIZE(default_src_caps), SOP_MSG);
@@ -1621,6 +1630,10 @@ static void usbpd_sm(struct work_struct *w)
usbpd_set_state(pd, PE_SRC_TRANSITION_TO_DEFAULT);
break;
+ case PE_SNK_STARTUP:
+ usbpd_set_state(pd, PE_SNK_STARTUP);
+ break;
+
case PE_SNK_WAIT_FOR_CAPABILITIES:
if (data_recvd == MSG_SOURCE_CAPABILITIES) {
val.intval = 0;
@@ -2008,9 +2021,8 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
{
struct usbpd *pd = container_of(nb, struct usbpd, psy_nb);
union power_supply_propval val;
- bool pd_allowed;
enum power_supply_typec_mode typec_mode;
- enum power_supply_type psy_type;
+ bool do_work = false;
int ret;
if (ptr != pd->usb_psy || evt != PSY_EVENT_PROP_CHANGED)
@@ -2024,7 +2036,9 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
return ret;
}
- pd_allowed = val.intval;
+ if (pd->pd_allowed != val.intval)
+ do_work = true;
+ pd->pd_allowed = val.intval;
ret = power_supply_get_property(pd->usb_psy,
POWER_SUPPLY_PROP_PRESENT, &val);
@@ -2044,38 +2058,6 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
typec_mode = val.intval;
- /*
- * Don't proceed if cable is connected but PD_ALLOWED is false.
- * It means the PMIC may still be in the middle of performing
- * charger type detection.
- */
- if (!pd_allowed && typec_mode != POWER_SUPPLY_TYPEC_NONE)
- return 0;
-
- /*
- * Workaround for PMIC HW bug.
- *
- * During hard reset or PR swap (sink to source) 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 pd->hard_reset can be
- * momentarily true even when a non-PD capable source is attached, and
- * can't be distinguished from a physical disconnect. 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 (typec_mode == POWER_SUPPLY_TYPEC_NONE &&
- (pd->in_pr_swap ||
- (pd->psy_type != POWER_SUPPLY_TYPE_USB &&
- pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT))) {
- usbpd_dbg(&pd->dev, "Ignoring disconnect due to %s\n",
- pd->in_pr_swap ? "PR swap" : "hard reset");
- return 0;
- }
-
ret = power_supply_get_property(pd->usb_psy,
POWER_SUPPLY_PROP_TYPE, &val);
if (ret) {
@@ -2083,61 +2065,91 @@ static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
return ret;
}
- psy_type = val.intval;
+ if (pd->psy_type != val.intval)
+ do_work = true;
+ pd->psy_type = val.intval;
usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n",
- typec_mode, pd->vbus_present, psy_type,
+ typec_mode, pd->vbus_present, pd->psy_type,
usbpd_get_plug_orientation(pd));
- /* any change? */
- if (pd->typec_mode == typec_mode && pd->psy_type == psy_type)
- return 0;
+ if (pd->typec_mode != typec_mode) {
+ pd->typec_mode = typec_mode;
+ do_work = true;
- pd->typec_mode = typec_mode;
- pd->psy_type = psy_type;
+ switch (typec_mode) {
+ /* Disconnect */
+ case POWER_SUPPLY_TYPEC_NONE:
+ if (pd->in_pr_swap) {
+ usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n");
+ do_work = false;
+ }
- switch (typec_mode) {
- /* Disconnect */
- case POWER_SUPPLY_TYPEC_NONE:
- kick_sm(pd, 0);
- break;
+ /*
+ * 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
+ * pd->hard_reset can be momentarily true even when a
+ * non-PD capable source is attached, and can't be
+ * distinguished from a physical disconnect. 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");
+ do_work = false;
+ }
- /* Sink states */
- case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
- case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
- case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
- usbpd_info(&pd->dev, "Type-C Source (%s) connected\n",
- src_current(typec_mode));
- if (pd->current_pr != PR_SINK ||
- pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) {
+ break;
+
+ /* Sink states */
+ case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+ case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+ case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+ usbpd_info(&pd->dev, "Type-C Source (%s) connected\n",
+ src_current(typec_mode));
pd->current_pr = PR_SINK;
- kick_sm(pd, 0);
- }
- break;
+ pd->in_pr_swap = false;
+ break;
- /* Source states */
- case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE:
- case POWER_SUPPLY_TYPEC_SINK:
- usbpd_info(&pd->dev, "Type-C Sink%s connected\n",
- typec_mode == POWER_SUPPLY_TYPEC_SINK ?
- "" : " (powered)");
- if (pd->current_pr != PR_SRC) {
+ /* Source states */
+ case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE:
+ case POWER_SUPPLY_TYPEC_SINK:
+ usbpd_info(&pd->dev, "Type-C Sink%s connected\n",
+ typec_mode == POWER_SUPPLY_TYPEC_SINK ?
+ "" : " (powered)");
pd->current_pr = PR_SRC;
- kick_sm(pd, 0);
- }
- break;
+ pd->in_pr_swap = false;
+ break;
- case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY:
- usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n");
- break;
- case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
- usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n");
- break;
- default:
- usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n", typec_mode);
- break;
+ case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY:
+ usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n");
+ break;
+ case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
+ usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n");
+ break;
+ default:
+ usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n",
+ typec_mode);
+ break;
+ }
}
+ /* only queue state machine if CC state or PD_ALLOWED changes */
+ if (do_work)
+ kick_sm(pd, 0);
+
return 0;
}