diff options
Diffstat (limited to 'drivers/usb/dwc2/gadget.c')
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 149 |
1 files changed, 90 insertions, 59 deletions
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index b2f6da5b65cc..300ea4969f0c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -885,10 +885,10 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep, } /* DMA sg buffer */ - for_each_sg(ureq->sg, sg, ureq->num_sgs, i) { + for_each_sg(ureq->sg, sg, ureq->num_mapped_sgs, i) { dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc, sg_dma_address(sg) + sg->offset, sg_dma_len(sg), - sg_is_last(sg)); + (i == (ureq->num_mapped_sgs - 1))); desc_count += hs_ep->desc_count; } @@ -3424,8 +3424,11 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_hsotg_init_fifo(hsotg); - if (!is_usb_reset) + if (!is_usb_reset) { dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON); + if (hsotg->params.eusb2_disc) + dwc2_set_bit(hsotg, GOTGCTL, GOTGCTL_EUSB2_DISC_SUPP); + } dcfg |= DCFG_EPMISCNT(1); @@ -4612,6 +4615,7 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) spin_lock_irqsave(&hsotg->lock, flags); hsotg->driver = NULL; + hsotg->gadget.dev.of_node = NULL; hsotg->gadget.speed = USB_SPEED_UNKNOWN; hsotg->enabled = 0; @@ -5200,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; @@ -5220,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); @@ -5306,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. * @@ -5316,25 +5366,34 @@ void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) { u32 gpwrdn; + u32 gusbcfg; + u32 pcgcctl; int ret = 0; /* 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); + gusbcfg = dwc2_readl(hsotg, GUSBCFG); + if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) { + /* ULPI interface */ + gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY; + } + dwc2_writel(hsotg, gpwrdn, GPWRDN); + udelay(10); + + /* Suspend the Phy Clock */ + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(10); + + gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn |= GPWRDN_PMUACTV; dwc2_writel(hsotg, gpwrdn, GPWRDN); udelay(10); @@ -5393,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; @@ -5435,6 +5495,11 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, if (reset) dwc2_clear_bit(hsotg, DCFG, DCFG_DEVADDR_MASK); + /* Reset ULPI latch */ + gpwrdn = dwc2_readl(hsotg, GPWRDN); + gpwrdn &= ~GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY; + dwc2_writel(hsotg, gpwrdn, GPWRDN); + /* De-assert Wakeup Logic */ gpwrdn = dwc2_readl(hsotg, GPWRDN); gpwrdn &= ~GPWRDN_PMUACTV; @@ -5450,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); @@ -5457,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); @@ -5504,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 @@ -5563,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); @@ -5584,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 */ |