[PATCH v2 15/15] usb: dwc2: Get rid of useless error checks in suspend interrupt
Squashed from Douglas Anderson's suggested commit "usb: dwc2: Get rid of useless error checks for hibernation/partial power down" - After this commit there should never be any case where dwc2_enter_partial_power_down() and dwc2_enter_hibernation() are called when 'params.power_down' is not correct. Get rid of the pile of error checking. - As part of this cleanup some of the error messages not to have __func__ in them. That's not needed for dev_err() calls since they already have the device name as part of the message. Signed-off-by: Artur Petrosyan Signed-off-by: Douglas Anderson --- Changes in v2: - None drivers/usb/dwc2/core.c | 3 --- drivers/usb/dwc2/core_intr.c | 18 +++--- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 576c262dba55..6f70ab9577b4 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -391,9 +391,6 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg) */ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host) { - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_HIBERNATION) - return -ENOTSUPP; - if (is_host) return dwc2_host_enter_hibernation(hsotg); else diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 470458ac664b..a5ab03808da6 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -535,13 +535,10 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) switch (hsotg->params.power_down) { case DWC2_POWER_DOWN_PARAM_PARTIAL: ret = dwc2_enter_partial_power_down(hsotg); - if (ret) { - if (ret != -ENOTSUPP) - dev_err(hsotg->dev, - "%s: enter partial_power_down failed\n", - __func__); - goto skip_power_saving; - } + if (ret) + dev_err(hsotg->dev, + "enter partial_power_down failed\n"); + udelay(100); /* Ask phy to be suspended */ @@ -550,10 +547,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: ret = dwc2_enter_hibernation(hsotg, 0); - if (ret && ret != -ENOTSUPP) + if (ret) dev_err(hsotg->dev, - "%s: enter hibernation failed\n", - __func__); + "enter hibernation failed\n"); break; case DWC2_POWER_DOWN_PARAM_NONE: /* @@ -562,7 +558,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) */ dwc2_gadget_enter_clock_gating(hsotg); } -skip_power_saving: + /* * Change to L2 (suspend) state before releasing * spinlock -- 2.25.1
[PATCH v2 11/15] usb: dwc2: Add hibernation entering flow by system suspend
Adds a new flow of entering hibernation when PC is hibernated or suspended. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 10 ++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 093b1717df01..92848629cc61 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4387,6 +4387,16 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) clear_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: + /* Enter hibernation */ + spin_unlock_irqrestore(>lock, flags); + ret = dwc2_enter_hibernation(hsotg, 1); + if (ret) + dev_err(hsotg->dev, "enter hibernation failed\n"); + spin_lock_irqsave(>lock, flags); + + /* After entering suspend, hardware is not accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; case DWC2_POWER_DOWN_PARAM_NONE: /* * If not hibernation nor partial power down are supported, -- 2.25.1
[PATCH v2 12/15] usb: dwc2: Add hibernation exiting flow by system resume
Adds a new flow of exiting hibernation when PC is resumed from suspend state. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 10 ++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 92848629cc61..035d4911a3c3 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4470,6 +4470,16 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: + ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); + if (ret) + dev_err(hsotg->dev, "exit hibernation failed.\n"); + + /* +* Set HW accessible bit before powering on the controller +* since an interrupt may rise. +*/ + set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; case DWC2_POWER_DOWN_PARAM_NONE: /* * If not hibernation nor partial power down are supported, -- 2.25.1
[PATCH v2 14/15] usb: dwc2: Update dwc2_handle_usb_suspend_intr function.
To avoid working in two modes (partial power down and hibernation) changed conditions for entering partial power down or hibernation. Instead of checking hw_params.power_optimized and hw_params.hibernation now checking power_down param which already set to one of the options (Hibernation or Partial Power Down) based on OTG_EN_PWROPT. Signed-off-by: Artur Petrosyan Signed-off-by: Minas Harutyunyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index f8963c0cf6af..470458ac664b 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -532,7 +532,8 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) return; } if (dsts & DSTS_SUSPSTS) { - if (hsotg->hw_params.power_optimized) { + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: ret = dwc2_enter_partial_power_down(hsotg); if (ret) { if (ret != -ENOTSUPP) @@ -541,21 +542,22 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) __func__); 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); - } else if (hsotg->hw_params.hibernation) { + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: ret = dwc2_enter_hibernation(hsotg, 0); if (ret && ret != -ENOTSUPP) dev_err(hsotg->dev, "%s: enter hibernation failed\n", __func__); - } else { + break; + case DWC2_POWER_DOWN_PARAM_NONE: /* -* If not hibernation nor partial power down are supported, +* If neither hibernation nor partial power down are supported, * clock gating is used to save power. */ dwc2_gadget_enter_clock_gating(hsotg); -- 2.25.1
[PATCH v2 13/15] usb: dwc2: Add exit hibernation mode before removing drive
When dwc2 core is in hibernation mode loading driver again causes driver fail. Because in that mode registers are not accessible. In order to exit from hibernation checking dwc2 core power saving state in "dwc2_driver_remove()" function. If core is in hibernation, then checking the operational mode of the driver. To check whether dwc2 core is operating in host mode or device mode there is one way which is retrieving the backup value of "gotgctl" and compare the "CurMod" value. If previously core entered hibernation in host mode then the exit is performed for host if not then exit is performed for device mode. The introduced checking is because in hibernation state all registers are not accessible. Signed-off-by: Artur Petrosyan Reported-by: kernel test robot Reported-by: Dan Carpenter --- drivers/usb/dwc2/platform.c | 15 +++ 1 file changed, 15 insertions(+) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index f8b819cfa80e..8ad33e042a14 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -316,8 +316,23 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + struct dwc2_gregs_backup *gr; int ret = 0; + gr = >gr_backup; + + /* Exit Hibernation when driver is removed. */ + if (hsotg->hibernated) { + if (gr->gotgctl & GOTGCTL_CURMODE_HOST) + ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); + else + ret = dwc2_exit_hibernation(hsotg, 0, 0, 0); + + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } + /* Exit Partial Power Down when driver is removed. */ if (hsotg->in_ppd) { ret = dwc2_exit_partial_power_down(hsotg, 0, true); -- 2.25.1
[PATCH v2 10/15] usb: dwc2: Allow exit hibernation in urb enqueue
When core is in hibernation state and an external hub is connected, upper layer sends URB enqueue request, which results in port reset issue. - Added exit from hibernation state to avoid port reset issue and process upper layer request properly. Signed-off-by: Artur Petrosyan --- Changes in v2: - Moved duplicated error checking *if* conditions from innermost to outside if. drivers/usb/dwc2/hcd.c | 14 ++ 1 file changed, 14 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index cc9ad6cf02d9..093b1717df01 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4631,12 +4631,26 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, struct dwc2_qh *qh; bool qh_allocated = false; struct dwc2_qtd *qtd; + struct dwc2_gregs_backup *gr; + + gr = >gr_backup; if (dbg_urb(urb)) { dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n"); dwc2_dump_urb_info(hcd, urb, "urb_enqueue"); } + if (hsotg->hibernated) { + if (gr->gotgctl & GOTGCTL_CURMODE_HOST) + retval = dwc2_exit_hibernation(hsotg, 0, 0, 1); + else + retval = dwc2_exit_hibernation(hsotg, 0, 0, 0); + + if (retval) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } + if (hsotg->in_ppd) { retval = dwc2_exit_partial_power_down(hsotg, 0, true); if (retval) -- 2.25.1
[PATCH v2 09/15] usb: dwc2: Move exit hibernation to dwc2_port_resume() function
This move is done to call hibernation exit handler in "dwc2_port_resume()" function when core receives port resume. Otherwise it could be confusing to exit hibernation in "dwc2_hcd_hub_control()" function but other power saving modes in "dwc2_port_resume()" function. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 13 +++-- 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 43a2298b7d42..cc9ad6cf02d9 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3383,6 +3383,11 @@ int dwc2_port_resume(struct dwc2_hsotg *hsotg) "exit partial_power_down failed.\n"); break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: + /* Exit host hibernation. */ + ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); + if (ret) + dev_err(hsotg->dev, "exit hibernation failed.\n"); + break; case DWC2_POWER_DOWN_PARAM_NONE: /* * If not hibernation nor partial power down are supported, @@ -3446,12 +3451,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dev_dbg(hsotg->dev, "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); - if (hsotg->bus_suspended) { - if (hsotg->hibernated) - dwc2_exit_hibernation(hsotg, 0, 0, 1); - else - dwc2_port_resume(hsotg); - } + if (hsotg->bus_suspended) + retval = dwc2_port_resume(hsotg); break; case USB_PORT_FEAT_POWER: -- 2.25.1
[PATCH v2 08/15] usb: dwc2: Move enter hibernation to dwc2_port_suspend() function
This move is done to call enter hibernation handler in "dwc2_port_suspend()" function when core receives port suspend. Otherwise it could be confusing to enter to hibernation in "dwc2_hcd_hub_control()" function but other power saving modes in "dwc2_port_suspend()" function. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 18 ++ 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index ff945c40ef8a..43a2298b7d42 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3321,6 +3321,18 @@ int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) "enter partial_power_down failed.\n"); break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: + /* +* Perform spin unlock and lock because in +* "dwc2_host_enter_hibernation()" function there is a spinlock +* logic which prevents servicing of any IRQ during entering +* hibernation. +*/ + spin_unlock_irqrestore(>lock, flags); + ret = dwc2_enter_hibernation(hsotg, 1); + if (ret) + dev_err(hsotg->dev, "enter hibernation failed.\n"); + spin_lock_irqsave(>lock, flags); + break; case DWC2_POWER_DOWN_PARAM_NONE: /* * If not hibernation nor partial power down are supported, @@ -3650,10 +3662,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); if (windex != hsotg->otg_port) goto error; - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_HIBERNATION) - dwc2_enter_hibernation(hsotg, 1); - else - dwc2_port_suspend(hsotg, windex); + if (!hsotg->bus_suspended) + retval = dwc2_port_suspend(hsotg, windex); break; case USB_PORT_FEAT_POWER: -- 2.25.1
[PATCH v2 07/15] usb: dwc2: Clear GINTSTS_RESTOREDONE bit after restore is generated.
When hibernation exit is performed the dwc2_hib_restore_common() function is called. In that function we wait until GINTSTS_RESTOREDONE bit is set. However, after the setting of that bit we get a lot of (dwc2_hsotg_irq:) interrupts which indicates that (GINTSTS.RstrDoneInt) restore done interrupt is asserted. To avoid restore done interrupt storm after restore is generated clear GINTSTS_RESTOREDONE bit in GINTSTS register. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index eccd96fa164e..576c262dba55 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -299,6 +299,12 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, __func__); } else { dev_dbg(hsotg->dev, "restore done generated here\n"); + + /* +* To avoid restore done interrupt storm after restore is +* generated clear GINTSTS_RESTOREDONE bit. +*/ + dwc2_writel(hsotg, GINTSTS_RESTOREDONE, GINTSTS); } } -- 2.25.1
[PATCH v2 06/15] usb: dwc2: Clear fifo_map when resetting core.
Switching from device mode to host mode by disconnecting device cable core enters and exits form hibernation. However, the fifo map remains not cleared. It results to a WARNING (WARNING: CPU: 5 PID: 0 at drivers/usb/dwc2/ gadget.c:307 dwc2_hsotg_init_fifo+0x12/0x152 [dwc2]) if in host mode we disconnect the micro a to b host cable. Because core reset occurs. To avoid the WARNING, fifo_map should be cleared in dwc2_core_reset() function by taking into account configs. fifo_map must be cleared only if driver is configured in "CONFIG_USB_DWC2_PERIPHERAL" or "CONFIG_USB_DWC2_DUAL_ROLE" mode. - Added "static inline void dwc2_clear_fifo_map()" helper function to clear fifo_map with peripheral or dual role mode. - Added a dummy version of "dwc2_clear_fifo_map()" helper for host-only mode. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.c | 16 drivers/usb/dwc2/core.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index cb65f7f60573..eccd96fa164e 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -470,6 +470,22 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) dwc2_writel(hsotg, greset, GRSTCTL); } + /* +* Switching from device mode to host mode by disconnecting +* device cable core enters and exits form hibernation. +* However, the fifo map remains not cleared. It results +* to a WARNING (WARNING: CPU: 5 PID: 0 at drivers/usb/dwc2/ +* gadget.c:307 dwc2_hsotg_init_fifo+0x12/0x152 [dwc2]) +* if in host mode we disconnect the micro a to b host +* cable. Because core reset occurs. +* To avoid the WARNING, fifo_map should be cleared +* in dwc2_core_reset() function by taking into account configs. +* fifo_map must be cleared only if driver is configured in +* "CONFIG_USB_DWC2_PERIPHERAL" or "CONFIG_USB_DWC2_DUAL_ROLE" +* mode. +*/ + dwc2_clear_fifo_map(hsotg); + /* Wait for AHB master IDLE state */ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 1)) { dev_warn(hsotg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n", diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 8c12b3061f7f..e1f432095565 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1423,6 +1423,8 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg); void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg); +static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) +{ hsotg->fifo_map = 0; } #else static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1467,6 +1469,7 @@ static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {} static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) {} #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) -- 2.25.1
[PATCH v2 05/15] usb: dwc2: Allow exiting hibernation from gpwrdn rst detect
When device cable is disconnected core receives suspend interrupt and enters hibernation. After entering into hibernation GPWRDN_RST_DET and GPWRDN_STS_CHGINT interrupts are asserted. Allowed exit from gadget hibernation from GPWRDN_RST_DET by checking only linestate. Changed the return type of "dwc2_handle_gpwrdn_intr()" function from void to int because exit from hibernation functions have a return value. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 23 ++- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 27d729fad227..f8963c0cf6af 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -751,10 +751,11 @@ static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg, * The GPWRDN interrupts are those that occur in both Host and * Device mode while core is in hibernated state. */ -static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) +static int dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) { u32 gpwrdn; int linestate; + int ret = 0; gpwrdn = dwc2_readl(hsotg, GPWRDN); /* clear all interrupt */ @@ -778,17 +779,27 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) if (hsotg->hw_params.hibernation && hsotg->hibernated) { if (gpwrdn & GPWRDN_IDSTS) { - dwc2_exit_hibernation(hsotg, 0, 0, 0); + ret = dwc2_exit_hibernation(hsotg, 0, 0, 0); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); call_gadget(hsotg, resume); } else { - dwc2_exit_hibernation(hsotg, 1, 0, 1); + ret = dwc2_exit_hibernation(hsotg, 1, 0, 1); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); } } } else if ((gpwrdn & GPWRDN_RST_DET) && (gpwrdn & GPWRDN_RST_DET_MSK)) { dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__); - if (!linestate && (gpwrdn & GPWRDN_BSESSVLD)) - dwc2_exit_hibernation(hsotg, 0, 1, 0); + if (!linestate) { + ret = dwc2_exit_hibernation(hsotg, 0, 1, 0); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } } else if ((gpwrdn & GPWRDN_STS_CHGINT) && (gpwrdn & GPWRDN_STS_CHGINT_MSK)) { dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__); @@ -800,6 +811,8 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) */ dwc_handle_gpwrdn_disc_det(hsotg, gpwrdn); } + + return ret; } /* -- 2.25.1
[PATCH v2 04/15] usb: dwc2: Fix hibernation between host and device modes.
When core is in hibernation in host mode and a device cable was connected then driver exited from device hibernation. However, registers saved for host mode and when exited from device hibernation register restore would be done for device register which was wrong because there was no device registers stored to restore. - Added dwc_handle_gpwrdn_disc_det() function which handles gpwrdn disconnect detect flow and exits hibernation without restoring the registers. - Updated exiting from hibernation in GPWRDN_STS_CHGINT with calling dwc_handle_gpwrdn_disc_det() function. Here no register is restored which is the solution described above. Fixes: 65c9c4c6b01f ("usb: dwc2: Add dwc2_handle_gpwrdn_intr() handler") Signed-off-by: Artur Petrosyan Signed-off-by: Minas Harutyunyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 154 +++ 1 file changed, 83 insertions(+), 71 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 550c52c1a0c7..27d729fad227 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -680,6 +680,71 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) return 0; } +/** + * dwc_handle_gpwrdn_disc_det() - Handles the gpwrdn disconnect detect. + * Exits hibernation without restoring registers. + * + * @hsotg: Programming view of DWC_otg controller + * @gpwrdn: GPWRDN register + */ +static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg, + u32 gpwrdn) +{ + u32 gpwrdn_tmp; + + /* Switch-on voltage to the core */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Reset core */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Disable Power Down Clamp */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Deassert reset core */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp |= GPWRDN_PWRDNRSTN; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Disable PMU interrupt */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + + /* De-assert Wakeup Logic */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUACTV; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + + hsotg->hibernated = 0; + hsotg->bus_suspended = 0; + + if (gpwrdn & GPWRDN_IDSTS) { + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hsotg_core_init_disconnected(hsotg, false); + dwc2_hsotg_core_connect(hsotg); + } else { + hsotg->op_state = OTG_STATE_A_HOST; + + /* Initialize the Core for Host mode */ + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hcd_start(hsotg); + } +} + /* * GPWRDN interrupt handler. * @@ -701,64 +766,14 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) if ((gpwrdn & GPWRDN_DISCONN_DET) && (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) { - u32 gpwrdn_tmp; - dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__); - - /* Switch-on voltage to the core */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Reset core */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Disable Power Down Clamp */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Deassert reset core */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp |= GPWRDN_PWRDNRSTN; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Disable PMU interrupt */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); -
[PATCH v2 03/15] usb: dwc2: Fix host mode hibernation exit with remote wakeup flow.
Added setting "port_connect_status_change" flag to "1" in order to re-enumerate, because after exit from hibernation port connection status is not detected. Fixes: c5c403dc4336 ("usb: dwc2: Add host/device hibernation functions") Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 10 +- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index cda3f931195d..ff945c40ef8a 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5650,7 +5650,15 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, return ret; } - dwc2_hcd_rem_wakeup(hsotg); + if (rem_wakeup) { + dwc2_hcd_rem_wakeup(hsotg); + /* +* Change "port_connect_status_change" flag to re-enumerate, +* because after exit from hibernation port connection status +* is not detected. +*/ + hsotg->flags.b.port_connect_status_change = 1; + } hsotg->hibernated = 0; hsotg->bus_suspended = 0; -- 2.25.1
[PATCH v2 02/15] usb: dwc2: Reset DEVADDR after exiting gadget hibernation.
Initially resetting device address was done in dwc2_hsotg_irq() interrupt handler. However, when core is hibernated USB RESET is not handled in dwc2_hsotg_irq() handler, instead USB RESET interrupt is handled in dwc2_handle_gpwrdn_intr() handler. - Added reset device address to zero when core exits from gadget hibernation. Signed-off-by: Artur Petrosyan Signed-off-by: Minas Harutyunyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/gadget.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2f50f3e62caa..e6bb1bdb2760 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -5305,6 +5305,10 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, dwc2_writel(hsotg, dr->dcfg, DCFG); dwc2_writel(hsotg, dr->dctl, DCTL); + /* On USB Reset, reset device address to zero */ + if (reset) + dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK); + /* De-assert Wakeup Logic */ gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PMUACTV; -- 2.25.1
[PATCH v2 01/15] usb: dwc2: Update exit hibernation when port reset is asserted
No need to check for "DWC2_POWER_DOWN_PARAM_HIBERNATION" param as "hsotg->hibernated" flag is already enough for exiting from hibernation mode. - Removes checking of "DWC2_POWER_DOWN_PARAM_HIBERNATION" param. - For code readability Hibernation exit code moved after debug message print. - Added "dwc2_exit_hibernation()" function error checking. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 17 +++-- 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 04a1b53d65af..cda3f931195d 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3668,9 +3668,17 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, break; case USB_PORT_FEAT_RESET: - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_HIBERNATION && - hsotg->hibernated) - dwc2_exit_hibernation(hsotg, 0, 1, 1); + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_RESET\n"); + + hprt0 = dwc2_read_hprt0(hsotg); + + if (hsotg->hibernated) { + retval = dwc2_exit_hibernation(hsotg, 0, 1, 1); + if (retval) + dev_err(hsotg->dev, + "exit hibernation failed\n"); + } if (hsotg->in_ppd) { retval = dwc2_exit_partial_power_down(hsotg, 1, @@ -3684,9 +3692,6 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) dwc2_host_exit_clock_gating(hsotg, 0); - hprt0 = dwc2_read_hprt0(hsotg); - dev_dbg(hsotg->dev, - "SetPortFeature - USB_PORT_FEAT_RESET\n"); pcgctl = dwc2_readl(hsotg, PCGCTL); pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK); dwc2_writel(hsotg, pcgctl, PCGCTL); -- 2.25.1
[PATCH v2 00/15] usb: dwc2: Fix Hibernation issues.
This patch set fixes and improves hibernation mode for dwc2 core. It adds support for the following cases 1. Hibernation entering/exiting flow by system suspend/resume. 2. Exiting hibernation mode before removing driver and urb enqueue. 3. Exiting hibernation from gpwrdn rst detect. It updates the implementation of dwc2 entering and exiting hibernation mode when a port is suspended or resumed or reset asserted. It fixes hibernation issues between host and device mode when core enters to hibernation in host mode and mode change happens. Also, a fix is introduced for remote wakeup flow. The patch set also improves implementation of entering to hibernation from dwc2_handle_usb_suspend_intr() handler. Changes since V1: Updated bellow patches. Moved duplicated error checking *if* conditions from innermost to outside if. 1. usb: dwc2: Add exit hibernation mode before removing drive 2. usb: dwc2: Allow exit hibernation in urb enqueue Fixed an uninitialized pointer variable which was reported by kernel test robot in "usb: dwc2: Add exit hibernation mode before removing drive" patch. After fixing added Reported-by tags for kernel test robot and Dan Carpenter. Also removed some text from cover letter because I think that is why vger.kernel.org was rejecting it. Artur Petrosyan (15): usb: dwc2: Update exit hibernation when port reset is asserted usb: dwc2: Reset DEVADDR after exiting gadget hibernation. usb: dwc2: Fix host mode hibernation exit with remote wakeup flow. usb: dwc2: Fix hibernation between host and device modes. usb: dwc2: Allow exiting hibernation from gpwrdn rst detect usb: dwc2: Clear fifo_map when resetting core. usb: dwc2: Clear GINTSTS_RESTOREDONE bit after restore is generated. usb: dwc2: Move enter hibernation to dwc2_port_suspend() function usb: dwc2: Move exit hibernation to dwc2_port_resume() function usb: dwc2: Allow exit hibernation in urb enqueue usb: dwc2: Add hibernation entering flow by system suspend usb: dwc2: Add hibernation exiting flow by system resume usb: dwc2: Add exit hibernation mode before removing drive usb: dwc2: Update dwc2_handle_usb_suspend_intr function. usb: dwc2: Get rid of useless error checks in suspend interrupt drivers/usb/dwc2/core.c | 25 - drivers/usb/dwc2/core.h | 3 + drivers/usb/dwc2/core_intr.c | 205 +++ drivers/usb/dwc2/gadget.c| 4 + drivers/usb/dwc2/hcd.c | 92 +--- drivers/usb/dwc2/platform.c | 15 +++ 6 files changed, 233 insertions(+), 111 deletions(-) base-commit: 4b853c236c7b5161a2e444bd8b3c76fe5aa5ddcb -- 2.25.1
Re: [PATCH 10/15] usb: dwc2: Allow exit hibernation in urb enqueue
Hi Sergei, On 4/16/2021 09:43, Artur Petrosyan wrote: > Hi Sergei, > > On 4/15/2021 13:12, Sergei Shtylyov wrote: >> On 15.04.2021 8:40, Artur Petrosyan wrote: >> >>> When core is in hibernation state and an external >>> hub is connected, upper layer sends URB enqueue request, >>> which results in port reset issue. >>> >>> - Added exit from hibernation state to avoid port >>> reset issue and process upper layer request properly. >>> >>> Signed-off-by: Artur Petrosyan >>> --- >>> drivers/usb/dwc2/hcd.c | 17 + >>> 1 file changed, 17 insertions(+) >>> >>> diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c >>> index cc9ad6cf02d9..3b03b2d73aaa 100644 >>> --- a/drivers/usb/dwc2/hcd.c >>> +++ b/drivers/usb/dwc2/hcd.c >>> @@ -4631,12 +4631,29 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd >>> *hcd, struct urb *urb, >>> struct dwc2_qh *qh; >>> bool qh_allocated = false; >>> struct dwc2_qtd *qtd; >>> + struct dwc2_gregs_backup *gr; >>> + >>> + gr = >gr_backup; >>> >>> if (dbg_urb(urb)) { >>> dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n"); >>> dwc2_dump_urb_info(hcd, urb, "urb_enqueue"); >>> } >>> >>> + if (hsotg->hibernated) { >>> + if (gr->gotgctl & GOTGCTL_CURMODE_HOST) { >>> + retval = dwc2_exit_hibernation(hsotg, 0, 0, 1); >>> + if (retval) >>> + dev_err(hsotg->dev, >>> + "exit hibernation failed.\n"); >>> + } else { >>> + retval = dwc2_exit_hibernation(hsotg, 0, 0, 0); >>> + if (retval) >>> + dev_err(hsotg->dev, >>> + "exit hibernation failed.\n"); >> >> Why not put these identical *if*s outside the the outer *if*? >> > Well the reason that the conditions are implemented like they are, is > that the inner if checks whether core operates in host mode or device > mode and the outside if checks if the core is hibernated or not. > > So now imagine that the ifs are combined and core is not hibernated but > the operational mode of the driver is let's say gadget. If the case is > such then the driver will try to exit from gadget hibernation because of > the else condition as the if condition will be false again because core > is not hibernated. As a result if we combine the outside and inner ifs > then it will try to exit from gadget hibernation but core is not > hibernated and that would be an issue. > Sorry I got your point wrong there. You meant the ifs for dev_err(). Thank you for the review I will update them. > >> >>> + } >>> + } >>> + >>> if (hsotg->in_ppd) { >>> retval = dwc2_exit_partial_power_down(hsotg, 0, true); >>> if (retval) >> >> MBR, Sergei >> > > Regards, > Artur >
Re: [PATCH 13/15] usb: dwc2: Add exit hibernation mode before removing drive
Hi Sergei, On 4/15/2021 13:24, Sergei Shtylyov wrote: > On 15.04.2021 8:41, Artur Petrosyan wrote: > >> When dwc2 core is in hibernation mode loading >> driver again causes driver fail. Because in >> that mode registers are not accessible. >> >> In order to exit from hibernation checking >> dwc2 core power saving state in "dwc2_driver_remove()" >> function. If core is in hibernation, then checking the >> operational mode of the driver. To check whether dwc2 core >> is operating in host mode or device mode there is one way >> which is retrieving the backup value of "gotgctl" and compare >> the "CurMod" value. If previously core entered hibernation >> in host mode then the exit is performed for host if not then >> exit is performed for device mode. The introduced checking >> is because in hibernation state all registers are not >> accessible. >> >> Signed-off-by: Artur Petrosyan >> --- >>drivers/usb/dwc2/platform.c | 16 >>1 file changed, 16 insertions(+) >> >> diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c >> index f8b819cfa80e..2ae4748ed5ec 100644 >> --- a/drivers/usb/dwc2/platform.c >> +++ b/drivers/usb/dwc2/platform.c >> @@ -316,8 +316,24 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg >> *hsotg) >>static int dwc2_driver_remove(struct platform_device *dev) >>{ >> struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); >> +struct dwc2_gregs_backup *gr; >> int ret = 0; >> >> +/* Exit Hibernation when driver is removed. */ >> +if (hsotg->hibernated) { >> +if (gr->gotgctl & GOTGCTL_CURMODE_HOST) { >> +ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); >> +if (ret) >> +dev_err(hsotg->dev, >> +"exit hibernation failed.\n"); >> +} else { >> +ret = dwc2_exit_hibernation(hsotg, 0, 0, 0); >> +if (ret) >> +dev_err(hsotg->dev, >> +"exit hibernation failed.\n"); > > Again, why duplicate the innermost *if*? Again the reason is that combination of inner and outside ifs would give as a situation when core would not be hibernated but driver would try to exit from host or device hibernation. > >> + } >> +} >> + >> /* Exit Partial Power Down when driver is removed. */ >> if (hsotg->in_ppd) { >> ret = dwc2_exit_partial_power_down(hsotg, 0, true); > > MBR, Sergei > Regards, Artur
Re: [PATCH 10/15] usb: dwc2: Allow exit hibernation in urb enqueue
Hi Sergei, On 4/15/2021 13:12, Sergei Shtylyov wrote: > On 15.04.2021 8:40, Artur Petrosyan wrote: > >> When core is in hibernation state and an external >> hub is connected, upper layer sends URB enqueue request, >> which results in port reset issue. >> >> - Added exit from hibernation state to avoid port >> reset issue and process upper layer request properly. >> >> Signed-off-by: Artur Petrosyan >> --- >>drivers/usb/dwc2/hcd.c | 17 + >>1 file changed, 17 insertions(+) >> >> diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c >> index cc9ad6cf02d9..3b03b2d73aaa 100644 >> --- a/drivers/usb/dwc2/hcd.c >> +++ b/drivers/usb/dwc2/hcd.c >> @@ -4631,12 +4631,29 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd >> *hcd, struct urb *urb, >> struct dwc2_qh *qh; >> bool qh_allocated = false; >> struct dwc2_qtd *qtd; >> +struct dwc2_gregs_backup *gr; >> + >> +gr = >gr_backup; >> >> if (dbg_urb(urb)) { >> dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n"); >> dwc2_dump_urb_info(hcd, urb, "urb_enqueue"); >> } >> >> +if (hsotg->hibernated) { >> +if (gr->gotgctl & GOTGCTL_CURMODE_HOST) { >> +retval = dwc2_exit_hibernation(hsotg, 0, 0, 1); >> +if (retval) >> +dev_err(hsotg->dev, >> +"exit hibernation failed.\n"); >> +} else { >> +retval = dwc2_exit_hibernation(hsotg, 0, 0, 0); >> +if (retval) >> +dev_err(hsotg->dev, >> +"exit hibernation failed.\n"); > > Why not put these identical *if*s outside the the outer *if*? > Well the reason that the conditions are implemented like they are, is that the inner if checks whether core operates in host mode or device mode and the outside if checks if the core is hibernated or not. So now imagine that the ifs are combined and core is not hibernated but the operational mode of the driver is let's say gadget. If the case is such then the driver will try to exit from gadget hibernation because of the else condition as the if condition will be false again because core is not hibernated. As a result if we combine the outside and inner ifs then it will try to exit from gadget hibernation but core is not hibernated and that would be an issue. > >> +} >> +} >> + >> if (hsotg->in_ppd) { >> retval = dwc2_exit_partial_power_down(hsotg, 0, true); >> if (retval) > > MBR, Sergei > Regards, Artur
[PATCH 15/15] usb: dwc2: Get rid of useless error checks in suspend interrupt
Squashed from Douglas Anderson's suggested commit "usb: dwc2: Get rid of useless error checks for hibernation/partial power down" - After this commit there should never be any case where dwc2_enter_partial_power_down() and dwc2_enter_hibernation() are called when 'params.power_down' is not correct. Get rid of the pile of error checking. - As part of this cleanup some of the error messages not to have __func__ in them. That's not needed for dev_err() calls since they already have the device name as part of the message. Signed-off-by: Artur Petrosyan Signed-off-by: Douglas Anderson --- drivers/usb/dwc2/core.c | 3 --- drivers/usb/dwc2/core_intr.c | 18 +++--- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 576c262dba55..6f70ab9577b4 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -391,9 +391,6 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg) */ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host) { - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_HIBERNATION) - return -ENOTSUPP; - if (is_host) return dwc2_host_enter_hibernation(hsotg); else diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 470458ac664b..a5ab03808da6 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -535,13 +535,10 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) switch (hsotg->params.power_down) { case DWC2_POWER_DOWN_PARAM_PARTIAL: ret = dwc2_enter_partial_power_down(hsotg); - if (ret) { - if (ret != -ENOTSUPP) - dev_err(hsotg->dev, - "%s: enter partial_power_down failed\n", - __func__); - goto skip_power_saving; - } + if (ret) + dev_err(hsotg->dev, + "enter partial_power_down failed\n"); + udelay(100); /* Ask phy to be suspended */ @@ -550,10 +547,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: ret = dwc2_enter_hibernation(hsotg, 0); - if (ret && ret != -ENOTSUPP) + if (ret) dev_err(hsotg->dev, - "%s: enter hibernation failed\n", - __func__); + "enter hibernation failed\n"); break; case DWC2_POWER_DOWN_PARAM_NONE: /* @@ -562,7 +558,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) */ dwc2_gadget_enter_clock_gating(hsotg); } -skip_power_saving: + /* * Change to L2 (suspend) state before releasing * spinlock -- 2.25.1
[PATCH 14/15] usb: dwc2: Update dwc2_handle_usb_suspend_intr function.
To avoid working in two modes (partial power down and hibernation) changed conditions for entering partial power down or hibernation. Instead of checking hw_params.power_optimized and hw_params.hibernation now checking power_down param which already set to one of the options (Hibernation or Partial Power Down) based on OTG_EN_PWROPT. Signed-off-by: Artur Petrosyan Signed-off-by: Minas Harutyunyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index f8963c0cf6af..470458ac664b 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -532,7 +532,8 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) return; } if (dsts & DSTS_SUSPSTS) { - if (hsotg->hw_params.power_optimized) { + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: ret = dwc2_enter_partial_power_down(hsotg); if (ret) { if (ret != -ENOTSUPP) @@ -541,21 +542,22 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) __func__); 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); - } else if (hsotg->hw_params.hibernation) { + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: ret = dwc2_enter_hibernation(hsotg, 0); if (ret && ret != -ENOTSUPP) dev_err(hsotg->dev, "%s: enter hibernation failed\n", __func__); - } else { + break; + case DWC2_POWER_DOWN_PARAM_NONE: /* -* If not hibernation nor partial power down are supported, +* If neither hibernation nor partial power down are supported, * clock gating is used to save power. */ dwc2_gadget_enter_clock_gating(hsotg); -- 2.25.1
[PATCH 13/15] usb: dwc2: Add exit hibernation mode before removing drive
When dwc2 core is in hibernation mode loading driver again causes driver fail. Because in that mode registers are not accessible. In order to exit from hibernation checking dwc2 core power saving state in "dwc2_driver_remove()" function. If core is in hibernation, then checking the operational mode of the driver. To check whether dwc2 core is operating in host mode or device mode there is one way which is retrieving the backup value of "gotgctl" and compare the "CurMod" value. If previously core entered hibernation in host mode then the exit is performed for host if not then exit is performed for device mode. The introduced checking is because in hibernation state all registers are not accessible. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/platform.c | 16 1 file changed, 16 insertions(+) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index f8b819cfa80e..2ae4748ed5ec 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -316,8 +316,24 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + struct dwc2_gregs_backup *gr; int ret = 0; + /* Exit Hibernation when driver is removed. */ + if (hsotg->hibernated) { + if (gr->gotgctl & GOTGCTL_CURMODE_HOST) { + ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } else { + ret = dwc2_exit_hibernation(hsotg, 0, 0, 0); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } + } + /* Exit Partial Power Down when driver is removed. */ if (hsotg->in_ppd) { ret = dwc2_exit_partial_power_down(hsotg, 0, true); -- 2.25.1
[PATCH 12/15] usb: dwc2: Add hibernation exiting flow by system resume
Adds a new flow of exiting hibernation when PC is resumed from suspend state. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 10 ++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index db8eb1940d17..c92307775863 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4470,6 +4470,16 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: + ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); + if (ret) + dev_err(hsotg->dev, "exit hibernation failed.\n"); + + /* +* Set HW accessible bit before powering on the controller +* since an interrupt may rise. +*/ + set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; case DWC2_POWER_DOWN_PARAM_NONE: /* * If not hibernation nor partial power down are supported, -- 2.25.1
[PATCH 11/15] usb: dwc2: Add hibernation entering flow by system suspend
Adds a new flow of entering hibernation when PC is hibernated or suspended. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 10 ++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 3b03b2d73aaa..db8eb1940d17 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4387,6 +4387,16 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) clear_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: + /* Enter hibernation */ + spin_unlock_irqrestore(>lock, flags); + ret = dwc2_enter_hibernation(hsotg, 1); + if (ret) + dev_err(hsotg->dev, "enter hibernation failed\n"); + spin_lock_irqsave(>lock, flags); + + /* After entering suspend, hardware is not accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; case DWC2_POWER_DOWN_PARAM_NONE: /* * If not hibernation nor partial power down are supported, -- 2.25.1
[PATCH 10/15] usb: dwc2: Allow exit hibernation in urb enqueue
When core is in hibernation state and an external hub is connected, upper layer sends URB enqueue request, which results in port reset issue. - Added exit from hibernation state to avoid port reset issue and process upper layer request properly. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 17 + 1 file changed, 17 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index cc9ad6cf02d9..3b03b2d73aaa 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4631,12 +4631,29 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, struct dwc2_qh *qh; bool qh_allocated = false; struct dwc2_qtd *qtd; + struct dwc2_gregs_backup *gr; + + gr = >gr_backup; if (dbg_urb(urb)) { dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n"); dwc2_dump_urb_info(hcd, urb, "urb_enqueue"); } + if (hsotg->hibernated) { + if (gr->gotgctl & GOTGCTL_CURMODE_HOST) { + retval = dwc2_exit_hibernation(hsotg, 0, 0, 1); + if (retval) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } else { + retval = dwc2_exit_hibernation(hsotg, 0, 0, 0); + if (retval) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } + } + if (hsotg->in_ppd) { retval = dwc2_exit_partial_power_down(hsotg, 0, true); if (retval) -- 2.25.1
[PATCH 09/15] usb: dwc2: Move exit hibernation to dwc2_port_resume() function
This move is done to call hibernation exit handler in "dwc2_port_resume()" function when core receives port resume. Otherwise it could be confusing to exit hibernation in "dwc2_hcd_hub_control()" function but other power saving modes in "dwc2_port_resume()" function. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 13 +++-- 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 43a2298b7d42..cc9ad6cf02d9 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3383,6 +3383,11 @@ int dwc2_port_resume(struct dwc2_hsotg *hsotg) "exit partial_power_down failed.\n"); break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: + /* Exit host hibernation. */ + ret = dwc2_exit_hibernation(hsotg, 0, 0, 1); + if (ret) + dev_err(hsotg->dev, "exit hibernation failed.\n"); + break; case DWC2_POWER_DOWN_PARAM_NONE: /* * If not hibernation nor partial power down are supported, @@ -3446,12 +3451,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dev_dbg(hsotg->dev, "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); - if (hsotg->bus_suspended) { - if (hsotg->hibernated) - dwc2_exit_hibernation(hsotg, 0, 0, 1); - else - dwc2_port_resume(hsotg); - } + if (hsotg->bus_suspended) + retval = dwc2_port_resume(hsotg); break; case USB_PORT_FEAT_POWER: -- 2.25.1
[PATCH 08/15] usb: dwc2: Move enter hibernation to dwc2_port_suspend() function
This move is done to call enter hibernation handler in "dwc2_port_suspend()" function when core receives port suspend. Otherwise it could be confusing to enter to hibernation in "dwc2_hcd_hub_control()" function but other power saving modes in "dwc2_port_suspend()" function. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 18 ++ 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index ff945c40ef8a..43a2298b7d42 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3321,6 +3321,18 @@ int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) "enter partial_power_down failed.\n"); break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: + /* +* Perform spin unlock and lock because in +* "dwc2_host_enter_hibernation()" function there is a spinlock +* logic which prevents servicing of any IRQ during entering +* hibernation. +*/ + spin_unlock_irqrestore(>lock, flags); + ret = dwc2_enter_hibernation(hsotg, 1); + if (ret) + dev_err(hsotg->dev, "enter hibernation failed.\n"); + spin_lock_irqsave(>lock, flags); + break; case DWC2_POWER_DOWN_PARAM_NONE: /* * If not hibernation nor partial power down are supported, @@ -3650,10 +3662,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); if (windex != hsotg->otg_port) goto error; - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_HIBERNATION) - dwc2_enter_hibernation(hsotg, 1); - else - dwc2_port_suspend(hsotg, windex); + if (!hsotg->bus_suspended) + retval = dwc2_port_suspend(hsotg, windex); break; case USB_PORT_FEAT_POWER: -- 2.25.1
[PATCH 07/15] usb: dwc2: Clear GINTSTS_RESTOREDONE bit after restore is generated.
When hibernation exit is performed the dwc2_hib_restore_common() function is called. In that function we wait until GINTSTS_RESTOREDONE bit is set. However, after the setting of that bit we get a lot of (dwc2_hsotg_irq:) interrupts which indicates that (GINTSTS.RstrDoneInt) restore done interrupt is asserted. To avoid restore done interrupt storm after restore is generated clear GINTSTS_RESTOREDONE bit in GINTSTS register. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index eccd96fa164e..576c262dba55 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -299,6 +299,12 @@ void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, __func__); } else { dev_dbg(hsotg->dev, "restore done generated here\n"); + + /* +* To avoid restore done interrupt storm after restore is +* generated clear GINTSTS_RESTOREDONE bit. +*/ + dwc2_writel(hsotg, GINTSTS_RESTOREDONE, GINTSTS); } } -- 2.25.1
[PATCH 06/15] usb: dwc2: Clear fifo_map when resetting core.
Switching from device mode to host mode by disconnecting device cable core enters and exits form hibernation. However, the fifo map remains not cleared. It results to a WARNING (WARNING: CPU: 5 PID: 0 at drivers/usb/dwc2/ gadget.c:307 dwc2_hsotg_init_fifo+0x12/0x152 [dwc2]) if in host mode we disconnect the micro a to b host cable. Because core reset occurs. To avoid the WARNING, fifo_map should be cleared in dwc2_core_reset() function by taking into account configs. fifo_map must be cleared only if driver is configured in "CONFIG_USB_DWC2_PERIPHERAL" or "CONFIG_USB_DWC2_DUAL_ROLE" mode. - Added "static inline void dwc2_clear_fifo_map()" helper function to clear fifo_map with peripheral or dual role mode. - Added a dummy version of "dwc2_clear_fifo_map()" helper for host-only mode. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.c | 16 drivers/usb/dwc2/core.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index cb65f7f60573..eccd96fa164e 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -470,6 +470,22 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) dwc2_writel(hsotg, greset, GRSTCTL); } + /* +* Switching from device mode to host mode by disconnecting +* device cable core enters and exits form hibernation. +* However, the fifo map remains not cleared. It results +* to a WARNING (WARNING: CPU: 5 PID: 0 at drivers/usb/dwc2/ +* gadget.c:307 dwc2_hsotg_init_fifo+0x12/0x152 [dwc2]) +* if in host mode we disconnect the micro a to b host +* cable. Because core reset occurs. +* To avoid the WARNING, fifo_map should be cleared +* in dwc2_core_reset() function by taking into account configs. +* fifo_map must be cleared only if driver is configured in +* "CONFIG_USB_DWC2_PERIPHERAL" or "CONFIG_USB_DWC2_DUAL_ROLE" +* mode. +*/ + dwc2_clear_fifo_map(hsotg); + /* Wait for AHB master IDLE state */ if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 1)) { dev_warn(hsotg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n", diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 8c12b3061f7f..e1f432095565 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1423,6 +1423,8 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg); void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg); +static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) +{ hsotg->fifo_map = 0; } #else static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1467,6 +1469,7 @@ static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {} static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) {} #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) -- 2.25.1
[PATCH 05/15] usb: dwc2: Allow exiting hibernation from gpwrdn rst detect
When device cable is disconnected core receives suspend interrupt and enters hibernation. After entering into hibernation GPWRDN_RST_DET and GPWRDN_STS_CHGINT interrupts are asserted. Allowed exit from gadget hibernation from GPWRDN_RST_DET by checking only linestate. Changed the return type of "dwc2_handle_gpwrdn_intr()" function from void to int because exit from hibernation functions have a return value. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 23 ++- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 27d729fad227..f8963c0cf6af 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -751,10 +751,11 @@ static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg, * The GPWRDN interrupts are those that occur in both Host and * Device mode while core is in hibernated state. */ -static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) +static int dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) { u32 gpwrdn; int linestate; + int ret = 0; gpwrdn = dwc2_readl(hsotg, GPWRDN); /* clear all interrupt */ @@ -778,17 +779,27 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) if (hsotg->hw_params.hibernation && hsotg->hibernated) { if (gpwrdn & GPWRDN_IDSTS) { - dwc2_exit_hibernation(hsotg, 0, 0, 0); + ret = dwc2_exit_hibernation(hsotg, 0, 0, 0); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); call_gadget(hsotg, resume); } else { - dwc2_exit_hibernation(hsotg, 1, 0, 1); + ret = dwc2_exit_hibernation(hsotg, 1, 0, 1); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); } } } else if ((gpwrdn & GPWRDN_RST_DET) && (gpwrdn & GPWRDN_RST_DET_MSK)) { dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__); - if (!linestate && (gpwrdn & GPWRDN_BSESSVLD)) - dwc2_exit_hibernation(hsotg, 0, 1, 0); + if (!linestate) { + ret = dwc2_exit_hibernation(hsotg, 0, 1, 0); + if (ret) + dev_err(hsotg->dev, + "exit hibernation failed.\n"); + } } else if ((gpwrdn & GPWRDN_STS_CHGINT) && (gpwrdn & GPWRDN_STS_CHGINT_MSK)) { dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__); @@ -800,6 +811,8 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) */ dwc_handle_gpwrdn_disc_det(hsotg, gpwrdn); } + + return ret; } /* -- 2.25.1
[PATCH 04/15] usb: dwc2: Fix hibernation between host and device modes.
When core is in hibernation in host mode and a device cable was connected then driver exited from device hibernation. However, registers saved for host mode and when exited from device hibernation register restore would be done for device register which was wrong because there was no device registers stored to restore. - Added dwc_handle_gpwrdn_disc_det() function which handles gpwrdn disconnect detect flow and exits hibernation without restoring the registers. - Updated exiting from hibernation in GPWRDN_STS_CHGINT with calling dwc_handle_gpwrdn_disc_det() function. Here no register is restored which is the solution described above. Fixes: 65c9c4c6b01f ("usb: dwc2: Add dwc2_handle_gpwrdn_intr() handler") Signed-off-by: Artur Petrosyan Signed-off-by: Minas Harutyunyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 154 +++ 1 file changed, 83 insertions(+), 71 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 550c52c1a0c7..27d729fad227 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -680,6 +680,71 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) return 0; } +/** + * dwc_handle_gpwrdn_disc_det() - Handles the gpwrdn disconnect detect. + * Exits hibernation without restoring registers. + * + * @hsotg: Programming view of DWC_otg controller + * @gpwrdn: GPWRDN register + */ +static inline void dwc_handle_gpwrdn_disc_det(struct dwc2_hsotg *hsotg, + u32 gpwrdn) +{ + u32 gpwrdn_tmp; + + /* Switch-on voltage to the core */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Reset core */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Disable Power Down Clamp */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Deassert reset core */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp |= GPWRDN_PWRDNRSTN; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + udelay(5); + + /* Disable PMU interrupt */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + + /* De-assert Wakeup Logic */ + gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUACTV; + dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); + + hsotg->hibernated = 0; + hsotg->bus_suspended = 0; + + if (gpwrdn & GPWRDN_IDSTS) { + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hsotg_core_init_disconnected(hsotg, false); + dwc2_hsotg_core_connect(hsotg); + } else { + hsotg->op_state = OTG_STATE_A_HOST; + + /* Initialize the Core for Host mode */ + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hcd_start(hsotg); + } +} + /* * GPWRDN interrupt handler. * @@ -701,64 +766,14 @@ static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) if ((gpwrdn & GPWRDN_DISCONN_DET) && (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) { - u32 gpwrdn_tmp; - dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__); - - /* Switch-on voltage to the core */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Reset core */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Disable Power Down Clamp */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Deassert reset core */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp |= GPWRDN_PWRDNRSTN; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); - udelay(10); - - /* Disable PMU interrupt */ - gpwrdn_tmp = dwc2_readl(hsotg, GPWRDN); - gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; - dwc2_writel(hsotg, gpwrdn_tmp, GPWRDN); -
[PATCH 03/15] usb: dwc2: Fix host mode hibernation exit with remote wakeup flow.
Added setting "port_connect_status_change" flag to "1" in order to re-enumerate, because after exit from hibernation port connection status is not detected. Fixes: c5c403dc4336 ("usb: dwc2: Add host/device hibernation functions") Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 10 +- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index cda3f931195d..ff945c40ef8a 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5650,7 +5650,15 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, return ret; } - dwc2_hcd_rem_wakeup(hsotg); + if (rem_wakeup) { + dwc2_hcd_rem_wakeup(hsotg); + /* +* Change "port_connect_status_change" flag to re-enumerate, +* because after exit from hibernation port connection status +* is not detected. +*/ + hsotg->flags.b.port_connect_status_change = 1; + } hsotg->hibernated = 0; hsotg->bus_suspended = 0; -- 2.25.1
[PATCH 01/15] usb: dwc2: Update exit hibernation when port reset is asserted
No need to check for "DWC2_POWER_DOWN_PARAM_HIBERNATION" param as "hsotg->hibernated" flag is already enough for exiting from hibernation mode. - Removes checking of "DWC2_POWER_DOWN_PARAM_HIBERNATION" param. - For code readability Hibernation exit code moved after debug message print. - Added "dwc2_exit_hibernation()" function error checking. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 17 +++-- 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 04a1b53d65af..cda3f931195d 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3668,9 +3668,17 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, break; case USB_PORT_FEAT_RESET: - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_HIBERNATION && - hsotg->hibernated) - dwc2_exit_hibernation(hsotg, 0, 1, 1); + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_RESET\n"); + + hprt0 = dwc2_read_hprt0(hsotg); + + if (hsotg->hibernated) { + retval = dwc2_exit_hibernation(hsotg, 0, 1, 1); + if (retval) + dev_err(hsotg->dev, + "exit hibernation failed\n"); + } if (hsotg->in_ppd) { retval = dwc2_exit_partial_power_down(hsotg, 1, @@ -3684,9 +3692,6 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) dwc2_host_exit_clock_gating(hsotg, 0); - hprt0 = dwc2_read_hprt0(hsotg); - dev_dbg(hsotg->dev, - "SetPortFeature - USB_PORT_FEAT_RESET\n"); pcgctl = dwc2_readl(hsotg, PCGCTL); pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK); dwc2_writel(hsotg, pcgctl, PCGCTL); -- 2.25.1
[PATCH 02/15] usb: dwc2: Reset DEVADDR after exiting gadget hibernation.
Initially resetting device address was done in dwc2_hsotg_irq() interrupt handler. However, when core is hibernated USB RESET is not handled in dwc2_hsotg_irq() handler, instead USB RESET interrupt is handled in dwc2_handle_gpwrdn_intr() handler. - Added reset device address to zero when core exits from gadget hibernation. Signed-off-by: Artur Petrosyan Signed-off-by: Minas Harutyunyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/gadget.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2f50f3e62caa..e6bb1bdb2760 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -5305,6 +5305,10 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, dwc2_writel(hsotg, dr->dcfg, DCFG); dwc2_writel(hsotg, dr->dctl, DCTL); + /* On USB Reset, reset device address to zero */ + if (reset) + dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK); + /* De-assert Wakeup Logic */ gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PMUACTV; -- 2.25.1
Re: [PATCH v2 00/12] usb: dwc2: Add clock gating support.
Hi Greg, On 4/13/2021 11:35, Artur Petrosyan wrote: > This patch set adds clock gating power saving support for > dwc2 core. > It adds support for the following cases described by programming guide. >1. Enter to clock gating from USB_SUSPEND interrupt. >2. Clock gating entering flow by system suspend. >3. Clock gating exiting flow by system resume. >4. Exit clock gating from wakeup interrupt. >5. Exit clock gating from session request interrupt. >6. Exit clock gating when port reset is asserted. > > Additional cases to exit form clock gating were needed which are not > described in clock gating programming guide. >1. Added clock gating exit flow before removing driver >2. Exit clock gating in urb enqueue. > > It updates the implementation of dwc2 entering and exiting clock > gating when a port is suspended or resumed. > > The patch set also adds the implementation of function handlers > for entering and exiting host or device clock gating. > > NOTE: This is the third patch set in the power saving mode fixes > series. > This patch set is part of multiple series and is continuation > of the "usb: dwc2: Fix and improve power saving modes" patch set. > (Patch set link: https://marc.info/?l=linux-usb=160379622403975=2). > The patches that were included in the "usb: dwc2: > Fix and improve power saving modes" which was submitted > earlier was too large and needed to be split up into > smaller patch sets. > > Changes since V1: > Re sending the patch set as v1 because vger.kernel.org rejected > the cover letter. With error code "550 5.7.1 Content-Policy" > No changes in the patches or the source code. > > > Artur Petrosyan (12): >usb: dwc2: Add device clock gating support functions >usb: dwc2: Add host clock gating support functions >usb: dwc2: Allow entering clock gating from USB_SUSPEND interrupt >usb: dwc2: Add exit clock gating from wakeup interrupt >usb: dwc2: Add exit clock gating from session request interrupt >usb: dwc2: Add exit clock gating when port reset is asserted >usb: dwc2: Update enter clock gating when port is suspended >usb: dwc2: Update exit clock gating when port is resumed >usb: dwc2: Allow exit clock gating in urb enqueue >usb: dwc2: Add clock gating entering flow by system suspend >usb: dwc2: Add clock gating exiting flow by system resume >usb: dwc2: Add exit clock gating before removing driver > > drivers/usb/dwc2/core.h | 15 ++- > drivers/usb/dwc2/core_intr.c | 62 > drivers/usb/dwc2/gadget.c| 71 ++ > drivers/usb/dwc2/hcd.c | 177 +++ > drivers/usb/dwc2/platform.c | 9 ++ > 5 files changed, 272 insertions(+), 62 deletions(-) > > > base-commit: 9bc46a12c53d8268392774172742aa9e5dd6953d > This cover letter for the clock gating patches is being rejected by "vger.kernel.org". I have tried to send one more time as v2 but again it is rejected. The error message is "vger.kernel.org Remote Server returned '554 5.7.1
[PATCH v2 12/12] usb: dwc2: Add exit clock gating before removing driver
When dwc2 core is in clock gating mode loading driver again causes driver fail. Because in that mode registers are not accessible. Added a flow of exiting clock gating mode to avoid the driver reload failure. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/platform.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index b28b8cd45799..f8b819cfa80e 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -326,6 +326,15 @@ static int dwc2_driver_remove(struct platform_device *dev) "exit partial_power_down failed\n"); } + /* Exit clock gating when driver is removed. */ + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && + hsotg->bus_suspended) { + if (dwc2_is_device_mode(hsotg)) + dwc2_gadget_exit_clock_gating(hsotg, 0); + else + dwc2_host_exit_clock_gating(hsotg, 0); + } + dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) dwc2_hcd_remove(hsotg); -- 2.25.1
[PATCH v2 11/12] usb: dwc2: Add clock gating exiting flow by system resume
If not hibernation nor partial power down are supported, port resume is done using the clock gating programming flow. Adds a new flow of exiting clock gating when PC is resumed. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 22 ++ 1 file changed, 22 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 09dcd37b9ef8..04a1b53d65af 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4445,6 +4445,28 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: case DWC2_POWER_DOWN_PARAM_NONE: + /* +* If not hibernation nor partial power down are supported, +* port resume is done using the clock gating programming flow. +*/ + spin_unlock_irqrestore(>lock, flags); + dwc2_host_exit_clock_gating(hsotg, 0); + + /* +* Initialize the Core for Host mode, as after system resume +* the global interrupts are disabled. +*/ + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hcd_reinit(hsotg); + spin_lock_irqsave(>lock, flags); + + /* +* Set HW accessible bit before powering on the controller +* since an interrupt may rise. +*/ + set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; default: hsotg->lx_state = DWC2_L0; goto unlock; -- 2.25.1
[PATCH v2 09/12] usb: dwc2: Allow exit clock gating in urb enqueue
When core is in clock gating state and an external hub is connected, upper layer sends URB enqueue request, which results in port reset issue. Added exit from clock gating state to avoid port reset issue and process upper layer request properly. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 8 1 file changed, 8 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 8a42675ab94e..31d6a1b87228 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4597,6 +4597,14 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, "exit partial_power_down failed\n"); } + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && + hsotg->bus_suspended) { + if (dwc2_is_device_mode(hsotg)) + dwc2_gadget_exit_clock_gating(hsotg, 0); + else + dwc2_host_exit_clock_gating(hsotg, 0); + } + if (!ep) return -EINVAL; -- 2.25.1
[PATCH v2 10/12] usb: dwc2: Add clock gating entering flow by system suspend
If not hibernation nor partial power down are supported, clock gating is used to save power. Adds a new flow of entering clock gating when PC is suspended. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 31d6a1b87228..09dcd37b9ef8 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4372,6 +4372,15 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: case DWC2_POWER_DOWN_PARAM_NONE: + /* +* If not hibernation nor partial power down are supported, +* clock gating is used to save power. +*/ + dwc2_host_enter_clock_gating(hsotg); + + /* After entering suspend, hardware is not accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; default: goto skip_power_saving; } -- 2.25.1
[PATCH v2 05/12] usb: dwc2: Add exit clock gating from session request interrupt
Added clock gating exit flow from session request interrupt handler according programming guide. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/core_intr.c | 19 +-- 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index c764407e7633..550c52c1a0c7 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -316,12 +316,19 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) hsotg->lx_state); if (dwc2_is_device_mode(hsotg)) { - if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd) { - ret = dwc2_exit_partial_power_down(hsotg, 0, - true); - if (ret) - dev_err(hsotg->dev, - "exit power_down failed\n"); + if (hsotg->lx_state == DWC2_L2) { + if (hsotg->in_ppd) { + ret = dwc2_exit_partial_power_down(hsotg, 0, + true); + if (ret) + dev_err(hsotg->dev, + "exit power_down failed\n"); + } + + /* Exit gadget mode clock gating. */ + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_gadget_exit_clock_gating(hsotg, 0); } /* -- 2.25.1
[PATCH v2 08/12] usb: dwc2: Update exit clock gating when port is resumed
Updates the implementation of exiting clock gating mode when core receives port resume. Instead of setting the required bit fields of the registers inline, called the "dwc2_host_exit_clock_gating()" function. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 29 - 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index e1225fe6c61a..8a42675ab94e 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3359,8 +3359,6 @@ int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) int dwc2_port_resume(struct dwc2_hsotg *hsotg) { unsigned long flags; - u32 hprt0; - u32 pcgctl; int ret = 0; spin_lock_irqsave(>lock, flags); @@ -3374,33 +3372,14 @@ int dwc2_port_resume(struct dwc2_hsotg *hsotg) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: case DWC2_POWER_DOWN_PARAM_NONE: - default: /* -* If power_down is supported, Phy clock is already resumed -* after registers restore. +* If not hibernation nor partial power down are supported, +* port resume is done using the clock gating programming flow. */ - if (!hsotg->params.power_down) { - pcgctl = dwc2_readl(hsotg, PCGCTL); - pcgctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgctl, PCGCTL); - spin_unlock_irqrestore(>lock, flags); - msleep(20); - spin_lock_irqsave(>lock, flags); - } - - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_RES; - hprt0 &= ~HPRT0_SUSP; - dwc2_writel(hsotg, hprt0, HPRT0); spin_unlock_irqrestore(>lock, flags); - - msleep(USB_RESUME_TIMEOUT); - + dwc2_host_exit_clock_gating(hsotg, 0); spin_lock_irqsave(>lock, flags); - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 &= ~(HPRT0_RES | HPRT0_SUSP); - dwc2_writel(hsotg, hprt0, HPRT0); - hsotg->bus_suspended = false; + break; } spin_unlock_irqrestore(>lock, flags); -- 2.25.1
[PATCH v2 06/12] usb: dwc2: Add exit clock gating when port reset is asserted
Adds clock gating exit flow when set port feature reset is received in suspended state. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index f1c24c15d185..27f030d5de54 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3712,6 +3712,10 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "exit partial_power_down failed\n"); } + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_host_exit_clock_gating(hsotg, 0); + hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_RESET\n"); -- 2.25.1
[PATCH v2 07/12] usb: dwc2: Update enter clock gating when port is suspended
Updates the implementation of entering clock gating mode when core receives port suspend. Instead of setting the required bit fields of the registers inline, called the "dwc2_host_enter_clock_gating()" function. Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 19 --- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 27f030d5de54..e1225fe6c61a 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3298,7 +3298,6 @@ static int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *hsotg) int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) { unsigned long flags; - u32 hprt0; u32 pcgctl; u32 gotgctl; int ret = 0; @@ -3323,22 +3322,12 @@ int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: case DWC2_POWER_DOWN_PARAM_NONE: - default: - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_SUSP; - dwc2_writel(hsotg, hprt0, HPRT0); - hsotg->bus_suspended = true; /* -* If power_down is supported, Phy clock will be suspended -* after registers are backuped. +* If not hibernation nor partial power down are supported, +* clock gating is used to save power. */ - if (!hsotg->params.power_down) { - /* Suspend the Phy Clock */ - pcgctl = dwc2_readl(hsotg, PCGCTL); - pcgctl |= PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgctl, PCGCTL); - udelay(10); - } + dwc2_host_enter_clock_gating(hsotg); + break; } /* For HNP the bus must be suspended for at least 200ms */ -- 2.25.1
[PATCH v2 04/12] usb: dwc2: Add exit clock gating from wakeup interrupt
Added exit from clock gating mode when wakeup interrupt is detected. To exit from the clock gating in device mode "dwc2_gadget_exit_clock_gating()" function is used with rem_wakeup parameter 0. To exit clock gating in host mode "dwc2_host_exit_clock_gating()" with rem_wakeup parameter 1. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 33 ++--- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index ab7fe303c0f9..c764407e7633 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -415,17 +415,24 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) if (dwc2_is_device_mode(hsotg)) { dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dwc2_readl(hsotg, DSTS)); - if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd) { - u32 dctl = dwc2_readl(hsotg, DCTL); - /* Clear Remote Wakeup Signaling */ - dctl &= ~DCTL_RMTWKUPSIG; - dwc2_writel(hsotg, dctl, DCTL); - ret = dwc2_exit_partial_power_down(hsotg, 1, - true); - if (ret) - dev_err(hsotg->dev, - "exit partial_power_down failed\n"); - call_gadget(hsotg, resume); + if (hsotg->lx_state == DWC2_L2) { + if (hsotg->in_ppd) { + u32 dctl = dwc2_readl(hsotg, DCTL); + /* Clear Remote Wakeup Signaling */ + dctl &= ~DCTL_RMTWKUPSIG; + dwc2_writel(hsotg, dctl, DCTL); + ret = dwc2_exit_partial_power_down(hsotg, 1, + true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + call_gadget(hsotg, resume); + } + + /* Exit gadget mode clock gating. */ + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_gadget_exit_clock_gating(hsotg, 0); } else { /* Change to L0 state */ hsotg->lx_state = DWC2_L0; @@ -440,6 +447,10 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) "exit partial_power_down failed\n"); } + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_host_exit_clock_gating(hsotg, 1); + /* * If we've got this quirk then the PHY is stuck upon * wakeup. Assert reset. This will propagate out and -- 2.25.1
[PATCH v2 03/12] usb: dwc2: Allow entering clock gating from USB_SUSPEND interrupt
If core doesn't support hibernation or partial power down power saving options, power can still be saved using clock gating on all the clocks. - Added entering clock gating state from USB_SUSPEND interrupt. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 10 +++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 8c0152b514be..ab7fe303c0f9 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -529,14 +529,18 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) /* Ask phy to be suspended */ if (!IS_ERR_OR_NULL(hsotg->uphy)) usb_phy_set_suspend(hsotg->uphy, true); - } - - if (hsotg->hw_params.hibernation) { + } else if (hsotg->hw_params.hibernation) { ret = dwc2_enter_hibernation(hsotg, 0); if (ret && ret != -ENOTSUPP) dev_err(hsotg->dev, "%s: enter hibernation failed\n", __func__); + } else { + /* +* If not hibernation nor partial power down are supported, +* clock gating is used to save power. +*/ + dwc2_gadget_enter_clock_gating(hsotg); } skip_power_saving: /* -- 2.25.1
[PATCH v2 02/12] usb: dwc2: Add host clock gating support functions
Added host clock gating support functions according programming guide. Added function names: dwc2_host_enter_clock_gating() dwc2_host_exit_clock_gating() Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.h | 5 +++ drivers/usb/dwc2/hcd.c | 86 + 2 files changed, 91 insertions(+) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index e5597796dca4..8c12b3061f7f 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1486,6 +1486,8 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg); int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup, bool restore); +void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg); +void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup); bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2); static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) { schedule_work(>phy_reset_work); } @@ -1521,6 +1523,9 @@ static inline int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg) static inline int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup, bool restore) { return 0; } +static inline void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, + int rem_wakeup) {} static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) { return false; } static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index f096006df96f..f1c24c15d185 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5821,3 +5821,89 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "Exiting host partial power down completed.\n"); return ret; } + +/** + * dwc2_host_enter_clock_gating() - Put controller in clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * + * This function is for entering Host mode clock gating. + */ +void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg) +{ + u32 hprt0; + u32 pcgctl; + + dev_dbg(hsotg->dev, "Entering host clock gating.\n"); + + /* Put this port in suspend mode. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + + /* Set the Phy Clock bit as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Set the Gate hclk as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + hsotg->bus_suspended = true; + hsotg->lx_state = DWC2_L2; +} + +/** + * dwc2_host_exit_clock_gating() - Exit controller from clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by remote wakeup + * + * This function is for exiting Host mode clock gating. + */ +void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup) +{ + u32 hprt0; + u32 pcgctl; + + dev_dbg(hsotg->dev, "Exiting host clock gating.\n"); + + /* Clear the Gate hclk. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Phy Clock bit. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Drive resume signaling and exit suspend mode on the port. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_RES; + hprt0 &= ~HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + udelay(5); + + if (!rem_wakeup) { + /* In case of port resume need to wait for 40 ms */ + msleep(USB_RESUME_TIMEOUT); + + /* Stop driveing resume signaling on the port. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 &= ~HPRT0_RES; + dwc2_writel(hsotg, hprt0, HPRT0); + + hsotg->bus_suspended = false; + hsotg->lx_state = DWC2_L0; + } else { + mod_timer(>wkp_timer, + jiffies + msecs_to_jiffies(71)); + } +} -- 2.25.1
[PATCH v2 01/12] usb: dwc2: Add device clock gating support functions
Added device clock gating support functions according programming guide. Moved "bus_suspended" flag to "dwc2_hsotg" struct because we need to set that flag while entering to clock gating in case when the driver is built in peripheral mode. Added function names: dwc2_gadget_enter_clock_gating() dwc2_gadget_exit_clock_gating() Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.h | 10 -- drivers/usb/dwc2/gadget.c | 71 +++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 5a7850482e57..e5597796dca4 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -866,6 +866,7 @@ struct dwc2_hregs_backup { * @ll_hw_enabled: Status of low-level hardware resources. * @hibernated:True if core is hibernated * @in_ppd:True if core is partial power down mode. + * @bus_suspended: True if bus is suspended * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a * remote wakeup. * @phy_off_for_suspend: Status of whether we turned the PHY off at suspend. @@ -1023,7 +1024,6 @@ struct dwc2_hregs_backup { * a pointer to an array of register definitions, the * array size and the base address where the register bank * is to be found. - * @bus_suspended: True if bus is suspended * @last_frame_num:Number of last frame. Range from 0 to 32768 * @frame_num_array:Used only if CONFIG_USB_DWC2_TRACK_MISSED_SOFS is * defined, for missed SOFs tracking. Array holds that @@ -1062,6 +1062,7 @@ struct dwc2_hsotg { unsigned int ll_hw_enabled:1; unsigned int hibernated:1; unsigned int in_ppd:1; + bool bus_suspended; unsigned int reset_phy_on_wake:1; unsigned int need_phy_for_wake:1; unsigned int phy_off_for_suspend:1; @@ -1145,7 +1146,6 @@ struct dwc2_hsotg { unsigned long hs_periodic_bitmap[ DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)]; u16 periodic_qh_count; - bool bus_suspended; bool new_connection; u16 last_frame_num; @@ -1415,6 +1415,9 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg); int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore); +void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg); +void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, + int rem_wakeup); int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); @@ -1453,6 +1456,9 @@ static inline int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg) static inline int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) { return 0; } +static inline void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, +int rem_wakeup) {} static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index e08baee4987b..2f50f3e62caa 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -5483,3 +5483,74 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "Exiting device partial Power Down completed.\n"); return ret; } + +/** + * dwc2_gadget_enter_clock_gating() - Put controller in clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return: non-zero if failed to enter device partial power down. + * + * This function is for entering device mode clock gating. + */ +void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg) +{ + u32 pcgctl; + + dev_dbg(hsotg->dev, "Entering device clock gating.\n"); + + /* Set the Phy Clock bit as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Set the Gate hclk as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + hsotg->lx_state = DWC2_L2; + hsotg->bus_suspended = true; +} + +/* + * dwc2_gadget_exit_clock_gating() - Exit controller from device clock gating. + * +
[PATCH 12/12] usb: dwc2: Add exit clock gating before removing driver
When dwc2 core is in clock gating mode loading driver again causes driver fail. Because in that mode registers are not accessible. Added a flow of exiting clock gating mode to avoid the driver reload failure. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/platform.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index b28b8cd45799..f8b819cfa80e 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -326,6 +326,15 @@ static int dwc2_driver_remove(struct platform_device *dev) "exit partial_power_down failed\n"); } + /* Exit clock gating when driver is removed. */ + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && + hsotg->bus_suspended) { + if (dwc2_is_device_mode(hsotg)) + dwc2_gadget_exit_clock_gating(hsotg, 0); + else + dwc2_host_exit_clock_gating(hsotg, 0); + } + dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) dwc2_hcd_remove(hsotg); -- 2.25.1
[PATCH 11/12] usb: dwc2: Add clock gating exiting flow by system resume
If not hibernation nor partial power down are supported, port resume is done using the clock gating programming flow. Adds a new flow of exiting clock gating when PC is resumed. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 22 ++ 1 file changed, 22 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 09dcd37b9ef8..04a1b53d65af 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4445,6 +4445,28 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: case DWC2_POWER_DOWN_PARAM_NONE: + /* +* If not hibernation nor partial power down are supported, +* port resume is done using the clock gating programming flow. +*/ + spin_unlock_irqrestore(>lock, flags); + dwc2_host_exit_clock_gating(hsotg, 0); + + /* +* Initialize the Core for Host mode, as after system resume +* the global interrupts are disabled. +*/ + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hcd_reinit(hsotg); + spin_lock_irqsave(>lock, flags); + + /* +* Set HW accessible bit before powering on the controller +* since an interrupt may rise. +*/ + set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; default: hsotg->lx_state = DWC2_L0; goto unlock; -- 2.25.1
[PATCH 09/12] usb: dwc2: Allow exit clock gating in urb enqueue
When core is in clock gating state and an external hub is connected, upper layer sends URB enqueue request, which results in port reset issue. Added exit from clock gating state to avoid port reset issue and process upper layer request properly. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 8 1 file changed, 8 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 8a42675ab94e..31d6a1b87228 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4597,6 +4597,14 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, "exit partial_power_down failed\n"); } + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && + hsotg->bus_suspended) { + if (dwc2_is_device_mode(hsotg)) + dwc2_gadget_exit_clock_gating(hsotg, 0); + else + dwc2_host_exit_clock_gating(hsotg, 0); + } + if (!ep) return -EINVAL; -- 2.25.1
[PATCH 10/12] usb: dwc2: Add clock gating entering flow by system suspend
If not hibernation nor partial power down are supported, clock gating is used to save power. Adds a new flow of entering clock gating when PC is suspended. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 31d6a1b87228..09dcd37b9ef8 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4372,6 +4372,15 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: case DWC2_POWER_DOWN_PARAM_NONE: + /* +* If not hibernation nor partial power down are supported, +* clock gating is used to save power. +*/ + dwc2_host_enter_clock_gating(hsotg); + + /* After entering suspend, hardware is not accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; default: goto skip_power_saving; } -- 2.25.1
[PATCH 08/12] usb: dwc2: Update exit clock gating when port is resumed
Updates the implementation of exiting clock gating mode when core receives port resume. Instead of setting the required bit fields of the registers inline, called the "dwc2_host_exit_clock_gating()" function. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 29 - 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index e1225fe6c61a..8a42675ab94e 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3359,8 +3359,6 @@ int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) int dwc2_port_resume(struct dwc2_hsotg *hsotg) { unsigned long flags; - u32 hprt0; - u32 pcgctl; int ret = 0; spin_lock_irqsave(>lock, flags); @@ -3374,33 +3372,14 @@ int dwc2_port_resume(struct dwc2_hsotg *hsotg) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: case DWC2_POWER_DOWN_PARAM_NONE: - default: /* -* If power_down is supported, Phy clock is already resumed -* after registers restore. +* If not hibernation nor partial power down are supported, +* port resume is done using the clock gating programming flow. */ - if (!hsotg->params.power_down) { - pcgctl = dwc2_readl(hsotg, PCGCTL); - pcgctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgctl, PCGCTL); - spin_unlock_irqrestore(>lock, flags); - msleep(20); - spin_lock_irqsave(>lock, flags); - } - - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_RES; - hprt0 &= ~HPRT0_SUSP; - dwc2_writel(hsotg, hprt0, HPRT0); spin_unlock_irqrestore(>lock, flags); - - msleep(USB_RESUME_TIMEOUT); - + dwc2_host_exit_clock_gating(hsotg, 0); spin_lock_irqsave(>lock, flags); - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 &= ~(HPRT0_RES | HPRT0_SUSP); - dwc2_writel(hsotg, hprt0, HPRT0); - hsotg->bus_suspended = false; + break; } spin_unlock_irqrestore(>lock, flags); -- 2.25.1
[PATCH 07/12] usb: dwc2: Update enter clock gating when port is suspended
Updates the implementation of entering clock gating mode when core receives port suspend. Instead of setting the required bit fields of the registers inline, called the "dwc2_host_enter_clock_gating()" function. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 19 --- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 27f030d5de54..e1225fe6c61a 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3298,7 +3298,6 @@ static int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *hsotg) int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) { unsigned long flags; - u32 hprt0; u32 pcgctl; u32 gotgctl; int ret = 0; @@ -3323,22 +3322,12 @@ int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) break; case DWC2_POWER_DOWN_PARAM_HIBERNATION: case DWC2_POWER_DOWN_PARAM_NONE: - default: - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_SUSP; - dwc2_writel(hsotg, hprt0, HPRT0); - hsotg->bus_suspended = true; /* -* If power_down is supported, Phy clock will be suspended -* after registers are backuped. +* If not hibernation nor partial power down are supported, +* clock gating is used to save power. */ - if (!hsotg->params.power_down) { - /* Suspend the Phy Clock */ - pcgctl = dwc2_readl(hsotg, PCGCTL); - pcgctl |= PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgctl, PCGCTL); - udelay(10); - } + dwc2_host_enter_clock_gating(hsotg); + break; } /* For HNP the bus must be suspended for at least 200ms */ -- 2.25.1
[PATCH 06/12] usb: dwc2: Add exit clock gating when port reset is asserted
Adds clock gating exit flow when set port feature reset is received in suspended state. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 4 1 file changed, 4 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index f1c24c15d185..27f030d5de54 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3712,6 +3712,10 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "exit partial_power_down failed\n"); } + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_host_exit_clock_gating(hsotg, 0); + hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_RESET\n"); -- 2.25.1
[PATCH 04/12] usb: dwc2: Add exit clock gating from wakeup interrupt
Added exit from clock gating mode when wakeup interrupt is detected. To exit from the clock gating in device mode "dwc2_gadget_exit_clock_gating()" function is used with rem_wakeup parameter 0. To exit clock gating in host mode "dwc2_host_exit_clock_gating()" with rem_wakeup parameter 1. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 33 ++--- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index ab7fe303c0f9..c764407e7633 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -415,17 +415,24 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) if (dwc2_is_device_mode(hsotg)) { dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dwc2_readl(hsotg, DSTS)); - if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd) { - u32 dctl = dwc2_readl(hsotg, DCTL); - /* Clear Remote Wakeup Signaling */ - dctl &= ~DCTL_RMTWKUPSIG; - dwc2_writel(hsotg, dctl, DCTL); - ret = dwc2_exit_partial_power_down(hsotg, 1, - true); - if (ret) - dev_err(hsotg->dev, - "exit partial_power_down failed\n"); - call_gadget(hsotg, resume); + if (hsotg->lx_state == DWC2_L2) { + if (hsotg->in_ppd) { + u32 dctl = dwc2_readl(hsotg, DCTL); + /* Clear Remote Wakeup Signaling */ + dctl &= ~DCTL_RMTWKUPSIG; + dwc2_writel(hsotg, dctl, DCTL); + ret = dwc2_exit_partial_power_down(hsotg, 1, + true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + call_gadget(hsotg, resume); + } + + /* Exit gadget mode clock gating. */ + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_gadget_exit_clock_gating(hsotg, 0); } else { /* Change to L0 state */ hsotg->lx_state = DWC2_L0; @@ -440,6 +447,10 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) "exit partial_power_down failed\n"); } + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_host_exit_clock_gating(hsotg, 1); + /* * If we've got this quirk then the PHY is stuck upon * wakeup. Assert reset. This will propagate out and -- 2.25.1
[PATCH 02/12] usb: dwc2: Add host clock gating support functions
Added host clock gating support functions according programming guide. Added function names: dwc2_host_enter_clock_gating() dwc2_host_exit_clock_gating() Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.h | 5 +++ drivers/usb/dwc2/hcd.c | 86 + 2 files changed, 91 insertions(+) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index e5597796dca4..8c12b3061f7f 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1486,6 +1486,8 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg); int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup, bool restore); +void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg); +void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup); bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2); static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) { schedule_work(>phy_reset_work); } @@ -1521,6 +1523,9 @@ static inline int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg) static inline int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup, bool restore) { return 0; } +static inline void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, + int rem_wakeup) {} static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) { return false; } static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index f096006df96f..f1c24c15d185 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5821,3 +5821,89 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "Exiting host partial power down completed.\n"); return ret; } + +/** + * dwc2_host_enter_clock_gating() - Put controller in clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * + * This function is for entering Host mode clock gating. + */ +void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg) +{ + u32 hprt0; + u32 pcgctl; + + dev_dbg(hsotg->dev, "Entering host clock gating.\n"); + + /* Put this port in suspend mode. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + + /* Set the Phy Clock bit as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Set the Gate hclk as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + hsotg->bus_suspended = true; + hsotg->lx_state = DWC2_L2; +} + +/** + * dwc2_host_exit_clock_gating() - Exit controller from clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by remote wakeup + * + * This function is for exiting Host mode clock gating. + */ +void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup) +{ + u32 hprt0; + u32 pcgctl; + + dev_dbg(hsotg->dev, "Exiting host clock gating.\n"); + + /* Clear the Gate hclk. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Phy Clock bit. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Drive resume signaling and exit suspend mode on the port. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_RES; + hprt0 &= ~HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + udelay(5); + + if (!rem_wakeup) { + /* In case of port resume need to wait for 40 ms */ + msleep(USB_RESUME_TIMEOUT); + + /* Stop driveing resume signaling on the port. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 &= ~HPRT0_RES; + dwc2_writel(hsotg, hprt0, HPRT0); + + hsotg->bus_suspended = false; + hsotg->lx_state = DWC2_L0; + } else { + mod_timer(>wkp_timer, + jiffies + msecs_to_jiffies(71)); + } +} -- 2.25.1
[PATCH 03/12] usb: dwc2: Allow entering clock gating from USB_SUSPEND interrupt
If core doesn't support hibernation or partial power down power saving options, power can still be saved using clock gating on all the clocks. - Added entering clock gating state from USB_SUSPEND interrupt. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 10 +++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 8c0152b514be..ab7fe303c0f9 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -529,14 +529,18 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) /* Ask phy to be suspended */ if (!IS_ERR_OR_NULL(hsotg->uphy)) usb_phy_set_suspend(hsotg->uphy, true); - } - - if (hsotg->hw_params.hibernation) { + } else if (hsotg->hw_params.hibernation) { ret = dwc2_enter_hibernation(hsotg, 0); if (ret && ret != -ENOTSUPP) dev_err(hsotg->dev, "%s: enter hibernation failed\n", __func__); + } else { + /* +* If not hibernation nor partial power down are supported, +* clock gating is used to save power. +*/ + dwc2_gadget_enter_clock_gating(hsotg); } skip_power_saving: /* -- 2.25.1
[PATCH 01/12] usb: dwc2: Add device clock gating support functions
Added device clock gating support functions according programming guide. Moved "bus_suspended" flag to "dwc2_hsotg" struct because we need to set that flag while entering to clock gating in case when the driver is built in peripheral mode. Added function names: dwc2_gadget_enter_clock_gating() dwc2_gadget_exit_clock_gating() Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.h | 10 -- drivers/usb/dwc2/gadget.c | 71 +++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 5a7850482e57..e5597796dca4 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -866,6 +866,7 @@ struct dwc2_hregs_backup { * @ll_hw_enabled: Status of low-level hardware resources. * @hibernated:True if core is hibernated * @in_ppd:True if core is partial power down mode. + * @bus_suspended: True if bus is suspended * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a * remote wakeup. * @phy_off_for_suspend: Status of whether we turned the PHY off at suspend. @@ -1023,7 +1024,6 @@ struct dwc2_hregs_backup { * a pointer to an array of register definitions, the * array size and the base address where the register bank * is to be found. - * @bus_suspended: True if bus is suspended * @last_frame_num:Number of last frame. Range from 0 to 32768 * @frame_num_array:Used only if CONFIG_USB_DWC2_TRACK_MISSED_SOFS is * defined, for missed SOFs tracking. Array holds that @@ -1062,6 +1062,7 @@ struct dwc2_hsotg { unsigned int ll_hw_enabled:1; unsigned int hibernated:1; unsigned int in_ppd:1; + bool bus_suspended; unsigned int reset_phy_on_wake:1; unsigned int need_phy_for_wake:1; unsigned int phy_off_for_suspend:1; @@ -1145,7 +1146,6 @@ struct dwc2_hsotg { unsigned long hs_periodic_bitmap[ DIV_ROUND_UP(DWC2_HS_SCHEDULE_US, BITS_PER_LONG)]; u16 periodic_qh_count; - bool bus_suspended; bool new_connection; u16 last_frame_num; @@ -1415,6 +1415,9 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg); int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore); +void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg); +void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, + int rem_wakeup); int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); @@ -1453,6 +1456,9 @@ static inline int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg) static inline int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) { return 0; } +static inline void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_gadget_exit_clock_gating(struct dwc2_hsotg *hsotg, +int rem_wakeup) {} static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index e08baee4987b..2f50f3e62caa 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -5483,3 +5483,74 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "Exiting device partial Power Down completed.\n"); return ret; } + +/** + * dwc2_gadget_enter_clock_gating() - Put controller in clock gating. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return: non-zero if failed to enter device partial power down. + * + * This function is for entering device mode clock gating. + */ +void dwc2_gadget_enter_clock_gating(struct dwc2_hsotg *hsotg) +{ + u32 pcgctl; + + dev_dbg(hsotg->dev, "Entering device clock gating.\n"); + + /* Set the Phy Clock bit as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + /* Set the Gate hclk as suspend is received. */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_GATEHCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(5); + + hsotg->lx_state = DWC2_L2; + hsotg->bus_suspended = true; +} + +/* + * dwc2_gadget_exit_clock_gating() - Exit controller from device clock gating. + * +
[PATCH 05/12] usb: dwc2: Add exit clock gating from session request interrupt
Added clock gating exit flow from session request interrupt handler according programming guide. Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/core_intr.c | 19 +-- 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index c764407e7633..550c52c1a0c7 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -316,12 +316,19 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) hsotg->lx_state); if (dwc2_is_device_mode(hsotg)) { - if (hsotg->lx_state == DWC2_L2 && hsotg->in_ppd) { - ret = dwc2_exit_partial_power_down(hsotg, 0, - true); - if (ret) - dev_err(hsotg->dev, - "exit power_down failed\n"); + if (hsotg->lx_state == DWC2_L2) { + if (hsotg->in_ppd) { + ret = dwc2_exit_partial_power_down(hsotg, 0, + true); + if (ret) + dev_err(hsotg->dev, + "exit power_down failed\n"); + } + + /* Exit gadget mode clock gating. */ + if (hsotg->params.power_down == + DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended) + dwc2_gadget_exit_clock_gating(hsotg, 0); } /* -- 2.25.1
Re: [PATCH v2 00/14] usb: dwc2: Fix Partial Power down issues.
Hi Greg, On 4/8/2021 13:17, Artur Petrosyan wrote: > Hi Greg, > > On 4/8/2021 11:28, Artur Petrosyan wrote: >> This patch set fixes and improves the Partial Power Down mode for >> dwc2 core. >> It adds support for the following cases >> 1. Entering and exiting partial power down when a port is >> suspended, resumed, port reset is asserted. >> 2. Exiting the partial power down mode before removing driver. >> 3. Exiting partial power down in wakeup detected interrupt handler. >> 4. Exiting from partial power down mode when connector ID. >> status changes to "connId B >> >> It updates and fixes the implementation of dwc2 entering and >> exiting partial power down mode when the system (PC) is suspended. >> >> The patch set also improves the implementation of function handlers >> for entering and exiting host or device partial power down. >> >> NOTE: This is the second patch set in the power saving mode fixes >> series. >> This patch set is part of multiple series and is continuation >> of the "usb: dwc2: Fix and improve power saving modes" patch set. >> (Patch set link: >> https://urldefense.com/v3/__https://marc.info/?l=linux-usb=160379622403975=2__;!!A4F2R9G_pg!IJ-Xl1ZwQU2kmqHB3ITyWyno9BgpWUsC647AqK7GIlgzJu9BzT6VN7jt--__fGdMtgWF69M$ >> ). >> The patches that were included in the "usb: dwc2: >> Fix and improve power saving modes" which was submitted >> earlier was too large and needed to be split up into >> smaller patch sets. >> >> Changes since V1: >> No changes in the patches or the source code. >> Sending the second version of the patch set because the first version >> was not received by vger.kernel.org. >> >> >> >> Artur Petrosyan (14): >> usb: dwc2: Add device partial power down functions >> usb: dwc2: Add host partial power down functions >> usb: dwc2: Update enter and exit partial power down functions >> usb: dwc2: Add partial power down exit flow in wakeup intr. >> usb: dwc2: Update port suspend/resume function definitions. >> usb: dwc2: Add enter partial power down when port is suspended >> usb: dwc2: Add exit partial power down when port is resumed >> usb: dwc2: Add exit partial power down when port reset is asserted >> usb: dwc2: Add part. power down exit from >> dwc2_conn_id_status_change(). >> usb: dwc2: Allow exit partial power down in urb enqueue >> usb: dwc2: Fix session request interrupt handler >> usb: dwc2: Update partial power down entering by system suspend >> usb: dwc2: Fix partial power down exiting by system resume >> usb: dwc2: Add exit partial power down before removing driver >> >>drivers/usb/dwc2/core.c | 113 ++--- >>drivers/usb/dwc2/core.h | 27 ++- >>drivers/usb/dwc2/core_intr.c | 46 ++-- >>drivers/usb/dwc2/gadget.c| 148 ++- >>drivers/usb/dwc2/hcd.c | 458 +-- >>drivers/usb/dwc2/hw.h| 1 + >>drivers/usb/dwc2/platform.c | 11 +- >>7 files changed, 558 insertions(+), 246 deletions(-) >> >> >> base-commit: e9fcb07704fcef6fa6d0333fd2b3a62442eaf45b >> > > Re sending as a "v2" did not work :(. > The patches are not in lore again. > > Could the issue be with a comma in the end of To: or Cc: list? > Let me remove the comma in the end of those lists and try sending as "v3". > > Regards, > Artur > I just removed the comma in the end of those lists and resent the patch set as a "v3" and they are already seen in lore. There is one strange thing though on lore. Some patch titles are not fully visible. For sure the issue was comma in the end of To: or Cc: lists. Not working example. To: Greg Kroah-Hartman , linux-...@vger.kernel.org, linux-kernel@vger.kernel.org, Working example. To: Greg Kroah-Hartman , linux-...@vger.kernel.org, linux-kernel@vger.kernel.org If the comma is at least in the end of one of those lists (To: or Cc:) vger.kernel.org mailing server will not accept them. Regards, Artur
[PATCH v3 14/14] usb: dwc2: Add exit partial power down before removing driver
When dwc2 core is in partial power down mode loading driver again causes driver fail. Because in that mode registers are not accessible. Added a flow of exiting the partial power down mode to avoid the driver reload failure. Signed-off-by: Artur Petrosyan --- Changes in v3: - None Changes in v2: - None drivers/usb/dwc2/platform.c | 11 ++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 5f18acac7406..b28b8cd45799 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -316,6 +316,15 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + int ret = 0; + + /* Exit Partial Power Down when driver is removed. */ + if (hsotg->in_ppd) { + ret = dwc2_exit_partial_power_down(hsotg, 0, true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + } dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) @@ -334,7 +343,7 @@ static int dwc2_driver_remove(struct platform_device *dev) reset_control_assert(hsotg->reset); reset_control_assert(hsotg->reset_ecc); - return 0; + return ret; } /** -- 2.25.1
[PATCH v3 13/14] usb: dwc2: Fix partial power down exiting by system resume
Fixes the implementation of exiting from partial power down power saving mode when PC is resumed. Added port connection status checking which prevents exiting from Partial Power Down mode from _dwc2_hcd_resume() if not in Partial Power Down mode. Rearranged the implementation to get rid of many "if" statements. NOTE: Switch case statement is used for hibernation partial power down and clock gating mode determination. In this patch only Partial Power Down is implemented the Hibernation and clock gating implementations are planned to be added. Cc: Fixes: 6f6d70597c15 ("usb: dwc2: bus suspend/resume for hosts with DWC2_POWER_DOWN_PARAM_NONE") Signed-off-by: Artur Petrosyan --- Changes in v3: - None Changes in v2: - None drivers/usb/dwc2/hcd.c | 90 +- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 34030bafdff4..f096006df96f 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4427,7 +4427,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; - u32 pcgctl; + u32 hprt0; int ret = 0; spin_lock_irqsave(>lock, flags); @@ -4438,11 +4438,40 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) if (hsotg->lx_state != DWC2_L2) goto unlock; - if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) { + hprt0 = dwc2_read_hprt0(hsotg); + + /* +* Added port connection status checking which prevents exiting from +* Partial Power Down mode from _dwc2_hcd_resume() if not in Partial +* Power Down mode. +*/ + if (hprt0 & HPRT0_CONNSTS) { + hsotg->lx_state = DWC2_L0; + goto unlock; + } + + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: + ret = dwc2_exit_partial_power_down(hsotg, 0, true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + /* +* Set HW accessible bit before powering on the controller +* since an interrupt may rise. +*/ + set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + case DWC2_POWER_DOWN_PARAM_NONE: + default: hsotg->lx_state = DWC2_L0; goto unlock; } + /* Change Root port status, as port status change occurred after resume.*/ + hsotg->flags.b.port_suspend_change = 1; + /* * Enable power if not already done. * This must not be spinlocked since duration @@ -4454,52 +4483,25 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(>lock, flags); } - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - /* -* Set HW accessible bit before powering on the controller -* since an interrupt may rise. -*/ - set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); - - - /* Exit partial_power_down */ - ret = dwc2_exit_partial_power_down(hsotg, 0, true); - if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit partial_power_down failed\n"); - } else { - pcgctl = readl(hsotg->regs + PCGCTL); - pcgctl &= ~PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); - } - - hsotg->lx_state = DWC2_L0; - + /* Enable external vbus supply after resuming the port. */ spin_unlock_irqrestore(>lock, flags); + dwc2_vbus_supply_init(hsotg); - if (hsotg->bus_suspended) { - spin_lock_irqsave(>lock, flags); - hsotg->flags.b.port_suspend_change = 1; - spin_unlock_irqrestore(>lock, flags); - dwc2_port_resume(hsotg); - } else { - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - dwc2_vbus_supply_init(hsotg); - - /* Wait for controller to correctly update D+/D- level */ - usleep_range(3000, 5000); - } + /* Wait for controller to correctly update D+/D- level */ + usleep_range(3000, 5000); + spin_lock_irqsave(>lock, flags); - /* -* Clear Port Enable and Port Status changes. -* Enable Port Power. -*/ - dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET | - HPRT0_ENACHG, HPRT0); - /* Wait for controller to de
[PATCH v3 12/14] usb: dwc2: Update partial power down entering by system suspend
With current implementation the port power is being disabled, which is not required by the programming guide. Also, if there is a system which works only in "DWC2_POWER_DOWN_PARAM_NONE" (clock gating) mode the current implementation does not set Gate hclk bit in pcgctl register. Rearranges and updates the implementation of entering to partial power down power saving mode when PC is suspended to get rid of many "if" statements and removes disabling of port power. NOTE: Switch case statement is used for hibernation partial power down and clock gating mode determination. In this patch only Partial Power Down is implemented the Hibernation and clock gating implementations are planned to be added. Signed-off-by: Artur Petrosyan --- Changes in v3: - None Changes in v2: - None drivers/usb/dwc2/hcd.c | 53 ++ 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index cb52bc41bfb8..34030bafdff4 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4367,8 +4367,6 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; int ret = 0; - u32 hprt0; - u32 pcgctl; spin_lock_irqsave(>lock, flags); @@ -4384,47 +4382,32 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) if (hsotg->op_state == OTG_STATE_B_PERIPHERAL) goto unlock; - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL || - hsotg->flags.b.port_connect_status == 0) + if (hsotg->bus_suspended) goto skip_power_saving; - /* -* Drive USB suspend and disable port Power -* if usb bus is not suspended. -*/ - if (!hsotg->bus_suspended) { - hprt0 = dwc2_read_hprt0(hsotg); - if (hprt0 & HPRT0_CONNSTS) { - hprt0 |= HPRT0_SUSP; - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) - hprt0 &= ~HPRT0_PWR; - dwc2_writel(hsotg, hprt0, HPRT0); - } - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - spin_unlock_irqrestore(>lock, flags); - dwc2_vbus_supply_exit(hsotg); - spin_lock_irqsave(>lock, flags); - } else { - pcgctl = readl(hsotg->regs + PCGCTL); - pcgctl |= PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); - } - } + if (hsotg->flags.b.port_connect_status == 0) + goto skip_power_saving; - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: /* Enter partial_power_down */ ret = dwc2_enter_partial_power_down(hsotg); - if (ret) { - if (ret != -ENOTSUPP) - dev_err(hsotg->dev, - "enter partial_power_down failed\n"); - goto skip_power_saving; - } - - /* After entering partial_power_down, hardware is no more accessible */ + if (ret) + dev_err(hsotg->dev, + "enter partial_power_down failed\n"); + /* After entering suspend, hardware is not accessible */ clear_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + case DWC2_POWER_DOWN_PARAM_NONE: + default: + goto skip_power_saving; } + spin_unlock_irqrestore(>lock, flags); + dwc2_vbus_supply_exit(hsotg); + spin_lock_irqsave(>lock, flags); + /* Ask phy to be suspended */ if (!IS_ERR_OR_NULL(hsotg->uphy)) { spin_unlock_irqrestore(>lock, flags); -- 2.25.1
[PATCH v3 11/14] usb: dwc2: Fix session request interrupt handler
According to programming guide in host mode, port power must be turned on in session request interrupt handlers. Cc: Fixes: 21795c826a45 ("usb: dwc2: exit hibernation on session request") Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 8 1 file changed, 8 insertions(+) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 0a7f9330907f..8c0152b514be 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -307,6 +307,7 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) { int ret; + u32 hprt0; /* Clear interrupt */ dwc2_writel(hsotg, GINTSTS_SESSREQINT, GINTSTS); @@ -328,6 +329,13 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) * established */ dwc2_hsotg_disconnect(hsotg); + } else { + /* Turn on the port power bit. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_PWR; + dwc2_writel(hsotg, hprt0, HPRT0); + /* Connect hcd after port power is set. */ + dwc2_hcd_connect(hsotg); } } -- 2.25.1
[PATCH v3 10/14] usb: dwc2: Allow exit partial power down in urb enqueue
When core is in partial power down state and an external hub is connected, upper layer sends URB enqueue request, which results in port reset issue. Added exit from partial power down state to avoid port reset issue and process upper layer request correctly. Signed-off-by: Artur Petrosyan --- Changes in v3: - None Changes in v2: - None drivers/usb/dwc2/hcd.c | 7 +++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 9529e9839961..cb52bc41bfb8 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4633,6 +4633,13 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, dwc2_dump_urb_info(hcd, urb, "urb_enqueue"); } + if (hsotg->in_ppd) { + retval = dwc2_exit_partial_power_down(hsotg, 0, true); + if (retval) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + } + if (!ep) return -EINVAL; -- 2.25.1
[PATCH v3 09/14] usb: dwc2: Add part. power down exit from dwc2_conn_id_status_change().
Before changing to connector B exiting from Partial Power Down is required. - Added exiting from Partial Power Down mode when connector ID status changes to "connId B". Because if connector ID status changed to B connector while core was in partial power down mode, HANG would accrue from a soft reset. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/hcd.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 7c7496719152..9529e9839961 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3206,6 +3206,15 @@ static void dwc2_conn_id_status_change(struct work_struct *work) if (count > 250) dev_err(hsotg->dev, "Connection id status change timed out\n"); + + /* +* Exit Partial Power Down without restoring registers. +* No need to check the return value as registers +* are not being restored. +*/ + if (hsotg->in_ppd && hsotg->lx_state == DWC2_L2) + dwc2_exit_partial_power_down(hsotg, 0, false); + hsotg->op_state = OTG_STATE_B_PERIPHERAL; dwc2_core_init(hsotg, false); dwc2_enable_global_interrupts(hsotg); -- 2.25.1
[PATCH v3 08/14] usb: dwc2: Add exit partial power down when port reset is asserted
Adds Partial Power Down exiting flow when set port feature reset is received in suspended state. Signed-off-by: Artur Petrosyan --- Changes in v3: - None Changes in v2: - None drivers/usb/dwc2/hcd.c | 9 + 1 file changed, 9 insertions(+) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 720354df014b..7c7496719152 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3694,6 +3694,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_HIBERNATION && hsotg->hibernated) dwc2_exit_hibernation(hsotg, 0, 1, 1); + + if (hsotg->in_ppd) { + retval = dwc2_exit_partial_power_down(hsotg, 1, + true); + if (retval) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + } + hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_RESET\n"); -- 2.25.1
[PATCH v3 07/14] usb: dwc2: Add exit partial power down when port is resumed
Added flow of exiting Partial Power Down in "dwc2_port_resume()" function when core receives resume. NOTE: Switch case statement is used for hibernation partial power down and clock gating mode determination. In this patch only Partial Power Down is implemented the Hibernation and clock gating implementations are planned to be added. Signed-off-by: Artur Petrosyan --- Changes in v3: - None Changes in v2: - None drivers/usb/dwc2/core.h | 5 ++-- drivers/usb/dwc2/hcd.c | 61 ++--- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 76807abd753b..5a7850482e57 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1471,7 +1471,7 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force); void dwc2_hcd_start(struct dwc2_hsotg *hsotg); int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup); int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex); -void dwc2_port_resume(struct dwc2_hsotg *hsotg); +int dwc2_port_resume(struct dwc2_hsotg *hsotg); int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg); int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); @@ -1497,7 +1497,8 @@ static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) { return 0; } static inline int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) { return 0; } -static inline void dwc2_port_resume(struct dwc2_hsotg *hsotg) {} +static inline int dwc2_port_resume(struct dwc2_hsotg *hsotg) +{ return 0; } static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index e7fb0d5940bc..720354df014b 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3353,44 +3353,61 @@ int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) * * @hsotg: Programming view of the DWC_otg controller * + * Return: non-zero if failed to exit suspend mode for host. + * * This function is for exiting Host mode suspend. * Must NOT be called with interrupt disabled or spinlock held. */ -void dwc2_port_resume(struct dwc2_hsotg *hsotg) +int dwc2_port_resume(struct dwc2_hsotg *hsotg) { unsigned long flags; u32 hprt0; u32 pcgctl; + int ret = 0; spin_lock_irqsave(>lock, flags); - /* -* If power_down is supported, Phy clock is already resumed -* after registers restore. -*/ - if (!hsotg->params.power_down) { - pcgctl = dwc2_readl(hsotg, PCGCTL); - pcgctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgctl, PCGCTL); + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: + ret = dwc2_exit_partial_power_down(hsotg, 0, true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed.\n"); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + case DWC2_POWER_DOWN_PARAM_NONE: + default: + /* +* If power_down is supported, Phy clock is already resumed +* after registers restore. +*/ + if (!hsotg->params.power_down) { + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + spin_unlock_irqrestore(>lock, flags); + msleep(20); + spin_lock_irqsave(>lock, flags); + } + + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_RES; + hprt0 &= ~HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); spin_unlock_irqrestore(>lock, flags); - msleep(20); + + msleep(USB_RESUME_TIMEOUT); + spin_lock_irqsave(>lock, flags); + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 &= ~(HPRT0_RES | HPRT0_SUSP); + dwc2_writel(hsotg, hprt0, HPRT0); + hsotg->bus_suspended = false; } - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_RES; - hprt0 &= ~HPRT0_SUSP; - dwc2_writel(hsotg, hprt0, HPRT0); spin_unlock_irqrestore(>lock, flags); - msleep(USB_RESUME_TIMEOUT); - - spin_lock_irqsave(>lock, flags); - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 &= ~(HPRT0_RES | HPRT0_SUSP); - dwc2_writel(hsotg, hprt0, HPRT0); - hsotg->bus_suspended = false; - spin_unlock_irqrestore(>lock, flags); + return ret; } /* Handles hub class-specific requests */ -- 2.25.1
[PATCH v3 06/14] usb: dwc2: Add enter partial power down when port is suspended
Adds flow of entering Partial Power Down in "dwc2_port_suspend()" function when core receives suspend. NOTE: Switch case statement is used for hibernation partial power down and clock gating mode determination. In this patch only Partial Power Down is implemented the Hibernation and clock gating implementations are planned to be added. Signed-off-by: Artur Petrosyan --- Changes in v3: - None Changes in v2: - None drivers/usb/dwc2/core.h | 5 +++-- drivers/usb/dwc2/hcd.c | 48 ++--- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index b7d99cf9e84c..76807abd753b 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1470,7 +1470,7 @@ void dwc2_hcd_connect(struct dwc2_hsotg *hsotg); void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force); void dwc2_hcd_start(struct dwc2_hsotg *hsotg); int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup); -void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex); +int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex); void dwc2_port_resume(struct dwc2_hsotg *hsotg); int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg); int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); @@ -1495,7 +1495,8 @@ static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) { return 0; } -static inline void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) {} +static inline int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) +{ return 0; } static inline void dwc2_port_resume(struct dwc2_hsotg *hsotg) {} static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg) { return 0; } diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index f4247a66c2b2..e7fb0d5940bc 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3281,15 +3281,18 @@ static int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *hsotg) * @hsotg: Programming view of the DWC_otg controller * @windex: The control request wIndex field * + * Return: non-zero if failed to enter suspend mode for host. + * * This function is for entering Host mode suspend. * Must NOT be called with interrupt disabled or spinlock held. */ -void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) +int dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) { unsigned long flags; u32 hprt0; u32 pcgctl; u32 gotgctl; + int ret = 0; dev_dbg(hsotg->dev, "%s()\n", __func__); @@ -3302,22 +3305,31 @@ void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) hsotg->op_state = OTG_STATE_A_SUSPEND; } - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_SUSP; - dwc2_writel(hsotg, hprt0, HPRT0); - - hsotg->bus_suspended = true; - - /* -* If power_down is supported, Phy clock will be suspended -* after registers are backuped. -*/ - if (!hsotg->params.power_down) { - /* Suspend the Phy Clock */ - pcgctl = dwc2_readl(hsotg, PCGCTL); - pcgctl |= PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgctl, PCGCTL); - udelay(10); + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: + ret = dwc2_enter_partial_power_down(hsotg); + if (ret) + dev_err(hsotg->dev, + "enter partial_power_down failed.\n"); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + case DWC2_POWER_DOWN_PARAM_NONE: + default: + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + hsotg->bus_suspended = true; + /* +* If power_down is supported, Phy clock will be suspended +* after registers are backuped. +*/ + if (!hsotg->params.power_down) { + /* Suspend the Phy Clock */ + pcgctl = dwc2_readl(hsotg, PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgctl, PCGCTL); + udelay(10); + } } /* For HNP the bus must be suspended for at least 200ms */ @@ -3332,6 +3344,8 @@ void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) } else { spin_unlock_irqrestore(>lock, flags); } + + return ret; } /** -- 2.25.1
[PATCH v3 05/14] usb: dwc2: Update port suspend/resume function definitions.
Earlier "dwc2_port_suspend()" and "dwc2_port_resume()" functions were implemented without proper description and host or device mode difference. - Added "dwc2_port_suspend" and "dwc2_port_resume" functions to "core.h" header file. - Updated function description in documentation. Signed-off-by: Artur Petrosyan --- Changes in v3: - None Changes in v2: - None drivers/usb/dwc2/core.h | 4 drivers/usb/dwc2/hcd.c | 25 +++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 39037709a2ad..b7d99cf9e84c 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1470,6 +1470,8 @@ void dwc2_hcd_connect(struct dwc2_hsotg *hsotg); void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force); void dwc2_hcd_start(struct dwc2_hsotg *hsotg); int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup); +void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex); +void dwc2_port_resume(struct dwc2_hsotg *hsotg); int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg); int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); @@ -1493,6 +1495,8 @@ static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) { return 0; } +static inline void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) {} +static inline void dwc2_port_resume(struct dwc2_hsotg *hsotg) {} static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index dd0362e07444..f4247a66c2b2 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -56,8 +56,6 @@ #include "core.h" #include "hcd.h" -static void dwc2_port_resume(struct dwc2_hsotg *hsotg); - /* * = * Host Core Layer Functions @@ -3277,8 +3275,16 @@ static int dwc2_host_is_b_hnp_enabled(struct dwc2_hsotg *hsotg) return hcd->self.b_hnp_enable; } -/* Must NOT be called with interrupt disabled or spinlock held */ -static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) +/** + * dwc2_port_suspend() - Put controller in suspend mode for host. + * + * @hsotg: Programming view of the DWC_otg controller + * @windex: The control request wIndex field + * + * This function is for entering Host mode suspend. + * Must NOT be called with interrupt disabled or spinlock held. + */ +void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) { unsigned long flags; u32 hprt0; @@ -3328,8 +3334,15 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) } } -/* Must NOT be called with interrupt disabled or spinlock held */ -static void dwc2_port_resume(struct dwc2_hsotg *hsotg) +/** + * dwc2_port_resume() - Exit controller from suspend mode for host. + * + * @hsotg: Programming view of the DWC_otg controller + * + * This function is for exiting Host mode suspend. + * Must NOT be called with interrupt disabled or spinlock held. + */ +void dwc2_port_resume(struct dwc2_hsotg *hsotg) { unsigned long flags; u32 hprt0; -- 2.25.1
[PATCH v3 04/14] usb: dwc2: Add partial power down exit flow in wakeup intr.
According to programming guide added host partial power down exit flow in wakeup detected interrupt handler. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core_intr.c | 17 - 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 1fb957ce6c25..0a7f9330907f 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -423,15 +423,14 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) hsotg->lx_state = DWC2_L0; } } else { - if (hsotg->params.power_down) - return; - - if (hsotg->lx_state != DWC2_L1) { - u32 pcgcctl = dwc2_readl(hsotg, PCGCTL); - - /* Restart the Phy Clock */ - pcgcctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgcctl, PCGCTL); + if (hsotg->lx_state == DWC2_L2) { + if (hsotg->in_ppd) { + ret = dwc2_exit_partial_power_down(hsotg, 1, + true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + } /* * If we've got this quirk then the PHY is stuck upon -- 2.25.1
[PATCH v3 03/14] usb: dwc2: Update enter and exit partial power down functions
These are wrapper functions which are calling device or host enter/exit partial power down functions. This change is done because we need to separate device and host partial power down functions as the programming flow has a lot of difference between host and device. With this update during partial power down exit driver relies on backup value of "GOTGCTL_CURMODE_HOST" to determine the mode of core before entering to PPD. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.c | 113 ++- drivers/usb/dwc2/core.h | 3 +- drivers/usb/dwc2/core_intr.c | 21 --- drivers/usb/dwc2/gadget.c| 20 --- drivers/usb/dwc2/hcd.c | 2 +- drivers/usb/dwc2/hw.h| 1 + 6 files changed, 45 insertions(+), 115 deletions(-) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index fec17a2d2447..cb65f7f60573 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -131,54 +131,26 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down. * * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by Reset. * @restore: Controller registers need to be restored */ -int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) +int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, int rem_wakeup, +bool restore) { - u32 pcgcctl; - int ret = 0; - - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) - return -ENOTSUPP; - - pcgcctl = dwc2_readl(hsotg, PCGCTL); - pcgcctl &= ~PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgcctl, PCGCTL); - - pcgcctl = dwc2_readl(hsotg, PCGCTL); - pcgcctl &= ~PCGCTL_PWRCLMP; - dwc2_writel(hsotg, pcgcctl, PCGCTL); - - pcgcctl = dwc2_readl(hsotg, PCGCTL); - pcgcctl &= ~PCGCTL_RSTPDWNMODULE; - dwc2_writel(hsotg, pcgcctl, PCGCTL); + struct dwc2_gregs_backup *gr; - udelay(100); - if (restore) { - ret = dwc2_restore_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore registers\n", - __func__); - return ret; - } - if (dwc2_is_host_mode(hsotg)) { - ret = dwc2_restore_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore host registers\n", - __func__); - return ret; - } - } else { - ret = dwc2_restore_device_registers(hsotg, 0); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore device registers\n", - __func__); - return ret; - } - } - } + gr = >gr_backup; - return ret; + /* +* Restore host or device regisers with the same mode core enterted +* to partial power down by checking "GOTGCTL_CURMODE_HOST" backup +* value of the "gotgctl" register. +*/ + if (gr->gotgctl & GOTGCTL_CURMODE_HOST) + return dwc2_host_exit_partial_power_down(hsotg, rem_wakeup, +restore); + else + return dwc2_gadget_exit_partial_power_down(hsotg, restore); } /** @@ -188,57 +160,10 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) */ int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg) { - u32 pcgcctl; - int ret = 0; - - if (!hsotg->params.power_down) - return -ENOTSUPP; - - /* Backup all registers */ - ret = dwc2_backup_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup global registers\n", - __func__); - return ret; - } - - if (dwc2_is_host_mode(hsotg)) { - ret = dwc2_backup_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup host registers\n", - __func__); - return ret; - } - } else { - ret = dwc2_backup_device_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup device registers\n", - __func__); - return ret; -
[PATCH v3 02/14] usb: dwc2: Add host partial power down functions
For host mode Partial Power Down entering and exiting separate functions are needed to implement the logic. Earlier the logic was implemented in one function. Which was confusing the readability. Also both host and device implementations were in the same function. - Added host partial power down functions which must be called by dwc2_enter_partial_power_down()/dwc2_exit_partial_power_down() functions. Added function names: dwc2_host_enter_partial_power_down() dwc2_host_exit_partial_power_down() NOTE: There is a checkpatch "CHECK" warning on "udelay(100)". The delay is needed to properly exit gadget Partial Power Down A delay less than 100 doesn't work. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.h | 8 ++ drivers/usb/dwc2/hcd.c | 160 2 files changed, 168 insertions(+) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index ed54d834138d..1a97df8bf5cb 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1474,6 +1474,9 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset); +int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg); +int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, + int rem_wakeup, bool restore); bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2); static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) { schedule_work(>phy_reset_work); } @@ -1500,6 +1503,11 @@ static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset) { return 0; } +static inline int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, + int rem_wakeup, bool restore) +{ return 0; } static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) { return false; } static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 1a9789ec5847..35e617be4bc3 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -5607,3 +5607,163 @@ bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) /* No reason to keep the PHY powered, so allow poweroff */ return true; } + +/** + * dwc2_host_enter_partial_power_down() - Put controller in partial + * power down. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return: non-zero if failed to enter host partial power down. + * + * This function is for entering Host mode partial power down. + */ +int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg) +{ + u32 pcgcctl; + u32 hprt0; + int ret = 0; + + dev_dbg(hsotg->dev, "Entering host partial power down started.\n"); + + /* Put this port in suspend mode. */ + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_SUSP; + dwc2_writel(hsotg, hprt0, HPRT0); + udelay(5); + + /* Wait for the HPRT0.PrtSusp register field to be set */ + if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 3000)) + dev_warn(hsotg->dev, "Suspend wasn't generated\n"); + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + + /* +* Clear any pending interrupts since dwc2 will not be able to +* clear them after entering partial_power_down. +*/ + dwc2_writel(hsotg, 0x, GINTSTS); + + /* Put the controller in low power state */ + pcgcctl = dwc2_readl(hsotg, PCGCTL); + + pcgcctl |= PCGCTL_PWRCLMP; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(5); + + pcgcctl |= PCGCTL_RSTPDWNMODULE; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(5); + + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + + /* Set in_ppd flag to 1 as here core enters suspend. */ + hsotg->in_ppd = 1; + hsotg->lx_state = DWC2_L2; + hsotg->bus_suspended = true; + + dev_dbg(hsotg->dev, "Entering host partial power down completed.\n"); + +
[PATCH v3 01/14] usb: dwc2: Add device partial power down functions
For device mode Partial Power Down entering and exiting separate functions are needed to implement the logic. Earlier the logic was implemented in one function. Which was confusing the readability. Also both host and device implementations were in the same function. - Added device partial power down functions which must be called by dwc2_enter_partial_power_down()/dwc2_exit_partial_power_down() functions. - Added "in_ppd" flag in "dwc2_hsotg" struct to indicate the core state after entering into partial power down mode. Added function names: dwc2_gadget_enter_partial_power_down() dwc2_gadget_exit_partial_power_down() NOTE: There is a checkpatch "CHECK" warning on "udelay(100)". The delay is needed to properly exit gadget Partial Power Down A delay less than 100 doesn't work. Signed-off-by: Artur Petrosyan Acked-by: Minas Harutyunyan --- drivers/usb/dwc2/core.h | 10 +++ drivers/usb/dwc2/gadget.c | 128 ++ 2 files changed, 138 insertions(+) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index d0ebe721fb98..ed54d834138d 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -865,6 +865,7 @@ struct dwc2_hregs_backup { * @gadget_enabled:Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled: Status of low-level hardware resources. * @hibernated:True if core is hibernated + * @in_ppd:True if core is partial power down mode. * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a * remote wakeup. * @phy_off_for_suspend: Status of whether we turned the PHY off at suspend. @@ -1060,6 +1061,7 @@ struct dwc2_hsotg { unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; unsigned int hibernated:1; + unsigned int in_ppd:1; unsigned int reset_phy_on_wake:1; unsigned int need_phy_for_wake:1; unsigned int phy_off_for_suspend:1; @@ -1409,6 +1411,9 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup); int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg); int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset); +int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg); +int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, + bool restore); int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); @@ -1442,6 +1447,11 @@ static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) static inline int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset) { return 0; } +static inline int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, + bool restore) +{ return 0; } static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index ad4c94366dad..98a2a63c67ae 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -5351,3 +5351,131 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, return ret; } + +/** + * dwc2_gadget_enter_partial_power_down() - Put controller in partial + * power down. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return: non-zero if failed to enter device partial power down. + * + * This function is for entering device mode partial power down. + */ +int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg) +{ + u32 pcgcctl; + int ret = 0; + + dev_dbg(hsotg->dev, "Entering device partial power down started.\n"); + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + + /* +* Clear any pending interrupts since dwc2 will not be able to +* clear them after entering partial_power_down. +*/ + dwc2_writel(hsotg, 0x, GINTSTS); + + /* Put the controller in low power state */ + pcgcctl = dwc2_readl(hsotg, PCGCTL); + + pcgcctl |
[PATCH v3 00/14] usb: dwc2: Fix Partial Power down issues.
This patch set fixes and improves the Partial Power Down mode for dwc2 core. It adds support for the following cases 1. Entering and exiting partial power down when a port is suspended, resumed, port reset is asserted. 2. Exiting the partial power down mode before removing driver. 3. Exiting partial power down in wakeup detected interrupt handler. 4. Exiting from partial power down mode when connector ID. status changes to "connId B It updates and fixes the implementation of dwc2 entering and exiting partial power down mode when the system (PC) is suspended. The patch set also improves the implementation of function handlers for entering and exiting host or device partial power down. NOTE: This is the second patch set in the power saving mode fixes series. This patch set is part of multiple series and is continuation of the "usb: dwc2: Fix and improve power saving modes" patch set. (Patch set link: https://marc.info/?l=linux-usb=160379622403975=2). The patches that were included in the "usb: dwc2: Fix and improve power saving modes" which was submitted earlier was too large and needed to be split up into smaller patch sets. Changes since V2: No changes in the patches or the source code. Assuming that the issue due to which the patches are not reaching to vger.kernel.org is a comma in the end of To: or Cc: lists removed commas in the end of those lists in each email of patches. Artur Petrosyan (14): usb: dwc2: Add device partial power down functions usb: dwc2: Add host partial power down functions usb: dwc2: Update enter and exit partial power down functions usb: dwc2: Add partial power down exit flow in wakeup intr. usb: dwc2: Update port suspend/resume function definitions. usb: dwc2: Add enter partial power down when port is suspended usb: dwc2: Add exit partial power down when port is resumed usb: dwc2: Add exit partial power down when port reset is asserted usb: dwc2: Add part. power down exit from dwc2_conn_id_status_change(). usb: dwc2: Allow exit partial power down in urb enqueue usb: dwc2: Fix session request interrupt handler usb: dwc2: Update partial power down entering by system suspend usb: dwc2: Fix partial power down exiting by system resume usb: dwc2: Add exit partial power down before removing driver drivers/usb/dwc2/core.c | 113 ++--- drivers/usb/dwc2/core.h | 27 ++- drivers/usb/dwc2/core_intr.c | 46 ++-- drivers/usb/dwc2/gadget.c| 148 ++- drivers/usb/dwc2/hcd.c | 458 +-- drivers/usb/dwc2/hw.h| 1 + drivers/usb/dwc2/platform.c | 11 +- 7 files changed, 558 insertions(+), 246 deletions(-) base-commit: e9fcb07704fcef6fa6d0333fd2b3a62442eaf45b -- 2.25.1
Re: [PATCH v2 00/14] usb: dwc2: Fix Partial Power down issues.
Hi Greg, On 4/8/2021 11:28, Artur Petrosyan wrote: > This patch set fixes and improves the Partial Power Down mode for > dwc2 core. > It adds support for the following cases > 1. Entering and exiting partial power down when a port is > suspended, resumed, port reset is asserted. > 2. Exiting the partial power down mode before removing driver. > 3. Exiting partial power down in wakeup detected interrupt handler. > 4. Exiting from partial power down mode when connector ID. > status changes to "connId B > > It updates and fixes the implementation of dwc2 entering and > exiting partial power down mode when the system (PC) is suspended. > > The patch set also improves the implementation of function handlers > for entering and exiting host or device partial power down. > > NOTE: This is the second patch set in the power saving mode fixes > series. > This patch set is part of multiple series and is continuation > of the "usb: dwc2: Fix and improve power saving modes" patch set. > (Patch set link: https://marc.info/?l=linux-usb=160379622403975=2). > The patches that were included in the "usb: dwc2: > Fix and improve power saving modes" which was submitted > earlier was too large and needed to be split up into > smaller patch sets. > > Changes since V1: > No changes in the patches or the source code. > Sending the second version of the patch set because the first version > was not received by vger.kernel.org. > > > > Artur Petrosyan (14): >usb: dwc2: Add device partial power down functions >usb: dwc2: Add host partial power down functions >usb: dwc2: Update enter and exit partial power down functions >usb: dwc2: Add partial power down exit flow in wakeup intr. >usb: dwc2: Update port suspend/resume function definitions. >usb: dwc2: Add enter partial power down when port is suspended >usb: dwc2: Add exit partial power down when port is resumed >usb: dwc2: Add exit partial power down when port reset is asserted >usb: dwc2: Add part. power down exit from > dwc2_conn_id_status_change(). >usb: dwc2: Allow exit partial power down in urb enqueue >usb: dwc2: Fix session request interrupt handler >usb: dwc2: Update partial power down entering by system suspend >usb: dwc2: Fix partial power down exiting by system resume >usb: dwc2: Add exit partial power down before removing driver > > drivers/usb/dwc2/core.c | 113 ++--- > drivers/usb/dwc2/core.h | 27 ++- > drivers/usb/dwc2/core_intr.c | 46 ++-- > drivers/usb/dwc2/gadget.c| 148 ++- > drivers/usb/dwc2/hcd.c | 458 +-- > drivers/usb/dwc2/hw.h| 1 + > drivers/usb/dwc2/platform.c | 11 +- > 7 files changed, 558 insertions(+), 246 deletions(-) > > > base-commit: e9fcb07704fcef6fa6d0333fd2b3a62442eaf45b > Re sending as a "v2" did not work :(. The patches are not in lore again. Could the issue be with a comma in the end of To: or Cc: list? Let me remove the comma in the end of those lists and try sending as "v3". Regards, Artur
[PATCH v2 13/14] usb: dwc2: Fix partial power down exiting by system resume
Fixes the implementation of exiting from partial power down power saving mode when PC is resumed. Added port connection status checking which prevents exiting from Partial Power Down mode from _dwc2_hcd_resume() if not in Partial Power Down mode. Rearranged the implementation to get rid of many "if" statements. NOTE: Switch case statement is used for hibernation partial power down and clock gating mode determination. In this patch only Partial Power Down is implemented the Hibernation and clock gating implementations are planned to be added. Cc: Fixes: 6f6d70597c15 ("usb: dwc2: bus suspend/resume for hosts with DWC2_POWER_DOWN_PARAM_NONE") Signed-off-by: Artur Petrosyan --- Changes in v2: - None drivers/usb/dwc2/hcd.c | 90 +- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 34030bafdff4..f096006df96f 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4427,7 +4427,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; - u32 pcgctl; + u32 hprt0; int ret = 0; spin_lock_irqsave(>lock, flags); @@ -4438,11 +4438,40 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) if (hsotg->lx_state != DWC2_L2) goto unlock; - if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) { + hprt0 = dwc2_read_hprt0(hsotg); + + /* +* Added port connection status checking which prevents exiting from +* Partial Power Down mode from _dwc2_hcd_resume() if not in Partial +* Power Down mode. +*/ + if (hprt0 & HPRT0_CONNSTS) { + hsotg->lx_state = DWC2_L0; + goto unlock; + } + + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: + ret = dwc2_exit_partial_power_down(hsotg, 0, true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + /* +* Set HW accessible bit before powering on the controller +* since an interrupt may rise. +*/ + set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + case DWC2_POWER_DOWN_PARAM_NONE: + default: hsotg->lx_state = DWC2_L0; goto unlock; } + /* Change Root port status, as port status change occurred after resume.*/ + hsotg->flags.b.port_suspend_change = 1; + /* * Enable power if not already done. * This must not be spinlocked since duration @@ -4454,52 +4483,25 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(>lock, flags); } - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - /* -* Set HW accessible bit before powering on the controller -* since an interrupt may rise. -*/ - set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); - - - /* Exit partial_power_down */ - ret = dwc2_exit_partial_power_down(hsotg, 0, true); - if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit partial_power_down failed\n"); - } else { - pcgctl = readl(hsotg->regs + PCGCTL); - pcgctl &= ~PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); - } - - hsotg->lx_state = DWC2_L0; - + /* Enable external vbus supply after resuming the port. */ spin_unlock_irqrestore(>lock, flags); + dwc2_vbus_supply_init(hsotg); - if (hsotg->bus_suspended) { - spin_lock_irqsave(>lock, flags); - hsotg->flags.b.port_suspend_change = 1; - spin_unlock_irqrestore(>lock, flags); - dwc2_port_resume(hsotg); - } else { - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - dwc2_vbus_supply_init(hsotg); - - /* Wait for controller to correctly update D+/D- level */ - usleep_range(3000, 5000); - } + /* Wait for controller to correctly update D+/D- level */ + usleep_range(3000, 5000); + spin_lock_irqsave(>lock, flags); - /* -* Clear Port Enable and Port Status changes. -* Enable Port Power. -*/ - dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET | - HPRT0_ENACHG, HPRT0); - /* Wait for controller to detect Por
[PATCH v2 00/14] usb: dwc2: Fix Partial Power down issues.
This patch set fixes and improves the Partial Power Down mode for dwc2 core. It adds support for the following cases 1. Entering and exiting partial power down when a port is suspended, resumed, port reset is asserted. 2. Exiting the partial power down mode before removing driver. 3. Exiting partial power down in wakeup detected interrupt handler. 4. Exiting from partial power down mode when connector ID. status changes to "connId B It updates and fixes the implementation of dwc2 entering and exiting partial power down mode when the system (PC) is suspended. The patch set also improves the implementation of function handlers for entering and exiting host or device partial power down. NOTE: This is the second patch set in the power saving mode fixes series. This patch set is part of multiple series and is continuation of the "usb: dwc2: Fix and improve power saving modes" patch set. (Patch set link: https://marc.info/?l=linux-usb=160379622403975=2). The patches that were included in the "usb: dwc2: Fix and improve power saving modes" which was submitted earlier was too large and needed to be split up into smaller patch sets. Changes since V1: No changes in the patches or the source code. Sending the second version of the patch set because the first version was not received by vger.kernel.org. Artur Petrosyan (14): usb: dwc2: Add device partial power down functions usb: dwc2: Add host partial power down functions usb: dwc2: Update enter and exit partial power down functions usb: dwc2: Add partial power down exit flow in wakeup intr. usb: dwc2: Update port suspend/resume function definitions. usb: dwc2: Add enter partial power down when port is suspended usb: dwc2: Add exit partial power down when port is resumed usb: dwc2: Add exit partial power down when port reset is asserted usb: dwc2: Add part. power down exit from dwc2_conn_id_status_change(). usb: dwc2: Allow exit partial power down in urb enqueue usb: dwc2: Fix session request interrupt handler usb: dwc2: Update partial power down entering by system suspend usb: dwc2: Fix partial power down exiting by system resume usb: dwc2: Add exit partial power down before removing driver drivers/usb/dwc2/core.c | 113 ++--- drivers/usb/dwc2/core.h | 27 ++- drivers/usb/dwc2/core_intr.c | 46 ++-- drivers/usb/dwc2/gadget.c| 148 ++- drivers/usb/dwc2/hcd.c | 458 +-- drivers/usb/dwc2/hw.h| 1 + drivers/usb/dwc2/platform.c | 11 +- 7 files changed, 558 insertions(+), 246 deletions(-) base-commit: e9fcb07704fcef6fa6d0333fd2b3a62442eaf45b -- 2.25.1
Re: [PATCH 00/14] usb: dwc2: Fix Partial Power down issues.
Hi Greg, On 4/7/2021 14:00, Artur Petrosyan wrote: > This patch set fixes and improves the Partial Power Down mode for > dwc2 core. > It adds support for the following cases > 1. Entering and exiting partial power down when a port is > suspended, resumed, port reset is asserted. > 2. Exiting the partial power down mode before removing driver. > 3. Exiting partial power down in wakeup detected interrupt handler. > 4. Exiting from partial power down mode when connector ID. > status changes to "connId B > > It updates and fixes the implementation of dwc2 entering and > exiting partial power down mode when the system (PC) is suspended. > > The patch set also improves the implementation of function handlers > for entering and exiting host or device partial power down. > > NOTE: This is the second patch set in the power saving mode fixes > series. > This patch set is part of multiple series and is continuation > of the "usb: dwc2: Fix and improve power saving modes" patch set. > (Patch set link: https://marc.info/?l=linux-usb=160379622403975=2). > The patches that were included in the "usb: dwc2: > Fix and improve power saving modes" which was submitted > earlier was too large and needed to be split up into > smaller patch sets. > > > Artur Petrosyan (14): >usb: dwc2: Add device partial power down functions >usb: dwc2: Add host partial power down functions >usb: dwc2: Update enter and exit partial power down functions >usb: dwc2: Add partial power down exit flow in wakeup intr. >usb: dwc2: Update port suspend/resume function definitions. >usb: dwc2: Add enter partial power down when port is suspended >usb: dwc2: Add exit partial power down when port is resumed >usb: dwc2: Add exit partial power down when port reset is asserted >usb: dwc2: Add part. power down exit from > dwc2_conn_id_status_change(). >usb: dwc2: Allow exit partial power down in urb enqueue >usb: dwc2: Fix session request interrupt handler >usb: dwc2: Update partial power down entering by system suspend >usb: dwc2: Fix partial power down exiting by system resume >usb: dwc2: Add exit partial power down before removing driver > > drivers/usb/dwc2/core.c | 113 ++--- > drivers/usb/dwc2/core.h | 27 ++- > drivers/usb/dwc2/core_intr.c | 46 ++-- > drivers/usb/dwc2/gadget.c| 148 ++- > drivers/usb/dwc2/hcd.c | 458 +-- > drivers/usb/dwc2/hw.h| 1 + > drivers/usb/dwc2/platform.c | 11 +- > 7 files changed, 558 insertions(+), 246 deletions(-) > > > base-commit: e9fcb07704fcef6fa6d0333fd2b3a62442eaf45b > I have submitted this patch set yesterday. It contains 14 patches. But only 2 of those patches were received by LKML only the cover letter and the 13th patch. (https://lore.kernel.org/linux-usb/cover.1617782102.git.arthur.petros...@synopsys.com/T/#t) I checked here at Synopsys, Minas did receive all the patches as his email is in To list. Could this be an issue of vger.kernel.org mailing server? Because I checked every local possibility that could result to such behavior. The patch 13 which was received by LKML has the similar content as the other patches. The mailing tool that was used is ssmtp, checked all the configurations everything is fine. Could you please suggest what should I do in this situation? Regards, Artur
[PATCH 13/14] usb: dwc2: Fix partial power down exiting by system resume
Fixes the implementation of exiting from partial power down power saving mode when PC is resumed. Added port connection status checking which prevents exiting from Partial Power Down mode from _dwc2_hcd_resume() if not in Partial Power Down mode. Rearranged the implementation to get rid of many "if" statements. NOTE: Switch case statement is used for hibernation partial power down and clock gating mode determination. In this patch only Partial Power Down is implemented the Hibernation and clock gating implementations are planned to be added. Cc: Fixes: 6f6d70597c15 ("usb: dwc2: bus suspend/resume for hosts with DWC2_POWER_DOWN_PARAM_NONE") Signed-off-by: Artur Petrosyan --- drivers/usb/dwc2/hcd.c | 90 +- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 34030bafdff4..f096006df96f 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4427,7 +4427,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; - u32 pcgctl; + u32 hprt0; int ret = 0; spin_lock_irqsave(>lock, flags); @@ -4438,11 +4438,40 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) if (hsotg->lx_state != DWC2_L2) goto unlock; - if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) { + hprt0 = dwc2_read_hprt0(hsotg); + + /* +* Added port connection status checking which prevents exiting from +* Partial Power Down mode from _dwc2_hcd_resume() if not in Partial +* Power Down mode. +*/ + if (hprt0 & HPRT0_CONNSTS) { + hsotg->lx_state = DWC2_L0; + goto unlock; + } + + switch (hsotg->params.power_down) { + case DWC2_POWER_DOWN_PARAM_PARTIAL: + ret = dwc2_exit_partial_power_down(hsotg, 0, true); + if (ret) + dev_err(hsotg->dev, + "exit partial_power_down failed\n"); + /* +* Set HW accessible bit before powering on the controller +* since an interrupt may rise. +*/ + set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + case DWC2_POWER_DOWN_PARAM_NONE: + default: hsotg->lx_state = DWC2_L0; goto unlock; } + /* Change Root port status, as port status change occurred after resume.*/ + hsotg->flags.b.port_suspend_change = 1; + /* * Enable power if not already done. * This must not be spinlocked since duration @@ -4454,52 +4483,25 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(>lock, flags); } - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - /* -* Set HW accessible bit before powering on the controller -* since an interrupt may rise. -*/ - set_bit(HCD_FLAG_HW_ACCESSIBLE, >flags); - - - /* Exit partial_power_down */ - ret = dwc2_exit_partial_power_down(hsotg, 0, true); - if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit partial_power_down failed\n"); - } else { - pcgctl = readl(hsotg->regs + PCGCTL); - pcgctl &= ~PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); - } - - hsotg->lx_state = DWC2_L0; - + /* Enable external vbus supply after resuming the port. */ spin_unlock_irqrestore(>lock, flags); + dwc2_vbus_supply_init(hsotg); - if (hsotg->bus_suspended) { - spin_lock_irqsave(>lock, flags); - hsotg->flags.b.port_suspend_change = 1; - spin_unlock_irqrestore(>lock, flags); - dwc2_port_resume(hsotg); - } else { - if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { - dwc2_vbus_supply_init(hsotg); - - /* Wait for controller to correctly update D+/D- level */ - usleep_range(3000, 5000); - } + /* Wait for controller to correctly update D+/D- level */ + usleep_range(3000, 5000); + spin_lock_irqsave(>lock, flags); - /* -* Clear Port Enable and Port Status changes. -* Enable Port Power. -*/ - dwc2_writel(hsotg, HPRT0_PWR | HPRT0_CONNDET | - HPRT0_ENACHG, HPRT0); - /* Wait for controller to detect Port Connect */ - u
[PATCH 00/14] usb: dwc2: Fix Partial Power down issues.
This patch set fixes and improves the Partial Power Down mode for dwc2 core. It adds support for the following cases 1. Entering and exiting partial power down when a port is suspended, resumed, port reset is asserted. 2. Exiting the partial power down mode before removing driver. 3. Exiting partial power down in wakeup detected interrupt handler. 4. Exiting from partial power down mode when connector ID. status changes to "connId B It updates and fixes the implementation of dwc2 entering and exiting partial power down mode when the system (PC) is suspended. The patch set also improves the implementation of function handlers for entering and exiting host or device partial power down. NOTE: This is the second patch set in the power saving mode fixes series. This patch set is part of multiple series and is continuation of the "usb: dwc2: Fix and improve power saving modes" patch set. (Patch set link: https://marc.info/?l=linux-usb=160379622403975=2). The patches that were included in the "usb: dwc2: Fix and improve power saving modes" which was submitted earlier was too large and needed to be split up into smaller patch sets. Artur Petrosyan (14): usb: dwc2: Add device partial power down functions usb: dwc2: Add host partial power down functions usb: dwc2: Update enter and exit partial power down functions usb: dwc2: Add partial power down exit flow in wakeup intr. usb: dwc2: Update port suspend/resume function definitions. usb: dwc2: Add enter partial power down when port is suspended usb: dwc2: Add exit partial power down when port is resumed usb: dwc2: Add exit partial power down when port reset is asserted usb: dwc2: Add part. power down exit from dwc2_conn_id_status_change(). usb: dwc2: Allow exit partial power down in urb enqueue usb: dwc2: Fix session request interrupt handler usb: dwc2: Update partial power down entering by system suspend usb: dwc2: Fix partial power down exiting by system resume usb: dwc2: Add exit partial power down before removing driver drivers/usb/dwc2/core.c | 113 ++--- drivers/usb/dwc2/core.h | 27 ++- drivers/usb/dwc2/core_intr.c | 46 ++-- drivers/usb/dwc2/gadget.c| 148 ++- drivers/usb/dwc2/hcd.c | 458 +-- drivers/usb/dwc2/hw.h| 1 + drivers/usb/dwc2/platform.c | 11 +- 7 files changed, 558 insertions(+), 246 deletions(-) base-commit: e9fcb07704fcef6fa6d0333fd2b3a62442eaf45b -- 2.25.1
Re: [RFC PATCH] usb: dwc2: Try usb_get_phy_by_phandle instead of usb_get_phy
Hi Jules, On 12/26/2020 17:45, Jules Maselbas wrote: > Hi Artur, > > On Fri, Dec 25, 2020 at 11:41:04AM +0000, Artur Petrosyan wrote: >>> @@ -251,7 +251,12 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg >>> *hsotg) >>> } >>> >>> if (!hsotg->phy) { >>> - hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2); >>> + if (hsotg->dev->of_node) >>> + i = of_property_match_string(hsotg->dev->of_node, >>> "phy-names", "usb2-phy"); >> >> According the device tree you have provided the value of "i" will always >> be "0". > Yes > >>> + if (hsotg->dev->of_node && i >= 0) >>> + hsotg->uphy = devm_usb_get_phy_by_phandle(hsotg->dev, >>> "phys", i); >> >> Why do you use the value of "i" while in "<_phy0>" you have only one >> phy. If you had several phy-names and the value of "i" gets more than 0, >> then based on your usb_phy0 "devm_usb_get_phy_by_phandle" function will >> return error. So, maybe it would be more correct (based on your device >> tree), to use below command >> hsotg->uphy = devm_usb_get_phy_by_phandle(hsotg->dev, "phys", 0); > Yes I could use 0 instead of i, but I would like this to work not only > for my case where the "usb2-phy" phandle comes first. > > I've tried to follow what's done in phy-core.c, as done by the function > phy_get. Where it first call "of_property_match_string" and then get the > phy with the matched index. > > I don't see how, in my case, the function "devm_usb_get_phy_by_phandle" can > be called with i greater than 0, and returning an error. I have noticed that there might be no need to check for "i >= 0" at all because "of_parse_phandle()" function has a check for index to not be less then 0. > > Best, > Jules > Regards, Artur
Re: [RFC PATCH] usb: dwc2: Try usb_get_phy_by_phandle instead of usb_get_phy
Hi Jules, On 12/16/2020 20:59, Jules Maselbas wrote: > On probe the dwc2 driver tries two path to get an usb phy, first calling > devm_phy_get() and secondly with devm_usb_get_phy(). > > However the current implementation of devm_phy_get() never return a valid > phy for usb-nop-xceiv. And the current implementation of devm_usb_get_phy > only check for phy that's has already been registered. > > During boot, I see the dwc2 driver being probed before the usb-nop-xceiv > probe, this means that during the dwc2 probe the function devm_usb_get_phy > never finds the a phy (because it hasn't been registered yet) but never > cause the dwc2 probe to defer. > > I tried to follow what is done by dwc3_core_get_phy(): if the current > device has an of_node then try to get the usb_phy by phandle instead of > using devm_usb_get_phy(). This way when the probe order is not good the > devm_usb_get_phy_by_phandle() function will return -EPROBE_DEFER. > > Signed-off-by: Jules Maselbas > --- 8< --- > > A snippet of the device-tree source I am using: > { > phys = <_phy0>; > phy-names = "usb2-phy"; > }; > _phy0 { > #phy-cells = <0>; > compatible = "usb-nop-xceiv"; > reset-gpios = < 0 GPIO_ACTIVE_LOW>; > }; > --- > drivers/usb/dwc2/platform.c | 7 ++- > 1 file changed, 6 insertions(+), 1 deletion(-) > > diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c > index db9fd4bd1a38..b58ce996add7 100644 > --- a/drivers/usb/dwc2/platform.c > +++ b/drivers/usb/dwc2/platform.c > @@ -251,7 +251,12 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg > *hsotg) > } > > if (!hsotg->phy) { > - hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2); > + if (hsotg->dev->of_node) > + i = of_property_match_string(hsotg->dev->of_node, > "phy-names", "usb2-phy"); According the device tree you have provided the value of "i" will always be "0". > + if (hsotg->dev->of_node && i >= 0) > + hsotg->uphy = devm_usb_get_phy_by_phandle(hsotg->dev, > "phys", i); Why do you use the value of "i" while in "<_phy0>" you have only one phy. If you had several phy-names and the value of "i" gets more than 0, then based on your usb_phy0 "devm_usb_get_phy_by_phandle" function will return error. So, maybe it would be more correct (based on your device tree), to use below command hsotg->uphy = devm_usb_get_phy_by_phandle(hsotg->dev, "phys", 0); > + else > + hsotg->uphy = devm_usb_get_phy(hsotg->dev, > USB_PHY_TYPE_USB2); > if (IS_ERR(hsotg->uphy)) { > ret = PTR_ERR(hsotg->uphy); > switch (ret) { > Regards, Artur
Re: [PATCH] usb: dwc2: Remove redundant null check before clk_prepare_enable/clk_disable_unprepare
On 12/4/2020 12:36, Xu Wang wrote: > Because clk_prepare_enable() and clk_disable_unprepare() already checked > NULL clock parameter, so the additional checks are unnecessary, just > remove them. > > Signed-off-by: Xu Wang Reviewed-by: Artur Petrosyan > --- > drivers/usb/dwc2/platform.c | 11 --- > 1 file changed, 4 insertions(+), 7 deletions(-) > > diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c > index 5f18acac7406..ba2b491c7f82 100644 > --- a/drivers/usb/dwc2/platform.c > +++ b/drivers/usb/dwc2/platform.c > @@ -143,11 +143,9 @@ static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg > *hsotg) > if (ret) > return ret; > > - if (hsotg->clk) { > - ret = clk_prepare_enable(hsotg->clk); > - if (ret) > - return ret; > - } > + ret = clk_prepare_enable(hsotg->clk); > + if (ret) > + return ret; > > if (hsotg->uphy) { > ret = usb_phy_init(hsotg->uphy); > @@ -195,8 +193,7 @@ static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg > *hsotg) > if (ret) > return ret; > > - if (hsotg->clk) > - clk_disable_unprepare(hsotg->clk); > + clk_disable_unprepare(hsotg->clk); > > return 0; > } >
Re: [PATCH v2 0/5] USB: dwc2: Allow wakeup from suspend; enable for rk3288-veyron
Hi Doug, On 4/19/2019 15:43, Artur Petrosyan wrote: > Hi, > > On 4/18/2019 19:55, Doug Anderson wrote: >> Hi, >> >> On Thu, Apr 18, 2019 at 5:41 AM Minas Harutyunyan >> wrote: >>> Did you consider/reviewed patch series from Artur Petrosyan "[PATCH >>> 00/14] usb: dwc2: Fix and improve power saving modes" (submitted on >>> April 12) which fixing partial power down and hibernation flows for both >>> modes: host and device? >>> I suspect that this both patch series can be in conflict. >> >> No, I wasn't aware of them. I'd like to try them out, but it looks >> like it's currently impossible because they're not archived anywhere >> that I can find. >> >> 1. LKML wasn't copied, so I can't find them on lore.kernel.org. It is >> suggested to CC LKML on all patches. >> >> 2. The Linux USB patchwork only has the cover letter plus the first 6 >> patches. See >> <https://urldefense.proofpoint.com/v2/url?u=https-3A__patchwork.kernel.org_cover_10898333_=DwIBaQ=DPL6_X_6JkXFx7AXWqB0tg=9hPBFKCJ_nBjJhGVrrlYOeOQjP_HlVzYqrC_D7niMJI=rJbofHXqjP3sUBgEikaZ89fRWtsquvhFUkhSPguCiWw=_CrI0uolquXIYC5SuoZ1vBs7BM19VfCceI96qZm9kAY=> >> >> 3. Searching my own archives I only see the cover letter plus the >> first 6 patches. >> >> Maybe you have some other pointer to how I can retrieve them? I guess >> I could try the first 6 patches without the later 8 but I'd rather get >> the whole set... >> >> >> -Doug >> > > > I have resend the patch set. You can find it with cover letter > "[PATCH v1 00/14] usb: dwc2: Fix and improve power saving modes." > > Let me know if you need any help. > I am so sorry to inform you that the patches have not reached to LKML. Looks like there is a problem with the kernel mailing list servers or our local servers. Will try to fix this as soon as possible and let you know about it. -- Regards, Artur
Re: [PATCH] usb: dwc2: Disable power down feature on Samsung SoCs
Hi Marek, On 12/6/2018 18:20, Marek Szyprowski wrote: > Hi Artur, > > On 2018-12-04 15:28, Artur Petrosyan wrote: >> On 11/20/2018 19:38, Marek Szyprowski wrote: >>> Power down feature of DWC2 module integrated in Samsung SoCs doesn't work >>> properly or needs some additional handling in PHY or SoC glue layer, so >>> disable it for now. Without disabling power down, DWC2 causes random memory >>> trashes and fails enumeration if there is no USB link to host on driver >>> probe. >>> >>> Fixes: 03ea6d6e9e1ff1 ("usb: dwc2: Enable power down") >>> Signed-off-by: Marek Szyprowski >>> --- >>>drivers/usb/dwc2/params.c | 10 +- >>>1 file changed, 9 insertions(+), 1 deletion(-) >>> >>> diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c >>> index 7c1b6938f212..266157ae179a 100644 >>> --- a/drivers/usb/dwc2/params.c >>> +++ b/drivers/usb/dwc2/params.c >>> @@ -71,6 +71,13 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) >>> p->power_down = false; >>>} >>> >>> +static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg) >>> +{ >>> + struct dwc2_core_params *p = >params; >>> + >>> + p->power_down = 0; >>> +} >>> + >>>static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) >>>{ >>> struct dwc2_core_params *p = >params; >>> @@ -151,7 +158,8 @@ const struct of_device_id dwc2_of_match_table[] = { >>> { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params }, >>> { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params }, >>> { .compatible = "snps,dwc2" }, >>> - { .compatible = "samsung,s3c6400-hsotg" }, >>> + { .compatible = "samsung,s3c6400-hsotg", >>> + .data = dwc2_set_s3c6400_params }, >>> { .compatible = "amlogic,meson8-usb", >>> .data = dwc2_set_amlogic_params }, >>> { .compatible = "amlogic,meson8b-usb", >>> >> Could you please provide dmesg logs with verbose Debug enabled >> configuration and the register dump. >> So that we can see what the issue is related to. > > I'm not sure if this helps much. The problem happens if usb cable is not > connected on boot. Then, when I connect and disconnect USB cable, > strange memory trashes happens. From brief tests, the trashing doesn't > happen when debug messages are enabled, so it is related to timings. > IMHO the simplest fix for now is to disable powerdown on Samsung SoCs. > > Here is the initialization related part (booted without usb cable > connected): > > # dmesg | grep dwc > [ 2.261821] dwc2 1248.hsotg: Linked as a consumer to regulator.15 > [ 2.266410] dwc2 1248.hsotg: Linked as a consumer to regulator.12 > [ 2.273996] dwc2 1248.hsotg: dwc2_check_params: Invalid parameter > besl=1 > [ 2.279405] dwc2 1248.hsotg: dwc2_check_params: Invalid parameter > g_np_tx_fifo_size=1024 > [ 2.287882] dwc2 1248.hsotg: NonPeriodic TXFIFO size: 768 > [ 2.287894] dwc2 1248.hsotg: RXFIFO size: 2048 > [ 2.288003] dwc2 1248.hsotg: EPs: 16, dedicated fifos, 7808 > entries in SPRAM > [ 2.296631] dwc2 1248.hsotg: DCFG=0x0820, DCTL=0x, > DIEPMSK= > [ 2.303044] dwc2 1248.hsotg: GAHBCFG=0x, GHWCFG1=0x > [ 2.309437] dwc2 1248.hsotg: GRXFSIZ=0x1f00, GNPTXFSIZ=0x03001f00 > [ 2.316258] dwc2 1248.hsotg: DPTx[1] FSize=768, StAddr=0x2200 > [ 2.322630] dwc2 1248.hsotg: DPTx[2] FSize=768, StAddr=0x2500 > [ 2.329099] dwc2 1248.hsotg: DPTx[3] FSize=768, StAddr=0x2800 > [ 2.335521] dwc2 1248.hsotg: DPTx[4] FSize=768, StAddr=0x2b00 > [ 2.341900] dwc2 1248.hsotg: DPTx[5] FSize=768, StAddr=0x2e00 > [ 2.348366] dwc2 1248.hsotg: DPTx[6] FSize=768, StAddr=0x3100 > [ 2.354802] dwc2 1248.hsotg: DPTx[7] FSize=768, StAddr=0x3400 > [ 2.361169] dwc2 1248.hsotg: DPTx[8] FSize=768, StAddr=0x3700 > [ 2.367637] dwc2 1248.hsotg: DPTx[9] FSize=768, StAddr=0x3a00 > [ 2.374059] dwc2 1248.hsotg: DPTx[10] FSize=768, StAddr=0x3d00 > [ 2.380526] dwc2 1248.hsotg: DPTx[11] FSize=768, StAddr=0x4000 > [ 2.387079] dwc2 1248.hsotg: DPTx[12] FSize=768, StAddr=0x4300 > [ 2.393589] dwc2 1248.hsotg: DPTx[13] FSize=768, StAddr=0x4600 > [ 2.400055] dwc2 1248.hsotg: DPTx[14] FSize=768, StAddr=0x4900 > [ 2.406611] dwc2 1248.hsotg: DPTx[15]
Re: [PATCH] usb: dwc2: Disable power down feature on Samsung SoCs
Hi Marek, On 11/20/2018 19:38, Marek Szyprowski wrote: > Power down feature of DWC2 module integrated in Samsung SoCs doesn't work > properly or needs some additional handling in PHY or SoC glue layer, so > disable it for now. Without disabling power down, DWC2 causes random memory > trashes and fails enumeration if there is no USB link to host on driver > probe. > > Fixes: 03ea6d6e9e1ff1 ("usb: dwc2: Enable power down") > Signed-off-by: Marek Szyprowski > --- > drivers/usb/dwc2/params.c | 10 +- > 1 file changed, 9 insertions(+), 1 deletion(-) > > diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c > index 7c1b6938f212..266157ae179a 100644 > --- a/drivers/usb/dwc2/params.c > +++ b/drivers/usb/dwc2/params.c > @@ -71,6 +71,13 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) > p->power_down = false; > } > > +static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg) > +{ > + struct dwc2_core_params *p = >params; > + > + p->power_down = 0; > +} > + > static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) > { > struct dwc2_core_params *p = >params; > @@ -151,7 +158,8 @@ const struct of_device_id dwc2_of_match_table[] = { > { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params }, > { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params }, > { .compatible = "snps,dwc2" }, > - { .compatible = "samsung,s3c6400-hsotg" }, > + { .compatible = "samsung,s3c6400-hsotg", > + .data = dwc2_set_s3c6400_params }, > { .compatible = "amlogic,meson8-usb", > .data = dwc2_set_amlogic_params }, > { .compatible = "amlogic,meson8b-usb", > Could you please provide dmesg logs with verbose Debug enabled configuration and the register dump. So that we can see what the issue is related to. Regards, Artur
Re: [PATCH] usb: dwc2: Disable power down feature on Samsung SoCs
Hi Marek, On 11/20/2018 19:38, Marek Szyprowski wrote: > Power down feature of DWC2 module integrated in Samsung SoCs doesn't work > properly or needs some additional handling in PHY or SoC glue layer, so > disable it for now. Without disabling power down, DWC2 causes random memory > trashes and fails enumeration if there is no USB link to host on driver > probe. > > Fixes: 03ea6d6e9e1ff1 ("usb: dwc2: Enable power down") > Signed-off-by: Marek Szyprowski > --- > drivers/usb/dwc2/params.c | 10 +- > 1 file changed, 9 insertions(+), 1 deletion(-) > > diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c > index 7c1b6938f212..266157ae179a 100644 > --- a/drivers/usb/dwc2/params.c > +++ b/drivers/usb/dwc2/params.c > @@ -71,6 +71,13 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) > p->power_down = false; > } > > +static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg) > +{ > + struct dwc2_core_params *p = >params; > + > + p->power_down = 0; > +} > + > static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) > { > struct dwc2_core_params *p = >params; > @@ -151,7 +158,8 @@ const struct of_device_id dwc2_of_match_table[] = { > { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params }, > { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params }, > { .compatible = "snps,dwc2" }, > - { .compatible = "samsung,s3c6400-hsotg" }, > + { .compatible = "samsung,s3c6400-hsotg", > + .data = dwc2_set_s3c6400_params }, > { .compatible = "amlogic,meson8-usb", > .data = dwc2_set_amlogic_params }, > { .compatible = "amlogic,meson8b-usb", > Could you please provide dmesg logs with verbose Debug enabled configuration and the register dump. So that we can see what the issue is related to. Regards, Artur
Re: [PATCH] usb: dwc2: Fix HiKey regression caused by power_down feature
Hi John, On 9/25/2018 21:59, John Stultz wrote: > On Tue, Sep 25, 2018 at 3:04 AM, Artur Petrosyan > wrote: >> Just a clarification by this commit "[PATCH] usb: dwc2: Fix HiKey >> regression caused by power_down feature" >> https://urldefense.proofpoint.com/v2/url?u=https-3A__marc.info_-3Fl-3Dlinux-2Dusb-26m-3D152669095513248-26w-3D2=DwIBaQ=DPL6_X_6JkXFx7AXWqB0tg=9hPBFKCJ_nBjJhGVrrlYOeOQjP_HlVzYqrC_D7niMJI=0lMkv7adFVwkzyaUzD6-pUG0iwg4fd6b1-aHQgbqvSI=m8SZvo3J_Za08sMbo-S9EkhoA06YnzEN-SRm-uTPnbg= >> >> the power_down is disabled setting "p->power_down = false;" in >> "dwc2_set_his_params" function. >> >> Could you please clarify that the testes done for those 3 patches were >> done enabling "p->power_down = true;" in "dwc2_set_his_params" function. > > So if I remove the "power_down = true" initialization, USB does not > seem to function. > > If I boot w/ the gadget port removed, the USB host ports do work, but > plugging in the gadget cable results in a bunch of: > dwc2 f72c.usb: Waiting for Host Mode, Mode=Peripheral > messages. > > If I boot w/ the gadget port plugged in, USB gadget mode doesn't seem > to function at all, and when I remove the gadget cable nothing > happens, it doesn't switch to host mode. > > thanks > -john > We would like to buy the HiKey board to perform testes. We found this HiKey LeMaker to have USB 2.0 ports https://www.ebay.com/itm/HiKey-LeMaker-version-2GB-Kirin-620-SoC-8-core-ARM-Cortex-A53-CPU-ARM-Mali-450/263958047308?hash=item3d75202a4c:g:aGsAAOSwOkxbqqot on ebay. Could you please confirm that it is the right board to test the issues you mention. Regards, Artur
Re: [PATCH] usb: dwc2: Fix HiKey regression caused by power_down feature
Hi John, On 9/25/2018 21:59, John Stultz wrote: > On Tue, Sep 25, 2018 at 3:04 AM, Artur Petrosyan > wrote: >> Just a clarification by this commit "[PATCH] usb: dwc2: Fix HiKey >> regression caused by power_down feature" >> https://urldefense.proofpoint.com/v2/url?u=https-3A__marc.info_-3Fl-3Dlinux-2Dusb-26m-3D152669095513248-26w-3D2=DwIBaQ=DPL6_X_6JkXFx7AXWqB0tg=9hPBFKCJ_nBjJhGVrrlYOeOQjP_HlVzYqrC_D7niMJI=0lMkv7adFVwkzyaUzD6-pUG0iwg4fd6b1-aHQgbqvSI=m8SZvo3J_Za08sMbo-S9EkhoA06YnzEN-SRm-uTPnbg= >> >> the power_down is disabled setting "p->power_down = false;" in >> "dwc2_set_his_params" function. >> >> Could you please clarify that the testes done for those 3 patches were >> done enabling "p->power_down = true;" in "dwc2_set_his_params" function. > > So if I remove the "power_down = true" initialization, USB does not > seem to function. > > If I boot w/ the gadget port removed, the USB host ports do work, but > plugging in the gadget cable results in a bunch of: > dwc2 f72c.usb: Waiting for Host Mode, Mode=Peripheral > messages. > > If I boot w/ the gadget port plugged in, USB gadget mode doesn't seem > to function at all, and when I remove the gadget cable nothing > happens, it doesn't switch to host mode. > > thanks > -john > We would like to buy the HiKey board to perform testes. We found this HiKey LeMaker to have USB 2.0 ports https://www.ebay.com/itm/HiKey-LeMaker-version-2GB-Kirin-620-SoC-8-core-ARM-Cortex-A53-CPU-ARM-Mali-450/263958047308?hash=item3d75202a4c:g:aGsAAOSwOkxbqqot on ebay. Could you please confirm that it is the right board to test the issues you mention. Regards, Artur