diff options
Diffstat (limited to 'drivers/usb/dwc3/dwc3-msm.c')
-rw-r--r-- | drivers/usb/dwc3/dwc3-msm.c | 62 |
1 files changed, 49 insertions, 13 deletions
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 5f547793eb9f..1c7d7af6787a 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -202,6 +202,7 @@ struct dwc3_msm { struct power_supply *usb_psy; struct work_struct vbus_draw_work; bool in_host_mode; + bool in_device_mode; enum usb_device_speed max_rh_port_speed; unsigned int tx_fifo_size; bool vbus_active; @@ -239,6 +240,8 @@ struct dwc3_msm { struct delayed_work sdp_check; bool usb_compliance_mode; struct mutex suspend_resume_mutex; + + enum usb_device_speed override_usb_speed; }; #define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */ @@ -1574,8 +1577,17 @@ static void dwc3_restart_usb_work(struct work_struct *w) mdwc->in_restart = false; /* Force reconnect only if cable is still connected */ - if (mdwc->vbus_active) + if (mdwc->vbus_active) { + if (mdwc->override_usb_speed) { + dwc->maximum_speed = mdwc->override_usb_speed; + dwc->gadget.max_speed = dwc->maximum_speed; + dbg_event(0xFF, "override_usb_speed", + mdwc->override_usb_speed); + mdwc->override_usb_speed = 0; + } + dwc3_resume_work(&mdwc->resume_work); + } dwc->err_evt_seen = false; flush_delayed_work(&mdwc->sm_work); @@ -1934,7 +1946,7 @@ static int dwc3_msm_prepare_suspend(struct dwc3_msm *mdwc) unsigned long timeout; u32 reg = 0; - if ((mdwc->in_host_mode || mdwc->vbus_active) + if ((mdwc->in_host_mode || mdwc->in_device_mode) && dwc3_msm_is_superspeed(mdwc) && !mdwc->in_restart) { if (!atomic_read(&mdwc->in_p3)) { dev_err(mdwc->dev, "Not in P3,aborting LPM sequence\n"); @@ -2017,8 +2029,6 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation) int ret, i; struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); - dbg_event(0xFF, "Ctl Sus", atomic_read(&dwc->in_lpm)); - mutex_lock(&mdwc->suspend_resume_mutex); if (atomic_read(&dwc->in_lpm)) { dev_dbg(mdwc->dev, "%s: Already suspended\n", __func__); @@ -2134,8 +2144,8 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation) clk_disable_unprepare(mdwc->xo_clk); /* Perform controller power collapse */ - if ((!mdwc->in_host_mode && (!mdwc->vbus_active || mdwc->in_restart)) || - hibernation) { + if ((!mdwc->in_host_mode && (!mdwc->in_device_mode || mdwc->in_restart)) + || hibernation) { mdwc->lpm_flags |= MDWC3_POWER_COLLAPSE; dev_dbg(mdwc->dev, "%s: power collapse\n", __func__); dwc3_msm_config_gdsc(mdwc, 0); @@ -2168,7 +2178,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation) * using HS_PHY_IRQ or SS_PHY_IRQ. Hence enable wakeup only in * case of host bus suspend and device bus suspend. */ - if (mdwc->vbus_active || mdwc->in_host_mode) { + if (mdwc->in_device_mode || mdwc->in_host_mode) { if (!mdwc->no_wakeup_src_in_hostmode) enable_irq_wake(mdwc->hs_phy_irq); enable_irq(mdwc->hs_phy_irq); @@ -2181,6 +2191,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation) } dev_info(mdwc->dev, "DWC3 in low power mode\n"); + dbg_event(0xFF, "Ctl Sus", atomic_read(&dwc->in_lpm)); mutex_unlock(&mdwc->suspend_resume_mutex); return 0; } @@ -2649,6 +2660,13 @@ static int dwc3_msm_id_notifier(struct notifier_block *nb, if (dwc->maximum_speed > dwc->max_hw_supp_speed) dwc->maximum_speed = dwc->max_hw_supp_speed; + if (!id && mdwc->override_usb_speed) { + dwc->maximum_speed = mdwc->override_usb_speed; + dbg_event(0xFF, "override_usb_speed", + mdwc->override_usb_speed); + mdwc->override_usb_speed = 0; + } + if (mdwc->id_state != id) { mdwc->id_state = id; dbg_event(0xFF, "id_state", mdwc->id_state); @@ -2833,14 +2851,19 @@ static ssize_t mode_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RW(mode); +/* This node only shows max speed supported dwc3 and it should be + * same as what is reported in udc/core.c max_speed node. For current + * operating gadget speed, query current_speed node which is implemented + * by udc/core.c + */ static ssize_t speed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); - return snprintf(buf, PAGE_SIZE, "%s", - usb_speed_string(dwc->max_hw_supp_speed)); + return snprintf(buf, PAGE_SIZE, "%s\n", + usb_speed_string(dwc->maximum_speed)); } static ssize_t speed_store(struct device *dev, struct device_attribute *attr, @@ -2850,14 +2873,25 @@ static ssize_t speed_store(struct device *dev, struct device_attribute *attr, struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); enum usb_device_speed req_speed = USB_SPEED_UNKNOWN; - if (sysfs_streq(buf, "high")) + /* DEVSPD can only have values SS(0x4), HS(0x0) and FS(0x1). + * per 3.20a data book. Allow only these settings. Note that, + * xhci does not support full-speed only mode. + */ + if (sysfs_streq(buf, "full")) + req_speed = USB_SPEED_FULL; + else if (sysfs_streq(buf, "high")) req_speed = USB_SPEED_HIGH; else if (sysfs_streq(buf, "super")) req_speed = USB_SPEED_SUPER; + else + return -EINVAL; - if (req_speed != USB_SPEED_UNKNOWN && - req_speed != dwc->max_hw_supp_speed) { - dwc->maximum_speed = dwc->max_hw_supp_speed = req_speed; + /* restart usb only works for device mode. Perform manual cable + * plug in/out for host mode restart. + */ + if (req_speed != dwc->maximum_speed && + req_speed <= dwc->max_hw_supp_speed) { + mdwc->override_usb_speed = req_speed; schedule_work(&mdwc->restart_usb_work); } @@ -3725,6 +3759,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) dwc3_msm_block_reset(mdwc, false); dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); + mdwc->in_device_mode = true; usb_gadget_vbus_connect(&dwc->gadget); #ifdef CONFIG_SMP mdwc->pm_qos_req_dma.type = PM_QOS_REQ_AFFINE_IRQ; @@ -3743,6 +3778,7 @@ static int dwc3_otg_start_peripheral(struct dwc3_msm *mdwc, int on) msm_dwc3_perf_vote_update(mdwc, false); pm_qos_remove_request(&mdwc->pm_qos_req_dma); + mdwc->in_device_mode = false; usb_gadget_vbus_disconnect(&dwc->gadget); usb_phy_notify_disconnect(mdwc->hs_phy, USB_SPEED_HIGH); usb_phy_notify_disconnect(mdwc->ss_phy, USB_SPEED_SUPER); |