Commit 09ca8adb authored by Linus Torvalds's avatar Linus Torvalds

Revert "USB: don't explicitly reenable root-hub status interrupts"

This reverts commit e8721549.

Andrey Borzenkov reports that it resulted in a totally hung machine for
him when loading the OHCI driver.  Extensive netconsole capture with
SysRq output shows that modprobe gets stuck in ohci_hub_status_data()
when probing and enabling the OHCI controller, see for example

	http://lkml.org/lkml/2008/7/5/236

for an analysis.

The problem appears to be an interrupt flood triggered by the commit
that gets reverted, and Andrey confirmed that the revert makes things
work for him again.
Reported-and-tested-by: default avatarAndrey Borzenkov <arvidjaar@mail.ru>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Acked-by: default avatarDavid Brownell <david-b@pacbell.net>
Cc: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b7279469
...@@ -924,6 +924,15 @@ static int register_root_hub(struct usb_hcd *hcd) ...@@ -924,6 +924,15 @@ static int register_root_hub(struct usb_hcd *hcd)
return retval; return retval;
} }
void usb_enable_root_hub_irq (struct usb_bus *bus)
{
struct usb_hcd *hcd;
hcd = container_of (bus, struct usb_hcd, self);
if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT)
hcd->driver->hub_irq_enable (hcd);
}
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
...@@ -210,6 +210,8 @@ struct hc_driver { ...@@ -210,6 +210,8 @@ struct hc_driver {
int (*bus_suspend)(struct usb_hcd *); int (*bus_suspend)(struct usb_hcd *);
int (*bus_resume)(struct usb_hcd *); int (*bus_resume)(struct usb_hcd *);
int (*start_port_reset)(struct usb_hcd *, unsigned port_num); int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
void (*hub_irq_enable)(struct usb_hcd *);
/* Needed only if port-change IRQs are level-triggered */
/* force handover of high-speed port to full-speed companion */ /* force handover of high-speed port to full-speed companion */
void (*relinquish_port)(struct usb_hcd *, int); void (*relinquish_port)(struct usb_hcd *, int);
......
...@@ -2073,6 +2073,8 @@ int usb_port_resume(struct usb_device *udev) ...@@ -2073,6 +2073,8 @@ int usb_port_resume(struct usb_device *udev)
} }
clear_bit(port1, hub->busy_bits); clear_bit(port1, hub->busy_bits);
if (!hub->hdev->parent && !hub->busy_bits[0])
usb_enable_root_hub_irq(hub->hdev->bus);
if (status == 0) if (status == 0)
status = finish_port_resume(udev); status = finish_port_resume(udev);
...@@ -3002,6 +3004,11 @@ static void hub_events(void) ...@@ -3002,6 +3004,11 @@ static void hub_events(void)
hub->activating = 0; hub->activating = 0;
/* If this is a root hub, tell the HCD it's okay to
* re-enable port-change interrupts now. */
if (!hdev->parent && !hub->busy_bits[0])
usb_enable_root_hub_irq(hdev->bus);
loop_autopm: loop_autopm:
/* Allow autosuspend if we're not going to run again */ /* Allow autosuspend if we're not going to run again */
if (list_empty(&hub->event_list)) if (list_empty(&hub->event_list))
...@@ -3227,6 +3234,8 @@ int usb_reset_device(struct usb_device *udev) ...@@ -3227,6 +3234,8 @@ int usb_reset_device(struct usb_device *udev)
break; break;
} }
clear_bit(port1, parent_hub->busy_bits); clear_bit(port1, parent_hub->busy_bits);
if (!parent_hdev->parent && !parent_hub->busy_bits[0])
usb_enable_root_hub_irq(parent_hdev->bus);
if (ret < 0) if (ret < 0)
goto re_enumerate; goto re_enumerate;
......
...@@ -261,6 +261,7 @@ static const struct hc_driver ohci_at91_hc_driver = { ...@@ -261,6 +261,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -288,6 +288,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = { ...@@ -288,6 +288,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -135,6 +135,7 @@ static struct hc_driver ohci_ep93xx_hc_driver = { ...@@ -135,6 +135,7 @@ static struct hc_driver ohci_ep93xx_hc_driver = {
.get_frame_number = ohci_get_frame, .get_frame_number = ohci_get_frame,
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -36,6 +36,18 @@ ...@@ -36,6 +36,18 @@
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
/* hcd->hub_irq_enable() */
static void ohci_rhsc_enable (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
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 \ #define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
...@@ -362,28 +374,18 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, ...@@ -362,28 +374,18 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
int any_connected) int any_connected)
{ {
int poll_rh = 1; int poll_rh = 1;
int rhsc;
rhsc = ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC;
switch (ohci->hc_control & OHCI_CTRL_HCFS) { switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_OPER: case OHCI_USB_OPER:
/* If no status changes are pending, enable status-change /* keep on polling until we know a device is connected
* interrupts. * and RHSC is enabled */
*/
if (!rhsc && !changed) {
rhsc = OHCI_INTR_RHSC;
ohci_writel(ohci, rhsc, &ohci->regs->intrenable);
}
/* Keep on polling until we know a device is connected
* and RHSC is enabled, or until we autostop.
*/
if (!ohci->autostop) { if (!ohci->autostop) {
if (any_connected || if (any_connected ||
!device_may_wakeup(&ohci_to_hcd(ohci) !device_may_wakeup(&ohci_to_hcd(ohci)
->self.root_hub->dev)) { ->self.root_hub->dev)) {
if (rhsc) if (ohci_readl(ohci, &ohci->regs->intrenable) &
OHCI_INTR_RHSC)
poll_rh = 0; poll_rh = 0;
} else { } else {
ohci->autostop = 1; ohci->autostop = 1;
...@@ -396,13 +398,12 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, ...@@ -396,13 +398,12 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
ohci->autostop = 0; ohci->autostop = 0;
ohci->next_statechange = jiffies + ohci->next_statechange = jiffies +
STATECHANGE_DELAY; STATECHANGE_DELAY;
} else if (rhsc && time_after_eq(jiffies, } else if (time_after_eq(jiffies,
ohci->next_statechange) ohci->next_statechange)
&& !ohci->ed_rm_list && !ohci->ed_rm_list
&& !(ohci->hc_control & && !(ohci->hc_control &
OHCI_SCHED_ENABLES)) { OHCI_SCHED_ENABLES)) {
ohci_rh_suspend(ohci, 1); ohci_rh_suspend(ohci, 1);
poll_rh = 0;
} }
} }
break; break;
...@@ -416,12 +417,6 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, ...@@ -416,12 +417,6 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
else else
usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); usb_hcd_resume_root_hub(ohci_to_hcd(ohci));
} else { } else {
if (!rhsc && (ohci->autostop ||
ohci_to_hcd(ohci)->self.root_hub->
do_remote_wakeup))
ohci_writel(ohci, OHCI_INTR_RHSC,
&ohci->regs->intrenable);
/* everything is idle, no need for polling */ /* everything is idle, no need for polling */
poll_rh = 0; poll_rh = 0;
} }
...@@ -443,16 +438,12 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci) ...@@ -443,16 +438,12 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci)
static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
int any_connected) int any_connected)
{ {
/* If RHSC is enabled, don't poll */ int poll_rh = 1;
if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)
return 0;
/* If no status changes are pending, enable status-change interrupts */ /* keep on polling until RHSC is enabled */
if (!changed) { if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)
ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); poll_rh = 0;
return 0; return poll_rh;
}
return 1;
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
......
...@@ -193,6 +193,7 @@ static const struct hc_driver ohci_lh7a404_hc_driver = { ...@@ -193,6 +193,7 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -466,6 +466,7 @@ static const struct hc_driver ohci_omap_hc_driver = { ...@@ -466,6 +466,7 @@ static const struct hc_driver ohci_omap_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -327,6 +327,7 @@ static const struct hc_driver ohci_pci_hc_driver = { ...@@ -327,6 +327,7 @@ static const struct hc_driver ohci_pci_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -280,6 +280,7 @@ static const struct hc_driver ohci_pnx4008_hc_driver = { ...@@ -280,6 +280,7 @@ static const struct hc_driver ohci_pnx4008_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -201,6 +201,7 @@ static const struct hc_driver ohci_pnx8550_hc_driver = { ...@@ -201,6 +201,7 @@ static const struct hc_driver ohci_pnx8550_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -72,6 +72,7 @@ static const struct hc_driver ohci_ppc_of_hc_driver = { ...@@ -72,6 +72,7 @@ static const struct hc_driver ohci_ppc_of_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -172,6 +172,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = { ...@@ -172,6 +172,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -68,6 +68,7 @@ static const struct hc_driver ps3_ohci_hc_driver = { ...@@ -68,6 +68,7 @@ static const struct hc_driver ps3_ohci_hc_driver = {
.get_frame_number = ohci_get_frame, .get_frame_number = ohci_get_frame,
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
.start_port_reset = ohci_start_port_reset, .start_port_reset = ohci_start_port_reset,
#if defined(CONFIG_PM) #if defined(CONFIG_PM)
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
......
...@@ -298,6 +298,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = { ...@@ -298,6 +298,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -466,6 +466,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = { ...@@ -466,6 +466,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
*/ */
.hub_status_data = ohci_s3c2410_hub_status_data, .hub_status_data = ohci_s3c2410_hub_status_data,
.hub_control = ohci_s3c2410_hub_control, .hub_control = ohci_s3c2410_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -231,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = { ...@@ -231,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -68,6 +68,7 @@ static const struct hc_driver ohci_sh_hc_driver = { ...@@ -68,6 +68,7 @@ static const struct hc_driver ohci_sh_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -75,6 +75,7 @@ static const struct hc_driver ohci_sm501_hc_driver = { ...@@ -75,6 +75,7 @@ static const struct hc_driver ohci_sm501_hc_driver = {
*/ */
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -81,6 +81,7 @@ static const struct hc_driver ssb_ohci_hc_driver = { ...@@ -81,6 +81,7 @@ static const struct hc_driver ssb_ohci_hc_driver = {
.hub_status_data = ohci_hub_status_data, .hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control, .hub_control = ohci_hub_control,
.hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend, .bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume, .bus_resume = ohci_bus_resume,
......
...@@ -2934,6 +2934,16 @@ static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num) ...@@ -2934,6 +2934,16 @@ static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num)
return 0; return 0;
} }
static void u132_hub_irq_enable(struct usb_hcd *hcd)
{
struct u132 *u132 = hcd_to_u132(hcd);
if (u132->going > 1) {
dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
, u132->going);
} else if (u132->going > 0)
dev_err(&u132->platform_dev->dev, "device is being removed\n");
}
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int u132_bus_suspend(struct usb_hcd *hcd) static int u132_bus_suspend(struct usb_hcd *hcd)
...@@ -2985,6 +2995,7 @@ static struct hc_driver u132_hc_driver = { ...@@ -2985,6 +2995,7 @@ static struct hc_driver u132_hc_driver = {
.bus_suspend = u132_bus_suspend, .bus_suspend = u132_bus_suspend,
.bus_resume = u132_bus_resume, .bus_resume = u132_bus_resume,
.start_port_reset = u132_start_port_reset, .start_port_reset = u132_start_port_reset,
.hub_irq_enable = u132_hub_irq_enable,
}; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment