]> git.hungrycats.org Git - linux/commitdiff
[PATCH] USB: ehci updates: CONFIG_PCI, integrated TT
authorDavid Brownell <david-b@pacbell.net>
Fri, 2 Apr 2004 01:56:28 +0000 (17:56 -0800)
committerGreg Kroah-Hartman <greg@kroah.com>
Fri, 2 Apr 2004 01:56:28 +0000 (17:56 -0800)
Generalize the driver a bit:

 - PCI-specific handling is restricted to a small chunk of
   init code.  Non-PCI implementations are in the pipeline.

 - Merge support from ARC International (Craig Nadler) for
   their integrated root hub transaction translators (on PCI).
   Other implementations should be similar.

drivers/usb/host/Kconfig
drivers/usb/host/ehci-dbg.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci.h
include/linux/pci_ids.h

index 55ec90a82ed647511c8b25266261e9a3d749cda3..946898ac46da7174ff268550199b32925d267257 100644 (file)
@@ -38,6 +38,17 @@ config USB_EHCI_SPLIT_ISO
          EHCI or USB 2.0 transaction translator implementations.
          It should work for ISO-OUT transfers, like audio.
 
+config USB_EHCI_ROOT_HUB_TT
+       bool "Root Hub Transaction Translators (EXPERIMENTAL)"
+       depends on USB_EHCI_HCD && EXPERIMENTAL
+       ---help---
+         Some EHCI chips have vendor-specific extensions to integrate
+         transaction translators, so that no OHCI or UHCI companion
+         controller is needed.  It's safe to say "y" even if your
+         controller doesn't support this feature.
+
+         This supports the EHCI implementation from ARC International.
+
 config USB_OHCI_HCD
        tristate "OHCI HCD support"
        depends on USB
index 989a378044919a326f7322f98aff143ca206b768..530cfd9f84ca0b155644be8fc2d9e50b109433d4 100644 (file)
@@ -628,8 +628,10 @@ show_registers (struct class_device *class_dev, char *buf)
        /* Capability Registers */
        i = HC_VERSION(readl (&ehci->caps->hc_capbase));
        temp = scnprintf (next, size,
-               "PCI device %s\nEHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n",
-               pci_name(to_pci_dev(hcd->self.controller)),
+               "bus %s device %s\n"
+               "EHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n",
+               hcd->self.controller->bus->name,
+               hcd->self.controller->bus_id,
                i >> 8, i & 0x0ff, ehci->hcd.state);
        size -= temp;
        next += temp;
index 75d6db2b0540590d8644e4ec07dc74a6fada3d94..1e61876ca0a7d6e0f8c001b9d478bc3fbc9e7ee5 100644 (file)
@@ -279,6 +279,8 @@ static void ehci_watchdog (unsigned long param)
        spin_unlock_irqrestore (&ehci->lock, flags);
 }
 
+#ifdef CONFIG_PCI
+
 /* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
  * off the controller (maybe it can boot from highspeed USB disks).
  */
@@ -307,6 +309,8 @@ static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
        return 0;
 }
 
+#endif
+
 static int
 ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
 {
@@ -335,8 +339,12 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
        dbg_hcs_params (ehci, "reset");
        dbg_hcc_params (ehci, "reset");
 
+#ifdef CONFIG_PCI
        /* EHCI 0.96 and later may have "extended capabilities" */
-       temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
+       if (hcd->self.controller->bus == &pci_bus_type)
+               temp = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params));
+       else
+               temp = 0;
        while (temp) {
                u32             cap;
 
@@ -356,6 +364,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
                }
                temp = (cap >> 8) & 0xff;
        }
+#endif
 
        /* cache this readonly data; minimize PCI reads */
        ehci->hcs_params = readl (&ehci->caps->hcs_params);
@@ -372,7 +381,7 @@ static int ehci_start (struct usb_hcd *hcd)
        struct usb_bus          *bus;
        int                     retval;
        u32                     hcc_params;
-       u8                      tempbyte;
+       u8                      sbrn = 0;
 
        init_timer (&ehci->watchdog);
        ehci->watchdog.function = ehci_watchdog;
@@ -406,6 +415,29 @@ static int ehci_start (struct usb_hcd *hcd)
        writel (INTR_MASK, &ehci->regs->intr_enable);
        writel (ehci->periodic_dma, &ehci->regs->frame_list);
 
+#ifdef CONFIG_PCI
+       if (hcd->self.controller->bus == &pci_bus_type) {
+               struct pci_dev          *pdev;
+
+               pdev = to_pci_dev(hcd->self.controller);
+
+               /* Serial Bus Release Number is at PCI 0x60 offset */
+               pci_read_config_byte(pdev, 0x60, &sbrn);
+
+               /* help hc dma work well with cachelines */
+               pci_set_mwi (pdev);
+
+               /* chip-specific init */
+               switch (pdev->vendor) {
+               case PCI_VENDOR_ID_ARC:
+                       if (pdev->device == PCI_DEVICE_ID_ARC_EHCI)
+                               ehci->is_arc_rh_tt = 1;
+                       break;
+               }
+
+       }
+#endif
+
        /*
         * 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
@@ -443,9 +475,6 @@ static int ehci_start (struct usb_hcd *hcd)
 #endif
        }
 
-       /* help hc dma work well with cachelines */
-       pci_set_mwi (to_pci_dev(ehci->hcd.self.controller));
-
        /* clear interrupt enables, set irq latency */
        temp = readl (&ehci->regs->command) & 0x0fff;
        if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
@@ -493,12 +522,10 @@ done2:
        writel (FLAG_CF, &ehci->regs->configured_flag);
        readl (&ehci->regs->command);   /* unblock posted write */
 
-        /* PCI Serial Bus Release Number is at 0x60 offset */
-       pci_read_config_byte(to_pci_dev(hcd->self.controller), 0x60, &tempbyte);
        temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
        ehci_info (ehci,
                "USB %x.%x enabled, EHCI %x.%02x, driver %s\n",
-               ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
+               ((sbrn & 0xf0)>>4), (sbrn & 0x0f),
                temp >> 8, temp & 0xff, DRIVER_VERSION);
 
        /*
@@ -995,7 +1022,9 @@ static const struct hc_driver ehci_driver = {
 
 /*-------------------------------------------------------------------------*/
 
