Skip to content

Commit d5ddcdf

Browse files
matnymangregkh
authored andcommitted
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: Mathias Nyman <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent a5da956 commit d5ddcdf

File tree

3 files changed

+64
-117
lines changed

3 files changed

+64
-117
lines changed

drivers/usb/host/pci-quirks.c

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -984,24 +984,17 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
984984
* Find the Legacy Support Capability register -
985985
* this is optional for xHCI host controllers.
986986
*/
987-
ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
988-
do {
989-
if ((ext_cap_offset + sizeof(val)) > len) {
990-
/* We're reading garbage from the controller */
991-
dev_warn(&pdev->dev,
992-
"xHCI controller failing to respond");
993-
return;
994-
}
987+
ext_cap_offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_LEGACY);
995988

996-
if (!ext_cap_offset)
997-
/* We've reached the end of the extended capabilities */
998-
goto hc_init;
989+
if (!ext_cap_offset)
990+
goto hc_init;
999991

1000-
val = readl(base + ext_cap_offset);
1001-
if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
1002-
break;
1003-
ext_cap_offset = xhci_find_next_cap_offset(base, ext_cap_offset);
1004-
} while (1);
992+
if ((ext_cap_offset + sizeof(val)) > len) {
993+
/* We're reading garbage from the controller */
994+
dev_warn(&pdev->dev, "xHCI controller failing to respond");
995+
return;
996+
}
997+
val = readl(base + ext_cap_offset);
1005998

1006999
/* If the BIOS owns the HC, signal that the OS wants it, and wait */
10071000
if (val & XHCI_HC_BIOS_OWNED) {

drivers/usb/host/xhci-ext-caps.h

Lines changed: 28 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -90,67 +90,40 @@
9090

9191
#include <linux/io.h>
9292

93-
/**
94-
* Return the next extended capability pointer register.
95-
*
96-
* @base PCI register base address.
97-
*
98-
* @ext_offset Offset of the 32-bit register that contains the extended
99-
* capabilites pointer. If searching for the first extended capability, pass
100-
* in XHCI_HCC_PARAMS_OFFSET. If searching for the next extended capability,
101-
* pass in the offset of the current extended capability register.
102-
*
103-
* Returns 0 if there is no next extended capability register or returns the register offset
104-
* from the PCI registers base address.
105-
*/
106-
static inline int xhci_find_next_cap_offset(void __iomem *base, int ext_offset)
107-
{
108-
u32 next;
109-
110-
next = readl(base + ext_offset);
111-
112-
if (ext_offset == XHCI_HCC_PARAMS_OFFSET) {
113-
/* Find the first extended capability */
114-
next = XHCI_HCC_EXT_CAPS(next);
115-
ext_offset = 0;
116-
} else {
117-
/* Find the next extended capability */
118-
next = XHCI_EXT_CAPS_NEXT(next);
119-
}
120-
121-
if (!next)
122-
return 0;
123-
/*
124-
* Address calculation from offset of extended capabilities
125-
* (or HCCPARAMS) register - see section 5.3.6 and section 7.
126-
*/
127-
return ext_offset + (next << 2);
128-
}
129-
13093
/**
13194
* Find the offset of the extended capabilities with capability ID id.
13295
*
133-
* @base PCI MMIO registers base address.
134-
* @ext_offset Offset from base of the first extended capability to look at,
135-
* or the address of HCCPARAMS.
136-
* @id Extended capability ID to search for.
96+
* @base PCI MMIO registers base address.
97+
* @start address at which to start looking, (0 or HCC_PARAMS to start at
98+
* beginning of list)
99+
* @id Extended capability ID to search for.
137100
*
138-
* This uses an arbitrary limit of XHCI_MAX_EXT_CAPS extended capabilities
139-
* to make sure that the list doesn't contain a loop.
101+
* Returns the offset of the next matching extended capability structure.
102+
* Some capabilities can occur several times, e.g., the XHCI_EXT_CAPS_PROTOCOL,
103+
* and this provides a way to find them all.
140104
*/
141-
static inline int xhci_find_ext_cap_by_id(void __iomem *base, int ext_offset, int id)
105+
106+
static inline int xhci_find_next_ext_cap(void __iomem *base, u32 start, int id)
142107
{
143108
u32 val;
144-
int limit = XHCI_MAX_EXT_CAPS;
145-
146-
while (ext_offset && limit > 0) {
147-
val = readl(base + ext_offset);
148-
if (XHCI_EXT_CAPS_ID(val) == id)
149-
break;
150-
ext_offset = xhci_find_next_cap_offset(base, ext_offset);
151-
limit--;
152-
}
153-
if (limit > 0)
154-
return ext_offset;
109+
u32 next;
110+
u32 offset;
111+
112+
offset = start;
113+
if (!start || start == XHCI_HCC_PARAMS_OFFSET) {
114+
val = readl(base + XHCI_HCC_PARAMS_OFFSET);
115+
offset = XHCI_HCC_EXT_CAPS(val) << 2;
116+
if (!offset)
117+
return 0;
118+
};
119+
do {
120+
val = readl(base + offset);
121+
if (XHCI_EXT_CAPS_ID(val) == id && offset != start)
122+
return offset;
123+
124+
next = XHCI_EXT_CAPS_NEXT(val);
125+
offset += next << 2;
126+
} while (next);
127+
155128
return 0;
156129
}

drivers/usb/host/xhci-mem.c

Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,17 +2064,19 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
20642064
}
20652065

