diff options
Diffstat (limited to 'drivers/usb/core/hcd.c')
| -rw-r--r-- | drivers/usb/core/hcd.c | 53 | 
1 files changed, 40 insertions, 13 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index bf10e9c4195..42a436478b7 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -818,12 +818,12 @@ static int usb_register_bus(struct usb_bus *bus)  	set_bit (busnum, busmap.busmap);  	bus->busnum = busnum; -	bus->dev = device_create(usb_host_class, bus->controller, MKDEV(0, 0), -				 "usb_host%d", busnum); +	bus->dev = device_create_drvdata(usb_host_class, bus->controller, +					 MKDEV(0, 0), bus, +					 "usb_host%d", busnum);  	result = PTR_ERR(bus->dev);  	if (IS_ERR(bus->dev))  		goto error_create_class_dev; -	dev_set_drvdata(bus->dev, bus);  	/* Add it to the local list of buses */  	list_add (&bus->bus_list, &usb_bus_list); @@ -924,6 +924,15 @@ static int register_root_hub(struct usb_hcd *hcd)  	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); +} +  /*-------------------------------------------------------------------------*/ @@ -1684,19 +1693,30 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);  irqreturn_t usb_hcd_irq (int irq, void *__hcd)  {  	struct usb_hcd		*hcd = __hcd; -	int			start = hcd->state; +	unsigned long		flags; +	irqreturn_t		rc; -	if (unlikely(start == HC_STATE_HALT || -	    !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) -		return IRQ_NONE; -	if (hcd->driver->irq (hcd) == IRQ_NONE) -		return IRQ_NONE; +	/* IRQF_DISABLED doesn't work correctly with shared IRQs +	 * when the first handler doesn't use it.  So let's just +	 * assume it's never used. +	 */ +	local_irq_save(flags); -	set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); +	if (unlikely(hcd->state == HC_STATE_HALT || +		     !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) { +		rc = IRQ_NONE; +	} else if (hcd->driver->irq(hcd) == IRQ_NONE) { +		rc = IRQ_NONE; +	} else { +		set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); + +		if (unlikely(hcd->state == HC_STATE_HALT)) +			usb_hc_died(hcd); +		rc = IRQ_HANDLED; +	} -	if (unlikely(hcd->state == HC_STATE_HALT)) -		usb_hc_died (hcd); -	return IRQ_HANDLED; +	local_irq_restore(flags); +	return rc;  }  /*-------------------------------------------------------------------------*/ @@ -1860,6 +1880,13 @@ int usb_add_hcd(struct usb_hcd *hcd,  	/* enable irqs just before we start the controller */  	if (hcd->driver->irq) { + +		/* IRQF_DISABLED doesn't work as advertised when used together +		 * with IRQF_SHARED. As usb_hcd_irq() will always disable +		 * interrupts we can remove it here. +		 */ +		irqflags &= ~IRQF_DISABLED; +  		snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",  				hcd->driver->description, hcd->self.busnum);  		if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,  |