summaryrefslogtreecommitdiff
path: root/drivers/usb/dwc2
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dwc2')
-rw-r--r--drivers/usb/dwc2/core.c1
-rw-r--r--drivers/usb/dwc2/core.h23
-rw-r--r--drivers/usb/dwc2/gadget.c116
-rw-r--r--drivers/usb/dwc2/hcd.c101
-rw-r--r--drivers/usb/dwc2/hcd_queue.c7
-rw-r--r--drivers/usb/dwc2/platform.c38
6 files changed, 175 insertions, 111 deletions
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 9919ab725d54..c3d24312db0f 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -43,6 +43,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
/* Backup global regs */
gr = &hsotg->gr_backup;
+ gr->gintsts = dwc2_readl(hsotg, GINTSTS);
gr->gotgctl = dwc2_readl(hsotg, GOTGCTL);
gr->gintmsk = dwc2_readl(hsotg, GINTMSK);
gr->gahbcfg = dwc2_readl(hsotg, GAHBCFG);
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 2bd74f3033ed..34127b890b2a 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -667,6 +667,7 @@ struct dwc2_hw_params {
/**
* struct dwc2_gregs_backup - Holds global registers state before
* entering partial power down
+ * @gintsts: Backup of GINTSTS register
* @gotgctl: Backup of GOTGCTL register
* @gintmsk: Backup of GINTMSK register
* @gahbcfg: Backup of GAHBCFG register
@@ -683,6 +684,7 @@ struct dwc2_hw_params {
* @valid: True if registers values backuped.
*/
struct dwc2_gregs_backup {
+ u32 gintsts;
u32 gotgctl;
u32 gintmsk;
u32 gahbcfg;
@@ -1127,6 +1129,9 @@ struct dwc2_hsotg {
#define DWC2_FS_IOT_ID 0x55310000
#define DWC2_HS_IOT_ID 0x55320000
+#define DWC2_RESTORE_DCTL BIT(0)
+#define DWC2_RESTORE_DCFG BIT(1)
+
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
u32 d32;
@@ -1420,7 +1425,7 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
#define dwc2_is_device_enabled(hsotg) (hsotg->enabled)
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags);
int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg);
int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
int rem_wakeup, int reset);
@@ -1435,6 +1440,9 @@ 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);
+int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg);
+int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
+ unsigned int flags);
static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg)
{ hsotg->fifo_map = 0; }
#else
@@ -1459,7 +1467,7 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
- int remote_wakeup)
+ unsigned int flags)
{ return 0; }
static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
{ return 0; }
@@ -1482,6 +1490,11 @@ 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 int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
+ unsigned int flags)
+{ return 0; }
static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) {}
#endif
@@ -1505,6 +1518,8 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
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);
+int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg);
+int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg);
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg)
{ schedule_work(&hsotg->phy_reset_work); }
#else
@@ -1544,6 +1559,10 @@ 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 int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {}
#endif
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index bd4c788f03bc..300ea4969f0c 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -5204,11 +5204,11 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
- * @remote_wakeup: Indicates whether resume is initiated by Device or Host.
+ * @flags: Defines which registers should be restored.
*
* Return: 0 if successful, negative error code otherwise
*/
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags)
{
struct dwc2_dregs_backup *dr;
int i;
@@ -5224,7 +5224,10 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
}
dr->valid = false;
- if (!remote_wakeup)
+ if (flags & DWC2_RESTORE_DCFG)
+ dwc2_writel(hsotg, dr->dcfg, DCFG);
+
+ if (flags & DWC2_RESTORE_DCTL)
dwc2_writel(hsotg, dr->dctl, DCTL);
dwc2_writel(hsotg, dr->daintmsk, DAINTMSK);
@@ -5310,6 +5313,49 @@ void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK));
}
+int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ /* 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;
+ }
+
+ return 0;
+}
+
+int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+ ret = dwc2_restore_device_registers(hsotg, flags);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore device registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
/**
* dwc2_gadget_enter_hibernation() - Put controller in Hibernation.
*
@@ -5327,18 +5373,9 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
/* Change to L2(suspend) state */
hsotg->lx_state = DWC2_L2;
dev_dbg(hsotg->dev, "Start of hibernation completed\n");
- 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__);
+ ret = dwc2_gadget_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
gpwrdn = GPWRDN_PWRDNRSTN;
udelay(10);
@@ -5415,6 +5452,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
u32 gpwrdn;
u32 dctl;
int ret = 0;
+ unsigned int flags = 0;
struct dwc2_gregs_backup *gr;
struct dwc2_dregs_backup *dr;
@@ -5477,6 +5515,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
dctl = dwc2_readl(hsotg, DCTL);
dctl |= DCTL_PWRONPRGDONE;
dwc2_writel(hsotg, dctl, DCTL);
+ flags |= DWC2_RESTORE_DCTL;
}
/* Wait for interrupts which must be cleared */
mdelay(2);
@@ -5484,20 +5523,9 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
- return ret;
- }
-
- /* Restore device registers */
- ret = dwc2_restore_device_registers(hsotg, rem_wakeup);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore device registers\n",
- __func__);
+ ret = dwc2_gadget_restore_critical_registers(hsotg, flags);
+ if (ret)
return ret;
- }
if (rem_wakeup) {
mdelay(10);
@@ -5531,19 +5559,9 @@ int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg)
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__);
+ ret = dwc2_gadget_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
/*
* Clear any pending interrupts since dwc2 will not be able to
@@ -5590,11 +5608,8 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
{
u32 pcgcctl;
u32 dctl;
- struct dwc2_dregs_backup *dr;
int ret = 0;
- dr = &hsotg->dr_backup;
-
dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n");
pcgcctl = dwc2_readl(hsotg, PCGCTL);
@@ -5611,21 +5626,10 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
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;
- }
- /* Restore DCFG */
- dwc2_writel(hsotg, dr->dcfg, DCFG);
-
- ret = dwc2_restore_device_registers(hsotg, 0);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore device registers\n",
- __func__);
+ ret = dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL |
+ DWC2_RESTORE_DCFG);
+ if (ret)
return ret;
- }
}
/* Set the Power-On Programming done bit */
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 8c3941ecaaf5..60ef8092259a 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -5081,7 +5081,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
cancel_work_sync(&hsotg->phy_reset_work);
- del_timer(&hsotg->wkp_timer);
+ timer_delete(&hsotg->wkp_timer);
}
static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
@@ -5474,6 +5474,49 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
return 0;
}
+int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ /* 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;
+ }
+
+ return 0;
+}
+
+int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+
+ ret = dwc2_restore_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore host registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
/**
* dwc2_host_enter_hibernation() - Put controller in Hibernation.
*
@@ -5489,18 +5532,9 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
u32 gpwrdn;
dev_dbg(hsotg->dev, "Preparing host for hibernation\n");
- 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__);
+ ret = dwc2_host_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
/* Enter USB Suspend Mode */
hprt0 = dwc2_readl(hsotg, HPRT0);
@@ -5694,20 +5728,9 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
+ ret = dwc2_host_restore_critical_registers(hsotg);
+ if (ret)
return ret;
- }
-
- /* Restore host registers */
- ret = dwc2_restore_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore host registers\n",
- __func__);
- return ret;
- }
if (rem_wakeup) {
dwc2_hcd_rem_wakeup(hsotg);
@@ -5774,19 +5797,9 @@ int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg)
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__);
+ ret = dwc2_host_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
/*
* Clear any pending interrupts since dwc2 will not be able to
@@ -5855,19 +5868,9 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
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;
- }
-
- ret = dwc2_restore_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore host registers\n",
- __func__);
+ ret = dwc2_host_restore_critical_registers(hsotg);
+ if (ret)
return ret;
- }
}
/* Drive resume signaling and exit suspend mode on the port. */
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 238c6fd50e75..b0098792dd22 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -1302,7 +1302,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
}
/* Cancel pending unreserve; if canceled OK, unreserve was pending */
- if (del_timer(&qh->unreserve_timer))
+ if (timer_delete(&qh->unreserve_timer))
WARN_ON(!qh->unreserve_pending);
/*
@@ -1459,8 +1459,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
/* Initialize QH */
qh->hsotg = hsotg;
timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0);
- hrtimer_init(&qh->wait_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- qh->wait_timer.function = &dwc2_wait_timer_fn;
+ hrtimer_setup(&qh->wait_timer, &dwc2_wait_timer_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
qh->ep_type = ep_type;
qh->ep_is_in = ep_is_in;
@@ -1615,7 +1614,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
/* Make sure any unreserve work is finished. */
- if (del_timer_sync(&qh->unreserve_timer)) {
+ if (timer_delete_sync(&qh->unreserve_timer)) {
unsigned long flags;
spin_lock_irqsave(&hsotg->lock, flags);
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 91c80a92d9b8..12b4dc07d08a 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -685,6 +685,14 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
regulator_disable(dwc2->usb33d);
}
+ if (is_device_mode)
+ ret = dwc2_gadget_backup_critical_registers(dwc2);
+ else
+ ret = dwc2_host_backup_critical_registers(dwc2);
+
+ if (ret)
+ return ret;
+
if (dwc2->ll_hw_enabled &&
(is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
ret = __dwc2_lowlevel_hw_disable(dwc2);
@@ -694,6 +702,24 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
return ret;
}
+static int dwc2_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_gregs_backup *gr;
+
+ gr = &hsotg->gr_backup;
+
+ if (!gr->valid) {
+ dev_err(hsotg->dev, "No valid register backup, failed to restore\n");
+ return -EINVAL;
+ }
+
+ if (gr->gintsts & GINTSTS_CURMODE_HOST)
+ return dwc2_host_restore_critical_registers(hsotg);
+
+ return dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL |
+ DWC2_RESTORE_DCFG);
+}
+
static int __maybe_unused dwc2_resume(struct device *dev)
{
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
@@ -706,6 +732,18 @@ static int __maybe_unused dwc2_resume(struct device *dev)
}
dwc2->phy_off_for_suspend = false;
+ /*
+ * During suspend it's possible that the power domain for the
+ * DWC2 controller is disabled and all register values get lost.
+ * In case the GUSBCFG register is not initialized, it's clear the
+ * registers must be restored.
+ */
+ if (!(dwc2_readl(dwc2, GUSBCFG) & GUSBCFG_TOUTCAL_MASK)) {
+ ret = dwc2_restore_critical_registers(dwc2);
+ if (ret)
+ return ret;
+ }
+
if (dwc2->params.activate_stm_id_vb_detection) {
unsigned long flags;
u32 ggpio, gotgctl;