From: David Brownell <[EMAIL PROTECTED]>

Moving the PCI-specific parts of the EHCI driver into their own file
created a few issues ... notably on resume paths which (like swsusp)
require re-initializing the controller.  This patch:

 - Splits the EHCI startup code into run-once HCD setup code and
   separate "init the hardware" reinit code.  (That reinit code is
   a superset of the "early usb handoff" code.)

 - Then it makes the PCI init code run both, and the resume code only
   run the reinit code.

 - It also removes needless pci wrappers around EHCI start/stop methods.

 - Removes a byteswap issue that would be seen on big-endian hardware.

The HCD glue still doesn't actually provide a good way to do all this
run-one init stuff in one place though.

Signed-off-by: David Brownell <[EMAIL PROTECTED]>
Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>

---
 drivers/usb/host/ehci-hcd.c |  155 +++++++++++++++++++++--------------------
 drivers/usb/host/ehci-pci.c |  166 ++++++++++++++++++++------------------------
 2 files changed, 161 insertions(+), 160 deletions(-)

--- usb-2.6.orig/drivers/usb/host/ehci-hcd.c
+++ usb-2.6/drivers/usb/host/ehci-hcd.c
@@ -411,50 +411,39 @@ static void ehci_stop (struct usb_hcd *h
        dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
 }
 
-static int ehci_run (struct usb_hcd *hcd)
+/* one-time init, only for memory state */
+static int ehci_init(struct usb_hcd *hcd)
 {
-       struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
        u32                     temp;
        int                     retval;
        u32                     hcc_params;
-       int                     first;
 
-       /* skip some things on restart paths */
-       first = (ehci->watchdog.data == 0);
-       if (first) {
-               init_timer (&ehci->watchdog);
-               ehci->watchdog.function = ehci_watchdog;
-               ehci->watchdog.data = (unsigned long) ehci;
-       }
+       spin_lock_init(&ehci->lock);
+
+       init_timer(&ehci->watchdog);
+       ehci->watchdog.function = ehci_watchdog;
+       ehci->watchdog.data = (unsigned long) ehci;
 
        /*
         * hw default: 1K periodic list heads, one per frame.
         * periodic_size can shrink by USBCMD update if hcc_params allows.
         */
        ehci->periodic_size = DEFAULT_I_TDPS;
-       if (first && (retval = ehci_mem_init (ehci, GFP_KERNEL)) < 0)
+       if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0)
                return retval;
 
        /* controllers may cache some of the periodic schedule ... */
-       hcc_params = readl (&ehci->caps->hcc_params);
-       if (HCC_ISOC_CACHE (hcc_params))        // full frame cache
+       hcc_params = readl(&ehci->caps->hcc_params);
+       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);
+               ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
 
        ehci->reclaim = NULL;
        ehci->reclaim_ready = 0;
        ehci->next_uframe = -1;
 
-       /* controller state:  unknown --> reset */
-
-       /* EHCI spec section 4.1 */
-       if ((retval = ehci_reset (ehci)) != 0) {
-               ehci_mem_cleanup (ehci);
-               return retval;
-       }
-       writel (ehci->periodic_dma, &ehci->regs->frame_list);
-
        /*
         * dedicate a qh for the async ring head, since we couldn't unlink
         * a 'real' qh without stopping the async schedule [4.8].  use it
@@ -462,37 +451,13 @@ static int ehci_run (struct usb_hcd *hcd
         * its dummy is used in hw_alt_next of many tds, to prevent the qh
         * from automatically advancing to the next td after short reads.
         */
-       if (first) {
-               ehci->async->qh_next.qh = NULL;
-               ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma);
-               ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD);
-               ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT);
-               ehci->async->hw_qtd_next = EHCI_LIST_END;
-               ehci->async->qh_state = QH_STATE_LINKED;
-               ehci->async->hw_alt_next = QTD_NEXT 
(ehci->async->dummy->qtd_dma);
-       }
-       writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
-
-       /*
-        * hcc_params controls whether ehci->regs->segment must (!!!)
-        * be used; it constrains QH/ITD/SITD and QTD locations.
-        * pci_pool consistent memory always uses segment zero.
-        * streaming mappings for I/O buffers, like pci_map_single(),
-        * can return segments above 4GB, if the device allows.
-        *
-        * NOTE:  the dma mask is visible through dma_supported(), so
-        * drivers can pass this info along ... like NETIF_F_HIGHDMA,
-        * Scsi_Host.highmem_io, and so forth.  It's readonly to all
-        * host side drivers though.
-        */
-       if (HCC_64BIT_ADDR (hcc_params)) {
-               writel (0, &ehci->regs->segment);
-#if 0
-// this is deeply broken on almost all architectures
-               if (!dma_set_mask (hcd->self.controller, DMA_64BIT_MASK))
-                       ehci_info (ehci, "enabled 64bit DMA\n");
-#endif
-       }
+       ehci->async->qh_next.qh = NULL;
+       ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma);
+       ehci->async->hw_info1 = cpu_to_le32(QH_HEAD);
+       ehci->async->hw_token = cpu_to_le32(QTD_STS_HALT);
+       ehci->async->hw_qtd_next = EHCI_LIST_END;
+       ehci->async->qh_state = QH_STATE_LINKED;
+       ehci->async->hw_alt_next = QTD_NEXT(ehci->async->dummy->qtd_dma);
 
        /* clear interrupt enables, set irq latency */
        if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
@@ -507,13 +472,13 @@ static int ehci_run (struct usb_hcd *hcd
                 * make problems:  throughput reduction (!), data errors...
                 */
                if (park) {
-                       park = min (park, (unsigned) 3);
+                       park = min(park, (unsigned) 3);
                        temp |= CMD_PARK;
                        temp |= park << 8;
                }
-               ehci_info (ehci, "park %d\n", park);
+               ehci_dbg(ehci, "park %d\n", park);
        }
-       if (HCC_PGM_FRAMELISTLEN (hcc_params)) {
+       if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
                /* periodic schedule size can be smaller than default */
                temp &= ~(3 << 2);
                temp |= (EHCI_TUNE_FLS << 2);
@@ -521,16 +486,63 @@ static int ehci_run (struct usb_hcd *hcd
                case 0: ehci->periodic_size = 1024; break;
                case 1: ehci->periodic_size = 512; break;
                case 2: ehci->periodic_size = 256; break;
-               default:        BUG ();
+               default:        BUG();
                }
        }
+       ehci->command = temp;
+
+       ehci->reboot_notifier.notifier_call = ehci_reboot;
+       register_reboot_notifier(&ehci->reboot_notifier);
+
+       return 0;
+}
+
+/* start HC running; it's halted, ehci_init() has been run (once) */
+static int ehci_run (struct usb_hcd *hcd)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
+       int                     retval;
+       u32                     temp;
+       u32                     hcc_params;
+
+       /* EHCI spec section 4.1 */
+       if ((retval = ehci_reset(ehci)) != 0) {
+               unregister_reboot_notifier(&ehci->reboot_notifier);
+               ehci_mem_cleanup(ehci);
+               return retval;
+       }
+       writel(ehci->periodic_dma, &ehci->regs->frame_list);
+       writel((u32)ehci->async->qh_dma, &ehci->regs->async_next);
+
+       /*
+        * hcc_params controls whether ehci->regs->segment must (!!!)
+        * be used; it constrains QH/ITD/SITD and QTD locations.
+        * pci_pool consistent memory always uses segment zero.
+        * streaming mappings for I/O buffers, like pci_map_single(),
+        * can return segments above 4GB, if the device allows.
+        *
+        * NOTE:  the dma mask is visible through dma_supported(), so
+        * drivers can pass this info along ... like NETIF_F_HIGHDMA,
+        * Scsi_Host.highmem_io, and so forth.  It's readonly to all
+        * host side drivers though.
+        */
+       hcc_params = readl(&ehci->caps->hcc_params);
+       if (HCC_64BIT_ADDR(hcc_params)) {
+               writel(0, &ehci->regs->segment);
+#if 0
+// this is deeply broken on almost all architectures
+               if (!dma_set_mask(hcd->self.controller, DMA_64BIT_MASK))
+                       ehci_info(ehci, "enabled 64bit DMA\n");
+#endif
+       }
+
+
        // Philips, Intel, and maybe others need CMD_RUN before the
        // root hub will detect new devices (why?); NEC doesn't
-       temp |= CMD_RUN;
-       writel (temp, &ehci->regs->command);
-       dbg_cmd (ehci, "init", temp);
-
-       /* set async sleep time = 10 us ... ? */
+       ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
+       ehci->command |= CMD_RUN;
+       writel (ehci->command, &ehci->regs->command);
+       dbg_cmd (ehci, "init", ehci->command);
 
        /*
         * Start, enabling full USB 2.0 functionality ... usb 1.1 devices
@@ -538,26 +550,23 @@ static int ehci_run (struct usb_hcd *hcd
         * involved with the root hub.  (Except where one is integrated,
         * and there's no companion controller unless maybe for USB OTG.)
         */
-       if (first) {
-               ehci->reboot_notifier.notifier_call = ehci_reboot;
-               register_reboot_notifier (&ehci->reboot_notifier);
-       }
-
        hcd->state = HC_STATE_RUNNING;
        writel (FLAG_CF, &ehci->regs->configured_flag);
