Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
[pandora-kernel.git] / drivers / usb / host / uhci-hcd.c
index 448b9d1..fba99b1 100644 (file)
@@ -48,7 +48,6 @@
 #include <asm/system.h>
 
 #include "uhci-hcd.h"
-#include "pci-quirks.h"
 
 /*
  * Version Information
@@ -94,7 +93,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
 /*
  * Calculate the link pointer DMA value for the first Skeleton QH in a frame.
  */
-static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
+static __hc32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
 {
        int skelnum;
 
@@ -116,7 +115,7 @@ static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame)
        skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES);
        if (skelnum <= 1)
                skelnum = 9;
-       return LINK_TO_QH(uhci->skelqh[skelnum]);
+       return LINK_TO_QH(uhci, uhci->skelqh[skelnum]);
 }
 
 #include "uhci-debug.c"
@@ -135,15 +134,12 @@ static void finish_reset(struct uhci_hcd *uhci)
         * We have to clear them by hand.
         */
        for (port = 0; port < uhci->rh_numports; ++port)
-               outw(0, uhci->io_addr + USBPORTSC1 + (port * 2));
+               uhci_writew(uhci, 0, USBPORTSC1 + (port * 2));
 
        uhci->port_c_suspend = uhci->resuming_ports = 0;
        uhci->rh_state = UHCI_RH_RESET;
        uhci->is_stopped = UHCI_IS_STOPPED;
-       uhci_to_hcd(uhci)->state = HC_STATE_HALT;
        clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
-
-       uhci->dead = 0;         /* Full reset resurrects the controller */
 }
 
 /*
@@ -153,7 +149,7 @@ static void finish_reset(struct uhci_hcd *uhci)
 static void uhci_hc_died(struct uhci_hcd *uhci)
 {
        uhci_get_current_frame_number(uhci);
-       uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr);
+       uhci->reset_hc(uhci);
        finish_reset(uhci);
        uhci->dead = 1;
 
@@ -168,97 +164,118 @@ static void uhci_hc_died(struct uhci_hcd *uhci)
  */
 static void check_and_reset_hc(struct uhci_hcd *uhci)
 {
-       if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr))
+       if (uhci->check_and_reset_hc(uhci))
                finish_reset(uhci);
 }
 
+#if defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC)
+/*
+ * The two functions below are generic reset functions that are used on systems
+ * that do not have keyboard and mouse legacy support. We assume that we are
+ * running on such a system if CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC is defined.
+ */
+
+/*
+ * Make sure the controller is completely inactive, unable to
+ * generate interrupts or do DMA.
+ */
+static void uhci_generic_reset_hc(struct uhci_hcd *uhci)
+{
+       /* Reset the HC - this will force us to get a
+        * new notification of any already connected
+        * ports due to the virtual disconnect that it
+        * implies.
+        */
+       uhci_writew(uhci, USBCMD_HCRESET, USBCMD);
+       mb();
+       udelay(5);
+       if (uhci_readw(uhci, USBCMD) & USBCMD_HCRESET)
+               dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n");
+
+       /* Just to be safe, disable interrupt requests and
+        * make sure the controller is stopped.
+        */
+       uhci_writew(uhci, 0, USBINTR);
+       uhci_writew(uhci, 0, USBCMD);
+}
+
+/*
+ * Initialize a controller that was newly discovered or has just been
+ * resumed.  In either case we can't be sure of its previous state.
+ *
+ * Returns: 1 if the controller was reset, 0 otherwise.
+ */
+static int uhci_generic_check_and_reset_hc(struct uhci_hcd *uhci)
+{
+       unsigned int cmd, intr;
+
+       /*
+        * When restarting a suspended controller, we expect all the
+        * settings to be the same as we left them:
+        *
+        *      Controller is stopped and configured with EGSM set;
+        *      No interrupts enabled except possibly Resume Detect.
+        *
+        * If any of these conditions are violated we do a complete reset.
+        */
+
+       cmd = uhci_readw(uhci, USBCMD);
+       if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) {
+               dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n",
+                               __func__, cmd);
+               goto reset_needed;
+       }
+
+       intr = uhci_readw(uhci, USBINTR);
+       if (intr & (~USBINTR_RESUME)) {
+               dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n",
+                               __func__, intr);
+               goto reset_needed;
+       }
+       return 0;
+
+reset_needed:
+       dev_dbg(uhci_dev(uhci), "Performing full reset\n");
+       uhci_generic_reset_hc(uhci);
+       return 1;
+}
+#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */
+
 /*
  * Store the basic register settings needed by the controller.
  */
 static void configure_hc(struct uhci_hcd *uhci)
 {
-       struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
-
        /* Set the frame length to the default: 1 ms exactly */
-       outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF);
+       uhci_writeb(uhci, USBSOF_DEFAULT, USBSOF);
 
        /* Store the frame list base address */
