diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-10-27 10:33:11 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-12-01 17:23:27 -0500 |
commit | 052ac01aeb84d8427ba8ac3d70991ac01b009b59 (patch) | |
tree | b232d996dab32777156c4d9f661711e8a29aee86 /drivers/usb/host | |
parent | b1878440d46a0dc357ed5c9687c534e20955e940 (diff) |
USB: OHCI: disable RHSC inside interrupt handler
This patch (as808b) moves the Root Hub Status Change interrupt-disable
code in ohci-hcd back into the interrupt handler proper, to avoid the
chance of adverse interactions with mediocre hardware implementations.
It also deletes the root-hub status timer from within the interrupt-enable
routine. There's no need to poll for status any more once interrupts are
re-enabled.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 10 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 24 |
2 files changed, 18 insertions, 16 deletions
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index ea4714e557e4..a95275a401b1 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c | |||
@@ -729,6 +729,16 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) | |||
729 | ohci->next_statechange = jiffies + STATECHANGE_DELAY; | 729 | ohci->next_statechange = jiffies + STATECHANGE_DELAY; |
730 | ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC, | 730 | ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC, |
731 | ®s->intrstatus); | 731 | ®s->intrstatus); |
732 | |||
733 | /* NOTE: Vendors didn't always make the same implementation | ||
734 | * choices for RHSC. Many followed the spec; RHSC triggers | ||
735 | * on an edge, like setting and maybe clearing a port status | ||
736 | * change bit. With others it's level-triggered, active | ||
737 | * until khubd clears all the port status change bits. We'll | ||
738 | * always disable it here and rely on polling until khubd | ||
739 | * re-enables it. | ||
740 | */ | ||
741 | ohci_writel(ohci, OHCI_INTR_RHSC, ®s->intrdisable); | ||
732 | usb_hcd_poll_rh_status(hcd); | 742 | usb_hcd_poll_rh_status(hcd); |
733 | } | 743 | } |
734 | 744 | ||
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 99357b99d133..1e5ed3bb1a93 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
@@ -41,7 +41,11 @@ static void ohci_rhsc_enable (struct usb_hcd *hcd) | |||
41 | { | 41 | { |
42 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | 42 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
43 | 43 | ||
44 | ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); | 44 | spin_lock_irq(&ohci->lock); |
45 | if (!ohci->autostop) | ||
46 | del_timer(&hcd->rh_timer); /* Prevent next poll */ | ||
47 | ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); | ||
48 | spin_unlock_irq(&ohci->lock); | ||
45 | } | 49 | } |
46 | 50 | ||
47 | #define OHCI_SCHED_ENABLES \ | 51 | #define OHCI_SCHED_ENABLES \ |
@@ -348,7 +352,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
348 | { | 352 | { |
349 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | 353 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
350 | int i, changed = 0, length = 1; | 354 | int i, changed = 0, length = 1; |
351 | int any_connected = 0, rhsc_enabled = 1; | 355 | int any_connected = 0; |
352 | unsigned long flags; | 356 | unsigned long flags; |
353 | 357 | ||
354 | spin_lock_irqsave (&ohci->lock, flags); | 358 | spin_lock_irqsave (&ohci->lock, flags); |
@@ -389,19 +393,6 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
389 | } | 393 | } |
390 | } | 394 | } |
391 | 395 | ||
392 | /* NOTE: vendors didn't always make the same implementation | ||
393 | * choices for RHSC. Sometimes it triggers on an edge (like | ||
394 | * setting and maybe clearing a port status change bit); and | ||
395 | * it's level-triggered on other silicon, active until khubd | ||
396 | * clears all active port status change bits. If it's still | ||
397 | * set (level-triggered) we must disable it and rely on | ||
398 | * polling until khubd re-enables it. | ||
399 | */ | ||
400 | if (ohci_readl (ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) { | ||
401 | ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable); | ||
402 | (void) ohci_readl (ohci, &ohci->regs->intrdisable); | ||
403 | rhsc_enabled = 0; | ||
404 | } | ||
405 | hcd->poll_rh = 1; | 396 | hcd->poll_rh = 1; |
406 | 397 | ||
407 | /* carry out appropriate state changes */ | 398 | /* carry out appropriate state changes */ |
@@ -412,7 +403,8 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
412 | * and RHSC is enabled */ | 403 | * and RHSC is enabled */ |
413 | if (!ohci->autostop) { | 404 | if (!ohci->autostop) { |
414 | if (any_connected) { | 405 | if (any_connected) { |
415 | if (rhsc_enabled) | 406 | if (ohci_readl(ohci, &ohci->regs->intrenable) & |
407 | OHCI_INTR_RHSC) | ||
416 | hcd->poll_rh = 0; | 408 | hcd->poll_rh = 0; |
417 | } else { | 409 | } else { |
418 | ohci->autostop = 1; | 410 | ohci->autostop = 1; |