diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/hcd.c | 38 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 19 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 15 | ||||
-rw-r--r-- | drivers/usb/host/ohci-q.c | 12 | ||||
-rw-r--r-- | drivers/usb/misc/sisusbvga/sisusb.c | 2 |
5 files changed, 64 insertions, 22 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e52ed1663b3c..27533b3ba8b9 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1685,19 +1685,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); - return IRQ_HANDLED; + if (unlikely(hcd->state == HC_STATE_HALT)) + usb_hc_died(hcd); + rc = IRQ_HANDLED; + } + + local_irq_restore(flags); + return rc; } /*-------------------------------------------------------------------------*/ @@ -1861,6 +1872,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, diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bf92d209a1a9..888e81e607c8 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -176,6 +176,15 @@ timer_action_done (struct ehci_hcd *ehci, enum ehci_timer_action action) static inline void timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) { + /* Don't override timeouts which shrink or (later) disable + * the async ring; just the I/O watchdog. Note that if a + * SHRINK were pending, OFF would never be requested. + */ + if (timer_pending(&ehci->watchdog) + && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) + & ehci->actions)) + return; + if (!test_and_set_bit (action, &ehci->actions)) { unsigned long t; @@ -191,15 +200,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) t = EHCI_SHRINK_JIFFIES; break; } - t += jiffies; - // all timings except IAA watchdog can be overridden. - // async queue SHRINK often precedes IAA. while it's ready - // to go OFF neither can matter, and afterwards the IO - // watchdog stops unless there's still periodic traffic. - if (time_before_eq(t, ehci->watchdog.expires) - && timer_pending (&ehci->watchdog)) - return; - mod_timer (&ehci->watchdog, t); + mod_timer(&ehci->watchdog, t + jiffies); } } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 33f1c1c32edf..a8160d65f32b 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1054,7 +1054,7 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_MFD_SM501 #include "ohci-sm501.c" -#define PLATFORM_DRIVER ohci_hcd_sm501_driver +#define SM501_OHCI_DRIVER ohci_hcd_sm501_driver #endif #if !defined(PCI_DRIVER) && \ @@ -1062,6 +1062,7 @@ MODULE_LICENSE ("GPL"); !defined(OF_PLATFORM_DRIVER) && \ !defined(SA1111_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(SM501_OHCI_DRIVER) && \ !defined(SSB_OHCI_DRIVER) #error "missing bus glue for ohci-hcd" #endif @@ -1121,9 +1122,18 @@ static int __init ohci_hcd_mod_init(void) goto error_ssb; #endif +#ifdef SM501_OHCI_DRIVER + retval = platform_driver_register(&SM501_OHCI_DRIVER); + if (retval < 0) + goto error_sm501; +#endif + return retval; /* Error path */ +#ifdef SM501_OHCI_DRIVER + error_sm501: +#endif #ifdef SSB_OHCI_DRIVER error_ssb: #endif @@ -1159,6 +1169,9 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { +#ifdef SM501_OHCI_DRIVER + platform_driver_unregister(&SM501_OHCI_DRIVER); +#endif #ifdef SSB_OHCI_DRIVER ssb_driver_unregister(&SSB_OHCI_DRIVER); #endif diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 9c9f3b59186f..9b547407c934 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -952,6 +952,7 @@ rescan_this: struct urb *urb; urb_priv_t *urb_priv; __hc32 savebits; + u32 tdINFO; td = list_entry (entry, struct td, td_list); urb = td->urb; @@ -966,6 +967,17 @@ rescan_this: savebits = *prev & ~cpu_to_hc32 (ohci, TD_MASK); *prev = td->hwNextTD | savebits; + /* If this was unlinked, the TD may not have been + * retired ... so manually save the data toggle. + * The controller ignores the value we save for + * control and ISO endpoints. + */ + tdINFO = hc32_to_cpup(ohci, &td->hwINFO); + if ((tdINFO & TD_T) == TD_T_DATA0) + ed->hwHeadP &= ~cpu_to_hc32(ohci, ED_C); + else if ((tdINFO & TD_T) == TD_T_DATA1) + ed->hwHeadP |= cpu_to_hc32(ohci, ED_C); + /* HC may have partly processed this TD */ td_done (ohci, urb, td); urb_priv->td_cnt++; diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index cb7fa0eaf3ae..33182f4c2267 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3264,8 +3264,6 @@ static void sisusb_disconnect(struct usb_interface *intf) /* decrement our usage count */ kref_put(&sisusb->kref, sisusb_delete); - - dev_info(&sisusb->sisusb_dev->dev, "Disconnected\n"); } static struct usb_device_id sisusb_table [] = { |