-       readl (&ehci->regs->command);   /* unblock posted write */
+       readl (&ehci->regs->command);   /* unblock posted writes */
 
        temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
        ehci_info (ehci,
-               "USB %x.%x %s, EHCI %x.%02x, driver %s\n",
+               "USB %x.%x started, EHCI %x.%02x, driver %s\n",
                ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f),
-               first ? "initialized" : "restarted",
                temp >> 8, temp & 0xff, DRIVER_VERSION);
 
        writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */
 
-       if (first)
-               create_debug_files (ehci);
+       /* GRR this is run-once init(), being done every time the HC starts.
+        * So long as they're part of class devices, we can't do it init()
+        * since the class device isn't created that early.
+        */
+       create_debug_files(ehci);
 
        return 0;
 }
--- usb-2.6.orig/drivers/usb/host/ehci-pci.c
+++ usb-2.6/drivers/usb/host/ehci-pci.c
@@ -58,15 +58,76 @@ static int bios_handoff(struct ehci_hcd 
        return 0;
 }
 
-/* called by khubd or root hub init threads */
+/* called after powerup, by probe or system-pm "wakeup" */
+static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
+{
+       u32                     temp;
+       int                     retval;
+       unsigned                count = 256/4;
+
+       /* optional debug port, normally in the first BAR */
+       temp = pci_find_capability(pdev, 0x0a);
+       if (temp) {
+               pci_read_config_dword(pdev, temp, &temp);
+               temp >>= 16;
+               if ((temp & (3 << 13)) == (1 << 13)) {
+                       temp &= 0x1fff;
+                       ehci->debug = ehci_to_hcd(ehci)->regs + temp;
+                       temp = readl(&ehci->debug->control);
+                       ehci_info(ehci, "debug port %d%s\n",
+                               HCS_DEBUG_PORT(ehci->hcs_params),
+                               (temp & DBGP_ENABLED)
+                                       ? " IN USE"
+                                       : "");
+                       if (!(temp & DBGP_ENABLED))
+                               ehci->debug = NULL;
+               }
+       }
+
+       temp = HCC_EXT_CAPS(readl(&ehci->caps->hcc_params));
+
+       /* EHCI 0.96 and later may have "extended capabilities" */
+       while (temp && count--) {
+               u32             cap;
+
+               pci_read_config_dword(pdev, temp, &cap);
+               ehci_dbg(ehci, "capability %04x at %02x\n", cap, temp);
+               switch (cap & 0xff) {
+               case 1:                 /* BIOS/SMM/... handoff */
+                       if (bios_handoff(ehci, temp, cap) != 0)
+                               return -EOPNOTSUPP;
+                       break;
+               case 0:                 /* illegal reserved capability */
+                       ehci_dbg(ehci, "illegal capability!\n");
+                       cap = 0;
+                       /* FALLTHROUGH */
+               default:                /* unknown */
+                       break;
+               }
+               temp = (cap >> 8) & 0xff;
+       }
+       if (!count) {
+               ehci_err(ehci, "bogus capabilities ... PCI problems!\n");
+               return -EIO;
+       }
+
+       /* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
+       retval = pci_set_mwi(pdev);
+       if (!retval)
+               ehci_dbg(ehci, "MWI active\n");
+
+       ehci_port_power(ehci, 0);
+
+       return 0;
+}
+
+/* called by khubd or root hub (re)init threads; leaves HC in halt state */
 static int ehci_pci_reset(struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
        struct pci_dev          *pdev = to_pci_dev(hcd->self.controller);
        u32                     temp;
-       unsigned                count = 256/4;
-
-       spin_lock_init (&ehci->lock);
+       int                     retval;
 
        ehci->caps = hcd->regs;
        ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
@@ -76,6 +137,10 @@ static int ehci_pci_reset(struct usb_hcd
        /* cache this readonly data; minimize chip reads */
        ehci->hcs_params = readl(&ehci->caps->hcs_params);
 
+       retval = ehci_halt(ehci);
+       if (retval)
+               return retval;
+
        /* NOTE:  only the parts below this line are PCI-specific */
 
        switch (pdev->vendor) {
@@ -111,57 +176,9 @@ static int ehci_pci_reset(struct usb_hcd
                break;
        }
 
-       /* optional debug port, normally in the first BAR */
-       temp = pci_find_capability(pdev, 0x0a);
-       if (temp) {
-               pci_read_config_dword(pdev, temp, &temp);
-               temp >>= 16;
-               if ((temp & (3 << 13)) == (1 << 13)) {
-                       temp &= 0x1fff;
-                       ehci->debug = hcd->regs + temp;
-                       temp = readl(&ehci->debug->control);
-                       ehci_info(ehci, "debug port %d%s\n",
-                               HCS_DEBUG_PORT(ehci->hcs_params),
-                               (temp & DBGP_ENABLED)
-                                       ? " IN USE"
-                                       : "");
-                       if (!(temp & DBGP_ENABLED))
-                               ehci->debug = NULL;
-               }
-       }
-
-       temp = HCC_EXT_CAPS(readl(&ehci->caps->hcc_params));
-
-       /* EHCI 0.96 and later may have "extended capabilities" */
-       while (temp && count--) {
-               u32             cap;
-
-               pci_read_config_dword(to_pci_dev(hcd->self.controller),
-                               temp, &cap);
-               ehci_dbg(ehci, "capability %04x at %02x\n", cap, temp);
-               switch (cap & 0xff) {
-               case 1:                 /* BIOS/SMM/... handoff */
-                       if (bios_handoff(ehci, temp, cap) != 0)
-                               return -EOPNOTSUPP;
-                       break;
-               case 0:                 /* illegal reserved capability */
-                       ehci_warn(ehci, "illegal capability!\n");
-                       cap = 0;
-                       /* FALLTHROUGH */
-               default:                /* unknown */
-                       break;
-               }
-               temp = (cap >> 8) & 0xff;
-       }
-       if (!count) {
-               ehci_err(ehci, "bogus capabilities ... PCI problems!\n");
-               return -EIO;
-       }
        if (ehci_is_TDI(ehci))
                ehci_reset(ehci);
 
-       ehci_port_power(ehci, 0);
-
        /* at least the Genesys GL880S needs fixup here */
        temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params);
        temp &= 0x0f;
@@ -184,39 +201,15 @@ static int ehci_pci_reset(struct usb_hcd
                }
        }
 
-       /* force HC to halt state */
-       return ehci_halt(ehci);
-}
-
-static int ehci_pci_start(struct usb_hcd *hcd)
-{
-       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
-       int                     result = 0;
-       struct pci_dev          *pdev;
-       u16                     port_wake;
-
-       pdev = to_pci_dev(hcd->self.controller);
-
        /* Serial Bus Release Number is at PCI 0x60 offset */
        pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
 
-       /* port wake capability, reported by boot firmware */
-       pci_read_config_word(pdev, 0x62, &port_wake);
-       hcd->can_wakeup = (port_wake & 1) != 0;
-
-       /* PCI Memory-Write-Invalidate cycle support is optional (uncommon) */
-       result = pci_set_mwi(pdev);
-       if (!result)
-               ehci_dbg(ehci, "MWI active\n");
-
-       return ehci_run(hcd);
-}
+       /* REVISIT:  per-port wake capability (PCI 0x62) currently unused */
 
-/* always called by thread; normally rmmod */
+       retval = ehci_pci_reinit(ehci, pdev);
 
-static void ehci_pci_stop(struct usb_hcd *hcd)
-{
-       ehci_stop(hcd);
+       /* finish init */
+       return ehci_init(hcd);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -250,6 +243,7 @@ static int ehci_pci_resume(struct usb_hc
        struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
        unsigned                port;
        struct usb_device       *root = hcd->self.root_hub;
+       struct pci_dev          *pdev = to_pci_dev(hcd->self.controller);
        int                     retval = -EINVAL;
 
        // maybe restore FLADJ
@@ -258,7 +252,7 @@ static int ehci_pci_resume(struct usb_hc
                msleep(100);
 
        /* If CF is clear, we lost PCI Vaux power and need to restart.  */
-       if (readl(&ehci->regs->configured_flag) != cpu_to_le32(FLAG_CF))
+       if (readl(&ehci->regs->configured_flag) != FLAG_CF)
                goto restart;
 
        /* If any port is suspended (or owned by the companion),
@@ -292,7 +286,7 @@ restart:
         */
        (void) ehci_halt(ehci);
        (void) ehci_reset(ehci);
-       (void) ehci_pci_reset(hcd);
+       (void) ehci_pci_reinit(ehci, pdev);
 
        /* emptying the schedule aborts any urbs */
        spin_lock_irq(&ehci->lock);
@@ -304,9 +298,7 @@ restart:
        /* restart; khubd will disconnect devices */
        retval = ehci_run(hcd);
 
-       /* here we "know" root ports should always stay powered;
-        * but some controllers may lose all power.
-        */
+       /* here we "know" root ports should always stay powered */
        ehci_port_power(ehci, 1);
 
        return retval;
@@ -328,12 +320,12 @@ static const struct hc_driver ehci_pci_h
         * basic lifecycle operations
         */
        .reset =                ehci_pci_reset,
-       .start =                ehci_pci_start,
+       .start =                ehci_run,
 #ifdef CONFIG_PM
        .suspend =              ehci_pci_suspend,
        .resume =               ehci_pci_resume,
 #endif
-       .stop =                 ehci_pci_stop,
+       .stop =                 ehci_stop,
 
        /*
         * managing i/o requests and associated device resources

--


-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems?  Stop!  Download the new AJAX search engine that makes
searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to