aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-spear.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/ehci-spear.c')
-rw-r--r--drivers/usb/host/ehci-spear.c83
1 files changed, 81 insertions, 2 deletions
diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c
index b115b0b76e33..6e928559169c 100644
--- a/drivers/usb/host/ehci-spear.c
+++ b/drivers/usb/host/ehci-spear.c
@@ -11,8 +11,10 @@
11* more details. 11* more details.
12*/ 12*/
13 13
14#include <linux/platform_device.h>
15#include <linux/clk.h> 14#include <linux/clk.h>
15#include <linux/jiffies.h>
16#include <linux/platform_device.h>
17#include <linux/pm.h>
16 18
17struct spear_ehci { 19struct spear_ehci {
18 struct ehci_hcd ehci; 20 struct ehci_hcd ehci;
@@ -90,6 +92,82 @@ static const struct hc_driver ehci_spear_hc_driver = {
90 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 92 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
91}; 93};
92 94
95#ifdef CONFIG_PM
96static int ehci_spear_drv_suspend(struct device *dev)
97{
98 struct usb_hcd *hcd = dev_get_drvdata(dev);
99 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
100 unsigned long flags;
101 int rc = 0;
102
103 if (time_before(jiffies, ehci->next_statechange))
104 msleep(10);
105
106 /*
107 * Root hub was already suspended. Disable irq emission and mark HW
108 * unaccessible. The PM and USB cores make sure that the root hub is
109 * either suspended or stopped.
110 */
111 spin_lock_irqsave(&ehci->lock, flags);
112 ehci_prepare_ports_for_controller_suspend(ehci, device_may_wakeup(dev));
113 ehci_writel(ehci, 0, &ehci->regs->intr_enable);
114 ehci_readl(ehci, &ehci->regs->intr_enable);
115 spin_unlock_irqrestore(&ehci->lock, flags);
116
117 return rc;
118}
119
120static int ehci_spear_drv_resume(struct device *dev)
121{
122 struct usb_hcd *hcd = dev_get_drvdata(dev);
123 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
124
125 if (time_before(jiffies, ehci->next_statechange))
126 msleep(100);
127
128 if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
129 int mask = INTR_MASK;
130
131 ehci_prepare_ports_for_controller_resume(ehci);
132
133 if (!hcd->self.root_hub->do_remote_wakeup)
134 mask &= ~STS_PCD;
135
136 ehci_writel(ehci, mask, &ehci->regs->intr_enable);
137 ehci_readl(ehci, &ehci->regs->intr_enable);
138 return 0;
139 }
140
141 usb_root_hub_lost_power(hcd->self.root_hub);
142
143 /*
144 * Else reset, to cope with power loss or flush-to-storage style
145 * "resume" having let BIOS kick in during reboot.
146 */
147 ehci_halt(ehci);
148 ehci_reset(ehci);
149
150 /* emptying the schedule aborts any urbs */
151 spin_lock_irq(&ehci->lock);
152 if (ehci->reclaim)
153 end_unlink_async(ehci);
154
155 ehci_work(ehci);
156 spin_unlock_irq(&ehci->lock);
157
158 ehci_writel(ehci, ehci->command, &ehci->regs->command);
159 ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
160 ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
161
162 /* here we "know" root ports should always stay powered */
163 ehci_port_power(ehci, 1);
164 return 0;
165}
166#endif /* CONFIG_PM */
167
168static SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend,
169 ehci_spear_drv_resume);
170
93static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) 171static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
94{ 172{
95 struct usb_hcd *hcd ; 173 struct usb_hcd *hcd ;
@@ -205,7 +283,8 @@ static struct platform_driver spear_ehci_hcd_driver = {
205 .shutdown = usb_hcd_platform_shutdown, 283 .shutdown = usb_hcd_platform_shutdown,
206 .driver = { 284 .driver = {
207 .name = "spear-ehci", 285 .name = "spear-ehci",
208 .bus = &platform_bus_type 286 .bus = &platform_bus_type,
287 .pm = &ehci_spear_pm_ops,
209 } 288 }
210}; 289};
211 290