diff options
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 51 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ohci-mem.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ohci-pci.c | 16 | ||||
-rw-r--r-- | drivers/usb/host/ohci.h | 2 |
5 files changed, 61 insertions, 14 deletions
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index ce05e5f7bed6..44717fab7435 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/dma-mapping.h> | 35 | #include <linux/dma-mapping.h> |
36 | #include <linux/dmapool.h> | 36 | #include <linux/dmapool.h> |
37 | #include <linux/reboot.h> | 37 | #include <linux/reboot.h> |
38 | #include <linux/workqueue.h> | ||
38 | 39 | ||
39 | #include <asm/io.h> | 40 | #include <asm/io.h> |
40 | #include <asm/irq.h> | 41 | #include <asm/irq.h> |
@@ -82,6 +83,8 @@ static const char hcd_name [] = "ohci_hcd"; | |||
82 | static void ohci_dump (struct ohci_hcd *ohci, int verbose); | 83 | static void ohci_dump (struct ohci_hcd *ohci, int verbose); |
83 | static int ohci_init (struct ohci_hcd *ohci); | 84 | static int ohci_init (struct ohci_hcd *ohci); |
84 | static void ohci_stop (struct usb_hcd *hcd); | 85 | static void ohci_stop (struct usb_hcd *hcd); |
86 | static int ohci_restart (struct ohci_hcd *ohci); | ||
87 | static void ohci_quirk_nec_worker (struct work_struct *work); | ||
85 | 88 | ||
86 | #include "ohci-hub.c" | 89 | #include "ohci-hub.c" |
87 | #include "ohci-dbg.c" | 90 | #include "ohci-dbg.c" |
@@ -651,9 +654,20 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) | |||
651 | } | 654 | } |
652 | 655 | ||
653 | if (ints & OHCI_INTR_UE) { | 656 | if (ints & OHCI_INTR_UE) { |
654 | disable (ohci); | ||
655 | ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); | ||
656 | // e.g. due to PCI Master/Target Abort | 657 | // e.g. due to PCI Master/Target Abort |
658 | if (ohci->flags & OHCI_QUIRK_NEC) { | ||
659 | /* Workaround for a silicon bug in some NEC chips used | ||
660 | * in Apple's PowerBooks. Adapted from Darwin code. | ||
661 | */ | ||
662 | ohci_err (ohci, "OHCI Unrecoverable Error, scheduling NEC chip restart\n"); | ||
663 | |||
664 | ohci_writel (ohci, OHCI_INTR_UE, ®s->intrdisable); | ||
665 | |||
666 | schedule_work (&ohci->nec_work); | ||
667 | } else { | ||
668 | disable (ohci); | ||
669 | ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); | ||
670 | } | ||
657 | 671 | ||
658 | ohci_dump (ohci, 1); | 672 | ohci_dump (ohci, 1); |
659 | ohci_usb_reset (ohci); | 673 | ohci_usb_reset (ohci); |
@@ -755,23 +769,16 @@ static void ohci_stop (struct usb_hcd *hcd) | |||
755 | /*-------------------------------------------------------------------------*/ | 769 | /*-------------------------------------------------------------------------*/ |
756 | 770 | ||
757 | /* must not be called from interrupt context */ | 771 | /* must not be called from interrupt context */ |
758 | |||
759 | #ifdef CONFIG_PM | ||
760 | |||
761 | static int ohci_restart (struct ohci_hcd *ohci) | 772 | static int ohci_restart (struct ohci_hcd *ohci) |
762 | { | 773 | { |
763 | int temp; | 774 | int temp; |
764 | int i; | 775 | int i; |
765 | struct urb_priv *priv; | 776 | struct urb_priv *priv; |
766 | 777 | ||
767 | /* mark any devices gone, so they do nothing till khubd disconnects. | ||
768 | * recycle any "live" eds/tds (and urbs) right away. | ||
769 | * later, khubd disconnect processing will recycle the other state, | ||
770 | * (either as disconnect/reconnect, or maybe someday as a reset). | ||
771 | */ | ||
772 | spin_lock_irq(&ohci->lock); | 778 | spin_lock_irq(&ohci->lock); |
773 | disable (ohci); | 779 | disable (ohci); |
774 | usb_root_hub_lost_power(ohci_to_hcd(ohci)->self.root_hub); | 780 | |
781 | /* Recycle any "live" eds/tds (and urbs). */ | ||
775 | if (!list_empty (&ohci->pending)) | 782 | if (!list_empty (&ohci->pending)) |
776 | ohci_dbg(ohci, "abort schedule...\n"); | 783 | ohci_dbg(ohci, "abort schedule...\n"); |
777 | list_for_each_entry (priv, &ohci->pending, pending) { | 784 | list_for_each_entry (priv, &ohci->pending, pending) { |
@@ -822,7 +829,27 @@ static int ohci_restart (struct ohci_hcd *ohci) | |||
822 | ohci_dbg(ohci, "restart complete\n"); | 829 | ohci_dbg(ohci, "restart complete\n"); |
823 | return 0; | 830 | return 0; |
824 | } | 831 | } |
825 | #endif | 832 | |
833 | /*-------------------------------------------------------------------------*/ | ||
834 | |||
835 | /* NEC workaround */ | ||
836 | static void ohci_quirk_nec_worker(struct work_struct *work) | ||
837 | { | ||
838 | struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work); | ||
839 | int status; | ||
840 | |||
841 | status = ohci_init(ohci); | ||
842 | if (status != 0) { | ||
843 | ohci_err(ohci, "Restarting NEC controller failed " | ||
844 | "in ohci_init, %d\n", status); | ||
845 | return; | ||
846 | } | ||
847 | |||
848 | status = ohci_restart(ohci); | ||
849 | if (status != 0) | ||
850 | ohci_err(ohci, "Restarting NEC controller failed " | ||
851 | "in ohci_restart, %d\n", status); | ||
852 | } | ||
826 | 853 | ||
827 | /*-------------------------------------------------------------------------*/ | 854 | /*-------------------------------------------------------------------------*/ |
828 | 855 | ||
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index bb9cc595219e..48e4b11f4d3e 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
@@ -55,8 +55,6 @@ static void dl_done_list (struct ohci_hcd *); | |||
55 | static void finish_unlinks (struct ohci_hcd *, u16); | 55 | static void finish_unlinks (struct ohci_hcd *, u16); |
56 | 56 | ||
57 | #ifdef CONFIG_PM | 57 | #ifdef CONFIG_PM |
58 | static int ohci_restart(struct ohci_hcd *ohci); | ||
59 | |||
60 | static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) | 58 | static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) |
61 | __releases(ohci->lock) | 59 | __releases(ohci->lock) |
62 | __acquires(ohci->lock) | 60 | __acquires(ohci->lock) |
@@ -191,6 +189,9 @@ __acquires(ohci->lock) | |||
191 | spin_unlock_irq (&ohci->lock); | 189 | spin_unlock_irq (&ohci->lock); |
192 | (void) ohci_init (ohci); | 190 | (void) ohci_init (ohci); |
193 | status = ohci_restart (ohci); | 191 | status = ohci_restart (ohci); |
192 | |||
193 | usb_root_hub_lost_power(hcd->self.root_hub); | ||
194 | |||
194 | spin_lock_irq (&ohci->lock); | 195 | spin_lock_irq (&ohci->lock); |
195 | } | 196 | } |
196 | return status; | 197 | return status; |
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index 2f20d3dc895b..450c7b460c5a 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c | |||
@@ -28,6 +28,7 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) | |||
28 | ohci->next_statechange = jiffies; | 28 | ohci->next_statechange = jiffies; |
29 | spin_lock_init (&ohci->lock); | 29 | spin_lock_init (&ohci->lock); |
30 | INIT_LIST_HEAD (&ohci->pending); | 30 | INIT_LIST_HEAD (&ohci->pending); |
31 | INIT_WORK (&ohci->nec_work, ohci_quirk_nec_worker); | ||
31 | } | 32 | } |
32 | 33 | ||
33 | /*-------------------------------------------------------------------------*/ | 34 | /*-------------------------------------------------------------------------*/ |
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 15013f4519ad..a5e2eb85d073 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c | |||
@@ -111,6 +111,18 @@ static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd) | |||
111 | #endif | 111 | #endif |
112 | } | 112 | } |
113 | 113 | ||
114 | /* Check for NEC chip and apply quirk for allegedly lost interrupts. | ||
115 | */ | ||
116 | static int ohci_quirk_nec(struct usb_hcd *hcd) | ||
117 | { | ||
118 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
119 | |||
120 | ohci->flags |= OHCI_QUIRK_NEC; | ||
121 | ohci_dbg (ohci, "enabled NEC chipset lost interrupt quirk\n"); | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
114 | /* List of quirks for OHCI */ | 126 | /* List of quirks for OHCI */ |
115 | static const struct pci_device_id ohci_pci_quirks[] = { | 127 | static const struct pci_device_id ohci_pci_quirks[] = { |
116 | { | 128 | { |
@@ -134,6 +146,10 @@ static const struct pci_device_id ohci_pci_quirks[] = { | |||
134 | .driver_data = (unsigned long)ohci_quirk_toshiba_scc, | 146 | .driver_data = (unsigned long)ohci_quirk_toshiba_scc, |
135 | }, | 147 | }, |
136 | { | 148 | { |
149 | PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB), | ||
150 | .driver_data = (unsigned long)ohci_quirk_nec, | ||
151 | }, | ||
152 | { | ||
137 | /* Toshiba portege 4000 */ | 153 | /* Toshiba portege 4000 */ |
138 | .vendor = PCI_VENDOR_ID_AL, | 154 | .vendor = PCI_VENDOR_ID_AL, |
139 | .device = 0x5237, | 155 | .device = 0x5237, |
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index c2b5ecfe5e9f..4ada43cf1387 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h | |||
@@ -397,8 +397,10 @@ struct ohci_hcd { | |||
397 | #define OHCI_QUIRK_BE_DESC 0x08 /* BE descriptors */ | 397 | #define OHCI_QUIRK_BE_DESC 0x08 /* BE descriptors */ |
398 | #define OHCI_QUIRK_BE_MMIO 0x10 /* BE registers */ | 398 | #define OHCI_QUIRK_BE_MMIO 0x10 /* BE registers */ |
399 | #define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/ | 399 | #define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/ |
400 | #define OHCI_QUIRK_NEC 0x40 /* lost interrupts */ | ||
400 | // there are also chip quirks/bugs in init logic | 401 | // there are also chip quirks/bugs in init logic |
401 | 402 | ||
403 | struct work_struct nec_work; /* Worker for NEC quirk */ | ||
402 | }; | 404 | }; |
403 | 405 | ||
404 | /* convert between an hcd pointer and the corresponding ohci_hcd */ | 406 | /* convert between an hcd pointer and the corresponding ohci_hcd */ |