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>
This commit is contained in:
parent
b1878440d4
commit
052ac01aeb
2 changed files with 18 additions and 16 deletions
|
@ -729,6 +729,16 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
|
|||
ohci->next_statechange = jiffies + STATECHANGE_DELAY;
|
||||
ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC,
|
||||
®s->intrstatus);
|
||||
|
||||
/* NOTE: Vendors didn't always make the same implementation
|
||||
* choices for RHSC. Many followed the spec; RHSC triggers
|
||||
* on an edge, like setting and maybe clearing a port status
|
||||
* change bit. With others it's level-triggered, active
|
||||
* until khubd clears all the port status change bits. We'll
|
||||
* always disable it here and rely on polling until khubd
|
||||
* re-enables it.
|
||||
*/
|
||||
ohci_writel(ohci, OHCI_INTR_RHSC, ®s->intrdisable);
|
||||
usb_hcd_poll_rh_status(hcd);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,11 @@ static void ohci_rhsc_enable (struct usb_hcd *hcd)
|
|||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
|
||||
ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
|
||||
spin_lock_irq(&ohci->lock);
|
||||
if (!ohci->autostop)
|
||||
del_timer(&hcd->rh_timer); /* Prevent next poll */
|
||||
ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
|
||||
spin_unlock_irq(&ohci->lock);
|
||||
}
|
||||
|
||||
#define OHCI_SCHED_ENABLES \
|
||||
|
@ -348,7 +352,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
|
|||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
int i, changed = 0, length = 1;
|
||||
int any_connected = 0, rhsc_enabled = 1;
|
||||
int any_connected = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave (&ohci->lock, flags);
|
||||
|
@ -389,19 +393,6 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
|
|||
}
|
||||
}
|
||||
|
||||
/* NOTE: vendors didn't always make the same implementation
|
||||
* choices for RHSC. Sometimes it triggers on an edge (like
|
||||
* setting and maybe clearing a port status change bit); and
|
||||
* it's level-triggered on other silicon, active until khubd
|
||||
* clears all active port status change bits. If it's still
|
||||
* set (level-triggered) we must disable it and rely on
|
||||
* polling until khubd re-enables it.
|
||||
*/
|
||||
if (ohci_readl (ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) {
|
||||
ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable);
|
||||
(void) ohci_readl (ohci, &ohci->regs->intrdisable);
|
||||
rhsc_enabled = 0;
|
||||
}
|
||||
hcd->poll_rh = 1;
|
||||
|
||||
/* carry out appropriate state changes */
|
||||
|
@ -412,7 +403,8 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
|
|||
* and RHSC is enabled */
|
||||
if (!ohci->autostop) {
|
||||
if (any_connected) {
|
||||
if (rhsc_enabled)
|
||||
if (ohci_readl(ohci, &ohci->regs->intrenable) &
|
||||
OHCI_INTR_RHSC)
|
||||
hcd->poll_rh = 0;
|
||||
} else {
|
||||
ohci->autostop = 1;
|
||||
|
|
Loading…
Reference in a new issue