> -----Original Message----- > From: Sarah Sharp [mailto:sarah.a.sharp@xxxxxxxxxxxxxxx] > Sent: Tuesday, November 30, 2010 8:58 AM > To: linux-usb@xxxxxxxxxxxxxxx > Cc: Alan Stern; John Youn; Xu, Andiry > Subject: [RFC 06/15] xhci: Setup array of USB 2.0 and USB 3.0 ports. > > An xHCI host controller contains USB 2.0 and USB 3.0 ports, which can > occur in any order in the PORTSC registers. We cannot read the port speed > bits in the PORTSC registers at init time to determine the port speed, > since those bits are only valid when a USB device is plugged into the > port. > > Instead, we read the "Supported Protocol Capability" registers in the xHC > Extended Capabilities space. Those describe the protocol, port offset in > the PORTSC registers, and port count. We use those registers to create > two arrays of pointers to the PORTSC registers, one for USB 3.0 ports, and > another for USB 2.0 ports. A third array keeps track of the port protocol > major revision, and is indexed with the internal xHCI port number. > > This will make it easy to keep track of the port types to handle the xHCI > driver faking two roothubs. The two arrays of pointers can be used to > index the port the USB core wants, and the third array can be used to map > xHC port status change event to the correct roothub that should process > that event. > > Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx> > --- > drivers/usb/host/xhci-mem.c | 164 > +++++++++++++++++++++++++++++++++++++++++++ > drivers/usb/host/xhci.h | 26 +++++++ > 2 files changed, 190 insertions(+), 0 deletions(-) > > diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c > index 2027706..e446639 100644 > --- a/drivers/usb/host/xhci-mem.c > +++ b/drivers/usb/host/xhci-mem.c > @@ -1443,6 +1443,13 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) > xhci->dcbaa = NULL; > > scratchpad_free(xhci); > + > + xhci->num_usb2_ports = 0; > + xhci->num_usb3_ports = 0; > + kfree(xhci->usb2_ports); > + kfree(xhci->usb3_ports); > + kfree(xhci->port_array); > + > xhci->page_size = 0; > xhci->page_shift = 0; > xhci->bus_suspended = 0; > @@ -1627,6 +1634,161 @@ static void xhci_set_hc_event_deq(struct xhci_hcd > *xhci) > &xhci->ir_set->erst_dequeue); > } > > +static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int > num_ports, > + u32 __iomem *addr, u8 major_revision) > +{ > + u32 temp, port_offset, port_count; > + int i; > + > + if (major_revision > 0x03) { > + xhci_warn(xhci, "Ignoring unknown port speed, " > + "Ext Cap %p, revision = 0x%x\n", > + addr, major_revision); > + /* Ignoring port protocol we can't understand. FIXME */ > + return; > + } > + > + /* Port offset and count in the third dword, see section 7.2 */ > + temp = xhci_readl(xhci, addr + 2); > + port_offset = XHCI_EXT_PORT_OFF(temp); > + port_count = XHCI_EXT_PORT_COUNT(temp); > + xhci_dbg(xhci, "Ext Cap %p, port offset = %u, " > + "count = %u, revision = 0x%x\n", > + addr, port_offset, port_count, major_revision); > + /* Port count includes the current port offset */ > + if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) > + /* WTF? "Valid values are '1' to MaxPorts" */ > + return; > + port_offset--; > + for (i = port_offset; i < (port_offset + port_count); i++) { > + /* Duplicate entry. Ignore the port if the revisions differ. > */ > + if (xhci->port_array[i] != 0) { > + xhci_warn(xhci, "Duplicate port entry, Ext Cap %p," > + " port %u\n", addr, i); > + xhci_warn(xhci, "Port was marked as USB %u, " > + "duplicated as USB %u\n", > + xhci->port_array[i], major_revision); > + /* Only adjust the roothub port counts if we haven't > + * found a similar duplicate. > + */ > + if (xhci->port_array[i] != major_revision && > + xhci->port_array[i] != (u8) -1) { > + if (xhci->port_array[i] == 0x03) > + xhci->num_usb3_ports--; > + else > + xhci->num_usb2_ports--; > + xhci->port_array[i] = (u8) -1; > + } > + /* FIXME: Should we disable the port? */ > + } > + xhci->port_array[i] = major_revision; > + if (major_revision == 0x03) > + xhci->num_usb3_ports++; > + else > + xhci->num_usb2_ports++; > + } > + /* FIXME: Should we disable ports not in the Extended Capabilities? > */ > +} > + > +/* > + * Scan the Extended Capabilities for the "Supported Protocol > Capabilities" that > + * specify what speeds each port is supposed to be. We can't count on > the port > + * speed bits in the PORTSC register being correct until a device is > connected, > + * but we need to set up the two fake roothubs with the correct number of > USB > + * 3.0 and USB 2.0 ports at host controller initialization time. > + */ > +static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) > +{ > + u32 __iomem *addr; > + u32 offset; > + unsigned int num_ports; > + int i, port_index; > + > + addr = &xhci->cap_regs->hcc_params; > + offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr)); >From xHCI spec, the offset read here should be multiple by 4 and add to the address base. > + if (offset == 0) { > + xhci_err(xhci, "No Extended Capability registers, " > + "unable to set up roothub.\n"); > + return -ENODEV; > + } > + > + num_ports = HCS_MAX_PORTS(xhci->hcs_params1); > + xhci->port_array = kzalloc(sizeof(*xhci->port_array)*num_ports, > flags); > + if (!xhci->port_array) > + return -ENOMEM; > + > + /* > + * For whatever reason, the first capability offset is from the > + * capability register base, not from the HCCPARAMS register. > + * See section 5.3.6 for offset calculation. > + */ > + addr = &xhci->cap_regs->hc_capbase + offset; So maybe here should be offset << 2? > + while (1) { > + u32 cap_id; > + > + cap_id = xhci_readl(xhci, addr); > + if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) > + xhci_add_in_port(xhci, num_ports, addr, > + (u8) XHCI_EXT_PORT_MAJOR(cap_id)); > + offset = XHCI_EXT_CAPS_NEXT(cap_id); > + if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports) > + == num_ports) > + break; > + /* > + * Once you're into the Extended Capabilities, the offset is > + * always relative to the register holding the offset. > + */ > + addr += offset; And here should be addr += offset << 2; Thanks, Andiry -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html