From 7f370ed0cfe9aa1520696c1c71e8a51e2c0bbcc1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 9 May 2016 15:27:01 +0300 Subject: usb: dwc3: core: get rid of DWC3_PM_OPS macro that macro is unnecessary and just adds pointless obfuscation. Let's remove it. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a590cd225bb7..245f4ff6ae16 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1209,16 +1209,12 @@ err_usb2phy_power: return ret; } +#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops dwc3_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) }; -#define DWC3_PM_OPS &(dwc3_dev_pm_ops) -#else -#define DWC3_PM_OPS NULL -#endif - #ifdef CONFIG_OF static const struct of_device_id of_dwc3_match[] = { { @@ -1250,7 +1246,7 @@ static struct platform_driver dwc3_driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), .acpi_match_table = ACPI_PTR(dwc3_acpi_match), - .pm = DWC3_PM_OPS, + .pm = &dwc3_dev_pm_ops, }, }; -- cgit v1.2.3-70-g09d2 From bcdb3272e889f4d2a5c8efc5e12b0fb2dcbf75e9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 10:42:23 +0300 Subject: usb: dwc3: core: move fladj to dwc3 structure this patch is in preparation for some further re-factoring in dwc3 initialization. No functional changes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 16 +++++++--------- drivers/usb/dwc3/core.h | 2 ++ 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 245f4ff6ae16..1f4ac355f384 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -149,9 +149,8 @@ static int dwc3_soft_reset(struct dwc3 *dwc) /* * dwc3_frame_length_adjustment - Adjusts frame length if required * @dwc3: Pointer to our controller context structure - * @fladj: Value of GFLADJ_30MHZ to adjust frame length */ -static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj) +static void dwc3_frame_length_adjustment(struct dwc3 *dwc) { u32 reg; u32 dft; @@ -159,15 +158,15 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj) if (dwc->revision < DWC3_REVISION_250A) return; - if (fladj == 0) + if (dwc->fladj == 0) return; reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); dft = reg & DWC3_GFLADJ_30MHZ_MASK; - if (!dev_WARN_ONCE(dwc->dev, dft == fladj, + if (!dev_WARN_ONCE(dwc->dev, dft == dwc->fladj, "request value same as default, ignoring\n")) { reg &= ~DWC3_GFLADJ_30MHZ_MASK; - reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); } } @@ -799,7 +798,6 @@ static int dwc3_probe(struct platform_device *pdev) u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; - u32 fladj = 0; int ret; @@ -909,7 +907,7 @@ static int dwc3_probe(struct platform_device *pdev) device_property_read_string(dev, "snps,hsphy_interface", &dwc->hsphy_interface); device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", - &fladj); + &dwc->fladj); if (pdata) { dwc->maximum_speed = pdata->maximum_speed; @@ -941,7 +939,7 @@ static int dwc3_probe(struct platform_device *pdev) tx_de_emphasis = pdata->tx_de_emphasis; dwc->hsphy_interface = pdata->hsphy_interface; - fladj = pdata->fladj_value; + dwc->fladj = pdata->fladj_value; } dwc->lpm_nyet_threshold = lpm_nyet_threshold; @@ -1022,7 +1020,7 @@ static int dwc3_probe(struct platform_device *pdev) } /* Adjust Frame Length */ - dwc3_frame_length_adjustment(dwc, fladj); + dwc3_frame_length_adjustment(dwc); usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 02990ab6d7f6..e01f6371d118 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -729,6 +729,7 @@ struct dwc3_scratchpad_array { * @gadget_driver: pointer to the gadget driver * @regs: base address for our registers * @regs_size: address space size + * @fladj: frame length adjustment * @nr_scratch: number of scratch buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) @@ -838,6 +839,7 @@ struct dwc3 { /* used for suspend/resume */ u32 gctl; + u32 fladj; u32 nr_scratch; u32 u1u2; u32 maximum_speed; -- cgit v1.2.3-70-g09d2 From c499ff71ff2a281366c6ec7a904c547d806cbcd1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 10:49:01 +0300 Subject: usb: dwc3: core: re-factor init and exit paths The idea of this patch is for dwc3_core_init() to abstract all the details about how to initialize dwc3 and dwc3_core_exit() to do the same for teardown. With this, we can simplify suspend/resume operations by a large margin and always know that we're going to start dwc3 from a known starting point. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 118 ++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 58 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 1f4ac355f384..cbdefbb3d302 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -506,6 +506,21 @@ static int dwc3_phy_setup(struct dwc3 *dwc) return 0; } +static void dwc3_core_exit(struct dwc3 *dwc) +{ + dwc3_event_buffers_cleanup(dwc); + + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); + + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); + phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); +} + /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -555,6 +570,10 @@ static int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err0; + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; + reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; @@ -621,22 +640,45 @@ static int dwc3_core_init(struct dwc3 *dwc) if (dwc->revision < DWC3_REVISION_190A) reg |= DWC3_GCTL_U2RSTECN; - dwc3_core_num_eps(dwc); - dwc3_writel(dwc->regs, DWC3_GCTL, reg); - ret = dwc3_alloc_scratch_buffers(dwc); - if (ret) - goto err1; + dwc3_core_num_eps(dwc); ret = dwc3_setup_scratch_buffers(dwc); if (ret) + goto err1; + + /* Adjust Frame Length */ + dwc3_frame_length_adjustment(dwc); + + usb_phy_set_suspend(dwc->usb2_phy, 0); + usb_phy_set_suspend(dwc->usb3_phy, 0); + ret = phy_power_on(dwc->usb2_generic_phy); + if (ret < 0) goto err2; + ret = phy_power_on(dwc->usb3_generic_phy); + if (ret < 0) + goto err3; + + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + goto err4; + } + return 0; +err4: + phy_power_off(dwc->usb2_generic_phy); + +err3: + phy_power_off(dwc->usb3_generic_phy); + err2: - dwc3_free_scratch_buffers(dwc); + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); + dwc3_core_exit(dwc); err1: usb_phy_shutdown(dwc->usb2_phy); @@ -648,15 +690,6 @@ err0: return ret; } -static void dwc3_core_exit(struct dwc3 *dwc) -{ - dwc3_free_scratch_buffers(dwc); - usb_phy_shutdown(dwc->usb2_phy); - usb_phy_shutdown(dwc->usb3_phy); - phy_exit(dwc->usb2_generic_phy); - phy_exit(dwc->usb3_generic_phy); -} - static int dwc3_core_get_phy(struct dwc3 *dwc) { struct device *dev = dwc->dev; @@ -951,10 +984,6 @@ static int dwc3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dwc); dwc3_cache_hwparams(dwc); - ret = dwc3_phy_setup(dwc); - if (ret) - goto err0; - ret = dwc3_core_get_phy(dwc); if (ret) goto err0; @@ -975,7 +1004,7 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); ret = -ENOMEM; - goto err1; + goto err0; } if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) @@ -986,10 +1015,14 @@ static int dwc3_probe(struct platform_device *pdev) if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) dwc->dr_mode = USB_DR_MODE_OTG; + ret = dwc3_alloc_scratch_buffers(dwc); + if (ret) + goto err1; + ret = dwc3_core_init(dwc); if (ret) { dev_err(dev, "failed to initialize core\n"); - goto err1; + goto err2; } /* Check the maximum_speed parameter */ @@ -1019,47 +1052,20 @@ static int dwc3_probe(struct platform_device *pdev) break; } - /* Adjust Frame Length */ - dwc3_frame_length_adjustment(dwc); - - usb_phy_set_suspend(dwc->usb2_phy, 0); - usb_phy_set_suspend(dwc->usb3_phy, 0); - ret = phy_power_on(dwc->usb2_generic_phy); - if (ret < 0) - goto err2; - - ret = phy_power_on(dwc->usb3_generic_phy); - if (ret < 0) - goto err3; - - ret = dwc3_event_buffers_setup(dwc); - if (ret) { - dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err4; - } - ret = dwc3_core_init_mode(dwc); if (ret) - goto err5; + goto err3; dwc3_debugfs_init(dwc); pm_runtime_allow(dev); return 0; -err5: - dwc3_event_buffers_cleanup(dwc); - -err4: - phy_power_off(dwc->usb3_generic_phy); - err3: - phy_power_off(dwc->usb2_generic_phy); + dwc3_event_buffers_cleanup(dwc); err2: - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); - dwc3_core_exit(dwc); + dwc3_free_scratch_buffers(dwc); err1: dwc3_free_event_buffers(dwc); @@ -1090,17 +1096,13 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_debugfs_exit(dwc); dwc3_core_exit_mode(dwc); - dwc3_event_buffers_cleanup(dwc); - dwc3_free_event_buffers(dwc); - - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); - phy_power_off(dwc->usb2_generic_phy); - phy_power_off(dwc->usb3_generic_phy); 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); -- cgit v1.2.3-70-g09d2 From 51f5d49ad6f011ee380b866ea617fd90584189a2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 10:52:58 +0300 Subject: usb: dwc3: core: simplify suspend/resume operations now that we have re-factored dwc3_core_init() and dwc3_core_exit() we can use them for suspend/resume operations. This will help us avoid some common mistakes when patching code when we have duplicated pieces of code doing the same thing. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 59 +++++-------------------------------------------- drivers/usb/dwc3/core.h | 3 --- 2 files changed, 5 insertions(+), 57 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index cbdefbb3d302..80e9affd3d77 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1113,33 +1113,19 @@ static int dwc3_remove(struct platform_device *pdev) static int dwc3_suspend(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_OTG: dwc3_gadget_suspend(dwc); - /* FALLTHROUGH */ + break; case USB_DR_MODE_HOST: default: - dwc3_event_buffers_cleanup(dwc); + /* do nothing */ break; } - dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL); - spin_unlock_irqrestore(&dwc->lock, flags); - - usb_phy_shutdown(dwc->usb3_phy); - usb_phy_shutdown(dwc->usb2_phy); - phy_exit(dwc->usb2_generic_phy); - phy_exit(dwc->usb3_generic_phy); - - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); - WARN_ON(phy_power_off(dwc->usb2_generic_phy) < 0); - WARN_ON(phy_power_off(dwc->usb3_generic_phy) < 0); + dwc3_core_exit(dwc); pinctrl_pm_select_sleep_state(dev); @@ -1149,36 +1135,14 @@ static int dwc3_suspend(struct device *dev) static int dwc3_resume(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); - unsigned long flags; int ret; pinctrl_pm_select_default_state(dev); - usb_phy_set_suspend(dwc->usb2_phy, 0); - usb_phy_set_suspend(dwc->usb3_phy, 0); - ret = phy_power_on(dwc->usb2_generic_phy); - if (ret < 0) + ret = dwc3_core_init(dwc); + if (ret) return ret; - ret = phy_power_on(dwc->usb3_generic_phy); - if (ret < 0) - goto err_usb2phy_power; - - usb_phy_init(dwc->usb3_phy); - usb_phy_init(dwc->usb2_phy); - ret = phy_init(dwc->usb2_generic_phy); - if (ret < 0) - goto err_usb3phy_power; - - ret = phy_init(dwc->usb3_generic_phy); - if (ret < 0) - goto err_usb2phy_init; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc3_event_buffers_setup(dwc); - dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl); - switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_OTG: @@ -1190,24 +1154,11 @@ static int dwc3_resume(struct device *dev) break; } - spin_unlock_irqrestore(&dwc->lock, flags); - pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); return 0; - -err_usb2phy_init: - phy_exit(dwc->usb2_generic_phy); - -err_usb3phy_power: - phy_power_off(dwc->usb3_generic_phy); - -err_usb2phy_power: - phy_power_off(dwc->usb2_generic_phy); - - return ret; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e01f6371d118..211729519eb4 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -836,9 +836,6 @@ struct dwc3 { enum usb_dr_mode dr_mode; - /* used for suspend/resume */ - u32 gctl; - u32 fladj; u32 nr_scratch; u32 u1u2; -- cgit v1.2.3-70-g09d2 From fc8bb91bc83ef82868533e75f5a11abc1158ec81 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 13:14:48 +0300 Subject: usb: dwc3: implement runtime PM 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 Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 148 +++++++++++++++++++++++++++++++++++++++++----- drivers/usb/dwc3/core.h | 9 +++ drivers/usb/dwc3/gadget.c | 32 +++++++++- 3 files changed, 174 insertions(+), 15 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 80e9affd3d77..bf1789f134ac 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,6 +996,9 @@ 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); @@ -1057,7 +1060,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 +1090,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 +1104,27 @@ 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_allow(&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 +1134,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 +1149,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 +1159,119 @@ 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; + } + + pm_runtime_mark_last_busy(dev); + + 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 +1282,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 5148c563167e..94b9fd2d583e 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -763,6 +763,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 @@ -773,6 +774,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 @@ -907,6 +909,7 @@ struct dwc3 { const char *hsphy_interface; + unsigned connected:1; unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; @@ -914,6 +917,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; @@ -1139,6 +1143,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) { @@ -1149,6 +1154,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 b59893c3093a..194775184a58 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) @@ -1081,6 +1084,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; @@ -1509,6 +1514,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) { @@ -1766,7 +1774,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; @@ -2355,12 +2365,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 @@ -2822,6 +2836,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) @@ -3028,3 +3049,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); + } +} -- cgit v1.2.3-70-g09d2 From d18e65470a4d9b6f7d7a72a0d102d009b45cf9d4 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 22 Apr 2016 11:17:40 +0300 Subject: usb: dwc3: remove handling of platform data No more users for it. Tested-by: John Youn Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 35 -------------------------- drivers/usb/dwc3/platform_data.h | 53 ---------------------------------------- 2 files changed, 88 deletions(-) delete mode 100644 drivers/usb/dwc3/platform_data.h (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index bf1789f134ac..9c4e1d8d01db 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -41,7 +41,6 @@ #include #include -#include "platform_data.h" #include "core.h" #include "gadget.h" #include "io.h" @@ -825,7 +824,6 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct dwc3_platform_data *pdata = dev_get_platdata(dev); struct resource *res; struct dwc3 *dwc; u8 lpm_nyet_threshold; @@ -942,39 +940,6 @@ static int dwc3_probe(struct platform_device *pdev) device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); - if (pdata) { - dwc->maximum_speed = pdata->maximum_speed; - dwc->has_lpm_erratum = pdata->has_lpm_erratum; - if (pdata->lpm_nyet_threshold) - lpm_nyet_threshold = pdata->lpm_nyet_threshold; - dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend; - if (pdata->hird_threshold) - hird_threshold = pdata->hird_threshold; - - dwc->usb3_lpm_capable = pdata->usb3_lpm_capable; - dwc->dr_mode = pdata->dr_mode; - - dwc->disable_scramble_quirk = pdata->disable_scramble_quirk; - dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk; - dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk; - dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk; - dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk; - dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk; - dwc->lfps_filter_quirk = pdata->lfps_filter_quirk; - dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk; - dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk; - dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk; - dwc->dis_enblslpm_quirk = pdata->dis_enblslpm_quirk; - dwc->dis_rxdet_inp3_quirk = pdata->dis_rxdet_inp3_quirk; - - dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk; - if (pdata->tx_de_emphasis) - tx_de_emphasis = pdata->tx_de_emphasis; - - dwc->hsphy_interface = pdata->hsphy_interface; - dwc->fladj = pdata->fladj_value; - } - dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h deleted file mode 100644 index 8826cca5fc6f..000000000000 --- a/drivers/usb/dwc3/platform_data.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * platform_data.h - USB DWC3 Platform Data Support - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * Author: Felipe Balbi - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 of - * the License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -struct dwc3_platform_data { - enum usb_device_speed maximum_speed; - enum usb_dr_mode dr_mode; - bool usb3_lpm_capable; - - unsigned is_utmi_l1_suspend:1; - u8 hird_threshold; - - u8 lpm_nyet_threshold; - - unsigned disable_scramble_quirk:1; - unsigned has_lpm_erratum:1; - unsigned u2exit_lfps_quirk:1; - unsigned u2ss_inp3_quirk:1; - unsigned req_p1p2p3_quirk:1; - unsigned del_p1p2p3_quirk:1; - unsigned del_phy_power_chg_quirk:1; - unsigned lfps_filter_quirk:1; - unsigned rx_detect_poll_quirk:1; - unsigned dis_u3_susphy_quirk:1; - unsigned dis_u2_susphy_quirk:1; - unsigned dis_enblslpm_quirk:1; - unsigned dis_rxdet_inp3_quirk:1; - - unsigned tx_de_emphasis_quirk:1; - unsigned tx_de_emphasis:2; - - u32 fladj_value; - - const char *hsphy_interface; -}; -- cgit v1.2.3-70-g09d2 From 5f82279a0c76825bfc6f5fa213ff82c932161462 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 7 Jun 2016 12:55:19 +0300 Subject: usb: dwc3: core: fixup dr_mode fallback selection We shouldn't change a host-only dwc3 to gadget-only if driver is built as gadget-only. Fix that up here. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 9c4e1d8d01db..8fceeb1a32a4 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -975,9 +975,13 @@ static int dwc3_probe(struct platform_device *pdev) goto err0; } - if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) + if (IS_ENABLED(CONFIG_USB_DWC3_HOST) && + (dwc->dr_mode == USB_DR_MODE_OTG || + dwc->dr_mode == USB_DR_MODE_UNKNOWN)) dwc->dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) && + (dwc->dr_mode == USB_DR_MODE_OTG || + dwc->dr_mode == USB_DR_MODE_UNKNOWN)) dwc->dr_mode = USB_DR_MODE_PERIPHERAL; if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) -- cgit v1.2.3-70-g09d2 From 328082376aea6016b63bca1e5c067a9539f9e8c9 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jun 2016 14:38:02 +0300 Subject: usb: dwc3: fix runtime PM in error path If there is a failure after pm_runtime_enable/get_sync() we need to call pm_runtime_disable/put_sync(). Otherwise it will lead to an unbalanced pm_runtime_enable() on the subsequent probe if the earlier probe bailed out due to -EPROBE_DEFER. pm_runtime_get_sync() can fail as well so deal with that case too. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 8fceeb1a32a4..ca22e4885116 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -965,14 +965,17 @@ static int dwc3_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY); pm_runtime_enable(dev); - pm_runtime_get_sync(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto err1; + pm_runtime_forbid(dev); ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); ret = -ENOMEM; - goto err0; + goto err2; } if (IS_ENABLED(CONFIG_USB_DWC3_HOST) && @@ -989,12 +992,12 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_alloc_scratch_buffers(dwc); if (ret) - goto err1; + goto err3; ret = dwc3_core_init(dwc); if (ret) { dev_err(dev, "failed to initialize core\n"); - goto err2; + goto err4; } /* Check the maximum_speed parameter */ @@ -1026,23 +1029,30 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_core_init_mode(dwc); if (ret) - goto err3; + goto err5; dwc3_debugfs_init(dwc); pm_runtime_put(dev); return 0; -err3: +err5: dwc3_event_buffers_cleanup(dwc); -err2: +err4: dwc3_free_scratch_buffers(dwc); -err1: +err3: dwc3_free_event_buffers(dwc); dwc3_ulpi_exit(dwc); +err2: + pm_runtime_allow(&pdev->dev); + +err1: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + err0: /* * restore res->start back to its original value so that, in case the -- cgit v1.2.3-70-g09d2 From 9522def40065194aa75b0a7b7e1ff5b8e2014724 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jun 2016 14:48:38 +0300 Subject: usb: dwc3: core: cleanup IRQ resources Implementations might use different IRQs for host, gadget so use named interrupt resources to allow device tree to specify the interrupts. Following are the interrupt names Peripheral Interrupt - peripheral HOST Interrupt - host Maintain backward compatibility for a single named interrupt ("dwc3_usb3") for all interrupts as well as unnamed interrupt at index 0 for all interrupts. As platform_get_irq() variants are used, tackle the -EPROBE_DEFER case as well. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 22 ++++++++-------------- drivers/usb/dwc3/gadget.c | 31 ++++++++++++++++++++++++++++--- drivers/usb/dwc3/host.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 18 deletions(-) (limited to 'drivers/usb/dwc3/core.c') diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ca22e4885116..946643157b78 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -766,7 +766,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) { - dev_err(dev, "failed to initialize gadget\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize gadget\n"); return ret; } break; @@ -774,7 +775,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); ret = dwc3_host_init(dwc); if (ret) { - dev_err(dev, "failed to initialize host\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize host\n"); return ret; } break; @@ -782,13 +784,15 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); ret = dwc3_host_init(dwc); if (ret) { - dev_err(dev, "failed to initialize host\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize host\n"); return ret; } ret = dwc3_gadget_init(dwc); if (ret) { - dev_err(dev, "failed to initialize gadget\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize gadget\n"); return ret; } break; @@ -843,16 +847,6 @@ static int dwc3_probe(struct platform_device *pdev) dwc->mem = mem; dwc->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "missing IRQ\n"); - return -ENODEV; - } - dwc->xhci_resources[1].start = res->start; - dwc->xhci_resources[1].end = res->end; - dwc->xhci_resources[1].flags = res->flags; - dwc->xhci_resources[1].name = res->name; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "missing memory resource\n"); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b9bc646511ce..f20975300594 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1765,7 +1765,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, int ret = 0; int irq; - irq = platform_get_irq(to_platform_device(dwc->dev), 0); + irq = dwc->irq_gadget; ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, IRQF_SHARED, "dwc3", dwc->ev_buf); if (ret) { @@ -1773,7 +1773,6 @@ static int dwc3_gadget_start(struct usb_gadget *g, irq, ret); goto err0; } - dwc->irq_gadget = irq; spin_lock_irqsave(&dwc->lock, flags); if (dwc->gadget_driver) { @@ -2891,7 +2890,33 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt) */ int dwc3_gadget_init(struct dwc3 *dwc) { - int ret; + int ret, irq; + struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); + + irq = platform_get_irq_byname(dwc3_pdev, "peripheral"); + if (irq == -EPROBE_DEFER) + return irq; + + if (irq <= 0) { + irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); + if (irq == -EPROBE_DEFER) + return irq; + + if (irq <= 0) { + irq = platform_get_irq(dwc3_pdev, 0); + if (irq <= 0) { + if (irq != -EPROBE_DEFER) { + dev_err(dwc->dev, + "missing peripheral IRQ\n"); + } + if (!irq) + irq = -EINVAL; + return irq; + } + } + } + + dwc->irq_gadget = irq; dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req), &dwc->ctrl_req_addr, GFP_KERNEL); diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index c679f63783ae..2e960edeb695 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -24,7 +24,48 @@ int dwc3_host_init(struct dwc3 *dwc) { struct platform_device *xhci; struct usb_xhci_pdata pdata; - int ret; + int ret, irq; + struct resource *res; + struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); + + irq = platform_get_irq_byname(dwc3_pdev, "host"); + if (irq == -EPROBE_DEFER) + return irq; + + if (irq <= 0) { + irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); + if (irq == -EPROBE_DEFER) + return irq; + + if (irq <= 0) { + irq = platform_get_irq(dwc3_pdev, 0); + if (irq <= 0) { + if (irq != -EPROBE_DEFER) { + dev_err(dwc->dev, + "missing host IRQ\n"); + } + if (!irq) + irq = -EINVAL; + return irq; + } else { + res = platform_get_resource(dwc3_pdev, + IORESOURCE_IRQ, 0); + } + } else { + res = platform_get_resource_byname(dwc3_pdev, + IORESOURCE_IRQ, + "dwc_usb3"); + } + + } else { + res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, + "host"); + } + + dwc->xhci_resources[1].start = irq; + dwc->xhci_resources[1].end = irq; + dwc->xhci_resources[1].flags = res->flags; + dwc->xhci_resources[1].name = res->name; xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); if (!xhci) { -- cgit v1.2.3-70-g09d2