diff options
author | David Brownell <david-b@pacbell.net> | 2005-04-09 12:00:29 -0400 |
---|---|---|
committer | Greg KH <gregkh@suse.de> | 2005-05-04 02:31:49 -0400 |
commit | 56c1e26d75008d39f1067f453719857a81109d9f (patch) | |
tree | f75ba4203962410bb9ec1ea159c4055a4ee09710 /drivers/usb/host/ehci-hcd.c | |
parent | e2e66446e08a7a365a59e25bbc8dd320ab3da0a6 (diff) |
[PATCH] USB: ehci power fixes
Miscellaneous updates for EHCI.
- Mostly updates the power switching on EHCI controllers. One routine
centralizes the "power on/off all ports" logic, and the capability to
do that is reported more correctly.
- Courtesy Colin Leroy, a patch to always power up ports after resumes
which didn't keep a USB device suspended. The reset-everything logic
powers down those ports (on some hardware) so something needs to turn
them back on.
- Minor tweaks/bugfixes for the debug port support.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ehci-hcd.c')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 65 |
1 files changed, 44 insertions, 21 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 84d2b93aca37..bc69bd7acebe 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -346,6 +346,22 @@ ehci_reboot (struct notifier_block *self, unsigned long code, void *null) | |||
346 | return 0; | 346 | return 0; |
347 | } | 347 | } |
348 | 348 | ||
349 | static void ehci_port_power (struct ehci_hcd *ehci, int is_on) | ||
350 | { | ||
351 | unsigned port; | ||
352 | |||
353 | if (!HCS_PPC (ehci->hcs_params)) | ||
354 | return; | ||
355 | |||
356 | ehci_dbg (ehci, "...power%s ports...\n", is_on ? "up" : "down"); | ||
357 | for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) | ||
358 | (void) ehci_hub_control(ehci_to_hcd(ehci), | ||
359 | is_on ? SetPortFeature : ClearPortFeature, | ||
360 | USB_PORT_FEAT_POWER, | ||
361 | port--, NULL, 0); | ||
362 | msleep(20); | ||
363 | } | ||
364 | |||
349 | 365 | ||
350 | /* called by khubd or root hub init threads */ | 366 | /* called by khubd or root hub init threads */ |
351 | 367 | ||
@@ -362,8 +378,10 @@ static int ehci_hc_reset (struct usb_hcd *hcd) | |||
362 | dbg_hcs_params (ehci, "reset"); | 378 | dbg_hcs_params (ehci, "reset"); |
363 | dbg_hcc_params (ehci, "reset"); | 379 | dbg_hcc_params (ehci, "reset"); |
364 | 380 | ||
381 | /* cache this readonly data; minimize chip reads */ | ||
382 | ehci->hcs_params = readl (&ehci->caps->hcs_params); | ||
383 | |||
365 | #ifdef CONFIG_PCI | 384 | #ifdef CONFIG_PCI |
366 | /* EHCI 0.96 and later may have "extended capabilities" */ | ||
367 | if (hcd->self.controller->bus == &pci_bus_type) { | 385 | if (hcd->self.controller->bus == &pci_bus_type) { |
368 | struct pci_dev *pdev = to_pci_dev(hcd->self.controller); | 386 | struct pci_dev *pdev = to_pci_dev(hcd->self.controller); |
369 | 387 | ||
@@ -383,9 +401,30 @@ static int ehci_hc_reset (struct usb_hcd *hcd) | |||
383 | break; | 401 | break; |
384 | } | 402 | } |
385 | 403 | ||
404 | /* optional debug port, normally in the first BAR */ | ||
405 | temp = pci_find_capability (pdev, 0x0a); | ||
406 | if (temp) { | ||
407 | pci_read_config_dword(pdev, temp, &temp); | ||
408 | temp >>= 16; | ||
409 | if ((temp & (3 << 13)) == (1 << 13)) { | ||
410 | temp &= 0x1fff; | ||
411 | ehci->debug = hcd->regs + temp; | ||
412 | temp = readl (&ehci->debug->control); | ||
413 | ehci_info (ehci, "debug port %d%s\n", | ||
414 | HCS_DEBUG_PORT(ehci->hcs_params), | ||
415 | (temp & DBGP_ENABLED) | ||
416 | ? " IN USE" | ||
417 | : ""); | ||
418 | if (!(temp & DBGP_ENABLED)) | ||
419 | ehci->debug = NULL; | ||
420 | } | ||
421 | } | ||
422 | |||
386 | temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); | 423 | temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); |
387 | } else | 424 | } else |
388 | temp = 0; | 425 | temp = 0; |
426 | |||
427 | /* EHCI 0.96 and later may have "extended capabilities" */ | ||
389 | while (temp && count--) { | 428 | while (temp && count--) { |
390 | u32 cap; | 429 | u32 cap; |
391 | 430 | ||
@@ -414,8 +453,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd) | |||
414 | ehci_reset (ehci); | 453 | ehci_reset (ehci); |
415 | #endif | 454 | #endif |
416 | 455 | ||
417 | /* cache this readonly data; minimize PCI reads */ | 456 | ehci_port_power (ehci, 0); |
418 | ehci->hcs_params = readl (&ehci->caps->hcs_params); | ||
419 | 457 | ||
420 | /* at least the Genesys GL880S needs fixup here */ | 458 | /* at least the Genesys GL880S needs fixup here */ |
421 | temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); | 459 | temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); |
@@ -657,16 +695,11 @@ done2: | |||
657 | static void ehci_stop (struct usb_hcd *hcd) | 695 | static void ehci_stop (struct usb_hcd *hcd) |
658 | { | 696 | { |
659 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 697 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
660 | u8 rh_ports, port; | ||
661 | 698 | ||
662 | ehci_dbg (ehci, "stop\n"); | 699 | ehci_dbg (ehci, "stop\n"); |
663 | 700 | ||
664 | /* Turn off port power on all root hub ports. */ | 701 | /* Turn off port power on all root hub ports. */ |
665 | rh_ports = HCS_N_PORTS (ehci->hcs_params); | 702 | ehci_port_power (ehci, 0); |
666 | for (port = 1; port <= rh_ports; port++) | ||
667 | (void) ehci_hub_control(hcd, | ||
668 | ClearPortFeature, USB_PORT_FEAT_POWER, | ||
669 | port, NULL, 0); | ||
670 | 703 | ||
671 | /* no more interrupts ... */ | 704 | /* no more interrupts ... */ |
672 | del_timer_sync (&ehci->watchdog); | 705 | del_timer_sync (&ehci->watchdog); |
@@ -748,7 +781,6 @@ static int ehci_resume (struct usb_hcd *hcd) | |||
748 | unsigned port; | 781 | unsigned port; |
749 | struct usb_device *root = hcd->self.root_hub; | 782 | struct usb_device *root = hcd->self.root_hub; |
750 | int retval = -EINVAL; | 783 | int retval = -EINVAL; |
751 | int powerup = 0; | ||
752 | 784 | ||
753 | // maybe restore (PCI) FLADJ | 785 | // maybe restore (PCI) FLADJ |
754 | 786 | ||
@@ -766,8 +798,6 @@ static int ehci_resume (struct usb_hcd *hcd) | |||
766 | up (&hcd->self.root_hub->serialize); | 798 | up (&hcd->self.root_hub->serialize); |
767 | break; | 799 | break; |
768 | } | 800 | } |
769 | if ((status & PORT_POWER) == 0) | ||
770 | powerup = 1; | ||
771 | if (!root->children [port]) | 801 | if (!root->children [port]) |
772 | continue; | 802 | continue; |
773 | dbg_port (ehci, __FUNCTION__, port + 1, status); | 803 | dbg_port (ehci, __FUNCTION__, port + 1, status); |
@@ -794,16 +824,9 @@ static int ehci_resume (struct usb_hcd *hcd) | |||
794 | retval = ehci_start (hcd); | 824 | retval = ehci_start (hcd); |
795 | 825 | ||
796 | /* here we "know" root ports should always stay powered; | 826 | /* here we "know" root ports should always stay powered; |
797 | * but some controllers may lost all power. | 827 | * but some controllers may lose all power. |
798 | */ | 828 | */ |
799 | if (powerup) { | 829 | ehci_port_power (ehci, 1); |
800 | ehci_dbg (ehci, "...powerup ports...\n"); | ||
801 | for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) | ||
802 | (void) ehci_hub_control(hcd, | ||
803 | SetPortFeature, USB_PORT_FEAT_POWER, | ||
804 | port--, NULL, 0); | ||
805 | msleep(20); | ||
806 | } | ||
807 | } | 830 | } |
808 | 831 | ||
809 | return retval; | 832 | return retval; |