diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/chipidea/ci.h | 14 | ||||
-rw-r--r-- | drivers/usb/chipidea/host.c | 46 |
2 files changed, 60 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 47d81a14c025..aabbc4633714 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -79,6 +79,10 @@ struct ci_role_driver { int (*start)(struct ci_hdrc *); void (*stop)(struct ci_hdrc *); irqreturn_t (*irq)(struct ci_hdrc *); + /* Save before suspend */ + void (*save)(struct ci_hdrc *); + /* Restore after power lost */ + void (*restore)(struct ci_hdrc *); const char *name; }; @@ -190,6 +194,16 @@ struct ci_hdrc { bool in_lpm; bool wakeup_int; struct timer_list timer; + /* register save area for suspend&resume */ + u32 pm_command; + u32 pm_status; + u32 pm_intr_enable; + u32 pm_frame_index; + u32 pm_segment; + u32 pm_frame_list; + u32 pm_async_next; + u32 pm_configured_flag; + u32 pm_portsc; }; static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index e073462c487c..b14810a940f2 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -339,6 +339,50 @@ bool ci_hdrc_host_has_device(struct ci_hdrc *ci) return false; } +void ci_hdrc_host_save_for_power_lost(struct ci_hdrc *ci) +{ + struct ehci_hcd *ehci = hcd_to_ehci(ci->hcd); + + /* save EHCI registers */ + ci->pm_command = ehci_readl(ehci, &ehci->regs->command); + ci->pm_command &= ~CMD_RUN; + ci->pm_status = ehci_readl(ehci, &ehci->regs->status); + ci->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable); + ci->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index); + ci->pm_segment = ehci_readl(ehci, &ehci->regs->segment); + ci->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list); + ci->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next); + ci->pm_configured_flag = + ehci_readl(ehci, &ehci->regs->configured_flag); + ci->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]); +} + +void ci_hdrc_host_restore_from_power_lost(struct ci_hdrc *ci) +{ + struct ehci_hcd *ehci = hcd_to_ehci(ci->hcd); + unsigned long flags; + u32 tmp; + + hw_device_reset(ci, USBMODE_CM_HC); + + spin_lock_irqsave(&ehci->lock, flags); + /* restore EHCI registers */ + ehci_writel(ehci, ci->pm_portsc, &ehci->regs->port_status[0]); + ehci_writel(ehci, ci->pm_command, &ehci->regs->command); + ehci_writel(ehci, ci->pm_intr_enable, &ehci->regs->intr_enable); + ehci_writel(ehci, ci->pm_frame_index, &ehci->regs->frame_index); + ehci_writel(ehci, ci->pm_segment, &ehci->regs->segment); + ehci_writel(ehci, ci->pm_frame_list, &ehci->regs->frame_list); + ehci_writel(ehci, ci->pm_async_next, &ehci->regs->async_next); + ehci_writel(ehci, ci->pm_configured_flag, + &ehci->regs->configured_flag); + + tmp = ehci_readl(ehci, &ehci->regs->command); + tmp |= CMD_RUN; + ehci_writel(ehci, tmp, &ehci->regs->command); + spin_unlock_irqrestore(&ehci->lock, flags); +} + void ci_hdrc_host_destroy(struct ci_hdrc *ci) { if (ci->role == CI_ROLE_HOST && ci->hcd) @@ -359,6 +403,8 @@ int ci_hdrc_host_init(struct ci_hdrc *ci) rdrv->start = host_start; rdrv->stop = host_stop; rdrv->irq = host_irq; + rdrv->save = ci_hdrc_host_save_for_power_lost; + rdrv->restore = ci_hdrc_host_restore_from_power_lost; rdrv->name = "host"; ci->roles[CI_ROLE_HOST] = rdrv; |