-       outl(uhci->frame_dma_handle, uhci->io_addr + USBFLBASEADD);
+       uhci_writel(uhci, uhci->frame_dma_handle, USBFLBASEADD);
 
        /* Set the current frame number */
-       outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER,
-                       uhci->io_addr + USBFRNUM);
-
-       /* Mark controller as not halted before we enable interrupts */
-       uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED;
-       mb();
-
-       /* Enable PIRQ */
-       pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
+       uhci_writew(uhci, uhci->frame_number & UHCI_MAX_SOF_NUMBER,
+                       USBFRNUM);
 
-       /* Disable platform-specific non-PME# wakeup */
-       if (pdev->vendor == PCI_VENDOR_ID_INTEL)
-               pci_write_config_byte(pdev, USBRES_INTEL, 0);
+       /* perform any arch/bus specific configuration */
+       if (uhci->configure_hc)
+               uhci->configure_hc(uhci);
 }
 
-
 static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci)
 {
-       int port;
-
        /* If we have to ignore overcurrent events then almost by definition
         * we can't depend on resume-detect interrupts. */
        if (ignore_oc)
                return 1;
 
-       switch (to_pci_dev(uhci_dev(uhci))->vendor) {
-           default:
-               break;
-
-           case PCI_VENDOR_ID_GENESYS:
-               /* Genesys Logic's GL880S controllers don't generate
-                * resume-detect interrupts.
-                */
-               return 1;
-
-           case PCI_VENDOR_ID_INTEL:
-               /* Some of Intel's USB controllers have a bug that causes
-                * resume-detect interrupts if any port has an over-current
-                * condition.  To make matters worse, some motherboards
-                * hardwire unused USB ports' over-current inputs active!
-                * To prevent problems, we will not enable resume-detect
-                * interrupts if any ports are OC.
-                */
-               for (port = 0; port < uhci->rh_numports; ++port) {
-                       if (inw(uhci->io_addr + USBPORTSC1 + port * 2) &
-                                       USBPORTSC_OC)
-                               return 1;
-               }
-               break;
-       }
-       return 0;
+       return uhci->resume_detect_interrupts_are_broken ?
+               uhci->resume_detect_interrupts_are_broken(uhci) : 0;
 }
 
 static int global_suspend_mode_is_broken(struct uhci_hcd *uhci)
 {
-       int port;
-       const char *sys_info;
-       static char bad_Asus_board[] = "A7V8X";
-
-       /* One of Asus's motherboards has a bug which causes it to
-        * wake up immediately from suspend-to-RAM if any of the ports
-        * are connected.  In such cases we will not set EGSM.
-        */
-       sys_info = dmi_get_system_info(DMI_BOARD_NAME);
-       if (sys_info && !strcmp(sys_info, bad_Asus_board)) {
-               for (port = 0; port < uhci->rh_numports; ++port) {
-                       if (inw(uhci->io_addr + USBPORTSC1 + port * 2) &
-                                       USBPORTSC_CCS)
-                               return 1;
-               }
-       }
-
-       return 0;
+       return uhci->global_suspend_mode_is_broken ?
+               uhci->global_suspend_mode_is_broken(uhci) : 0;
 }
 
 static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state)
@@ -321,8 +338,8 @@ __acquires(uhci->lock)
                        !int_enable)
                uhci->RD_enable = int_enable = 0;
 
-       outw(int_enable, uhci->io_addr + USBINTR);
-       outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD);
+       uhci_writew(uhci, int_enable, USBINTR);
+       uhci_writew(uhci, egsm_enable | USBCMD_CF, USBCMD);
        mb();
        udelay(5);
 
@@ -331,7 +348,7 @@ __acquires(uhci->lock)
         * controller should stop after a few microseconds.  Otherwise
         * we will give the controller one frame to stop.
         */
-       if (!auto_stop && !(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) {
+       if (!auto_stop && !(uhci_readw(uhci, USBSTS) & USBSTS_HCH)) {
                uhci->rh_state = UHCI_RH_SUSPENDING;
                spin_unlock_irq(&uhci->lock);
                msleep(1);
@@ -339,7 +356,7 @@ __acquires(uhci->lock)
                if (uhci->dead)
                        return;
        }
-       if (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH))
+       if (!(uhci_readw(uhci, USBSTS) & USBSTS_HCH))
                dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n");
 
        uhci_get_current_frame_number(uhci);
@@ -361,15 +378,14 @@ __acquires(uhci->lock)
 
 static void start_rh(struct uhci_hcd *uhci)
 {
-       uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
        uhci->is_stopped = 0;
 
        /* Mark it configured and running with a 64-byte max packet.
         * All interrupts are enabled, even though RESUME won't do anything.
         */
-       outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, uhci->io_addr + USBCMD);
-       outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP,
-                       uhci->io_addr + USBINTR);
+       uhci_writew(uhci, USBCMD_RS | USBCMD_CF | USBCMD_MAXP, USBCMD);
+       uhci_writew(uhci, USBINTR_TIMEOUT | USBINTR_RESUME |
+               USBINTR_IOC | USBINTR_SP, USBINTR);
        mb();
        uhci->rh_state = UHCI_RH_RUNNING;
        set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
@@ -392,9 +408,9 @@ __acquires(uhci->lock)
                unsigned egsm;
 
                /* Keep EGSM on if it was set before */
-               egsm = inw(uhci->io_addr + USBCMD) & USBCMD_EGSM;
+               egsm = uhci_readw(uhci, USBCMD) & USBCMD_EGSM;
                uhci->rh_state = UHCI_RH_RESUMING;
-               outw(USBCMD_FGR | USBCMD_CF | egsm, uhci->io_addr + USBCMD);
+               uhci_writew(uhci, USBCMD_FGR | USBCMD_CF | egsm, USBCMD);
                spin_unlock_irq(&uhci->lock);
                msleep(20);
                spin_lock_irq(&uhci->lock);
@@ -402,10 +418,10 @@ __acquires(uhci->lock)
                        return;
 
                /* End Global Resume and wait for EOP to be sent */
-               outw(USBCMD_CF, uhci->io_addr + USBCMD);
+               uhci_writew(uhci, USBCMD_CF, USBCMD);
                mb();
                udelay(4);
-               if (inw(uhci->io_addr + USBCMD) & USBCMD_FGR)
+               if (uhci_readw(uhci, USBCMD) & USBCMD_FGR)
                        dev_warn(uhci_dev(uhci), "FGR not stopped yet!\n");
        }
 
@@ -425,10 +441,10 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd)
         * interrupt cause.  Contrary to the UHCI specification, the
         * "HC Halted" status bit is persistent: it is RO, not R/WC.
         */
-       status = inw(uhci->io_addr + USBSTS);
+       status = uhci_readw(uhci, USBSTS);
        if (!(status & ~USBSTS_HCH))    /* shared interrupt, not mine */
                return IRQ_NONE;
-       outw(status, uhci->io_addr + USBSTS);           /* Clear it */
+       uhci_writew(uhci, status, USBSTS);              /* Clear it */
 
        if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
                if (status & USBSTS_HSE)
@@ -450,6 +466,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd)
                                        lprintk(errbuf);
                                }
                                uhci_hc_died(uhci);
+                               usb_hc_died(hcd);
 
                                /* Force a callback in case there are
                                 * pending unlinks */
@@ -483,7 +500,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
        if (!uhci->is_stopped) {
                unsigned delta;
 
-               delta = (inw(uhci->io_addr + USBFRNUM) - uhci->frame_number) &
+               delta = (uhci_readw(uhci, USBFRNUM) - uhci->frame_number) &
                                (UHCI_NUMFRAMES - 1);
                uhci->frame_number += delta;
        }
@@ -520,61 +537,6 @@ static void release_uhci(struct uhci_hcd *uhci)
                        uhci->frame, uhci->frame_dma_handle);
 }
 
-static int uhci_init(struct usb_hcd *hcd)
-{
-       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-       unsigned io_size = (unsigned) hcd->rsrc_len;
-       int port;
-
-       uhci->io_addr = (unsigned long) hcd->rsrc_start;
-
-       /* The UHCI spec says devices must have 2 ports, and goes on to say
-        * they may have more but gives no way to determine how many there
-        * are.  However according to the UHCI spec, Bit 7 of the port
-        * status and control register is always set to 1.  So we try to
-        * use this to our advantage.  Another common failure mode when
-        * a nonexistent register is addressed is to return all ones, so
-        * we test for that also.
-        */
-       for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) {
-               unsigned int portstatus;
-
-               portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2));
-               if (!(portstatus & 0x0080) || portstatus == 0xffff)
-                       break;
-       }
-       if (debug)
-               dev_info(uhci_dev(uhci), "detected %d ports\n", port);
-
-       /* Anything greater than 7 is weird so we'll ignore it. */
-       if (port > UHCI_RH_MAXCHILD) {
-               dev_info(uhci_dev(uhci), "port count misdetected? "
-                               "forcing to 2 ports\n");
-               port = 2;
-       }
-       uhci->rh_numports = port;
-
-       /* Kick BIOS off this hardware and reset if the controller
-        * isn't already safely quiescent.
-        */
-       check_and_reset_hc(uhci);
-       return 0;
-}
-
-/* Make sure the controller is quiescent and that we're not using it
- * any more.  This is mainly for the benefit of programs which, like kexec,
- * expect the hardware to be idle: not doing DMA or generating IRQs.
- *
- * This routine may be called in a damaged or failing kernel.  Hence we
- * do not acquire the spinlock before shutting down the controller.
- */
-static void uhci_shutdown(struct pci_dev *pdev)
-{
-       struct usb_hcd *hcd = pci_get_drvdata(pdev);
-
-       uhci_hc_died(hcd_to_uhci(hcd));
-}
-
 /*
  * Allocate a frame list, and then setup the skeleton
  *
@@ -669,16 +631,16 @@ static int uhci_start(struct usb_hcd *hcd)
         * 8 Interrupt queues; link all higher int queues to int1 = async
         */
        for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i)
-               uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh);
-       uhci->skel_async_qh->link = UHCI_PTR_TERM;
-       uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh);
+               uhci->skelqh[i]->link = LINK_TO_QH(uhci, uhci->skel_async_qh);
+       uhci->skel_async_qh->link = UHCI_PTR_TERM(uhci);
+       uhci->skel_term_qh->link = LINK_TO_QH(uhci, uhci->skel_term_qh);
 
        /* This dummy TD is to work around a bug in Intel PIIX controllers */
-       uhci_fill_td(uhci->term_td, 0, uhci_explen(0) |
+       uhci_fill_td(uhci, uhci->term_td, 0, uhci_explen(0) |
                        (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0);
-       uhci->term_td->link = UHCI_PTR_TERM;
+       uhci->term_td->link = UHCI_PTR_TERM(uhci);
        uhci->skel_async_qh->element = uhci->skel_term_qh->element =
-                       LINK_TO_TD(uhci->term_td);
+               LINK_TO_TD(uhci, uhci->term_td);
 
        /*
         * Fill the frame list: make all entries point to the proper
@@ -791,86 +753,6 @@ static int uhci_rh_resume(struct usb_hcd *hcd)
        return rc;
 }
 
-static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
-{
-       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-       struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci));
-       int rc = 0;
-
-       dev_dbg(uhci_dev(uhci), "%s\n", __func__);
-
-       spin_lock_irq(&uhci->lock);
-       if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
-               goto done_okay;         /* Already suspended or dead */
-
-       if (uhci->rh_state > UHCI_RH_SUSPENDED) {
-               dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n");
-               rc = -EBUSY;
-               goto done;
-       };
-
-       /* All PCI host controllers are required to disable IRQ generation
-        * at the source, so we must turn off PIRQ.
-        */
-       pci_write_config_word(pdev, USBLEGSUP, 0);
-       clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
-
-       /* Enable platform-specific non-PME# wakeup */
-       if (do_wakeup) {
-               if (pdev->vendor == PCI_VENDOR_ID_INTEL)
-                       pci_write_config_byte(pdev, USBRES_INTEL,
-                                       USBPORT1EN | USBPORT2EN);
-       }
-
-done_okay:
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-done:
-       spin_unlock_irq(&uhci->lock);
-       return rc;
-}
-
-static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
-{
-       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-
-       dev_dbg(uhci_dev(uhci), "%s\n", __func__);
-
-       /* Since we aren't in D3 any more, it's safe to set this flag
-        * even if the controller was dead.
-        */
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-
-       spin_lock_irq(&uhci->lock);
-
-       /* Make sure resume from hibernation re-enumerates everything */
-       if (hibernated)
-               uhci_hc_died(uhci);
-
-       /* The firmware or a boot kernel may have changed the controller
-        * settings during a system wakeup.  Check it and reconfigure
-        * to avoid problems.
-        */
-       check_and_reset_hc(uhci);
-
-       /* If the controller was dead before, it's back alive now */
-       configure_hc(uhci);
-
-       /* Tell the core if the controller had to be reset */
-       if (uhci->rh_state == UHCI_RH_RESET)
-               usb_root_hub_lost_power(hcd->self.root_hub);
-
-       spin_unlock_irq(&uhci->lock);
-
-       /* If interrupts don't work and remote wakeup is enabled then
-        * the suspended root hub needs to be polled.
-        */
-       if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup)
-               set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
-
-       /* Does the root hub have a port wakeup pending? */
-       usb_hcd_poll_rh_status(hcd);
-       return 0;
-}
 #endif
 
 /* Wait until a particular device/endpoint's QH is idle, and free it */
@@ -908,67 +790,62 @@ static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
        /* Minimize latency by avoiding the spinlock */
        frame_number = uhci->frame_number;
        barrier();
-       delta = (inw(uhci->io_addr + USBFRNUM) - frame_number) &
+       delta = (uhci_readw(uhci, USBFRNUM) - frame_number) &
                        (UHCI_NUMFRAMES - 1);
        return frame_number + delta;
 }
 
-static const char hcd_name[] = "uhci_hcd";
-
-static const struct hc_driver uhci_driver = {
-       .description =          hcd_name,
-       .product_desc =         "UHCI Host Controller",
-       .hcd_priv_size =        sizeof(struct uhci_hcd),
-
-       /* Generic hardware linkage */
-       .irq =                  uhci_irq,
-       .flags =                HCD_USB11,
-
-       /* Basic lifecycle operations */
-       .reset =                uhci_init,
-       .start =                uhci_start,
-#ifdef CONFIG_PM
-       .pci_suspend =          uhci_pci_suspend,
-       .pci_resume =           uhci_pci_resume,
-       .bus_suspend =          uhci_rh_suspend,
-       .bus_resume =           uhci_rh_resume,
-#endif
-       .stop =                 uhci_stop,
+/* Determines number of ports on controller */
+static int uhci_count_ports(struct usb_hcd *hcd)
+{
+       struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+       unsigned io_size = (unsigned) hcd->rsrc_len;
+       int port;
 
-       .urb_enqueue =          uhci_urb_enqueue,
-       .urb_dequeue =          uhci_urb_dequeue,
+       /* The UHCI spec says devices must have 2 ports, and goes on to say
+        * they may have more but gives no way to determine how many there
+        * are.  However according to the UHCI spec, Bit 7 of the port
+        * status and control register is always set to 1.  So we try to
+        * use this to our advantage.  Another common failure mode when
+        * a nonexistent register is addressed is to return all ones, so
+        * we test for that also.
+        */
+       for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) {
+               unsigned int portstatus;
 
-       .endpoint_disable =     uhci_hcd_endpoint_disable,
-       .get_frame_number =     uhci_hcd_get_frame_number,
+               portstatus = uhci_readw(uhci, USBPORTSC1 + (port * 2));
+               if (!(portstatus & 0x0080) || portstatus == 0xffff)
+                       break;
+       }
+       if (debug)
+               dev_info(uhci_dev(uhci), "detected %d ports\n", port);
 
-       .hub_status_data =      uhci_hub_status_data,
-       .hub_control =          uhci_hub_control,
-};
+       /* Anything greater than 7 is weird so we'll ignore it. */
+       if (port > UHCI_RH_MAXCHILD) {
+               dev_info(uhci_dev(uhci), "port count misdetected? "
+                               "forcing to 2 ports\n");
+               port = 2;
+       }
 
-static const struct pci_device_id uhci_pci_ids[] = { {
-       /* handle any USB UHCI controller */
-       PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0),
-       .driver_data =  (unsigned long) &uhci_driver,
-       }, { /* end: all zeroes */ }
-};
+       return port;
+}
 
-MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
+static const char hcd_name[] = "uhci_hcd";
 
-static struct pci_driver uhci_pci_driver = {
-       .name =         (char *)hcd_name,
-       .id_table =     uhci_pci_ids,
+#ifdef CONFIG_PCI
+#include "uhci-pci.c"
+#define        PCI_DRIVER              uhci_pci_driver
+#endif
 
-       .probe =        usb_hcd_pci_probe,
-       .remove =       usb_hcd_pci_remove,
-       .shutdown =     uhci_shutdown,
+#ifdef CONFIG_SPARC_LEON
+#include "uhci-grlib.c"
+#define PLATFORM_DRIVER                uhci_grlib_driver
+#endif
 
-#ifdef CONFIG_PM_SLEEP
-       .driver =       {
-               .pm =   &usb_hcd_pci_pm_ops
-       },
+#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER)
+#error "missing bus glue for uhci-hcd"
 #endif
-};
+
 static int __init uhci_hcd_init(void)
 {
        int retval = -ENOMEM;
@@ -994,13 +871,27 @@ static int __init uhci_hcd_init(void)
        if (!uhci_up_cachep)
                goto up_failed;
 
-       retval = pci_register_driver(&uhci_pci_driver);
-       if (retval)
-               goto init_failed;
+#ifdef PLATFORM_DRIVER
+       retval = platform_driver_register(&PLATFORM_DRIVER);
+       if (retval < 0)
+               goto clean0;
+#endif
+
+#ifdef PCI_DRIVER
+       retval = pci_register_driver(&PCI_DRIVER);
+       if (retval < 0)
+               goto clean1;
+#endif
 
        return 0;
 
-init_failed:
+#ifdef PCI_DRIVER
+clean1:
+#endif
+#ifdef PLATFORM_DRIVER
+       platform_driver_unregister(&PLATFORM_DRIVER);
+clean0:
+#endif
        kmem_cache_destroy(uhci_up_cachep);
 
 up_failed:
@@ -1017,7 +908,12 @@ errbuf_failed:
 
 static void __exit uhci_hcd_cleanup(void) 
 {
-       pci_unregister_driver(&uhci_pci_driver);
+#ifdef PLATFORM_DRIVER
+       platform_driver_unregister(&PLATFORM_DRIVER);
+#endif
+#ifdef PCI_DRIVER
+       pci_unregister_driver(&PCI_DRIVER);
+#endif
        kmem_cache_destroy(uhci_up_cachep);
        debugfs_remove(uhci_debugfs_root);
        kfree(errbuf);