From f81f46e1f530900323b6e32eba1af7244ca69537 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:02 +0200 Subject: usb: dwc2: implement hibernation during bus suspend/resume Allow controller to enter in hibernation during usb bus suspend and inform both phy and gadget about the suspended state. While in hibernation, the controller can't detect the resume condition. An external mechanism must call usb_phy_set_suspend on resume. Exit hibernation when controller gets the resume interrupt and inform only gadget driver about it. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core_intr.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'drivers/usb/dwc2/core_intr.c') diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 6cf047878dba..6ffb5a9c385e 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -334,6 +334,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) { + int ret; dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); @@ -345,6 +346,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Clear Remote Wakeup Signaling */ dctl &= ~DCTL_RMTWKUPSIG; writel(dctl, hsotg->regs + DCTL); + ret = dwc2_exit_hibernation(hsotg, true); + if (ret) + dev_err(hsotg->dev, "exit hibernation failed\n"); + + call_gadget(hsotg, resume); } /* Change to L0 state */ hsotg->lx_state = DWC2_L0; @@ -397,6 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) { u32 dsts; + int ret; dev_dbg(hsotg->dev, "USB SUSPEND\n"); @@ -411,6 +418,30 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", !!(dsts & DSTS_SUSPSTS), hsotg->hw_params.power_optimized); + if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) { + /* Ignore suspend request before enumeration */ + if (!dwc2_is_device_connected(hsotg)) { + dev_dbg(hsotg->dev, + "ignore suspend request before enumeration\n"); + goto clear_int; + } + + ret = dwc2_enter_hibernation(hsotg); + if (ret) { + dev_err(hsotg->dev, + "enter hibernation failed\n"); + goto skip_power_saving; + } + + udelay(100); + + /* Ask phy to be suspended */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) + usb_phy_set_suspend(hsotg->uphy, true); +skip_power_saving: + /* Call gadget suspend callback */ + call_gadget(hsotg, suspend); + } } else { if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); @@ -426,6 +457,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) /* Change to L2 (suspend) state */ hsotg->lx_state = DWC2_L2; +clear_int: /* Clear interrupt */ writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); } -- cgit v1.2.3 From 3eb42df3ebfbd8d46b831c26ecb90e128ad474a5 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:03 +0200 Subject: usb: dwc2: controller must update lx_state before releasing lock During suspend, there could a race condition between ep_queue and suspend interrupt if lx_state is updated after releasing spinlock in call_gadget(hsotg, suspend). Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core_intr.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/usb/dwc2/core_intr.c') diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 6ffb5a9c385e..0b7f2b2e580e 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -439,6 +439,12 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) if (!IS_ERR_OR_NULL(hsotg->uphy)) usb_phy_set_suspend(hsotg->uphy, true); skip_power_saving: + /* + * Change to L2 (suspend) state before releasing + * spinlock + */ + hsotg->lx_state = DWC2_L2; + /* Call gadget suspend callback */ call_gadget(hsotg, suspend); } @@ -446,6 +452,8 @@ skip_power_saving: if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); + /* Change to L2 (suspend) state */ + hsotg->lx_state = DWC2_L2; /* Clear the a_peripheral flag, back to a_host */ spin_unlock(&hsotg->lock); dwc2_hcd_start(hsotg); @@ -454,9 +462,6 @@ skip_power_saving: } } - /* Change to L2 (suspend) state */ - hsotg->lx_state = DWC2_L2; - clear_int: /* Clear interrupt */ writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); -- cgit v1.2.3 From 2d1165a4b95e25aed83fed737d53ab0c87b831e6 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 29 Apr 2015 22:09:18 +0200 Subject: usb: dwc2: remove dwc2_platform.ko As dwc2 pci module is now exporting dwc2 platform device, include platform.o in dwc2-y and remove USB_DWC2_PLATFORM configuration option. Driver will be built as two modules, dwc2.ko and dwc2_pci.ko. dwc2.ko is the new platform driver. Remove all EXPORT_SYMBOL_GPL as they are not needed any more. Acked-by: John Youn Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/Kconfig | 8 -------- drivers/usb/dwc2/Makefile | 5 +---- drivers/usb/dwc2/core.c | 3 --- drivers/usb/dwc2/core_intr.c | 1 - drivers/usb/dwc2/debugfs.c | 2 -- drivers/usb/dwc2/gadget.c | 5 ----- drivers/usb/dwc2/hcd.c | 2 -- 7 files changed, 1 insertion(+), 25 deletions(-) (limited to 'drivers/usb/dwc2/core_intr.c') diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 1bcb36ae6505..fd95ba6ec317 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -50,18 +50,10 @@ config USB_DWC2_DUAL_ROLE option requires USB_GADGET to be enabled. endchoice -config USB_DWC2_PLATFORM - tristate "DWC2 Platform" - default USB_DWC2_HOST || USB_DWC2_PERIPHERAL - help - The Designware USB2.0 platform interface module for - controllers directly connected to the CPU. - config USB_DWC2_PCI tristate "DWC2 PCI" depends on PCI default n - select USB_DWC2_PLATFORM select NOP_USB_XCEIV help The Designware USB2.0 PCI interface module for controllers diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index 324fbb40aa05..50fdaace1e73 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -2,7 +2,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC2) += dwc2.o -dwc2-y := core.o core_intr.o +dwc2-y := core.o core_intr.o platform.o ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) dwc2-y += hcd.o hcd_intr.o @@ -25,6 +25,3 @@ endif obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o dwc2_pci-y := pci.o - -obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o -dwc2_platform-y := platform.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 6acbe90a78cc..7f461e3bc7a1 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -3044,7 +3044,6 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); } -EXPORT_SYMBOL_GPL(dwc2_set_parameters); /** * During device initialization, read various hardware configuration @@ -3211,7 +3210,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) return 0; } -EXPORT_SYMBOL_GPL(dwc2_get_hwparams); /* * Sets all parameters to the given value. @@ -3227,7 +3225,6 @@ void dwc2_set_all_params(struct dwc2_core_params *params, int value) for (i = 0; i < size; i++) p[i] = value; } -EXPORT_SYMBOL_GPL(dwc2_set_all_params); u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 0b7f2b2e580e..9e510bb612bd 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -559,4 +559,3 @@ out: spin_unlock(&hsotg->lock); return retval; } -EXPORT_SYMBOL_GPL(dwc2_handle_common_intr); diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index af89537872a3..ef2ee3d9a25d 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -763,11 +763,9 @@ err1: err0: return ret; } -EXPORT_SYMBOL_GPL(dwc2_debugfs_init); void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) { debugfs_remove_recursive(hsotg->debug_root); hsotg->debug_root = NULL; } -EXPORT_SYMBOL_GPL(dwc2_debugfs_exit); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2b736156fbf0..4d47b7c09238 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2190,7 +2190,6 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) call_gadget(hsotg, disconnect); } -EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect); /** * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler @@ -3666,7 +3665,6 @@ err_clk: return ret; } -EXPORT_SYMBOL_GPL(dwc2_gadget_init); /** * s3c_hsotg_remove - remove function for hsotg driver @@ -3679,7 +3677,6 @@ int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) return 0; } -EXPORT_SYMBOL_GPL(s3c_hsotg_remove); int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) { @@ -3722,7 +3719,6 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_suspend); int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) { @@ -3754,4 +3750,3 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_resume); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 4773d2770363..d9b8cc36de52 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -2980,7 +2980,6 @@ error1: dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval); return retval; } -EXPORT_SYMBOL_GPL(dwc2_hcd_init); /* * Removes the HCD. @@ -3014,4 +3013,3 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) kfree(hsotg->frame_num_array); #endif } -EXPORT_SYMBOL_GPL(dwc2_hcd_remove); -- cgit v1.2.3 From 285046aa11ad85a4de24891f5458d45f50d1bcc5 Mon Sep 17 00:00:00 2001 From: Gregory Herrero Date: Wed, 29 Apr 2015 22:09:19 +0200 Subject: usb: dwc2: add hibernation core parameter dwc2 may not be able to exit from hibernation if the hardware does not provide a way to detect resume signalling in this state. Thus, add the possibility to disable hibernation feature. Acked-by: John Youn Signed-off-by: Gregory Herrero Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.c | 24 ++++++++++++++++++++++++ drivers/usb/dwc2/core.h | 7 +++++++ drivers/usb/dwc2/core_intr.c | 7 ++++--- drivers/usb/dwc2/platform.c | 2 ++ 4 files changed, 37 insertions(+), 3 deletions(-) (limited to 'drivers/usb/dwc2/core_intr.c') diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 7f461e3bc7a1..e5b546f1152e 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -342,6 +342,9 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) u32 pcgcctl; int ret = 0; + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + pcgcctl = readl(hsotg->regs + PCGCTL); pcgcctl &= ~PCGCTL_STOPPCLK; writel(pcgcctl, hsotg->regs + PCGCTL); @@ -392,6 +395,9 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) u32 pcgcctl; int ret = 0; + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + /* Backup all registers */ ret = dwc2_backup_global_registers(hsotg); if (ret) { @@ -2998,6 +3004,23 @@ static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg, hsotg->core_params->external_id_pin_ctl = val; } +static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter hibernation\n", + val); + dev_err(hsotg->dev, "hibernation must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val); + } + + hsotg->core_params->hibernation = val; +} + /* * This function is called during module intialization to pass module parameters * for the DWC_otg core. @@ -3043,6 +3066,7 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, dwc2_set_param_otg_ver(hsotg, params->otg_ver); dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); + dwc2_set_param_hibernation(hsotg, params->hibernation); } /** diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index d7fb1f793207..53b8de03f102 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -336,6 +336,12 @@ enum dwc2_ep0_state { * case. * 0 - No (default) * 1 - Yes + * @hibernation: Specifies whether the controller support hibernation. + * If hibernation is enabled, the controller will enter + * hibernation in both peripheral and host mode when + * needed. + * 0 - No (default) + * 1 - Yes * * The following parameters may be specified when starting the module. These * parameters define how the DWC_otg controller should be configured. A @@ -374,6 +380,7 @@ struct dwc2_core_params { int ahbcfg; int uframe_sched; int external_id_pin_ctl; + int hibernation; }; /** diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 9e510bb612bd..927be1e8b3dc 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -347,7 +347,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) dctl &= ~DCTL_RMTWKUPSIG; writel(dctl, hsotg->regs + DCTL); ret = dwc2_exit_hibernation(hsotg, true); - if (ret) + if (ret && (ret != -ENOTSUPP)) dev_err(hsotg->dev, "exit hibernation failed\n"); call_gadget(hsotg, resume); @@ -428,8 +428,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) ret = dwc2_enter_hibernation(hsotg); if (ret) { - dev_err(hsotg->dev, - "enter hibernation failed\n"); + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter hibernation failed\n"); goto skip_power_saving; } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 2562c9019955..90935304185a 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -78,6 +78,7 @@ static const struct dwc2_core_params params_bcm2835 = { .ahbcfg = 0x10, .uframe_sched = 0, .external_id_pin_ctl = -1, + .hibernation = -1, }; static const struct dwc2_core_params params_rk3066 = { @@ -107,6 +108,7 @@ static const struct dwc2_core_params params_rk3066 = { .ahbcfg = 0x7, /* INCR16 */ .uframe_sched = -1, .external_id_pin_ctl = -1, + .hibernation = -1, }; /** -- cgit v1.2.3