summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLi Jun <b47624@freescale.com>2014-06-29 08:11:59 +0800
committerLi Jun <B47624@freescale.com>2014-06-30 16:37:14 +0800
commit8624bcec9674554ed2538a3918dd5d4bd2b1e0ce (patch)
treefe82ec77165778d196f04baca5c15ccb01c11383 /drivers
parent566e041ef624ed62baf617c17983d44f042693f8 (diff)
ENGR00320439-4 usb: chipidea: host: save and restore for host power lost
This patch adds interfaces in role driver to prepare and restore state for power lost in system sleep. For host experienced power lost, we can save required resgisters value before suspend; and restore them after resume. Signed-off-by: Li Jun <b47624@freescale.com>
Diffstat (limited to 'drivers')
-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;