diff options
Diffstat (limited to 'drivers/usb/host/ehci-fsl.c')
-rw-r--r-- | drivers/usb/host/ehci-fsl.c | 221 |
1 files changed, 220 insertions, 1 deletions
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5c761df7fa83..f380bf97e5af 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c | |||
@@ -117,6 +117,9 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, | |||
117 | 117 | ||
118 | pdata->regs = hcd->regs; | 118 | pdata->regs = hcd->regs; |
119 | 119 | ||
120 | if (pdata->power_budget) | ||
121 | hcd->power_budget = pdata->power_budget; | ||
122 | |||
120 | /* | 123 | /* |
121 | * do platform specific init: check the clock, grab/config pins, etc. | 124 | * do platform specific init: check the clock, grab/config pins, etc. |
122 | */ | 125 | */ |
@@ -134,6 +137,30 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, | |||
134 | retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); | 137 | retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); |
135 | if (retval != 0) | 138 | if (retval != 0) |
136 | goto err4; | 139 | goto err4; |
140 | |||
141 | #ifdef CONFIG_USB_OTG | ||
142 | if (pdata->operating_mode == FSL_USB2_DR_OTG) { | ||
143 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
144 | |||
145 | ehci->transceiver = otg_get_transceiver(); | ||
146 | dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", | ||
147 | hcd, ehci, ehci->transceiver); | ||
148 | |||
149 | if (ehci->transceiver) { | ||
150 | retval = otg_set_host(ehci->transceiver, | ||
151 | &ehci_to_hcd(ehci)->self); | ||
152 | if (retval) { | ||
153 | if (ehci->transceiver) | ||
154 | put_device(ehci->transceiver->dev); | ||
155 | goto err4; | ||
156 | } | ||
157 | } else { | ||
158 | dev_err(&pdev->dev, "can't find transceiver\n"); | ||
159 | retval = -ENODEV; | ||
160 | goto err4; | ||
161 | } | ||
162 | } | ||
163 | #endif | ||
137 | return retval; | 164 | return retval; |
138 | 165 | ||
139 | err4: | 166 | err4: |
@@ -164,6 +191,12 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, | |||
164 | struct platform_device *pdev) | 191 | struct platform_device *pdev) |
165 | { | 192 | { |
166 | struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; | 193 | struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; |
194 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
195 | |||
196 | if (ehci->transceiver) { | ||
197 | otg_set_host(ehci->transceiver, NULL); | ||
198 | put_device(ehci->transceiver->dev); | ||
199 | } | ||
167 | 200 | ||
168 | usb_remove_hcd(hcd); | 201 | usb_remove_hcd(hcd); |
169 | 202 | ||
@@ -291,7 +324,7 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) | |||
291 | /* EHCI registers start at offset 0x100 */ | 324 | /* EHCI registers start at offset 0x100 */ |
292 | ehci->caps = hcd->regs + 0x100; | 325 | ehci->caps = hcd->regs + 0x100; |
293 | ehci->regs = hcd->regs + 0x100 + | 326 | ehci->regs = hcd->regs + 0x100 + |
294 | HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); | 327 | HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); |
295 | dbg_hcs_params(ehci, "reset"); | 328 | dbg_hcs_params(ehci, "reset"); |
296 | dbg_hcc_params(ehci, "reset"); | 329 | dbg_hcc_params(ehci, "reset"); |
297 | 330 | ||
@@ -328,6 +361,149 @@ struct ehci_fsl { | |||
328 | 361 | ||
329 | #ifdef CONFIG_PM | 362 | #ifdef CONFIG_PM |
330 | 363 | ||
364 | #ifdef CONFIG_PPC_MPC512x | ||
365 | static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) | ||
366 | { | ||
367 | struct usb_hcd *hcd = dev_get_drvdata(dev); | ||
368 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
369 | struct fsl_usb2_platform_data *pdata = dev->platform_data; | ||
370 | u32 tmp; | ||
371 | |||
372 | #ifdef DEBUG | ||
373 | u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE); | ||
374 | mode &= USBMODE_CM_MASK; | ||
375 | tmp = ehci_readl(ehci, hcd->regs + 0x140); /* usbcmd */ | ||
376 | |||
377 | dev_dbg(dev, "suspend=%d already_suspended=%d " | ||
378 | "mode=%d usbcmd %08x\n", pdata->suspended, | ||
379 | pdata->already_suspended, mode, tmp); | ||
380 | #endif | ||
381 | |||
382 | /* | ||
383 | * If the controller is already suspended, then this must be a | ||
384 | * PM suspend. Remember this fact, so that we will leave the | ||
385 | * controller suspended at PM resume time. | ||
386 | */ | ||
387 | if (pdata->suspended) { | ||
388 | dev_dbg(dev, "already suspended, leaving early\n"); | ||
389 | pdata->already_suspended = 1; | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | dev_dbg(dev, "suspending...\n"); | ||
394 | |||
395 | hcd->state = HC_STATE_SUSPENDED; | ||
396 | dev->power.power_state = PMSG_SUSPEND; | ||
397 | |||
398 | /* ignore non-host interrupts */ | ||
399 | clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
400 | |||
401 | /* stop the controller */ | ||
402 | tmp = ehci_readl(ehci, &ehci->regs->command); | ||
403 | tmp &= ~CMD_RUN; | ||
404 | ehci_writel(ehci, tmp, &ehci->regs->command); | ||
405 | |||
406 | /* save EHCI registers */ | ||
407 | pdata->pm_command = ehci_readl(ehci, &ehci->regs->command); | ||
408 | pdata->pm_command &= ~CMD_RUN; | ||
409 | pdata->pm_status = ehci_readl(ehci, &ehci->regs->status); | ||
410 | pdata->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable); | ||
411 | pdata->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index); | ||
412 | pdata->pm_segment = ehci_readl(ehci, &ehci->regs->segment); | ||
413 | pdata->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list); | ||
414 | pdata->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next); | ||
415 | pdata->pm_configured_flag = | ||
416 | ehci_readl(ehci, &ehci->regs->configured_flag); | ||
417 | pdata->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]); | ||
418 | pdata->pm_usbgenctrl = ehci_readl(ehci, | ||
419 | hcd->regs + FSL_SOC_USB_USBGENCTRL); | ||
420 | |||
421 | /* clear the W1C bits */ | ||
422 | pdata->pm_portsc &= cpu_to_hc32(ehci, ~PORT_RWC_BITS); | ||
423 | |||
424 | pdata->suspended = 1; | ||
425 | |||
426 | /* clear PP to cut power to the port */ | ||
427 | tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); | ||
428 | tmp &= ~PORT_POWER; | ||
429 | ehci_writel(ehci, tmp, &ehci->regs->port_status[0]); | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | static int ehci_fsl_mpc512x_drv_resume(struct device *dev) | ||
435 | { | ||
436 | struct usb_hcd *hcd = dev_get_drvdata(dev); | ||
437 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
438 | struct fsl_usb2_platform_data *pdata = dev->platform_data; | ||
439 | u32 tmp; | ||
440 | |||
441 | dev_dbg(dev, "suspend=%d already_suspended=%d\n", | ||
442 | pdata->suspended, pdata->already_suspended); | ||
443 | |||
444 | /* | ||
445 | * If the controller was already suspended at suspend time, | ||
446 | * then don't resume it now. | ||
447 | */ | ||
448 | if (pdata->already_suspended) { | ||
449 | dev_dbg(dev, "already suspended, leaving early\n"); | ||
450 | pdata->already_suspended = 0; | ||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | if (!pdata->suspended) { | ||
455 | dev_dbg(dev, "not suspended, leaving early\n"); | ||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | pdata->suspended = 0; | ||
460 | |||
461 | dev_dbg(dev, "resuming...\n"); | ||
462 | |||
463 | /* set host mode */ | ||
464 | tmp = USBMODE_CM_HOST | (pdata->es ? USBMODE_ES : 0); | ||
465 | ehci_writel(ehci, tmp, hcd->regs + FSL_SOC_USB_USBMODE); | ||
466 | |||
467 | ehci_writel(ehci, pdata->pm_usbgenctrl, | ||
468 | hcd->regs + FSL_SOC_USB_USBGENCTRL); | ||
469 | ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE, | ||
470 | hcd->regs + FSL_SOC_USB_ISIPHYCTRL); | ||
471 | |||
472 | /* restore EHCI registers */ | ||
473 | ehci_writel(ehci, pdata->pm_command, &ehci->regs->command); | ||
474 | ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable); | ||
475 | ehci_writel(ehci, pdata->pm_frame_index, &ehci->regs->frame_index); | ||
476 | ehci_writel(ehci, pdata->pm_segment, &ehci->regs->segment); | ||
477 | ehci_writel(ehci, pdata->pm_frame_list, &ehci->regs->frame_list); | ||
478 | ehci_writel(ehci, pdata->pm_async_next, &ehci->regs->async_next); | ||
479 | ehci_writel(ehci, pdata->pm_configured_flag, | ||
480 | &ehci->regs->configured_flag); | ||
481 | ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]); | ||
482 | |||
483 | set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); | ||
484 | hcd->state = HC_STATE_RUNNING; | ||
485 | dev->power.power_state = PMSG_ON; | ||
486 | |||
487 | tmp = ehci_readl(ehci, &ehci->regs->command); | ||
488 | tmp |= CMD_RUN; | ||
489 | ehci_writel(ehci, tmp, &ehci->regs->command); | ||
490 | |||
491 | usb_hcd_resume_root_hub(hcd); | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | #else | ||
496 | static inline int ehci_fsl_mpc512x_drv_suspend(struct device *dev) | ||
497 | { | ||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | static inline int ehci_fsl_mpc512x_drv_resume(struct device *dev) | ||
502 | { | ||
503 | return 0; | ||
504 | } | ||
505 | #endif /* CONFIG_PPC_MPC512x */ | ||
506 | |||
331 | static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd) | 507 | static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd) |
332 | { | 508 | { |
333 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | 509 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
@@ -341,6 +517,11 @@ static int ehci_fsl_drv_suspend(struct device *dev) | |||
341 | struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); | 517 | struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); |
342 | void __iomem *non_ehci = hcd->regs; | 518 | void __iomem *non_ehci = hcd->regs; |
343 | 519 | ||
520 | if (of_device_is_compatible(dev->parent->of_node, | ||
521 | "fsl,mpc5121-usb2-dr")) { | ||
522 | return ehci_fsl_mpc512x_drv_suspend(dev); | ||
523 | } | ||
524 | |||
344 | ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), | 525 | ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), |
345 | device_may_wakeup(dev)); | 526 | device_may_wakeup(dev)); |
346 | if (!fsl_deep_sleep()) | 527 | if (!fsl_deep_sleep()) |
@@ -357,6 +538,11 @@ static int ehci_fsl_drv_resume(struct device *dev) | |||
357 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | 538 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
358 | void __iomem *non_ehci = hcd->regs; | 539 | void __iomem *non_ehci = hcd->regs; |
359 | 540 | ||
541 | if (of_device_is_compatible(dev->parent->of_node, | ||
542 | "fsl,mpc5121-usb2-dr")) { | ||
543 | return ehci_fsl_mpc512x_drv_resume(dev); | ||
544 | } | ||
545 | |||
360 | ehci_prepare_ports_for_controller_resume(ehci); | 546 | ehci_prepare_ports_for_controller_resume(ehci); |
361 | if (!fsl_deep_sleep()) | 547 | if (!fsl_deep_sleep()) |
362 | return 0; | 548 | return 0; |
@@ -391,6 +577,38 @@ static struct dev_pm_ops ehci_fsl_pm_ops = { | |||
391 | #define EHCI_FSL_PM_OPS NULL | 577 | #define EHCI_FSL_PM_OPS NULL |
392 | #endif /* CONFIG_PM */ | 578 | #endif /* CONFIG_PM */ |
393 | 579 | ||
580 | #ifdef CONFIG_USB_OTG | ||
581 | static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port) | ||
582 | { | ||
583 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
584 | u32 status; | ||
585 | |||
586 | if (!port) | ||
587 | return -EINVAL; | ||
588 | |||
589 | port--; | ||
590 | |||
591 | /* start port reset before HNP protocol time out */ | ||
592 | status = readl(&ehci->regs->port_status[port]); | ||
593 | if (!(status & PORT_CONNECT)) | ||
594 | return -ENODEV; | ||
595 | |||
596 | /* khubd will finish the reset later */ | ||
597 | if (ehci_is_TDI(ehci)) { | ||
598 | writel(PORT_RESET | | ||
599 | (status & ~(PORT_CSC | PORT_PEC | PORT_OCC)), | ||
600 | &ehci->regs->port_status[port]); | ||
601 | } else { | ||
602 | writel(PORT_RESET, &ehci->regs->port_status[port]); | ||
603 | } | ||
604 | |||
605 | return 0; | ||
606 | } | ||
607 | #else | ||
608 | #define ehci_start_port_reset NULL | ||
609 | #endif /* CONFIG_USB_OTG */ | ||
610 | |||
611 | |||
394 | static const struct hc_driver ehci_fsl_hc_driver = { | 612 | static const struct hc_driver ehci_fsl_hc_driver = { |
395 | .description = hcd_name, | 613 | .description = hcd_name, |
396 | .product_desc = "Freescale On-Chip EHCI Host Controller", | 614 | .product_desc = "Freescale On-Chip EHCI Host Controller", |
@@ -430,6 +648,7 @@ static const struct hc_driver ehci_fsl_hc_driver = { | |||
430 | .hub_control = ehci_hub_control, | 648 | .hub_control = ehci_hub_control, |
431 | .bus_suspend = ehci_bus_suspend, | 649 | .bus_suspend = ehci_bus_suspend, |
432 | .bus_resume = ehci_bus_resume, | 650 | .bus_resume = ehci_bus_resume, |
651 | .start_port_reset = ehci_start_port_reset, | ||
433 | .relinquish_port = ehci_relinquish_port, | 652 | .relinquish_port = ehci_relinquish_port, |
434 | .port_handed_over = ehci_port_handed_over, | 653 | .port_handed_over = ehci_port_handed_over, |
435 | 654 | ||