Hi Felipe, On 30/05/16 14:35, Felipe Balbi wrote: > this patch implements the most basic pm_runtime > support for dwc3. Whenever USB cable is dettached, > then we will allow core to runtime_suspend. > > Runtime suspending will involve completely tearing > down event buffers and require a full soft-reset of > the IP. > > Note that a further optimization could be > implemented once we decide to support hibernation, > which is to allow runtime_suspend with cable > connected when bus is in U3. That's subject to a > separate patch, however. > > Tested-by: Baolin Wang <baolin.wang@xxxxxxxxxx> > Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxxxxxxxx> We've discussed this offline but for the record, this patch breaks DWC3 on OMAP platforms. At least on dra7-evm I could see both USB host and gadget breaking. I will try to implement the runtime resume hooks for dwc3-omap and let you know if we can make it work. cheers, -roger > --- > drivers/usb/dwc3/core.c | 146 +++++++++++++++++++++++++++++++++++++++++----- > drivers/usb/dwc3/core.h | 9 +++ > drivers/usb/dwc3/gadget.c | 32 +++++++++- > 3 files changed, 171 insertions(+), 16 deletions(-) > > diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c > index 80e9affd3d77..167d44375640 100644 > --- a/drivers/usb/dwc3/core.c > +++ b/drivers/usb/dwc3/core.c > @@ -48,7 +48,7 @@ > > #include "debug.h" > > -/* -------------------------------------------------------------------------- */ > +#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ > > void dwc3_set_mode(struct dwc3 *dwc, u32 mode) > { > @@ -996,9 +996,11 @@ static int dwc3_probe(struct platform_device *pdev) > dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); > } > > + pm_runtime_set_active(dev); > + pm_runtime_use_autosuspend(dev); > + pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY); > pm_runtime_enable(dev); > pm_runtime_get_sync(dev); > - pm_runtime_forbid(dev); > > ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); > if (ret) { > @@ -1057,7 +1059,7 @@ static int dwc3_probe(struct platform_device *pdev) > goto err3; > > dwc3_debugfs_init(dwc); > - pm_runtime_allow(dev); > + pm_runtime_put(dev); > > return 0; > > @@ -1087,6 +1089,7 @@ static int dwc3_remove(struct platform_device *pdev) > struct dwc3 *dwc = platform_get_drvdata(pdev); > struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + pm_runtime_get_sync(&pdev->dev); > /* > * restore res->start back to its original value so that, in case the > * probe is deferred, we don't end up getting error in request the > @@ -1100,24 +1103,26 @@ static int dwc3_remove(struct platform_device *pdev) > dwc3_core_exit(dwc); > dwc3_ulpi_exit(dwc); > > - dwc3_free_event_buffers(dwc); > - dwc3_free_scratch_buffers(dwc); > - > pm_runtime_put_sync(&pdev->dev); > pm_runtime_disable(&pdev->dev); > > + dwc3_free_event_buffers(dwc); > + dwc3_free_scratch_buffers(dwc); > + > return 0; > } > > -#ifdef CONFIG_PM_SLEEP > -static int dwc3_suspend(struct device *dev) > +#ifdef CONFIG_PM > +static int dwc3_suspend_common(struct dwc3 *dwc) > { > - struct dwc3 *dwc = dev_get_drvdata(dev); > + unsigned long flags; > > switch (dwc->dr_mode) { > case USB_DR_MODE_PERIPHERAL: > case USB_DR_MODE_OTG: > + spin_lock_irqsave(&dwc->lock, flags); > dwc3_gadget_suspend(dwc); > + spin_unlock_irqrestore(&dwc->lock, flags); > break; > case USB_DR_MODE_HOST: > default: > @@ -1127,18 +1132,14 @@ static int dwc3_suspend(struct device *dev) > > dwc3_core_exit(dwc); > > - pinctrl_pm_select_sleep_state(dev); > - > return 0; > } > > -static int dwc3_resume(struct device *dev) > +static int dwc3_resume_common(struct dwc3 *dwc) > { > - struct dwc3 *dwc = dev_get_drvdata(dev); > + unsigned long flags; > int ret; > > - pinctrl_pm_select_default_state(dev); > - > ret = dwc3_core_init(dwc); > if (ret) > return ret; > @@ -1146,7 +1147,9 @@ static int dwc3_resume(struct device *dev) > switch (dwc->dr_mode) { > case USB_DR_MODE_PERIPHERAL: > case USB_DR_MODE_OTG: > + spin_lock_irqsave(&dwc->lock, flags); > dwc3_gadget_resume(dwc); > + spin_unlock_irqrestore(&dwc->lock, flags); > /* FALLTHROUGH */ > case USB_DR_MODE_HOST: > default: > @@ -1154,6 +1157,117 @@ static int dwc3_resume(struct device *dev) > break; > } > > + return 0; > +} > + > +static int dwc3_runtime_checks(struct dwc3 *dwc) > +{ > + switch (dwc->dr_mode) { > + case USB_DR_MODE_PERIPHERAL: > + case USB_DR_MODE_OTG: > + if (dwc->connected) > + return -EBUSY; > + break; > + case USB_DR_MODE_HOST: > + default: > + /* do nothing */ > + break; > + } > + > + return 0; > +} > + > +static int dwc3_runtime_suspend(struct device *dev) > +{ > + struct dwc3 *dwc = dev_get_drvdata(dev); > + int ret; > + > + if (dwc3_runtime_checks(dwc)) > + return -EBUSY; > + > + ret = dwc3_suspend_common(dwc); > + if (ret) > + return ret; > + > + device_init_wakeup(dev, true); > + > + return 0; > +} > + > +static int dwc3_runtime_resume(struct device *dev) > +{ > + struct dwc3 *dwc = dev_get_drvdata(dev); > + int ret; > + > + device_init_wakeup(dev, false); > + > + ret = dwc3_resume_common(dwc); > + if (ret) > + return ret; > + > + switch (dwc->dr_mode) { > + case USB_DR_MODE_PERIPHERAL: > + case USB_DR_MODE_OTG: > + dwc3_gadget_process_pending_events(dwc); > + break; > + case USB_DR_MODE_HOST: > + default: > + /* do nothing */ > + break; > + } > + > + return 0; > +} > + > +static int dwc3_runtime_idle(struct device *dev) > +{ > + struct dwc3 *dwc = dev_get_drvdata(dev); > + > + switch (dwc->dr_mode) { > + case USB_DR_MODE_PERIPHERAL: > + case USB_DR_MODE_OTG: > + if (dwc3_runtime_checks(dwc)) > + return -EBUSY; > + break; > + case USB_DR_MODE_HOST: > + default: > + /* do nothing */ > + break; > + } > + > + pm_runtime_mark_last_busy(dev); > + pm_runtime_autosuspend(dev); > + > + return 0; > +} > +#endif /* CONFIG_PM */ > + > +#ifdef CONFIG_PM_SLEEP > +static int dwc3_suspend(struct device *dev) > +{ > + struct dwc3 *dwc = dev_get_drvdata(dev); > + int ret; > + > + ret = dwc3_suspend_common(dwc); > + if (ret) > + return ret; > + > + pinctrl_pm_select_sleep_state(dev); > + > + return 0; > +} > + > +static int dwc3_resume(struct device *dev) > +{ > + struct dwc3 *dwc = dev_get_drvdata(dev); > + int ret; > + > + pinctrl_pm_select_default_state(dev); > + > + ret = dwc3_resume_common(dwc); > + if (ret) > + return ret; > + > pm_runtime_disable(dev); > pm_runtime_set_active(dev); > pm_runtime_enable(dev); > @@ -1164,6 +1278,8 @@ static int dwc3_resume(struct device *dev) > > static const struct dev_pm_ops dwc3_dev_pm_ops = { > SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) > + SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume, > + dwc3_runtime_idle) > }; > > #ifdef CONFIG_OF > diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h > index 5f67c4bc1d37..a71e2604bb86 100644 > --- a/drivers/usb/dwc3/core.h > +++ b/drivers/usb/dwc3/core.h > @@ -762,6 +762,7 @@ struct dwc3_scratchpad_array { > * @lpm_nyet_threshold: LPM NYET response threshold > * @hird_threshold: HIRD threshold > * @hsphy_interface: "utmi" or "ulpi" > + * @connected: true when we're connected to a host, false otherwise > * @delayed_status: true when gadget driver asks for delayed status > * @ep0_bounced: true when we used bounce buffer > * @ep0_expect_in: true when we expect a DATA IN transfer > @@ -772,6 +773,7 @@ struct dwc3_scratchpad_array { > * 0 - utmi_sleep_n > * 1 - utmi_l1_suspend_n > * @is_fpga: true when we are using the FPGA board > + * @pending_events: true when we have pending IRQs to be handled > * @pullups_connected: true when Run/Stop bit is set > * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround > * @start_config_issued: true when StartConfig command has been issued > @@ -906,6 +908,7 @@ struct dwc3 { > > const char *hsphy_interface; > > + unsigned connected:1; > unsigned delayed_status:1; > unsigned ep0_bounced:1; > unsigned ep0_expect_in:1; > @@ -913,6 +916,7 @@ struct dwc3 { > unsigned has_lpm_erratum:1; > unsigned is_utmi_l1_suspend:1; > unsigned is_fpga:1; > + unsigned pending_events:1; > unsigned pullups_connected:1; > unsigned setup_packet_pending:1; > unsigned three_stage_setup:1; > @@ -1138,6 +1142,7 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, > #if !IS_ENABLED(CONFIG_USB_DWC3_HOST) > int dwc3_gadget_suspend(struct dwc3 *dwc); > int dwc3_gadget_resume(struct dwc3 *dwc); > +void dwc3_gadget_process_pending_events(struct dwc3 *dwc); > #else > static inline int dwc3_gadget_suspend(struct dwc3 *dwc) > { > @@ -1148,6 +1153,10 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc) > { > return 0; > } > + > +static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc) > +{ > +} > #endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ > > #if IS_ENABLED(CONFIG_USB_DWC3_ULPI) > diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c > index d9df5916efdb..a39e64bc0627 100644 > --- a/drivers/usb/dwc3/gadget.c > +++ b/drivers/usb/dwc3/gadget.c > @@ -199,6 +199,9 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, > spin_unlock(&dwc->lock); > usb_gadget_giveback_request(&dep->endpoint, &req->request); > spin_lock(&dwc->lock); > + > + if (dep->number > 1) > + pm_runtime_put(dwc->dev); > } > > int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) > @@ -1059,6 +1062,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) > return -EINVAL; > } > > + pm_runtime_get(dwc->dev); > + > req->request.actual = 0; > req->request.status = -EINPROGRESS; > req->direction = dep->direction; > @@ -1487,6 +1492,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) > u32 reg; > u32 timeout = 500; > > + if (pm_runtime_suspended(dwc->dev)) > + return 0; > + > reg = dwc3_readl(dwc->regs, DWC3_DCTL); > if (is_on) { > if (dwc->revision <= DWC3_REVISION_187A) { > @@ -1744,7 +1752,9 @@ static int dwc3_gadget_start(struct usb_gadget *g, > > dwc->gadget_driver = driver; > > - __dwc3_gadget_start(dwc); > + if (pm_runtime_active(dwc->dev)) > + __dwc3_gadget_start(dwc); > + > spin_unlock_irqrestore(&dwc->lock, flags); > > return 0; > @@ -2336,12 +2346,16 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) > dwc->gadget.speed = USB_SPEED_UNKNOWN; > dwc->setup_packet_pending = false; > usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED); > + > + dwc->connected = false; > } > > static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) > { > u32 reg; > > + dwc->connected = true; > + > /* > * WORKAROUND: DWC3 revisions <1.88a have an issue which > * would cause a missing Disconnect Event if there's a > @@ -2803,6 +2817,13 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) > u32 count; > u32 reg; > > + if (pm_runtime_suspended(dwc->dev)) { > + pm_runtime_get(dwc->dev); > + disable_irq_nosync(dwc->irq_gadget); > + dwc->pending_events = true; > + return IRQ_HANDLED; > + } > + > count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); > count &= DWC3_GEVNTCOUNT_MASK; > if (!count) > @@ -3009,3 +3030,12 @@ err1: > err0: > return ret; > } > + > +void dwc3_gadget_process_pending_events(struct dwc3 *dwc) > +{ > + if (dwc->pending_events) { > + dwc3_interrupt(dwc->irq_gadget, dwc->ev_buf); > + dwc->pending_events = false; > + enable_irq(dwc->irq_gadget); > + } > +} > -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html