20662066
static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
2067-
__le32 __iomem *addr, u8 major_revision, int max_caps)
2067+
__le32 __iomem *addr, int max_caps)
20682068
{
20692069
u32 temp, port_offset, port_count;
20702070
int i;
2071+
u8 major_revision;
20712072
struct xhci_hub *rhub;
20722073

20732074
temp = readl(addr);
2075+
major_revision = XHCI_EXT_PORT_MAJOR(temp);
20742076

2075-
if (XHCI_EXT_PORT_MAJOR(temp) == 0x03) {
2077+
if (major_revision == 0x03) {
20762078
rhub = &xhci->usb3_rhub;
2077-
} else if (XHCI_EXT_PORT_MAJOR(temp) <= 0x02) {
2079+
} else if (major_revision <= 0x02) {
20782080
rhub = &xhci->usb2_rhub;
20792081
} else {
20802082
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,
21902192
*/
21912193
static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
21922194
{
2193-
__le32 __iomem *addr, *tmp_addr;
2194-
u32 offset, tmp_offset;
2195+
void __iomem *base;
2196+
u32 offset;
21952197
unsigned int num_ports;
21962198
int i, j, port_index;
21972199
int cap_count = 0;
2198-
2199-
addr = &xhci->cap_regs->hcc_params;
2200-
offset = XHCI_HCC_EXT_CAPS(readl(addr));
2201-
if (offset == 0) {
2202-
xhci_err(xhci, "No Extended Capability registers, "
2203-
"unable to set up roothub.\n");
2204-
return -ENODEV;
2205-
}
2200+
u32 cap_start;
22062201

22072202
num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
22082203
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)
22202215
for (j = 0; j < XHCI_MAX_INTERVAL; j++)
22212216
INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints);
22222217
}
2218+
base = &xhci->cap_regs->hc_capbase;
22232219

2224-
/*
2225-
* For whatever reason, the first capability offset is from the
2226-
* capability register base, not from the HCCPARAMS register.
2227-
* See section 5.3.6 for offset calculation.
2228-
*/
2229-
addr = &xhci->cap_regs->hc_capbase + offset;
2230-
2231-
tmp_addr = addr;
2232-
tmp_offset = offset;
2220+
cap_start = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_PROTOCOL);
2221+
if (!cap_start) {
2222+
xhci_err(xhci, "No Extended Capability registers, unable to set up roothub\n");
2223+
return -ENODEV;
2224+
}
22332225

2226+
offset = cap_start;
22342227
/* count extended protocol capability entries for later caching */
2235-
do {
2236-
u32 cap_id;
2237-
cap_id = readl(tmp_addr);
2238-
if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
2239-
cap_count++;
2240-
tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id);
2241-
tmp_addr += tmp_offset;
2242-
} while (tmp_offset);
2228+
while (offset) {
2229+
cap_count++;
2230+
offset = xhci_find_next_ext_cap(base, offset,
2231+
XHCI_EXT_CAPS_PROTOCOL);
2232+
}
22432233

22442234
xhci->ext_caps = kzalloc(sizeof(*xhci->ext_caps) * cap_count, flags);
22452235
if (!xhci->ext_caps)
22462236
return -ENOMEM;
22472237

2248-
while (1) {
2249-
u32 cap_id;
2250-
2251-
cap_id = readl(addr);
2252-
if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
2253-
xhci_add_in_port(xhci, num_ports, addr,
2254-
(u8) XHCI_EXT_PORT_MAJOR(cap_id),
2255-
cap_count);
2256-
offset = XHCI_EXT_CAPS_NEXT(cap_id);
2257-
if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports)
2258-
== num_ports)
2238+
offset = cap_start;
2239+
2240+
while (offset) {
2241+
xhci_add_in_port(xhci, num_ports, base + offset, cap_count);
2242+
if (xhci->num_usb2_ports + xhci->num_usb3_ports == num_ports)
22592243
break;
2260-
/*
2261-
* Once you're into the Extended Capabilities, the offset is
2262-
* always relative to the register holding the offset.
2263-
*/
2264-
addr += offset;
2244+
offset = xhci_find_next_ext_cap(base, offset,
2245+
XHCI_EXT_CAPS_PROTOCOL);
22652246
}
22662247

22672248
if (xhci->num_usb2_ports == 0 && xhci->num_usb3_ports == 0) {

0 commit comments

Comments
 (0)