diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index c2d788bc4bc5..8e8c1a349e6a 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -54,6 +54,8 @@ #include "debug.h" #include "xhci.h" +#define SDP_CONNETION_CHECK_TIME 10000 /* in ms */ + /* time out to wait for USB cable status notification (in ms)*/ #define SM_INIT_TIMEOUT 30000 @@ -227,6 +229,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; }; #define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */ @@ -2625,6 +2628,42 @@ done: return NOTIFY_DONE; } + +static void check_for_sdp_connection(struct work_struct *w) +{ + int ret; + union power_supply_propval pval = {0}; + 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) { + if (!mdwc->usb_psy) { + mdwc->usb_psy = power_supply_get_by_name("usb"); + if (!mdwc->usb_psy) { + dev_dbg(mdwc->dev, + "Could not get usb power_supply\n"); + return; + } + } + pval.intval = -ETIMEDOUT; + ret = power_supply_set_property(mdwc->usb_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &pval); + if (ret) + dev_dbg(mdwc->dev, + "power supply error when setting property\n"); + + mdwc->vbus_active = 0; + dbg_event(0xFF, "Q RW SPD CHK", mdwc->vbus_active); + queue_work(mdwc->dwc3_wq, &mdwc->resume_work); + } +} + static int dwc3_msm_vbus_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { @@ -2833,6 +2872,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) { @@ -3586,28 +3626,38 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) return 0; } -static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) +int get_psy_type(struct dwc3_msm *mdwc) { union power_supply_propval pval = {0}; - int ret; if (mdwc->charging_disabled) - return 0; - - if (mdwc->max_power == mA) - return 0; + return -EINVAL; if (!mdwc->usb_psy) { mdwc->usb_psy = power_supply_get_by_name("usb"); if (!mdwc->usb_psy) { - dev_warn(mdwc->dev, "Could not get usb power_supply\n"); + dev_err(mdwc->dev, "Could not get usb psy\n"); return -ENODEV; } } - power_supply_get_property(mdwc->usb_psy, - POWER_SUPPLY_PROP_REAL_TYPE, &pval); - if (pval.intval != POWER_SUPPLY_TYPE_USB) + power_supply_get_property(mdwc->usb_psy, POWER_SUPPLY_PROP_REAL_TYPE, + &pval); + + return pval.intval; +} + +static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA) +{ + union power_supply_propval pval = {0}; + int ret, psy_type; + + if (mdwc->max_power == mA) + return 0; + + psy_type = get_psy_type(mdwc); + if (psy_type != POWER_SUPPLY_TYPE_USB && + psy_type != POWER_SUPPLY_TYPE_USB_FLOAT) return 0; dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA); @@ -3684,6 +3734,10 @@ static void dwc3_otg_sm_work(struct work_struct *w) work = 1; } else if (test_bit(B_SESS_VLD, &mdwc->inputs)) { dev_dbg(mdwc->dev, "b_sess_vld\n"); + if (get_psy_type(mdwc) == POWER_SUPPLY_TYPE_USB_FLOAT) + queue_delayed_work(mdwc->dwc3_wq, + &mdwc->sdp_check, + msecs_to_jiffies(SDP_CONNETION_CHECK_TIME)); /* * Increment pm usage count upon cable connect. Count * is decremented in OTG_STATE_B_PERIPHERAL state on @@ -3707,6 +3761,7 @@ static void dwc3_otg_sm_work(struct work_struct *w) !test_bit(ID, &mdwc->inputs)) { dev_dbg(mdwc->dev, "!id || !bsv\n"); mdwc->otg_state = OTG_STATE_B_IDLE; + cancel_delayed_work_sync(&mdwc->sdp_check); dwc3_otg_start_peripheral(mdwc, 0); /* * Decrement pm usage count upon cable disconnect @@ -3739,6 +3794,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->otg_state = OTG_STATE_B_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");