diff options
Diffstat (limited to 'drivers/usb/host/ohci-hcd.c')
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 51 |
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"; | |||
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 | ||