diff options
Diffstat (limited to 'drivers/usb/host/ehci-spear.c')
-rw-r--r-- | drivers/usb/host/ehci-spear.c | 83 |
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 | ||
17 | struct spear_ehci { | 19 | struct 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 | ||
96 | static 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 | |||
120 | static 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 | |||
168 | static SIMPLE_DEV_PM_OPS(ehci_spear_pm_ops, ehci_spear_drv_suspend, | ||
169 | ehci_spear_drv_resume); | ||
170 | |||
93 | static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) | 171 | static 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 | ||