Commit f1a40c85 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[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.
parent 15fea3db
...@@ -38,6 +38,17 @@ config USB_EHCI_SPLIT_ISO ...@@ -38,6 +38,17 @@ config USB_EHCI_SPLIT_ISO
EHCI or USB 2.0 transaction translator implementations. EHCI or USB 2.0 transaction translator implementations.
It should work for ISO-OUT transfers, like audio. 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 config USB_OHCI_HCD
tristate "OHCI HCD support" tristate "OHCI HCD support"
depends on USB depends on USB
......
...@@ -628,8 +628,10 @@ show_registers (struct class_device *class_dev, char *buf) ...@@ -628,8 +628,10 @@ show_registers (struct class_device *class_dev, char *buf)
/* Capability Registers */ /* Capability Registers */
i = HC_VERSION(readl (&ehci->caps->hc_capbase)); i = HC_VERSION(readl (&ehci->caps->hc_capbase));
temp = scnprintf (next, size, temp = scnprintf (next, size,
"PCI device %s\nEHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n", "bus %s device %s\n"
pci_name(to_pci_dev(hcd->self.controller)), "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); i >> 8, i & 0x0ff, ehci->hcd.state);
size -= temp; size -= temp;
next += temp; next += temp;
......
...@@ -279,6 +279,8 @@ static void ehci_watchdog (unsigned long param) ...@@ -279,6 +279,8 @@ static void ehci_watchdog (unsigned long param)
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
} }
#ifdef CONFIG_PCI
/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/... /* 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). * 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) ...@@ -307,6 +309,8 @@ static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
return 0; return 0;
} }
#endif
static int static int
ehci_reboot (struct notifier_block *self, unsigned long code, void *null) ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
{ {
...@@ -335,8 +339,12 @@ static int ehci_hc_reset (struct usb_hcd *hcd) ...@@ -335,8 +339,12 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
dbg_hcs_params (ehci, "reset"); dbg_hcs_params (ehci, "reset");
dbg_hcc_params (ehci, "reset"); dbg_hcc_params (ehci, "reset");
#ifdef CONFIG_PCI
/* EHCI 0.96 and later may have "extended capabilities" */ /* 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) { while (temp) {
u32 cap; u32 cap;
...@@ -356,6 +364,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd) ...@@ -356,6 +364,7 @@ static int ehci_hc_reset (struct usb_hcd *hcd)
} }
temp = (cap >> 8) & 0xff; temp = (cap >> 8) & 0xff;
} }
#endif
/* cache this readonly data; minimize PCI reads */ /* cache this readonly data; minimize PCI reads */
ehci->hcs_params = readl (&ehci->caps->hcs_params); ehci->hcs_params = readl (&ehci->caps->hcs_params);
...@@ -372,7 +381,7 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -372,7 +381,7 @@ static int ehci_start (struct usb_hcd *hcd)
struct usb_bus *bus; struct usb_bus *bus;
int retval; int retval;
u32 hcc_params; u32 hcc_params;
u8 tempbyte; u8 sbrn = 0;
init_timer (&ehci->watchdog); init_timer (&ehci->watchdog);
ehci->watchdog.function = ehci_watchdog; ehci->watchdog.function = ehci_watchdog;
...@@ -406,6 +415,29 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -406,6 +415,29 @@ static int ehci_start (struct usb_hcd *hcd)
writel (INTR_MASK, &ehci->regs->intr_enable); writel (INTR_MASK, &ehci->regs->intr_enable);
writel (ehci->periodic_dma, &ehci->regs->frame_list); 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 * 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 * a 'real' qh without stopping the async schedule [4.8]. use it
...@@ -443,9 +475,6 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -443,9 +475,6 @@ static int ehci_start (struct usb_hcd *hcd)
#endif #endif
} }
/* help hc dma work well with cachelines */
pci_set_mwi (to_pci_dev(ehci->hcd.self.controller));
/* clear interrupt enables, set irq latency */ /* clear interrupt enables, set irq latency */
temp = readl (&ehci->regs->command) & 0x0fff; temp = readl (&ehci->regs->command) & 0x0fff;
if (log2_irq_thresh < 0 || log2_irq_thresh > 6) if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
...@@ -493,12 +522,10 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -493,12 +522,10 @@ static int ehci_start (struct usb_hcd *hcd)
writel (FLAG_CF, &ehci->regs->configured_flag); writel (FLAG_CF, &ehci->regs->configured_flag);
readl (&ehci->regs->command); /* unblock posted write */ 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)); temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
ehci_info (ehci, ehci_info (ehci,
"USB %x.%x enabled, EHCI %x.%02x, driver %s\n", "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); temp >> 8, temp & 0xff, DRIVER_VERSION);
/* /*
...@@ -995,7 +1022,9 @@ static const struct hc_driver ehci_driver = { ...@@ -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 */ /* PCI driver selection metadata; PCI hotplugging uses this */
static const struct pci_device_id pci_ids [] = { { static const struct pci_device_id pci_ids [] = { {
...@@ -1021,6 +1050,9 @@ static struct pci_driver ehci_pci_driver = { ...@@ -1021,6 +1050,9 @@ static struct pci_driver ehci_pci_driver = {
#endif #endif
}; };
#endif /* PCI */
#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
MODULE_DESCRIPTION (DRIVER_INFO); MODULE_DESCRIPTION (DRIVER_INFO);
......
...@@ -40,6 +40,15 @@ static int check_reset_complete ( ...@@ -40,6 +40,15 @@ static int check_reset_complete (
/* if reset finished and it's still not enabled -- handoff */ /* if reset finished and it's still not enabled -- handoff */
if (!(port_status & PORT_PE)) { 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", ehci_dbg (ehci, "port %d full speed --> companion\n",
index + 1); index + 1);
...@@ -257,7 +266,8 @@ static int ehci_hub_control ( ...@@ -257,7 +266,8 @@ static int ehci_hub_control (
if (!(temp & PORT_OWNER)) { if (!(temp & PORT_OWNER)) {
if (temp & PORT_CONNECT) { if (temp & PORT_CONNECT) {
status |= 1 << USB_PORT_FEAT_CONNECTION; 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) if (temp & PORT_PE)
status |= 1 << USB_PORT_FEAT_ENABLE; status |= 1 << USB_PORT_FEAT_ENABLE;
...@@ -307,8 +317,12 @@ static int ehci_hub_control ( ...@@ -307,8 +317,12 @@ static int ehci_hub_control (
&ehci->regs->port_status [wIndex]); &ehci->regs->port_status [wIndex]);
break; break;
case USB_PORT_FEAT_RESET: 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 if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
&& !ehci_is_ARC(ehci)
&& PORT_USB11 (temp)) { && PORT_USB11 (temp)) {
ehci_dbg (ehci, ehci_dbg (ehci,
"port %d low speed --> companion\n", "port %d low speed --> companion\n",
......
...@@ -165,7 +165,10 @@ static void qtd_copy_status ( ...@@ -165,7 +165,10 @@ static void qtd_copy_status (
/* if async CSPLIT failed, try cleaning out the TT buffer */ /* if async CSPLIT failed, try cleaning out the TT buffer */
} else if (urb->dev->tt && !usb_pipeint (urb->pipe) } else if (urb->dev->tt && !usb_pipeint (urb->pipe)
&& ((token & QTD_STS_MMF) != 0 && ((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 #ifdef DEBUG
struct usb_device *tt = urb->dev->tt->hub; struct usb_device *tt = urb->dev->tt->hub;
dev_dbg (&tt->dev, dev_dbg (&tt->dev,
...@@ -576,7 +579,7 @@ clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh) ...@@ -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 // when each interface/altsetting is established. Unlink
// any previous qh and cancel its urbs first; endpoints are // any previous qh and cancel its urbs first; endpoints are
// implicitly reset then (data toggle too). // 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 ( ...@@ -674,7 +677,13 @@ qh_make (
info2 |= (EHCI_TUNE_MULT_TT << 30); info2 |= (EHCI_TUNE_MULT_TT << 30);
info2 |= urb->dev->ttport << 23; 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 } */ /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */
......
...@@ -85,6 +85,8 @@ struct ehci_hcd { /* one per controller */ ...@@ -85,6 +85,8 @@ struct ehci_hcd { /* one per controller */
unsigned long actions; unsigned long actions;
unsigned stamp; unsigned stamp;
unsigned is_arc_rh_tt:1; /* ARC roothub with TT */
/* irq statistics */ /* irq statistics */
#ifdef EHCI_STATS #ifdef EHCI_STATS
struct ehci_stats stats; struct ehci_stats stats;
...@@ -552,6 +554,44 @@ struct ehci_fstn { ...@@ -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 #ifndef DEBUG
#define STUB_DEBUG_FILES #define STUB_DEBUG_FILES
#endif /* DEBUG */ #endif /* DEBUG */
......
...@@ -1870,6 +1870,9 @@ ...@@ -1870,6 +1870,9 @@
#define PCI_DEVICE_ID_ALTIMA_AC9100 0x03ea #define PCI_DEVICE_ID_ALTIMA_AC9100 0x03ea
#define PCI_DEVICE_ID_ALTIMA_AC1003 0x03eb #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_VENDOR_ID_SYMPHONY 0x1c1c
#define PCI_DEVICE_ID_SYMPHONY_101 0x0001 #define PCI_DEVICE_ID_SYMPHONY_101 0x0001
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment