USB: OHCI: don't look at the root hub to get the number of ports
[safe/jmp/linux-2.6] / drivers / usb / host / ohci-hub.c
index 48e4b11..65cac8c 100644 (file)
 
 /*-------------------------------------------------------------------------*/
 
-/* hcd->hub_irq_enable() */
-static void ohci_rhsc_enable (struct usb_hcd *hcd)
-{
-       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
-
-       spin_lock_irq(&ohci->lock);
-       if (!ohci->autostop)
-               del_timer(&hcd->rh_timer);      /* Prevent next poll */
-       ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
-       spin_unlock_irq(&ohci->lock);
-}
-
 #define OHCI_SCHED_ENABLES \
        (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
 
@@ -103,11 +91,11 @@ __acquires(ohci->lock)
        finish_unlinks (ohci, ohci_frame_no(ohci));
 
        /* maybe resume can wake root hub */
-       if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev) ||
-                       autostop)
+       if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) {
                ohci->hc_control |= OHCI_CTRL_RWE;
-       else {
-               ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable);
+       } else {
+               ohci_writel(ohci, OHCI_INTR_RHSC | OHCI_INTR_RD,
+                               &ohci->regs->intrdisable);
                ohci->hc_control &= ~OHCI_CTRL_RWE;
        }
 
@@ -326,23 +314,79 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
        return rc;
 }
 
+/* Carry out the final steps of resuming the controller device */
+static void ohci_finish_controller_resume(struct usb_hcd *hcd)
+{
+       struct ohci_hcd         *ohci = hcd_to_ohci(hcd);
+       int                     port;
+       bool                    need_reinit = false;
+
+       /* See if the controller is already running or has been reset */
+       ohci->hc_control = ohci_readl(ohci, &ohci->regs->control);
+       if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
+               need_reinit = true;
+       } else {
+               switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+               case OHCI_USB_OPER:
+               case OHCI_USB_RESET:
+                       need_reinit = true;
+               }
+       }
+
+       /* If needed, reinitialize and suspend the root hub */
+       if (need_reinit) {
+               spin_lock_irq(&ohci->lock);
+               hcd->state = HC_STATE_RESUMING;
+               ohci_rh_resume(ohci);
+               hcd->state = HC_STATE_QUIESCING;
+               ohci_rh_suspend(ohci, 0);
+               hcd->state = HC_STATE_SUSPENDED;
+               spin_unlock_irq(&ohci->lock);
+       }
+
+       /* Normally just turn on port power and enable interrupts */
+       else {
+               ohci_dbg(ohci, "powerup ports\n");
+               for (port = 0; port < ohci->num_ports; port++)
+                       ohci_writel(ohci, RH_PS_PPS,
+                                       &ohci->regs->roothub.portstatus[port]);
+
+               ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrenable);
+               ohci_readl(ohci, &ohci->regs->intrenable);
+               msleep(20);
+       }
+}
+
 /* Carry out polling-, autostop-, and autoresume-related state changes */
 static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
-               int any_connected)
+               int any_connected, int rhsc_status)
 {
        int     poll_rh = 1;
+       int     rhsc_enable;
 
-       switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+       /* Some broken controllers never turn off RHCS in the interrupt
+        * status register.  For their sake we won't re-enable RHSC
+        * interrupts if the interrupt bit is already active.
+        */
+       rhsc_enable = ohci_readl(ohci, &ohci->regs->intrenable) &
+                       OHCI_INTR_RHSC;
 
+       switch (ohci->hc_control & OHCI_CTRL_HCFS) {
        case OHCI_USB_OPER:
-               /* keep on polling until we know a device is connected
-                * and RHSC is enabled */
+               /* If no status changes are pending, enable RHSC interrupts. */
+               if (!rhsc_enable && !rhsc_status && !changed) {
+                       rhsc_enable = OHCI_INTR_RHSC;
+                       ohci_writel(ohci, rhsc_enable, &ohci->regs->intrenable);
+               }
+
+               /* Keep on polling until we know a device is connected
+                * and RHSC is enabled, or until we autostop.
+                */
                if (!ohci->autostop) {
                        if (any_connected ||
                                        !device_may_wakeup(&ohci_to_hcd(ohci)
                                                ->self.root_hub->dev)) {
-                               if (ohci_readl(ohci, &ohci->regs->intrenable) &
-                                               OHCI_INTR_RHSC)
+                               if (rhsc_enable)
                                        poll_rh = 0;
                        } else {
                                ohci->autostop = 1;
@@ -361,21 +405,39 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
                                        && !(ohci->hc_control &
                                                OHCI_SCHED_ENABLES)) {
                                ohci_rh_suspend(ohci, 1);
+                               if (rhsc_enable)
+                                       poll_rh = 0;
                        }
                }
                break;
 
-       /* if there is a port change, autostart or ask to be resumed */
        case OHCI_USB_SUSPEND:
        case OHCI_USB_RESUME:
+               /* if there is a port change, autostart or ask to be resumed */
                if (changed) {
                        if (ohci->autostop)
                                ohci_rh_resume(ohci);
                        else
                                usb_hcd_resume_root_hub(ohci_to_hcd(ohci));
-               } else {
-                       /* everything is idle, no need for polling */
+
+               /* If remote wakeup is disabled, stop polling */
+               } else if (!ohci->autostop &&
+                               !ohci_to_hcd(ohci)->self.root_hub->
+                                       do_remote_wakeup) {
                        poll_rh = 0;
+
+               } else {
+                       /* If no status changes are pending,
+                        * enable RHSC interrupts
+                        */
+                       if (!rhsc_enable && !rhsc_status) {
+                               rhsc_enable = OHCI_INTR_RHSC;
+                               ohci_writel(ohci, rhsc_enable,
+                                               &ohci->regs->intrenable);
+                       }
+                       /* Keep polling until RHSC is enabled */
+                       if (rhsc_enable)
+                               poll_rh = 0;
                }
                break;
        }
@@ -393,14 +455,22 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci)
  * autostop isn't used when CONFIG_PM is turned off.
  */
 static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,
-               int any_connected)
+               int any_connected, int rhsc_status)
 {
-       int     poll_rh = 1;
-
-       /* keep on polling until RHSC is enabled */
+       /* If RHSC is enabled, don't poll */
        if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)
-               poll_rh = 0;
-       return poll_rh;
+               return 0;
+
+       /* If status changes are pending, continue polling.
+        * Conversely, if no status changes are pending but the RHSC
+        * status bit was set, then RHSC may be broken so continue polling.
+        */
+       if (changed || rhsc_status)
+               return 1;
+
+       /* It's safe to re-enable RHSC interrupts */
+       ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+       return 0;
 }
 
 #endif /* CONFIG_PM */
@@ -415,6 +485,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
        int             i, changed = 0, length = 1;
        int             any_connected = 0;
+       int             rhsc_status;
        unsigned long   flags;
 
        spin_lock_irqsave (&ohci->lock, flags);
@@ -440,6 +511,11 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
                length++;
        }
 
+       /* Clear the RHSC status flag before reading the port statuses */
+       ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus);
+       rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) &
+                       OHCI_INTR_RHSC;
+
        /* look at each port */
        for (i = 0; i < ohci->num_ports; i++) {
                u32     status = roothub_portstatus (ohci, i);
@@ -458,7 +534,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
        }
 
        hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed,
-                       any_connected);
+                       any_connected, rhsc_status);
 
 done:
        spin_unlock_irqrestore (&ohci->lock, flags);
@@ -529,8 +605,6 @@ static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
        return 0;
 }
 
-static void start_hnp(struct ohci_hcd *ohci);
-
 #else
 
 #define        ohci_start_port_reset           NULL
@@ -561,17 +635,21 @@ static void start_hnp(struct ohci_hcd *ohci);
 static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
 {
        __hc32 __iomem *portstat = &ohci->regs->roothub.portstatus [port];
-       u32     temp;
+       u32     temp = 0;
        u16     now = ohci_readl(ohci, &ohci->regs->fmnumber);
        u16     reset_done = now + PORT_RESET_MSEC;
+       int     limit_1 = DIV_ROUND_UP(PORT_RESET_MSEC, PORT_RESET_HW_MSEC);
 
        /* build a "continuous enough" reset signal, with up to
         * 3msec gap between pulses.  scheduler HZ==100 must work;
         * this might need to be deadline-scheduled.
         */
        do {
+               int limit_2;
+
                /* spin until any current reset finishes */
-               for (;;) {
+               limit_2 = PORT_RESET_HW_MSEC * 2;
+               while (--limit_2 >= 0) {
                        temp = ohci_readl (ohci, portstat);
                        /* handle e.g. CardBus eject */
                        if (temp == ~(u32)0)
@@ -581,6 +659,17 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
                        udelay (500);
                }
 
+               /* timeout (a hardware error) has been observed when
+                * EHCI sets CF while this driver is resetting a port;
+                * presumably other disconnect paths might do it too.
+                */
+               if (limit_2 < 0) {
+                       ohci_dbg(ohci,
+                               "port[%d] reset timeout, stat %08x\n",
+                               port, temp);
+                       break;
+               }
+
                if (!(temp & RH_PS_CCS))
                        break;
                if (temp & RH_PS_PRSC)
@@ -590,8 +679,11 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)
                ohci_writel (ohci, RH_PS_PRS, portstat);
                msleep(PORT_RESET_HW_MSEC);
                now = ohci_readl(ohci, &ohci->regs->fmnumber);
-       } while (tick_before(now, reset_done));
-       /* caller synchronizes using PRSC */
+       } while (tick_before(now, reset_done) && --limit_1 >= 0);
+
+       /* caller synchronizes using PRSC ... and handles PRS
+        * still being set when this returns.
+        */
 
        return 0;
 }
@@ -605,7 +697,7 @@ static int ohci_hub_control (
        u16             wLength
 ) {
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
-       int             ports = hcd_to_bus (hcd)->root_hub->maxchild;
+       int             ports = ohci->num_ports;
        u32             temp;
        int             retval = 0;
 
@@ -666,14 +758,14 @@ static int ohci_hub_control (
                break;
        case GetHubStatus:
                temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
-               put_unaligned(cpu_to_le32 (temp), (__le32 *) buf);
+               put_unaligned_le32(temp, buf);
                break;
        case GetPortStatus:
                if (!wIndex || wIndex > ports)
                        goto error;
                wIndex--;
                temp = roothub_portstatus (ohci, wIndex);
-               put_unaligned(cpu_to_le32 (temp), (__le32 *) buf);
+               put_unaligned_le32(temp, buf);
 
 #ifndef        OHCI_VERBOSE_DEBUG
        if (*(u16*)(buf+2))     /* only if wPortChange is interesting */
@@ -699,7 +791,7 @@ static int ohci_hub_control (
 #ifdef CONFIG_USB_OTG
                        if (hcd->self.otg_port == (wIndex + 1)
                                        && hcd->self.b_hnp_enable)
-                               start_hnp(ohci);
+                               ohci->start_hnp(ohci);
                        else
 #endif
                        ohci_writel (ohci, RH_PS_PSS,