RE: [RFC 06/15] xhci: Setup array of USB 2.0 and USB 3.0 ports.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> -----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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux