]> git.hungrycats.org Git - linux/commitdiff
[PATCH] USB OTG: ohci reset updates (2/5)
authorDavid Brownell <david-b@pacbell.net>
Tue, 24 Aug 2004 04:55:24 +0000 (21:55 -0700)
committerGreg Kroah-Hartman <greg@kroah.com>
Tue, 24 Aug 2004 04:55:24 +0000 (21:55 -0700)
Generic OTG and reset support for OTG.

 - Declare and use a start_hnp() board-specific procedure.  The OMAP
   implementation will come separately, it can't yet be configured.

 - When OTG is configured, implement the usb_bus_start_enum() hook;
   that just starts a root port reset.

 - When some task (usually khubd) resets a root port, make sure it takes
   the full 50 msec.  The OHCI reset timer is in hardware, and the host
   may need to issue multiple resets to guard against concurrent resume.

 - For backward compatibility, don't kick in the new 50 msec logic unless
   it could be needed:  without CONFIG_USB_SUSPEND, nothing will suspend
   so nothing could resume.  Which is good, at least until we start to
   measure how long a reset takes ... it seems chip-specific.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
drivers/usb/host/ohci-hub.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci-q.c

index f6263bf9b52956d66f999f330cd4dbe349268e6b..344b1cab16e1116e71d2339ac621182b0b332651 100644 (file)
@@ -435,6 +435,89 @@ ohci_hub_descriptor (
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef CONFIG_USB_OTG
+
+static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)
+{
+       struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+       u32                     status;
+
+       if (!port)
+               return -EINVAL;
+       port--;
+
+       /* start port reset before HNP protocol times out */
+       status = ohci_readl(&ohci->regs->roothub.portstatus [port]);
+       if (!(status & RH_PS_CCS))
+               return -ENODEV;
+
+       /* khubd will finish the reset later */
+       writel(RH_PS_PRS, &ohci->regs->roothub.portstatus [port]);
+       return 0;
+}
+
+static void start_hnp(struct ohci_hcd *ohci);
+
+#else
+
+#define        ohci_start_port_reset           NULL
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+
+/* See usb 7.1.7.5:  root hubs must issue at least 50 msec reset signaling,
+ * not necessarily continuous ... to guard against resume signaling.
+ * The short timeout is safe for non-root hubs, and is backward-compatible
+ * with earlier Linux hosts.
+ */
+#ifdef CONFIG_USB_SUSPEND
+#define        PORT_RESET_MSEC         50
+#else
+#define        PORT_RESET_MSEC         10
+#endif
+
+/* this timer value might be vendor-specific ... */
+#define        PORT_RESET_HW_MSEC      10
+
+/* wrap-aware logic stolen from <linux/jiffies.h> */
+#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)
+
+/* called from some task, normally khubd */
+static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port)
+{
+       u32     *portstat = &ohci->regs->roothub.portstatus [port];
+       u32     temp;
+       u16     now = readl(&ohci->regs->fmnumber);
+       u16     reset_done = now + PORT_RESET_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 {
+               /* spin until any current reset finishes */
+               for (;;) {
+                       temp = ohci_readl (portstat);
+                       if (!(temp & RH_PS_PRS))
+                               break;
+                       udelay (500);
+               } 
+
+               if (!(temp & RH_PS_CCS))
+                       break;
+               if (temp & RH_PS_PRSC)
+                       writel (RH_PS_PRSC, portstat);
+
+               /* start the next reset, sleep till it's probably done */
+               writel (RH_PS_PRS, portstat);
+               msleep(PORT_RESET_HW_MSEC);
+               now = readl(&ohci->regs->fmnumber);
+       } while (tick_before(now, reset_done));
+       /* caller synchronizes using PRSC */
+}
+
 static int ohci_hub_control (
        struct usb_hcd  *hcd,
        u16             typeReq,
@@ -533,6 +616,12 @@ static int ohci_hub_control (
                wIndex--;
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
+#ifdef CONFIG_USB_OTG
+                       if (ohci->hcd.self.otg_port == (wIndex + 1)
+                                       && ohci->hcd.self.b_hnp_enable)
+                               start_hnp(ohci);
+                       else
+#endif
                        writel (RH_PS_PSS,
                                &ohci->regs->roothub.portstatus [wIndex]);
                        break;
@@ -541,10 +630,7 @@ static int ohci_hub_control (
                                &ohci->regs->roothub.portstatus [wIndex]);
                        break;
                case USB_PORT_FEAT_RESET:
-                       temp = ohci_readl (&ohci->regs->roothub.portstatus [wIndex]);
-                       if (temp & RH_PS_CCS)
-                               writel (RH_PS_PRS,
-                                   &ohci->regs->roothub.portstatus [wIndex]);
+                       root_port_reset (ohci, wIndex);
                        break;
                default:
                        goto error;
index 8a246983527cd3548a938713f23b1250980d15c0..9c36e75c1f3715805eff2d9f6a41ee614c2db6dc 100644 (file)
@@ -566,6 +566,7 @@ static const struct hc_driver ohci_omap_hc_driver = {
        .hub_suspend =          ohci_hub_suspend,
        .hub_resume =           ohci_hub_resume,
 #endif
+       .start_port_reset =     ohci_start_port_reset,
 };
 
 /*-------------------------------------------------------------------------*/
index dc8bc13bf0aaa872860bef83aeb67ecb9dffcb28..73d9aee657d5636bc463f6203fd283df52ab4cf6 100644 (file)
@@ -242,6 +242,7 @@ static const struct hc_driver ohci_pci_hc_driver = {
        .hub_suspend =          ohci_hub_suspend,
        .hub_resume =           ohci_hub_resume,
 #endif
+       .start_port_reset =     ohci_start_port_reset,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -276,7 +277,7 @@ static int __init ohci_hcd_pci_init (void)
        if (usb_disabled())
                return -ENODEV;
 
-       printk (KERN_DEBUG "%s: block sizes: ed %Zd td %Zd\n", hcd_name,
+       pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,
                sizeof (struct ed), sizeof (struct td));
        return pci_module_init (&ohci_pci_driver);
 }
index aa54182c36a4a13fcf7bc97863c72c5881f9af5f..8d2e40005f005c390aad5c7b96fa4ccf88a78dc3 100644 (file)
@@ -900,9 +900,6 @@ static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
 
 /*-------------------------------------------------------------------------*/
 
-/* wrap-aware logic stolen from <linux/jiffies.h> */
-#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)
-
 /* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
 static void
 finish_unlinks (struct ohci_hcd *ohci, u16 tick, struct pt_regs *regs)