Commit 13b7ee2a953f07d994b6bc3439cdd4a718de6f80
Committed by
Greg Kroah-Hartman
1 parent
3dacdf11f1
Exists in
master
and in
7 other branches
USB: ehci-fsl: add MPC5121E specific suspend and resume
Signed-off-by: Anatolij Gustschin <agust@denx.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Showing 3 changed files with 172 additions and 0 deletions Side-by-side Diff
drivers/usb/host/ehci-fsl.c
... | ... | @@ -328,6 +328,149 @@ |
328 | 328 | |
329 | 329 | #ifdef CONFIG_PM |
330 | 330 | |
331 | +#ifdef CONFIG_PPC_MPC512x | |
332 | +static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) | |
333 | +{ | |
334 | + struct usb_hcd *hcd = dev_get_drvdata(dev); | |
335 | + struct ehci_hcd *ehci = hcd_to_ehci(hcd); | |
336 | + struct fsl_usb2_platform_data *pdata = dev->platform_data; | |
337 | + u32 tmp; | |
338 | + | |
339 | +#ifdef DEBUG | |
340 | + u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE); | |
341 | + mode &= USBMODE_CM_MASK; | |
342 | + tmp = ehci_readl(ehci, hcd->regs + 0x140); /* usbcmd */ | |
343 | + | |
344 | + dev_dbg(dev, "suspend=%d already_suspended=%d " | |
345 | + "mode=%d usbcmd %08x\n", pdata->suspended, | |
346 | + pdata->already_suspended, mode, tmp); | |
347 | +#endif | |
348 | + | |
349 | + /* | |
350 | + * If the controller is already suspended, then this must be a | |
351 | + * PM suspend. Remember this fact, so that we will leave the | |
352 | + * controller suspended at PM resume time. | |
353 | + */ | |
354 | + if (pdata->suspended) { | |
355 | + dev_dbg(dev, "already suspended, leaving early\n"); | |
356 | + pdata->already_suspended = 1; | |
357 | + return 0; | |
358 | + } | |
359 | + | |
360 | + dev_dbg(dev, "suspending...\n"); | |
361 | + | |
362 | + hcd->state = HC_STATE_SUSPENDED; | |
363 | + dev->power.power_state = PMSG_SUSPEND; | |
364 | + | |
365 | + /* ignore non-host interrupts */ | |
366 | + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | |
367 | + | |
368 | + /* stop the controller */ | |
369 | + tmp = ehci_readl(ehci, &ehci->regs->command); | |
370 | + tmp &= ~CMD_RUN; | |
371 | + ehci_writel(ehci, tmp, &ehci->regs->command); | |
372 | + | |
373 | + /* save EHCI registers */ | |
374 | + pdata->pm_command = ehci_readl(ehci, &ehci->regs->command); | |
375 | + pdata->pm_command &= ~CMD_RUN; | |
376 | + pdata->pm_status = ehci_readl(ehci, &ehci->regs->status); | |
377 | + pdata->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable); | |
378 | + pdata->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index); | |
379 | + pdata->pm_segment = ehci_readl(ehci, &ehci->regs->segment); | |
380 | + pdata->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list); | |
381 | + pdata->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next); | |
382 | + pdata->pm_configured_flag = | |
383 | + ehci_readl(ehci, &ehci->regs->configured_flag); | |
384 | + pdata->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]); | |
385 | + pdata->pm_usbgenctrl = ehci_readl(ehci, | |
386 | + hcd->regs + FSL_SOC_USB_USBGENCTRL); | |
387 | + | |
388 | + /* clear the W1C bits */ | |
389 | + pdata->pm_portsc &= cpu_to_hc32(ehci, ~PORT_RWC_BITS); | |
390 | + | |
391 | + pdata->suspended = 1; | |
392 | + | |
393 | + /* clear PP to cut power to the port */ | |
394 | + tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); | |
395 | + tmp &= ~PORT_POWER; | |
396 | + ehci_writel(ehci, tmp, &ehci->regs->port_status[0]); | |
397 | + | |
398 | + return 0; | |
399 | +} | |
400 | + | |
401 | +static int ehci_fsl_mpc512x_drv_resume(struct device *dev) | |
402 | +{ | |
403 | + struct usb_hcd *hcd = dev_get_drvdata(dev); | |
404 | + struct ehci_hcd *ehci = hcd_to_ehci(hcd); | |
405 | + struct fsl_usb2_platform_data *pdata = dev->platform_data; | |
406 | + u32 tmp; | |
407 | + | |
408 | + dev_dbg(dev, "suspend=%d already_suspended=%d\n", | |
409 | + pdata->suspended, pdata->already_suspended); | |
410 | + | |
411 | + /* | |
412 | + * If the controller was already suspended at suspend time, | |
413 | + * then don't resume it now. | |
414 | + */ | |
415 | + if (pdata->already_suspended) { | |
416 | + dev_dbg(dev, "already suspended, leaving early\n"); | |
417 | + pdata->already_suspended = 0; | |
418 | + return 0; | |
419 | + } | |
420 | + | |
421 | + if (!pdata->suspended) { | |
422 | + dev_dbg(dev, "not suspended, leaving early\n"); | |
423 | + return 0; | |
424 | + } | |
425 | + | |
426 | + pdata->suspended = 0; | |
427 | + | |
428 | + dev_dbg(dev, "resuming...\n"); | |
429 | + | |
430 | + /* set host mode */ | |
431 | + tmp = USBMODE_CM_HOST | (pdata->es ? USBMODE_ES : 0); | |
432 | + ehci_writel(ehci, tmp, hcd->regs + FSL_SOC_USB_USBMODE); | |
433 | + | |
434 | + ehci_writel(ehci, pdata->pm_usbgenctrl, | |
435 | + hcd->regs + FSL_SOC_USB_USBGENCTRL); | |
436 | + ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE, | |
437 | + hcd->regs + FSL_SOC_USB_ISIPHYCTRL); | |
438 | + | |
439 | + /* restore EHCI registers */ | |
440 | + ehci_writel(ehci, pdata->pm_command, &ehci->regs->command); | |
441 | + ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable); | |
442 | + ehci_writel(ehci, pdata->pm_frame_index, &ehci->regs->frame_index); | |
443 | + ehci_writel(ehci, pdata->pm_segment, &ehci->regs->segment); | |
444 | + ehci_writel(ehci, pdata->pm_frame_list, &ehci->regs->frame_list); | |
445 | + ehci_writel(ehci, pdata->pm_async_next, &ehci->regs->async_next); | |
446 | + ehci_writel(ehci, pdata->pm_configured_flag, | |
447 | + &ehci->regs->configured_flag); | |
448 | + ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]); | |
449 | + | |
450 | + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | |
451 | + hcd->state = HC_STATE_RUNNING; | |
452 | + dev->power.power_state = PMSG_ON; | |
453 | + | |
454 | + tmp = ehci_readl(ehci, &ehci->regs->command); | |
455 | + tmp |= CMD_RUN; | |
456 | + ehci_writel(ehci, tmp, &ehci->regs->command); | |
457 | + | |
458 | + usb_hcd_resume_root_hub(hcd); | |
459 | + | |
460 | + return 0; | |
461 | +} | |
462 | +#else | |
463 | +static inline int ehci_fsl_mpc512x_drv_suspend(struct device *dev) | |
464 | +{ | |
465 | + return 0; | |
466 | +} | |
467 | + | |
468 | +static inline int ehci_fsl_mpc512x_drv_resume(struct device *dev) | |
469 | +{ | |
470 | + return 0; | |
471 | +} | |
472 | +#endif /* CONFIG_PPC_MPC512x */ | |
473 | + | |
331 | 474 | static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd) |
332 | 475 | { |
333 | 476 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
... | ... | @@ -341,6 +484,11 @@ |
341 | 484 | struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); |
342 | 485 | void __iomem *non_ehci = hcd->regs; |
343 | 486 | |
487 | + if (of_device_is_compatible(dev->parent->of_node, | |
488 | + "fsl,mpc5121-usb2-dr")) { | |
489 | + return ehci_fsl_mpc512x_drv_suspend(dev); | |
490 | + } | |
491 | + | |
344 | 492 | ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), |
345 | 493 | device_may_wakeup(dev)); |
346 | 494 | if (!fsl_deep_sleep()) |
... | ... | @@ -356,6 +504,11 @@ |
356 | 504 | struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); |
357 | 505 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
358 | 506 | void __iomem *non_ehci = hcd->regs; |
507 | + | |
508 | + if (of_device_is_compatible(dev->parent->of_node, | |
509 | + "fsl,mpc5121-usb2-dr")) { | |
510 | + return ehci_fsl_mpc512x_drv_resume(dev); | |
511 | + } | |
359 | 512 | |
360 | 513 | ehci_prepare_ports_for_controller_resume(ehci); |
361 | 514 | if (!fsl_deep_sleep()) |
drivers/usb/host/ehci-fsl.h
... | ... | @@ -27,6 +27,10 @@ |
27 | 27 | #define PORT_PTS_SERIAL (3<<30) |
28 | 28 | #define PORT_PTS_PTW (1<<28) |
29 | 29 | #define FSL_SOC_USB_PORTSC2 0x188 |
30 | +#define FSL_SOC_USB_USBMODE 0x1a8 | |
31 | +#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */ | |
32 | +#define USBMODE_CM_HOST (3 << 0) /* controller mode: host */ | |
33 | +#define USBMODE_ES (1 << 2) /* (Big) Endian Select */ | |
30 | 34 | |
31 | 35 | #define FSL_SOC_USB_USBGENCTRL 0x200 |
32 | 36 | #define USBGENCTRL_PPP (1 << 3) |
include/linux/fsl_devices.h
... | ... | @@ -79,6 +79,21 @@ |
79 | 79 | unsigned have_sysif_regs:1; |
80 | 80 | unsigned invert_drvvbus:1; |
81 | 81 | unsigned invert_pwr_fault:1; |
82 | + | |
83 | + unsigned suspended:1; | |
84 | + unsigned already_suspended:1; | |
85 | + | |
86 | + /* register save area for suspend/resume */ | |
87 | + u32 pm_command; | |
88 | + u32 pm_status; | |
89 | + u32 pm_intr_enable; | |
90 | + u32 pm_frame_index; | |
91 | + u32 pm_segment; | |
92 | + u32 pm_frame_list; | |
93 | + u32 pm_async_next; | |
94 | + u32 pm_configured_flag; | |
95 | + u32 pm_portsc; | |
96 | + u32 pm_usbgenctrl; | |
82 | 97 | }; |
83 | 98 | |
84 | 99 | /* Flags in fsl_usb2_mph_platform_data */ |