-/* EHCI spec says PCI is required. */
+/* EHCI 1.0 doesn't require PCI */
+
+#ifdef CONFIG_PCI
 
 /* PCI driver selection metadata; PCI hotplugging uses this */
 static const struct pci_device_id pci_ids [] = { {
@@ -1021,6 +1050,9 @@ static struct pci_driver ehci_pci_driver = {
 #endif
 };
 
+#endif /* PCI */
+
+
 #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
 
 MODULE_DESCRIPTION (DRIVER_INFO);
index 2c5cf36c90ba54e16c2635b69676a7ee197a6581..5c3a0615211c245a00ee5e5dd7156d08ceb30317 100644 (file)
@@ -40,6 +40,15 @@ static int check_reset_complete (
 
        /* if reset finished and it's still not enabled -- handoff */
        if (!(port_status & PORT_PE)) {
+
+               /* with integrated TT, there's nobody to hand it to! */
+               if (ehci_is_ARC(ehci)) {
+                       ehci_dbg (ehci,
+                               "Failed to enable port %d on root hub TT\n",
+                               index+1);
+                       return port_status;
+               }
+
                ehci_dbg (ehci, "port %d full speed --> companion\n",
                        index + 1);
 
@@ -257,7 +266,8 @@ static int ehci_hub_control (
                if (!(temp & PORT_OWNER)) {
                        if (temp & PORT_CONNECT) {
                                status |= 1 << USB_PORT_FEAT_CONNECTION;
-                               status |= 1 << USB_PORT_FEAT_HIGHSPEED;
+                               // status may be from integrated TT
+                               status |= ehci_port_speed(ehci, temp);
                        }
                        if (temp & PORT_PE)
                                status |= 1 << USB_PORT_FEAT_ENABLE;
@@ -307,8 +317,12 @@ static int ehci_hub_control (
                                        &ehci->regs->port_status [wIndex]);
                        break;
                case USB_PORT_FEAT_RESET:
-                       /* line status bits may report this as low speed */
+                       /* line status bits may report this as low speed,
+                        * which can be fine if this root hub has a
+                        * transaction translator built in.
+                        */
                        if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
+                                       && !ehci_is_ARC(ehci)
                                        && PORT_USB11 (temp)) {
                                ehci_dbg (ehci,
                                        "port %d low speed --> companion\n",
index 69ca6429af12c6a972da997a8347d066c1f6e35f..fd8634b312313de0eeebc40a975914121b82657a 100644 (file)
@@ -165,7 +165,10 @@ static void qtd_copy_status (
                /* if async CSPLIT failed, try cleaning out the TT buffer */
                } else if (urb->dev->tt && !usb_pipeint (urb->pipe)
                                && ((token & QTD_STS_MMF) != 0
-                                       || QTD_CERR(token) == 0)) {
+                                       || QTD_CERR(token) == 0)
+                               && (!ehci_is_ARC(ehci)
+                                       || urb->dev->tt->hub !=
+                                               ehci->hcd.self.root_hub)) {
 #ifdef DEBUG
                        struct usb_device *tt = urb->dev->tt->hub;
                        dev_dbg (&tt->dev,
@@ -576,7 +579,7 @@ clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh)
 // when each interface/altsetting is established.  Unlink
 // any previous qh and cancel its urbs first; endpoints are
 // implicitly reset then (data toggle too).
-// That'd mean updating how usbcore talks to HCDs. (2.5?)
+// That'd mean updating how usbcore talks to HCDs. (2.7?)
 
 
 /*
@@ -674,7 +677,13 @@ qh_make (
 
                info2 |= (EHCI_TUNE_MULT_TT << 30);
                info2 |= urb->dev->ttport << 23;
-               info2 |= urb->dev->tt->hub->devnum << 16;
+
+               /* set the address of the TT; for ARC's integrated
+                * root hub tt, leave it zeroed.
+                */
+               if (!ehci_is_ARC(ehci)
+                               || urb->dev->tt->hub != ehci->hcd.self.root_hub)
+                       info2 |= urb->dev->tt->hub->devnum << 16;
 
                /* NOTE:  if (PIPE_INTERRUPT) { scheduler sets c-mask } */
 
index 5c6eef9674d62dfdef62666f86c2f3e6df54fcf0..8cd5b7b2c97a76b45fd9b2f086cf19aeface2f52 100644 (file)
@@ -85,6 +85,8 @@ struct ehci_hcd {                     /* one per controller */
        unsigned long           actions;
        unsigned                stamp;
 
+       unsigned                is_arc_rh_tt:1; /* ARC roothub with TT */
+
        /* irq statistics */
 #ifdef EHCI_STATS
        struct ehci_stats       stats;
@@ -552,6 +554,44 @@ struct ehci_fstn {
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef CONFIG_USB_EHCI_ROOT_HUB_TT
+
+/*
+ * Some EHCI controllers have a Transaction Translator built into the
+ * root hub. This is a non-standard feature.  Each controller will need
+ * to add code to the following inline functions, and call them as
+ * needed (mostly in root hub code).
+ */
+
+#define        ehci_is_ARC(e)                  ((e)->is_arc_rh_tt)
+
+/* Returns the speed of a device attached to a port on the root hub. */
+static inline unsigned int
+ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
+{
+       if (ehci_is_ARC(ehci)) {
+               switch ((portsc>>26)&3) {
+               case 0:
+                       return 0;
+               case 1:
+                       return (1<<USB_PORT_FEAT_LOWSPEED);
+               case 2:
+               default:
+                       return (1<<USB_PORT_FEAT_HIGHSPEED);
+               }
+       }
+       return (1<<USB_PORT_FEAT_HIGHSPEED);
+}
+
+#else
+
+#define        ehci_is_ARC(e)                  (0)
+
+#define        ehci_port_speed(ehci, portsc)   (1<<USB_PORT_FEAT_HIGHSPEED)
+#endif
+
+/*-------------------------------------------------------------------------*/
+
 #ifndef DEBUG
 #define STUB_DEBUG_FILES
 #endif /* DEBUG */
index 0a4637995937f325ab6a61387ed90228554fcc8a..35229125d010315a6bee5f4ade0703fc27af9ed3 100644 (file)
 #define PCI_DEVICE_ID_ALTIMA_AC9100    0x03ea
 #define PCI_DEVICE_ID_ALTIMA_AC1003    0x03eb
 
+#define PCI_VENDOR_ID_ARC               0x192E
+#define PCI_DEVICE_ID_ARC_EHCI          0x0101
+
 #define PCI_VENDOR_ID_SYMPHONY         0x1c1c
 #define PCI_DEVICE_ID_SYMPHONY_101     0x0001