fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / usb / host / ehci-hcd.c
index 9dd3d14..6e9fdd6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2000-2004 by David Brownell
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; either version 2 of the License, or (at your
@@ -16,7 +16,6 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/dmapool.h>
@@ -71,7 +70,7 @@
  * 2002-08-06  Handling for bulk and interrupt transfers is mostly shared;
  *     only scheduling is different, no arbitrary limitations.
  * 2002-07-25  Sanity check PCI reads, mostly for better cardbus support,
- *     clean up HC run state handshaking.
+ *     clean up HC run state handshaking.
  * 2002-05-24  Preliminary FS/LS interrupts, using scheduling shortcuts
  * 2002-05-11  Clear TT errors for FS/LS ctrl/bulk.  Fill in some other
  *     missing pieces:  enabling 64bit dma, handoff from BIOS/SMM.
@@ -127,6 +126,11 @@ static unsigned park = 0;
 module_param (park, uint, S_IRUGO);
 MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
 
+/* for flakey hardware, ignore overcurrent indicators */
+static int ignore_oc = 0;
+module_param (ignore_oc, bool, S_IRUGO);
+MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
+
 #define        INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
 
 /*-------------------------------------------------------------------------*/
@@ -255,7 +259,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
 
 /*-------------------------------------------------------------------------*/
 
-static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
+static void ehci_work(struct ehci_hcd *ehci);
 
 #include "ehci-hub.c"
 #include "ehci-mem.c"
@@ -274,7 +278,6 @@ static void ehci_watchdog (unsigned long param)
        /* lost IAA irqs wedge things badly; seen with a vt8235 */
        if (ehci->reclaim) {
                u32             status = readl (&ehci->regs->status);
-
                if (status & STS_IAA) {
                        ehci_vdbg (ehci, "lost IAA\n");
                        COUNT (ehci->stats.lost_iaa);
@@ -285,29 +288,44 @@ static void ehci_watchdog (unsigned long param)
 
        /* stop async processing after it's idled a bit */
        if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
-               start_unlink_async (ehci, ehci->async);
+               start_unlink_async (ehci, ehci->async);
 
        /* ehci could run by timer, without IRQs ... */
-       ehci_work (ehci, NULL);
+       ehci_work (ehci);
 
        spin_unlock_irqrestore (&ehci->lock, flags);
 }
 
-/* Reboot notifiers kick in for silicon on any bus (not just pci, etc).
+/* On some systems, leaving remote wakeup enabled prevents system shutdown.
+ * The firmware seems to think that powering off is a wakeup event!
+ * This routine turns off remote wakeup and everything else, on all ports.
+ */
+static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
+{
+       int     port = HCS_N_PORTS(ehci->hcs_params);
+
+       while (port--)
+               writel(PORT_RWC_BITS, &ehci->regs->port_status[port]);
+}
+
+/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
  * This forcibly disables dma and IRQs, helping kexec and other cases
  * where the next system software may expect clean state.
  */
-static int
-ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
+static void
+ehci_shutdown (struct usb_hcd *hcd)
 {
-       struct ehci_hcd         *ehci;
+       struct ehci_hcd *ehci;
 
-       ehci = container_of (self, struct ehci_hcd, reboot_notifier);
+       ehci = hcd_to_ehci (hcd);
        (void) ehci_halt (ehci);
+       ehci_turn_off_all_ports(ehci);
 
        /* make BIOS/etc use companion controller during reboot */
        writel (0, &ehci->regs->configured_flag);
-       return 0;
+
+       /* unblock posted writes */
+       readl(&ehci->regs->configured_flag);
 }
 
 static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
@@ -332,11 +350,11 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
  * ehci_work is called from some interrupts, timers, and so on.
  * it calls driver completion functions, after dropping ehci->lock.
  */
-static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
+static void ehci_work (struct ehci_hcd *ehci)
 {
        timer_action_done (ehci, TIMER_IO_WATCHDOG);
        if (ehci->reclaim_ready)
-               end_unlink_async (ehci, regs);
+               end_unlink_async (ehci);
 
        /* another CPU may drop ehci->lock during a schedule scan while
         * it reports urb completions.  this flag guards against bogus
@@ -345,9 +363,9 @@ static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
        if (ehci->scanning)
                return;
        ehci->scanning = 1;
-       scan_async (ehci, regs);
+       scan_async (ehci);
        if (ehci->next_uframe != -1)
-               scan_periodic (ehci, regs);
+               scan_periodic (ehci);
        ehci->scanning = 0;
 
        /* the IO watchdog guards against hardware or driver bugs that
@@ -382,14 +400,13 @@ static void ehci_stop (struct usb_hcd *hcd)
 
        /* let companion controllers work when we aren't */
        writel (0, &ehci->regs->configured_flag);
-       unregister_reboot_notifier (&ehci->reboot_notifier);
 
        remove_debug_files (ehci);
 
        /* root hub is shut down separately (first, when possible) */
        spin_lock_irq (&ehci->lock);
        if (ehci->async)
-               ehci_work (ehci, NULL);
+               ehci_work (ehci);
        spin_unlock_irq (&ehci->lock);
        ehci_mem_cleanup (ehci);
 
@@ -428,7 +445,7 @@ static int ehci_init(struct usb_hcd *hcd)
 
        /* controllers may cache some of the periodic schedule ... */
        hcc_params = readl(&ehci->caps->hcc_params);
-       if (HCC_ISOC_CACHE(hcc_params))         // full frame cache
+       if (HCC_ISOC_CACHE(hcc_params))         // full frame cache
                ehci->i_thresh = 8;
        else                                    // N microframes cached
                ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
@@ -484,9 +501,6 @@ static int ehci_init(struct usb_hcd *hcd)
        }
        ehci->command = temp;
 
-       ehci->reboot_notifier.notifier_call = ehci_reboot;
-       register_reboot_notifier(&ehci->reboot_notifier);
-
        return 0;
 }
 
@@ -500,7 +514,6 @@ static int ehci_run (struct usb_hcd *hcd)
 
        /* EHCI spec section 4.1 */
        if ((retval = ehci_reset(ehci)) != 0) {
-               unregister_reboot_notifier(&ehci->reboot_notifier);
                ehci_mem_cleanup(ehci);
                return retval;
        }
@@ -549,9 +562,10 @@ static int ehci_run (struct usb_hcd *hcd)
 
        temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
        ehci_info (ehci,
-               "USB %x.%x started, EHCI %x.%02x, driver %s\n",
+               "USB %x.%x started, EHCI %x.%02x, driver %s%s\n",
                ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
-               temp >> 8, temp & 0xff, DRIVER_VERSION);
+               temp >> 8, temp & 0xff, DRIVER_VERSION,
+               ignore_oc ? ", overcurrent ignored" : "");
 
        writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
 
@@ -566,7 +580,7 @@ static int ehci_run (struct usb_hcd *hcd)
 
 /*-------------------------------------------------------------------------*/
 
-static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
+static irqreturn_t ehci_irq (struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
        u32                     status;
@@ -621,15 +635,15 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
                unsigned        i = HCS_N_PORTS (ehci->hcs_params);
 
                /* resume root hub? */
-               status = readl (&ehci->regs->command);
-               if (!(status & CMD_RUN))
-                       writel (status | CMD_RUN, &ehci->regs->command);
+               if (!(readl(&ehci->regs->command) & CMD_RUN))
+                       usb_hcd_resume_root_hub(hcd);
 
                while (i--) {
-                       status = readl (&ehci->regs->port_status [i]);
-                       if (status & PORT_OWNER)
+                       int pstatus = readl (&ehci->regs->port_status [i]);
+
+                       if (pstatus & PORT_OWNER)
                                continue;
-                       if (!(status & PORT_RESUME)
+                       if (!(pstatus & PORT_RESUME)
                                        || ehci->reset_done [i] != 0)
                                continue;
 
@@ -639,7 +653,6 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
                         */
                        ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
                        ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
-                       usb_hcd_resume_root_hub(hcd);
                }
        }
 
@@ -662,7 +675,7 @@ dead:
        }
 
        if (bh)
-               ehci_work (ehci, regs);
+               ehci_work (ehci);
        spin_unlock (&ehci->lock);
        return IRQ_HANDLED;
 }
@@ -730,7 +743,7 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
        /* bypass IAA if the hc can't care */
        } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
-               end_unlink_async (ehci, NULL);
+               end_unlink_async (ehci);
 
        /* something else might have unlinked the qh by now */
        if (qh->qh_state == QH_STATE_LINKED)
@@ -767,7 +780,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
                        intr_deschedule (ehci, qh);
                        /* FALL THROUGH */
                case QH_STATE_IDLE:
-                       qh_completions (ehci, qh, NULL);
+                       qh_completions (ehci, qh);
                        break;
                default:
                        ehci_dbg (ehci, "bogus qh %p state %d\n",
@@ -889,8 +902,59 @@ MODULE_LICENSE ("GPL");
 
 #ifdef CONFIG_PCI
 #include "ehci-pci.c"
+#define        PCI_DRIVER              ehci_pci_driver
+#endif
+
+#ifdef CONFIG_MPC834x
+#include "ehci-fsl.c"
+#define        PLATFORM_DRIVER         ehci_fsl_driver
+#endif
+
+#ifdef CONFIG_SOC_AU1200
+#include "ehci-au1xxx.c"
+#define        PLATFORM_DRIVER         ehci_hcd_au1xxx_driver
 #endif
 
-#if !defined(CONFIG_PCI)
+#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER)
 #error "missing bus glue for ehci-hcd"
 #endif
+
+static int __init ehci_hcd_init(void)
+{
+       int retval = 0;
+
+       pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",
+                hcd_name,
+                sizeof(struct ehci_qh), sizeof(struct ehci_qtd),
+                sizeof(struct ehci_itd), sizeof(struct ehci_sitd));
+
+#ifdef PLATFORM_DRIVER
+       retval = platform_driver_register(&PLATFORM_DRIVER);
+       if (retval < 0)
+               return retval;
+#endif
+
+#ifdef PCI_DRIVER
+       retval = pci_register_driver(&PCI_DRIVER);
+       if (retval < 0) {
+#ifdef PLATFORM_DRIVER
+               platform_driver_unregister(&PLATFORM_DRIVER);
+#endif
+       }
+#endif
+
+       return retval;
+}
+module_init(ehci_hcd_init);
+
+static void __exit ehci_hcd_cleanup(void)
+{
+#ifdef PLATFORM_DRIVER
+       platform_driver_unregister(&PLATFORM_DRIVER);
+#endif
+#ifdef PCI_DRIVER
+       pci_unregister_driver(&PCI_DRIVER);
+#endif
+}
+module_exit(ehci_hcd_cleanup);
+