On Thu, Jun 28, 2012 at 8:49 PM, Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote: > This patch (as1563) removes a lot of duplicated code by moving the > EHCI controller suspend/resume routines into the core driver, where > the various platform drivers can invoke them as needed. > > Not only does this simplify these platform drivers, this also makes it > easier for other platform drivers to add suspend/resume support in the > future. > > Note: The patch does not touch the ehci-fsl.c file, because its > approach to suspend and resume is so different from all the others. > It will have to be handled specially by its maintainer. > > Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> > > --- > > drivers/usb/host/ehci-au1xxx.c | 73 +------------------------------ > drivers/usb/host/ehci-hcd.c | 89 +++++++++++++++++++++++++++++++++++++++ > drivers/usb/host/ehci-hub.c | 4 - > drivers/usb/host/ehci-msm.c | 19 +------- > drivers/usb/host/ehci-pci.c | 74 +------------------------------- > drivers/usb/host/ehci-platform.c | 7 +-- > drivers/usb/host/ehci-s5p.c | 61 +------------------------- > drivers/usb/host/ehci-sead3.c | 74 +------------------------------- > drivers/usb/host/ehci-spear.c | 61 +------------------------- > 9 files changed, 114 insertions(+), 348 deletions(-) > > Index: usb-3.4/drivers/usb/host/ehci-hcd.c > =================================================================== > --- usb-3.4.orig/drivers/usb/host/ehci-hcd.c > +++ usb-3.4/drivers/usb/host/ehci-hcd.c > @@ -1242,6 +1242,95 @@ static int ehci_get_frame (struct usb_hc > } > > /*-------------------------------------------------------------------------*/ > + > +#ifdef CONFIG_PM > + > +/* suspend/resume, section 4.3 */ > + > +/* These routines handle the generic parts of controller suspend/resume */ > + > +static int __maybe_unused ehci_suspend(struct usb_hcd *hcd, bool do_wakeup) > +{ > + struct ehci_hcd *ehci = hcd_to_ehci(hcd); > + > + if (time_before(jiffies, ehci->next_statechange)) > + msleep(10); > + > + /* > + * Root hub was already suspended. Disable IRQ emission and > + * mark HW unaccessible. The PM and USB cores make sure that > + * the root hub is either suspended or stopped. > + */ > + ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup); > + > + spin_lock_irq(&ehci->lock); > + ehci_writel(ehci, 0, &ehci->regs->intr_enable); > + (void) ehci_readl(ehci, &ehci->regs->intr_enable); > + > + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); > + spin_unlock_irq(&ehci->lock); > + > + return 0; > +} > + > +/* Returns 0 if power was preserved, 1 if power was lost */ > +static int __maybe_unused ehci_resume(struct usb_hcd *hcd, bool hibernated) > +{ > + struct ehci_hcd *ehci = hcd_to_ehci(hcd); > + > + if (time_before(jiffies, ehci->next_statechange)) > + msleep(100); > + > + /* Mark hardware accessible again as we are back to full power by now */ > + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); > + > + /* > + * If CF is still set and we aren't resuming from hibernation > + * then we maintained suspend power. > + * Just undo the effect of ehci_suspend(). > + */ > + if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF && > + !hibernated) { > + int mask = INTR_MASK; > + > + ehci_prepare_ports_for_controller_resume(ehci); > + if (!hcd->self.root_hub->do_remote_wakeup) > + mask &= ~STS_PCD; > + ehci_writel(ehci, mask, &ehci->regs->intr_enable); > + ehci_readl(ehci, &ehci->regs->intr_enable); > + return 0; > + } > + > + /* > + * Else reset, to cope with power loss or resume from hibernation > + * having let the firmware kick in during reboot. > + */ > + usb_root_hub_lost_power(hcd->self.root_hub); > + (void) ehci_halt(ehci); > + (void) ehci_reset(ehci); > + > + /* emptying the schedule aborts any urbs */ > + spin_lock_irq(&ehci->lock); > + if (ehci->reclaim) > + end_unlink_async(ehci); > + ehci_work(ehci); > + spin_unlock_irq(&ehci->lock); > + > + ehci_writel(ehci, ehci->command, &ehci->regs->command); > + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); > + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ > + > + /* here we "know" root ports should always stay powered */ > + ehci_port_power(ehci, 1); > + > + ehci->rh_state = EHCI_RH_SUSPENDED; > + return 1; > +} > + > +#endif > + > +/*-------------------------------------------------------------------------*/ > + > /* > * The EHCI in ChipIdea HDRC cannot be a separate module or device, > * because its registers (and irq) are shared between host/gadget/otg > Index: usb-3.4/drivers/usb/host/ehci-hub.c > =================================================================== > --- usb-3.4.orig/drivers/usb/host/ehci-hub.c > +++ usb-3.4/drivers/usb/host/ehci-hub.c > @@ -107,7 +107,7 @@ static void ehci_handover_companion_port > ehci->owned_ports = 0; > } > > -static int __maybe_unused ehci_port_change(struct ehci_hcd *ehci) > +static int ehci_port_change(struct ehci_hcd *ehci) > { > int i = HCS_N_PORTS(ehci->hcs_params); > > @@ -128,7 +128,7 @@ static int __maybe_unused ehci_port_chan > return 0; > } > > -static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, > +static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, > bool suspending, bool do_wakeup) > { > int port; > Index: usb-3.4/drivers/usb/host/ehci-au1xxx.c > =================================================================== > --- usb-3.4.orig/drivers/usb/host/ehci-au1xxx.c > +++ usb-3.4/drivers/usb/host/ehci-au1xxx.c > @@ -158,28 +158,10 @@ static int ehci_hcd_au1xxx_drv_remove(st > static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) > { > struct usb_hcd *hcd = dev_get_drvdata(dev); > - struct ehci_hcd *ehci = hcd_to_ehci(hcd); > - unsigned long flags; > - int rc = 0; > - > - if (time_before(jiffies, ehci->next_statechange)) > - msleep(10); > - > - /* Root hub was already suspended. Disable irq emission and > - * mark HW unaccessible. The PM and USB cores make sure that > - * the root hub is either suspended or stopped. > - */ > - ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev)); > - spin_lock_irqsave(&ehci->lock, flags); > - ehci_writel(ehci, 0, &ehci->regs->intr_enable); > - (void)ehci_readl(ehci, &ehci->regs->intr_enable); > - > - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); > - spin_unlock_irqrestore(&ehci->lock, flags); > - > - // could save FLADJ in case of Vaux power loss > - // ... we'd only use it to handle clock skew > + bool do_wakeup = device_may_wakeup(dev); > + int rc; > > + rc = ehci_suspend(hcd, do_wakeup); > alchemy_usb_control(ALCHEMY_USB_EHCI0, 0); > > return rc; > @@ -188,56 +170,9 @@ static int ehci_hcd_au1xxx_drv_suspend(s > static int ehci_hcd_au1xxx_drv_resume(struct device *dev) > { > struct usb_hcd *hcd = dev_get_drvdata(dev); > - struct ehci_hcd *ehci = hcd_to_ehci(hcd); > > alchemy_usb_control(ALCHEMY_USB_EHCI0, 1); > - > - // maybe restore FLADJ > - > - if (time_before(jiffies, ehci->next_statechange)) > - msleep(100); > - > - /* Mark hardware accessible again as we are out of D3 state by now */ > - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); > - > - /* If CF is still set, we maintained PCI Vaux power. > - * Just undo the effect of ehci_pci_suspend(). > - */ > - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { > - int mask = INTR_MASK; > - > - ehci_prepare_ports_for_controller_resume(ehci); > - if (!hcd->self.root_hub->do_remote_wakeup) > - mask &= ~STS_PCD; > - ehci_writel(ehci, mask, &ehci->regs->intr_enable); > - ehci_readl(ehci, &ehci->regs->intr_enable); > - return 0; > - } > - > - ehci_dbg(ehci, "lost power, restarting\n"); > - usb_root_hub_lost_power(hcd->self.root_hub); > - > - /* Else reset, to cope with power loss or flush-to-storage > - * style "resume" having let BIOS kick in during reboot. > - */ > - (void) ehci_halt(ehci); > - (void) ehci_reset(ehci); > - > - /* emptying the schedule aborts any urbs */ > - spin_lock_irq(&ehci->lock); > - if (ehci->reclaim) > - end_unlink_async(ehci); > - ehci_work(ehci); > - spin_unlock_irq(&ehci->lock); > - > - ehci_writel(ehci, ehci->command, &ehci->regs->command); > - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); > - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ > - > - /* here we "know" root ports should always stay powered */ > - ehci_port_power(ehci, 1); > - > - ehci->rh_state = EHCI_RH_SUSPENDED; > + ehci_resume(hcd, false); > > return 0; > } > Index: usb-3.4/drivers/usb/host/ehci-msm.c > =================================================================== > --- usb-3.4.orig/drivers/usb/host/ehci-msm.c > +++ usb-3.4/drivers/usb/host/ehci-msm.c > @@ -198,24 +198,11 @@ static int __devexit ehci_msm_remove(str > static int ehci_msm_pm_suspend(struct device *dev) > { > struct usb_hcd *hcd = dev_get_drvdata(dev); > - bool wakeup = device_may_wakeup(dev); > + bool do_wakeup = device_may_wakeup(dev); > > dev_dbg(dev, "ehci-msm PM suspend\n"); > > - /* > - * EHCI helper function has also the same check before manipulating > - * port wakeup flags. We do check here the same condition before > - * calling the same helper function to avoid bringing hardware > - * from Low power mode when there is no need for adjusting port > - * wakeup flags. > - */ > - if (hcd->self.root_hub->do_remote_wakeup && !wakeup) { > - pm_runtime_resume(dev); > - ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), > - wakeup); > - } > - > - return 0; > + return ehci_suspend(hcd, do_wakeup); > } > > static int ehci_msm_pm_resume(struct device *dev) > @@ -223,7 +210,7 @@ static int ehci_msm_pm_resume(struct dev > struct usb_hcd *hcd = dev_get_drvdata(dev); > > dev_dbg(dev, "ehci-msm PM resume\n"); > - ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); > + ehci_resume(hcd, false); > > return 0; > } > Index: usb-3.4/drivers/usb/host/ehci-pci.c > =================================================================== > --- usb-3.4.orig/drivers/usb/host/ehci-pci.c > +++ usb-3.4/drivers/usb/host/ehci-pci.c > @@ -331,29 +331,7 @@ done: > > static int ehci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) > { > - struct ehci_hcd *ehci = hcd_to_ehci(hcd); > - unsigned long flags; > - int rc = 0; > - > - if (time_before(jiffies, ehci->next_statechange)) > - msleep(10); > - > - /* Root hub was already suspended. Disable irq emission and > - * mark HW unaccessible. The PM and USB cores make sure that > - * the root hub is either suspended or stopped. > - */ > - ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup); > - spin_lock_irqsave (&ehci->lock, flags); > - ehci_writel(ehci, 0, &ehci->regs->intr_enable); > - (void)ehci_readl(ehci, &ehci->regs->intr_enable); > - > - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); > - spin_unlock_irqrestore (&ehci->lock, flags); > - > - // could save FLADJ in case of Vaux power loss > - // ... we'd only use it to handle clock skew > - > - return rc; > + return ehci_suspend(hcd, do_wakeup); > } > > static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev) > @@ -402,54 +380,8 @@ static int ehci_pci_resume(struct usb_hc > if (usb_is_intel_switchable_ehci(pdev)) > ehci_enable_xhci_companion(); > > - // maybe restore FLADJ > - > - if (time_before(jiffies, ehci->next_statechange)) > - msleep(100); > - > - /* Mark hardware accessible again as we are out of D3 state by now */ > - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); > - > - /* If CF is still set and we aren't resuming from hibernation > - * then we maintained PCI Vaux power. > - * Just undo the effect of ehci_pci_suspend(). > - */ > - if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF && > - !hibernated) { > - int mask = INTR_MASK; > - > - ehci_prepare_ports_for_controller_resume(ehci); > - if (!hcd->self.root_hub->do_remote_wakeup) > - mask &= ~STS_PCD; > - ehci_writel(ehci, mask, &ehci->regs->intr_enable); > - ehci_readl(ehci, &ehci->regs->intr_enable); > - return 0; > - } > - > - usb_root_hub_lost_power(hcd->self.root_hub); > - > - /* Else reset, to cope with power loss or flush-to-storage > - * style "resume" having let BIOS kick in during reboot. > - */ > - (void) ehci_halt(ehci); > - (void) ehci_reset(ehci); > - (void) ehci_pci_reinit(ehci, pdev); > - > - /* emptying the schedule aborts any urbs */ > - spin_lock_irq(&ehci->lock); > - if (ehci->reclaim) > - end_unlink_async(ehci); > - ehci_work(ehci); > - spin_unlock_irq(&ehci->lock); > - > - ehci_writel(ehci, ehci->command, &ehci->regs->command); > - ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); > - ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ > - > - /* here we "know" root ports should always stay powered */ > - ehci_port_power(ehci, 1); > - > - ehci->rh_state = EHCI_RH_SUSPENDED; > + if (ehci_resume(hcd, hibernated) != 0) > + (void) ehci_pci_reinit(ehci, pdev); > return 0; > } > #endif > Index: usb-3.4/drivers/usb/host/ehci-platform.c > =================================================================== > --- usb-3.4.orig/drivers/usb/host/ehci-platform.c > +++ usb-3.4/drivers/usb/host/ehci-platform.c > @@ -153,17 +153,16 @@ static int __devexit ehci_platform_remov > static int ehci_platform_suspend(struct device *dev) > { > struct usb_hcd *hcd = dev_get_drvdata(dev); > - bool wakeup = device_may_wakeup(dev); > + bool do_wakeup = device_may_wakeup(dev); > > - ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup); > - return 0; > + return ehci_suspend(hcd, do_wakeup); > } > > static int ehci_platform_resume(struct device *dev) > { > struct usb_hcd *hcd = dev_get_drvdata(dev); > > - ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); > + ehci_resume(hcd, false); > return 0; > } > so, we can use omap drivers to hook into this ehci platform driver right? then I think it requires call back functions for init, deinit, suspend, resume as in /drivers/ata/ahci_platform.c , so that I will do the hw specific operations in these hook functions not in the platform specific suspend/resume functions. regards keshava -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html