Commit d5ddcdf4 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

xhci: rework xhci extended capability list parsing functions

Replace the existing two extended capability parsing helper functions with
one called xhci_find_next_ext_cap().

The extended capabilities are read both in pci-quirks before xhci driver is
loaded, and inside the xhci driver when adding ports. The existing helpers
did not suit well for these cases and a lot of custom parsing code was
needed.

The new helper function simplifies these two cases a lot.

The motivation for this rework was that code to support xhci debug
capability needed to parse extended capabilities, and it included
yet another capability parsing helper specific for its needs. With
this solution it debug capability code can use this new  helper as well
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a5da9568
......@@ -984,24 +984,17 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
* Find the Legacy Support Capability register -
* this is optional for xHCI host controllers.
*/
ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
do {
if ((ext_cap_offset + sizeof(val)) > len) {
/* We're reading garbage from the controller */
dev_warn(&pdev->dev,
"xHCI controller failing to respond");
return;
}
ext_cap_offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_LEGACY);
if (!ext_cap_offset)
/* We've reached the end of the extended capabilities */
goto hc_init;
if ((ext_cap_offset + sizeof(val)) > len) {
/* We're reading garbage from the controller */
dev_warn(&pdev->dev, "xHCI controller failing to respond");
return;
}
val = readl(base + ext_cap_offset);
if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
break;
ext_cap_offset = xhci_find_next_cap_offset(base, ext_cap_offset);
} while (1);
/* If the BIOS owns the HC, signal that the OS wants it, and wait */
if (val & XHCI_HC_BIOS_OWNED) {
......
......@@ -90,67 +90,40 @@
#include <linux/io.h>
/**
* Return the next extended capability pointer register.
*
* @base PCI register base address.
*
* @ext_offset Offset of the 32-bit register that contains the extended
* capabilites pointer. If searching for the first extended capability, pass
* in XHCI_HCC_PARAMS_OFFSET. If searching for the next extended capability,
* pass in the offset of the current extended capability register.
*
* Returns 0 if there is no next extended capability register or returns the register offset
* from the PCI registers base address.
*/
static inline int xhci_find_next_cap_offset(void __iomem *base, int ext_offset)
{
u32 next;
next = readl(base + ext_offset);
if (ext_offset == XHCI_HCC_PARAMS_OFFSET) {
/* Find the first extended capability */
next = XHCI_HCC_EXT_CAPS(next);
ext_offset = 0;
} else {
/* Find the next extended capability */
next = XHCI_EXT_CAPS_NEXT(next);
}
if (!next)
return 0;
/*
* Address calculation from offset of extended capabilities
* (or HCCPARAMS) register - see section 5.3.6 and section 7.
*/
return ext_offset + (next << 2);
}
/**
* Find the offset of the extended capabilities with capability ID id.
*
* @base PCI MMIO registers base address.
* @ext_offset Offset from base of the first extended capability to look at,
* or the address of HCCPARAMS.
* @start address at which to start looking, (0 or HCC_PARAMS to start at
* beginning of list)
* @id Extended capability ID to search for.
*
* This uses an arbitrary limit of XHCI_MAX_EXT_CAPS extended capabilities
* to make sure that the list doesn't contain a loop.
* Returns the offset of the next matching extended capability structure.
* Some capabilities can occur several times, e.g., the XHCI_EXT_CAPS_PROTOCOL,
* and this provides a way to find them all.
*/
static inline int xhci_find_ext_cap_by_id(void __iomem *base, int ext_offset, int id)
static inline int xhci_find_next_ext_cap(void __iomem *base, u32 start, int id)
{
u32 val;
int limit = XHCI_MAX_EXT_CAPS;
while (ext_offset && limit > 0) {
val = readl(base + ext_offset);
if (XHCI_EXT_CAPS_ID(val) == id)
break;
ext_offset = xhci_find_next_cap_offset(base, ext_offset);
limit--;
}
if (limit > 0)
return ext_offset;
u32 next;
u32 offset;
offset = start;
if (!start || start == XHCI_HCC_PARAMS_OFFSET) {
val = readl(base + XHCI_HCC_PARAMS_OFFSET);
offset = XHCI_HCC_EXT_CAPS(val) << 2;
if (!offset)
return 0;
};
do {
val = readl(base + offset);
if (XHCI_EXT_CAPS_ID(val) == id && offset != start)
return offset;
next = XHCI_EXT_CAPS_NEXT(val);
offset += next << 2;
} while (next);
return 0;
}
......@@ -2064,17 +2064,19 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
}
static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
__le32 __iomem *addr, u8 major_revision, int max_caps)
__le32 __iomem *addr, int max_caps)
{
u32 temp, port_offset, port_count;
int i;
u8 major_revision;
struct xhci_hub *rhub;
temp = readl(addr);
major_revision = XHCI_EXT_PORT_MAJOR(temp);
if (XHCI_EXT_PORT_MAJOR(temp) == 0x03) {
if (major_revision == 0x03) {
rhub = &xhci->usb3_rhub;
} else if (XHCI_EXT_PORT_MAJOR(temp) <= 0x02) {
} else if (major_revision <= 0x02) {
rhub = &xhci->usb2_rhub;
} else {
xhci_warn(xhci, "Ignoring unknown port speed, "
......@@ -2190,19 +2192,12 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
*/
static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
{
__le32 __iomem *addr, *tmp_addr;
u32 offset, tmp_offset;
void __iomem *base;
u32 offset;
unsigned int num_ports;
int i, j, port_index;
int cap_count = 0;
addr = &xhci->cap_regs->hcc_params;
offset = XHCI_HCC_EXT_CAPS(readl(addr));
if (offset == 0) {
xhci_err(xhci, "No Extended Capability registers, "
"unable to set up roothub.\n");
return -ENODEV;
}
u32 cap_start;
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
xhci->port_array = kzalloc(sizeof(*xhci->port_array)*num_ports, flags);
......@@ -2220,48 +2215,34 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
for (j = 0; j < XHCI_MAX_INTERVAL; j++)
INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
}
base = &xhci->cap_regs->hc_capbase;
/*
* 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;
tmp_addr = addr;
tmp_offset = offset;
cap_start = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_PROTOCOL);
if (!cap_start) {
xhci_err(xhci, "No Extended Capability registers, unable to set up roothub\n");
return -ENODEV;
}
offset = cap_start;
/* count extended protocol capability entries for later caching */
do {
u32 cap_id;
cap_id = readl(tmp_addr);
if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
while (offset) {
cap_count++;
tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id);
tmp_addr += tmp_offset;
} while (tmp_offset);
offset = xhci_find_next_ext_cap(base, offset,
XHCI_EXT_CAPS_PROTOCOL);
}
xhci->ext_caps = kzalloc(sizeof(*xhci->ext_caps) * cap_count, flags);
if (!xhci->ext_caps)
return -ENOMEM;
while (1) {
u32 cap_id;
cap_id = readl(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),
cap_count);
offset = XHCI_EXT_CAPS_NEXT(cap_id);
if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports)
== num_ports)
offset = cap_start;
while (offset) {
xhci_add_in_port(xhci, num_ports, base + offset, cap_count);
if (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;
offset = xhci_find_next_ext_cap(base, offset,
XHCI_EXT_CAPS_PROTOCOL);
}
if (xhci->num_usb2_ports == 0 && xhci->num_usb3_ports == 0) {
......
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