aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ohci-hcd.c
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/host/ohci-hcd.c
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/host/ohci-hcd.c')
-rw-r--r--drivers/usb/host/ohci-hcd.c51
1 files changed, 39 insertions, 12 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