ChangeSet 1.1713.7.2, 2004/04/02 09:56:28-08:00, david-b@pacbell.net [PATCH] USB: ehci updates: CONFIG_PCI, integrated TT 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 | 11 +++++++++ drivers/usb/host/ehci-dbg.c | 6 +++-- drivers/usb/host/ehci-hcd.c | 50 ++++++++++++++++++++++++++++++++++++-------- drivers/usb/host/ehci-hub.c | 18 ++++++++++++++- drivers/usb/host/ehci-q.c | 15 ++++++++++--- drivers/usb/host/ehci.h | 40 +++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 3 ++ 7 files changed, 127 insertions(+), 16 deletions(-) diff -Nru a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig --- a/drivers/usb/host/Kconfig Wed Apr 14 14:34:12 2004 +++ b/drivers/usb/host/Kconfig Wed Apr 14 14:34:12 2004 @@ -38,6 +38,17 @@ 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 diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c --- a/drivers/usb/host/ehci-dbg.c Wed Apr 14 14:34:12 2004 +++ b/drivers/usb/host/ehci-dbg.c Wed Apr 14 14:34:12 2004 @@ -628,8 +628,10 @@ /* 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; diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Wed Apr 14 14:34:12 2004 +++ b/drivers/usb/host/ehci-hcd.c Wed Apr 14 14:34:12 2004 @@ -279,6 +279,8 @@ 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 @@ return 0; } +#endif + static int ehci_reboot (struct notifier_block *self, unsigned long code, void *null) { @@ -335,8 +339,12 @@ 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 @@ } temp = (cap >> 8) & 0xff; } +#endif /* cache this readonly data; minimize PCI reads */ ehci->hcs_params = readl (&ehci->caps->hcs_params); @@ -372,7 +381,7 @@ 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 @@ 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 @@ #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 @@ 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 @@ /*-------------------------------------------------------------------------*/ -/* 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 [] = { { @@ -1020,6 +1049,9 @@ .resume = usb_hcd_pci_resume, #endif }; + +#endif /* PCI */ + #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c --- a/drivers/usb/host/ehci-hub.c Wed Apr 14 14:34:12 2004 +++ b/drivers/usb/host/ehci-hub.c Wed Apr 14 14:34:12 2004 @@ -40,6 +40,15 @@ /* 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 @@ 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 @@ &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", diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- a/drivers/usb/host/ehci-q.c Wed Apr 14 14:34:12 2004 +++ b/drivers/usb/host/ehci-q.c Wed Apr 14 14:34:12 2004 @@ -165,7 +165,10 @@ /* 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 @@ // 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 @@ 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 } */ diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- a/drivers/usb/host/ehci.h Wed Apr 14 14:34:12 2004 +++ b/drivers/usb/host/ehci.h Wed Apr 14 14:34:12 2004 @@ -85,6 +85,8 @@ unsigned long actions; unsigned stamp; + unsigned is_arc_rh_tt:1; /* ARC roothub with TT */ + /* irq statistics */ #ifdef EHCI_STATS struct ehci_stats stats; @@ -549,6 +551,44 @@ dma_addr_t fstn_dma; union ehci_shadow fstn_next; /* ptr to periodic q entry */ } __attribute__ ((aligned (32))); + +/*-------------------------------------------------------------------------*/ + +#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<