From f1a40c850b54a213bd746f91bd7c92786fc744ed Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 1 Apr 2004 17:56:28 -0800 Subject: [PATCH] [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 --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 55ec90a82ed64..946898ac46da7 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -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 diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 989a378044919..530cfd9f84ca0 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -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; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 75d6db2b05405..1e61876ca0a7d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -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); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 2c5cf36c90ba5..5c3a0615211c2 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -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", diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 69ca6429af12c..fd8634b312313 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -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 } */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 5c6eef9674d62..8cd5b7b2c97a7 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -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<