aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorMichael Hanselmann <linux-kernel@hansmi.ch>2007-05-31 17:34:27 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-12 19:34:29 -0400
commitd576bb9f2769b315a795f77f0c33322a976add7a (patch)
tree56d59afa4d1e96b519e81434bd09598f76bf1fee /drivers/usb
parent020363384adfb02f26c1c038a127ed3da3d5cf37 (diff)
USB: Fix NEC OHCI chip silicon bug
This patch fixes a silicon bug in some NEC OHCI chips. The bug appears at random times and is very, very difficult to reproduce. Without the following patch, Linux would shut the chip and its associated devices down. In Apple PowerBooks this leads to an unusable keyboard and mouse (SSH still working). The idea of restarting the chip is taken from public Darwin code. Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/ohci-hcd.c51
-rw-r--r--drivers/usb/host/ohci-hub.c5
-rw-r--r--drivers/usb/host/ohci-mem.c1
-rw-r--r--drivers/usb/host/ohci-pci.c16
-rw-r--r--drivers/usb/host/ohci.h2
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";
82static void ohci_dump (struct ohci_hcd *ohci, int verbose); 83static void ohci_dump (struct ohci_hcd *ohci, int verbose);
83static int ohci_init (struct ohci_hcd *ohci); 84static int ohci_init (struct ohci_hcd *ohci);
84static void ohci_stop (struct usb_hcd *hcd); 85static void ohci_stop (struct usb_hcd *hcd);
86static int ohci_restart (struct ohci_hcd *ohci);
87static 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, &regs->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
761static int ohci_restart (struct ohci_hcd *ohci) 772static 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 */
836static 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 *);
55static void finish_unlinks (struct ohci_hcd *, u16); 55static void finish_unlinks (struct ohci_hcd *, u16);
56 56
57#ifdef CONFIG_PM 57#ifdef CONFIG_PM
58static int ohci_restart(struct ohci_hcd *ohci);
59
60static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) 58static 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 */
116static 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 */
115static const struct pci_device_id ohci_pci_quirks[] = { 127static 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 */