diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index c529b73b43f9..1176191f17b6 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -50,6 +50,8 @@ #include "debug.h" #include "xhci.h" +#define SDP_CONNECTION_CHECK_TIME 10000 /* in ms */ + /* time out to wait for USB cable status notification (in ms)*/ #define SM_INIT_TIMEOUT 30000 @@ -473,6 +475,7 @@ struct dwc3_msm { int pm_qos_latency; struct pm_qos_request pm_qos_req_dma; struct delayed_work perf_vote_work; + struct delayed_work sdp_check; struct mutex suspend_resume_mutex; enum usb_device_speed override_usb_speed; @@ -3554,6 +3557,24 @@ static int dwc3_msm_id_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +static void check_for_sdp_connection(struct work_struct *w) +{ + struct dwc3_msm *mdwc = + container_of(w, struct dwc3_msm, sdp_check.work); + struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); + + if (!mdwc->vbus_active) + return; + + /* floating D+/D- lines detected */ + if (dwc->gadget.state < USB_STATE_DEFAULT && + dwc3_gadget_get_link_state(dwc) != DWC3_LINK_STATE_CMPLY) { + mdwc->vbus_active = false; + dbg_event(0xFF, "Q RW SPD CHK", mdwc->vbus_active); + queue_work(mdwc->dwc3_wq, &mdwc->resume_work); + } +} + #define DP_PULSE_WIDTH_MSEC 200 static int dwc3_msm_vbus_notifier(struct notifier_block *nb, @@ -4091,6 +4112,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) INIT_WORK(&mdwc->vbus_draw_work, dwc3_msm_vbus_draw_work); INIT_DELAYED_WORK(&mdwc->sm_work, dwc3_otg_sm_work); INIT_DELAYED_WORK(&mdwc->perf_vote_work, msm_dwc3_perf_vote_work); + INIT_DELAYED_WORK(&mdwc->sdp_check, check_for_sdp_connection); mdwc->dwc3_wq = alloc_ordered_workqueue("dwc3_wq", 0); if (!mdwc->dwc3_wq) { @@ -4911,20 +4933,36 @@ static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned int mA) if (!mdwc->usb_psy) return 0; + chg_type = get_chg_type(mdwc); + if (chg_type == QTI_POWER_SUPPLY_TYPE_USB_FLOAT) { + /* + * Do not notify charger driver for any current and + * bail out if suspend happened with float cable + * connected + */ + if (mA == 2) + return 0; + + if (!mA) + pval.intval = -ETIMEDOUT; + else + pval.intval = 1000 * mA; + goto set_prop; + } + /* * Set the valid current only when the device * is connected to a Standard Downstream Port. */ - chg_type = get_chg_type(mdwc); if (mdwc->max_power == mA || (chg_type != -ENODEV && chg_type != POWER_SUPPLY_TYPE_USB)) return 0; - dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA); - /* Set max current limit in uA */ pval.intval = 1000 * mA; +set_prop: + dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA); ret = power_supply_set_property(mdwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &pval); if (ret) { @@ -5005,6 +5043,10 @@ static void dwc3_otg_sm_work(struct work_struct *w) work = true; } else if (test_bit(B_SESS_VLD, &mdwc->inputs)) { dev_dbg(mdwc->dev, "b_sess_vld\n"); + if (get_chg_type(mdwc) == QTI_POWER_SUPPLY_TYPE_USB_FLOAT) + queue_delayed_work(mdwc->dwc3_wq, + &mdwc->sdp_check, + msecs_to_jiffies(SDP_CONNECTION_CHECK_TIME)); /* * Increment pm usage count upon cable connect. Count * is decremented in DRD_STATE_PERIPHERAL state on @@ -5027,6 +5069,7 @@ static void dwc3_otg_sm_work(struct work_struct *w) !test_bit(ID, &mdwc->inputs)) { dev_dbg(mdwc->dev, "!id || !bsv\n"); mdwc->drd_state = DRD_STATE_IDLE; + cancel_delayed_work_sync(&mdwc->sdp_check); dwc3_otg_start_peripheral(mdwc, 0); /* * Decrement pm usage count upon cable disconnect @@ -5059,6 +5102,7 @@ static void dwc3_otg_sm_work(struct work_struct *w) if (!test_bit(B_SESS_VLD, &mdwc->inputs)) { dev_dbg(mdwc->dev, "BSUSP: !bsv\n"); mdwc->drd_state = DRD_STATE_IDLE; + cancel_delayed_work_sync(&mdwc->sdp_check); dwc3_otg_start_peripheral(mdwc, 0); } else if (!test_bit(B_SUSPEND, &mdwc->inputs)) { dev_dbg(mdwc->dev, "BSUSP !susp\n"); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e3e63baf231a..0d2a8d51cb9f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -80,6 +80,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc) return DWC3_DSTS_USBLNKST(reg); } +EXPORT_SYMBOL(dwc3_gadget_get_link_state); /** * dwc3_gadget_set_link_state - sets usb link to a particular state