diff options
author | Manuel Lauss <mano@roarinelk.homelinux.net> | 2008-06-23 03:09:37 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-07-21 18:16:44 -0400 |
commit | 42bfc7b44f724fb5ce838fc2f552a3eb8cd768ec (patch) | |
tree | d97b0ca257dbadd4fafc6c55cef742d877d78d07 | |
parent | 53c81a348fa1d21dd042d9c9a9f91795f83fed66 (diff) |
USB: Au1xxx-usb: suspend/resume support.
Copy the OHCI/EHCI PM callbacks of the PCI implementation since
they work equally well on Au1xxx hardware.
Tested on Au1200.
Signed-off-by: Manuel Lauss <mano@roarinelk.homelinux.net>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/host/ehci-au1xxx.c | 115 | ||||
-rw-r--r-- | drivers/usb/host/ohci-au1xxx.c | 59 |
2 files changed, 155 insertions, 19 deletions
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 12ed80684ad1..bf69f4739107 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c | |||
@@ -195,29 +195,124 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev) | |||
195 | return 0; | 195 | return 0; |
196 | } | 196 | } |
197 | 197 | ||
198 | /*TBD*/ | 198 | #ifdef CONFIG_PM |
199 | /*static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) | 199 | static int ehci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, |
200 | pm_message_t message) | ||
200 | { | 201 | { |
201 | struct platform_device *pdev = to_platform_device(dev); | 202 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
202 | struct usb_hcd *hcd = dev_get_drvdata(dev); | 203 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
204 | unsigned long flags; | ||
205 | int rc; | ||
203 | 206 | ||
204 | return 0; | 207 | return 0; |
208 | rc = 0; | ||
209 | |||
210 | if (time_before(jiffies, ehci->next_statechange)) | ||
211 | msleep(10); | ||
212 | |||
213 | /* Root hub was already suspended. Disable irq emission and | ||
214 | * mark HW unaccessible, bail out if RH has been resumed. Use | ||
215 | * the spinlock to properly synchronize with possible pending | ||
216 | * RH suspend or resume activity. | ||
217 | * | ||
218 | * This is still racy as hcd->state is manipulated outside of | ||
219 | * any locks =P But that will be a different fix. | ||
220 | */ | ||
221 | spin_lock_irqsave(&ehci->lock, flags); | ||
222 | if (hcd->state != HC_STATE_SUSPENDED) { | ||
223 | rc = -EINVAL; | ||
224 | goto bail; | ||
225 | } | ||
226 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); | ||
227 | (void)ehci_readl(ehci, &ehci->regs->intr_enable); | ||
228 | |||
229 | /* make sure snapshot being resumed re-enumerates everything */ | ||
230 | if (message.event == PM_EVENT_PRETHAW) { | ||
231 | ehci_halt(ehci); | ||
232 | ehci_reset(ehci); | ||
233 | } | ||
234 | |||
235 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
236 | |||
237 | au1xxx_stop_ehc(); | ||
238 | |||
239 | bail: | ||
240 | spin_unlock_irqrestore(&ehci->lock, flags); | ||
241 | |||
242 | // could save FLADJ in case of Vaux power loss | ||
243 | // ... we'd only use it to handle clock skew | ||
244 | |||
245 | return rc; | ||
205 | } | 246 | } |
206 | static int ehci_hcd_au1xxx_drv_resume(struct device *dev) | 247 | |
248 | |||
249 | static int ehci_hcd_au1xxx_drv_resume(struct platform_device *pdev) | ||
207 | { | 250 | { |
208 | struct platform_device *pdev = to_platform_device(dev); | 251 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
209 | struct usb_hcd *hcd = dev_get_drvdata(dev); | 252 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
253 | |||
254 | au1xxx_start_ehc(); | ||
255 | |||
256 | // maybe restore FLADJ | ||
257 | |||
258 | if (time_before(jiffies, ehci->next_statechange)) | ||
259 | msleep(100); | ||
260 | |||
261 | /* Mark hardware accessible again as we are out of D3 state by now */ | ||
262 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
263 | |||
264 | /* If CF is still set, we maintained PCI Vaux power. | ||
265 | * Just undo the effect of ehci_pci_suspend(). | ||
266 | */ | ||
267 | if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { | ||
268 | int mask = INTR_MASK; | ||
269 | |||
270 | if (!hcd->self.root_hub->do_remote_wakeup) | ||
271 | mask &= ~STS_PCD; | ||
272 | ehci_writel(ehci, mask, &ehci->regs->intr_enable); | ||
273 | ehci_readl(ehci, &ehci->regs->intr_enable); | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | ehci_dbg(ehci, "lost power, restarting\n"); | ||
278 | usb_root_hub_lost_power(hcd->self.root_hub); | ||
279 | |||
280 | /* Else reset, to cope with power loss or flush-to-storage | ||
281 | * style "resume" having let BIOS kick in during reboot. | ||
282 | */ | ||
283 | (void) ehci_halt(ehci); | ||
284 | (void) ehci_reset(ehci); | ||
285 | |||
286 | /* emptying the schedule aborts any urbs */ | ||
287 | spin_lock_irq(&ehci->lock); | ||
288 | if (ehci->reclaim) | ||
289 | end_unlink_async(ehci); | ||
290 | ehci_work(ehci); | ||
291 | spin_unlock_irq(&ehci->lock); | ||
292 | |||
293 | ehci_writel(ehci, ehci->command, &ehci->regs->command); | ||
294 | ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); | ||
295 | ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ | ||
296 | |||
297 | /* here we "know" root ports should always stay powered */ | ||
298 | ehci_port_power(ehci, 1); | ||
299 | |||
300 | hcd->state = HC_STATE_SUSPENDED; | ||
210 | 301 | ||
211 | return 0; | 302 | return 0; |
212 | } | 303 | } |
213 | */ | 304 | |
305 | #else | ||
306 | #define ehci_hcd_au1xxx_drv_suspend NULL | ||
307 | #define ehci_hcd_au1xxx_drv_resume NULL | ||
308 | #endif | ||
214 | 309 | ||
215 | static struct platform_driver ehci_hcd_au1xxx_driver = { | 310 | static struct platform_driver ehci_hcd_au1xxx_driver = { |
216 | .probe = ehci_hcd_au1xxx_drv_probe, | 311 | .probe = ehci_hcd_au1xxx_drv_probe, |
217 | .remove = ehci_hcd_au1xxx_drv_remove, | 312 | .remove = ehci_hcd_au1xxx_drv_remove, |
218 | .shutdown = usb_hcd_platform_shutdown, | 313 | .shutdown = usb_hcd_platform_shutdown, |
219 | /*.suspend = ehci_hcd_au1xxx_drv_suspend, */ | 314 | .suspend = ehci_hcd_au1xxx_drv_suspend, |
220 | /*.resume = ehci_hcd_au1xxx_drv_resume, */ | 315 | .resume = ehci_hcd_au1xxx_drv_resume, |
221 | .driver = { | 316 | .driver = { |
222 | .name = "au1xxx-ehci", | 317 | .name = "au1xxx-ehci", |
223 | .owner = THIS_MODULE, | 318 | .owner = THIS_MODULE, |
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index 29c970ad487a..c0948008fe3d 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c | |||
@@ -248,27 +248,68 @@ static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev) | |||
248 | return 0; | 248 | return 0; |
249 | } | 249 | } |
250 | 250 | ||
251 | /*TBD*/ | 251 | #ifdef CONFIG_PM |
252 | /*static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *dev) | 252 | static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *pdev, |
253 | pm_message_t message) | ||
253 | { | 254 | { |
254 | struct usb_hcd *hcd = platform_get_drvdata(dev); | 255 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
256 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
257 | unsigned long flags; | ||
258 | int rc; | ||
259 | |||
260 | rc = 0; | ||
261 | |||
262 | /* Root hub was already suspended. Disable irq emission and | ||
263 | * mark HW unaccessible, bail out if RH has been resumed. Use | ||
264 | * the spinlock to properly synchronize with possible pending | ||
265 | * RH suspend or resume activity. | ||
266 | * | ||
267 | * This is still racy as hcd->state is manipulated outside of | ||
268 | * any locks =P But that will be a different fix. | ||
269 | */ | ||
270 | spin_lock_irqsave(&ohci->lock, flags); | ||
271 | if (hcd->state != HC_STATE_SUSPENDED) { | ||
272 | rc = -EINVAL; | ||
273 | goto bail; | ||
274 | } | ||
275 | ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); | ||
276 | (void)ohci_readl(ohci, &ohci->regs->intrdisable); | ||
255 | 277 | ||
256 | return 0; | 278 | /* make sure snapshot being resumed re-enumerates everything */ |
279 | if (message.event == PM_EVENT_PRETHAW) | ||
280 | ohci_usb_reset(ohci); | ||
281 | |||
282 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
283 | |||
284 | au1xxx_stop_ohc(); | ||
285 | bail: | ||
286 | spin_unlock_irqrestore(&ohci->lock, flags); | ||
287 | |||
288 | return rc; | ||
257 | } | 289 | } |
258 | static int ohci_hcd_au1xxx_drv_resume(struct platform_device *dev) | 290 | |
291 | static int ohci_hcd_au1xxx_drv_resume(struct platform_device *pdev) | ||
259 | { | 292 | { |
260 | struct usb_hcd *hcd = platform_get_drvdata(dev); | 293 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
294 | |||
295 | au1xxx_start_ohc(); | ||
296 | |||
297 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
298 | ohci_finish_controller_resume(hcd); | ||
261 | 299 | ||
262 | return 0; | 300 | return 0; |
263 | } | 301 | } |
264 | */ | 302 | #else |
303 | #define ohci_hcd_au1xxx_drv_suspend NULL | ||
304 | #define ohci_hcd_au1xxx_drv_resume NULL | ||
305 | #endif | ||
265 | 306 | ||
266 | static struct platform_driver ohci_hcd_au1xxx_driver = { | 307 | static struct platform_driver ohci_hcd_au1xxx_driver = { |
267 | .probe = ohci_hcd_au1xxx_drv_probe, | 308 | .probe = ohci_hcd_au1xxx_drv_probe, |
268 | .remove = ohci_hcd_au1xxx_drv_remove, | 309 | .remove = ohci_hcd_au1xxx_drv_remove, |
269 | .shutdown = usb_hcd_platform_shutdown, | 310 | .shutdown = usb_hcd_platform_shutdown, |
270 | /*.suspend = ohci_hcd_au1xxx_drv_suspend, */ | 311 | .suspend = ohci_hcd_au1xxx_drv_suspend, |
271 | /*.resume = ohci_hcd_au1xxx_drv_resume, */ | 312 | .resume = ohci_hcd_au1xxx_drv_resume, |
272 | .driver = { | 313 | .driver = { |
273 | .name = "au1xxx-ohci", | 314 | .name = "au1xxx-ohci", |
274 | .owner = THIS_MODULE, | 315 | .owner = THIS_MODULE, |