summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/chipidea/ci.h14
-rw-r--r--drivers/usb/chipidea/host.c46
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;