]>
Commit | Line | Data |
---|---|---|
3d6a91bc GKH |
1 | From da6699ce4a889c3795624ccdcfe7181cc89f18e8 Mon Sep 17 00:00:00 2001 |
2 | From: Sarah Sharp <sarah.a.sharp@linux.intel.com> | |
3 | Date: Tue, 26 Oct 2010 16:47:13 -0700 | |
4 | Subject: xhci: Setup array of USB 2.0 and USB 3.0 ports. | |
5 | ||
6 | From: Sarah Sharp <sarah.a.sharp@linux.intel.com> | |
7 | ||
8 | commit da6699ce4a889c3795624ccdcfe7181cc89f18e8 upstream. | |
9 | ||
10 | An xHCI host controller contains USB 2.0 and USB 3.0 ports, which can | |
11 | occur in any order in the PORTSC registers. We cannot read the port speed | |
12 | bits in the PORTSC registers at init time to determine the port speed, | |
13 | since those bits are only valid when a USB device is plugged into the | |
14 | port. | |
15 | ||
16 | Instead, we read the "Supported Protocol Capability" registers in the xHC | |
17 | Extended Capabilities space. Those describe the protocol, port offset in | |
18 | the PORTSC registers, and port count. We use those registers to create | |
19 | two arrays of pointers to the PORTSC registers, one for USB 3.0 ports, and | |
20 | another for USB 2.0 ports. A third array keeps track of the port protocol | |
21 | major revision, and is indexed with the internal xHCI port number. | |
22 | ||
23 | This commit is a bit big, but it should be queued for stable because the "Don't | |
24 | let the USB core disable SuperSpeed ports" patch depends on it. There is no | |
25 | other way to determine which ports are SuperSpeed ports without this patch. | |
26 | ||
27 | Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> | |
28 | Tested-by: Don Zickus <dzickus@redhat.com> | |
29 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
30 | ||
31 | --- | |
32 | drivers/usb/host/xhci-mem.c | 164 ++++++++++++++++++++++++++++++++++++++++++++ | |
33 | drivers/usb/host/xhci.h | 27 +++++++ | |
34 | 2 files changed, 191 insertions(+) | |
35 | ||
36 | --- a/drivers/usb/host/xhci-mem.c | |
37 | +++ b/drivers/usb/host/xhci-mem.c | |
38 | @@ -1441,6 +1441,13 @@ void xhci_mem_cleanup(struct xhci_hcd *x | |
39 | xhci->dcbaa = NULL; | |
40 | ||
41 | scratchpad_free(xhci); | |
42 | + | |
43 | + xhci->num_usb2_ports = 0; | |
44 | + xhci->num_usb3_ports = 0; | |
45 | + kfree(xhci->usb2_ports); | |
46 | + kfree(xhci->usb3_ports); | |
47 | + kfree(xhci->port_array); | |
48 | + | |
49 | xhci->page_size = 0; | |
50 | xhci->page_shift = 0; | |
51 | } | |
52 | @@ -1624,6 +1631,161 @@ static void xhci_set_hc_event_deq(struct | |
53 | &xhci->ir_set->erst_dequeue); | |
54 | } | |
55 | ||
56 | +static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, | |
57 | + u32 __iomem *addr, u8 major_revision) | |
58 | +{ | |
59 | + u32 temp, port_offset, port_count; | |
60 | + int i; | |
61 | + | |
62 | + if (major_revision > 0x03) { | |
63 | + xhci_warn(xhci, "Ignoring unknown port speed, " | |
64 | + "Ext Cap %p, revision = 0x%x\n", | |
65 | + addr, major_revision); | |
66 | + /* Ignoring port protocol we can't understand. FIXME */ | |
67 | + return; | |
68 | + } | |
69 | + | |
70 | + /* Port offset and count in the third dword, see section 7.2 */ | |
71 | + temp = xhci_readl(xhci, addr + 2); | |
72 | + port_offset = XHCI_EXT_PORT_OFF(temp); | |
73 | + port_count = XHCI_EXT_PORT_COUNT(temp); | |
74 | + xhci_dbg(xhci, "Ext Cap %p, port offset = %u, " | |
75 | + "count = %u, revision = 0x%x\n", | |
76 | + addr, port_offset, port_count, major_revision); | |
77 | + /* Port count includes the current port offset */ | |
78 | + if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) | |
79 | + /* WTF? "Valid values are ‘1’ to MaxPorts" */ | |
80 | + return; | |
81 | + port_offset--; | |
82 | + for (i = port_offset; i < (port_offset + port_count); i++) { | |
83 | + /* Duplicate entry. Ignore the port if the revisions differ. */ | |
84 | + if (xhci->port_array[i] != 0) { | |
85 | + xhci_warn(xhci, "Duplicate port entry, Ext Cap %p," | |
86 | + " port %u\n", addr, i); | |
87 | + xhci_warn(xhci, "Port was marked as USB %u, " | |
88 | + "duplicated as USB %u\n", | |
89 | + xhci->port_array[i], major_revision); | |
90 | + /* Only adjust the roothub port counts if we haven't | |
91 | + * found a similar duplicate. | |
92 | + */ | |
93 | + if (xhci->port_array[i] != major_revision && | |
94 | + xhci->port_array[i] != (u8) -1) { | |
95 | + if (xhci->port_array[i] == 0x03) | |
96 | + xhci->num_usb3_ports--; | |
97 | + else | |
98 | + xhci->num_usb2_ports--; | |
99 | + xhci->port_array[i] = (u8) -1; | |
100 | + } | |
101 | + /* FIXME: Should we disable the port? */ | |
102 | + } | |
103 | + xhci->port_array[i] = major_revision; | |
104 | + if (major_revision == 0x03) | |
105 | + xhci->num_usb3_ports++; | |
106 | + else | |
107 | + xhci->num_usb2_ports++; | |
108 | + } | |
109 | + /* FIXME: Should we disable ports not in the Extended Capabilities? */ | |
110 | +} | |
111 | + | |
112 | +/* | |
113 | + * Scan the Extended Capabilities for the "Supported Protocol Capabilities" that | |
114 | + * specify what speeds each port is supposed to be. We can't count on the port | |
115 | + * speed bits in the PORTSC register being correct until a device is connected, | |
116 | + * but we need to set up the two fake roothubs with the correct number of USB | |
117 | + * 3.0 and USB 2.0 ports at host controller initialization time. | |
118 | + */ | |
119 | +static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) | |
120 | +{ | |
121 | + u32 __iomem *addr; | |
122 | + u32 offset; | |
123 | + unsigned int num_ports; | |
124 | + int i, port_index; | |
125 | + | |
126 | + addr = &xhci->cap_regs->hcc_params; | |
127 | + offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr)); | |
128 | + if (offset == 0) { | |
129 | + xhci_err(xhci, "No Extended Capability registers, " | |
130 | + "unable to set up roothub.\n"); | |
131 | + return -ENODEV; | |
132 | + } | |
133 | + | |
134 | + num_ports = HCS_MAX_PORTS(xhci->hcs_params1); | |
135 | + xhci->port_array = kzalloc(sizeof(*xhci->port_array)*num_ports, flags); | |
136 | + if (!xhci->port_array) | |
137 | + return -ENOMEM; | |
138 | + | |
139 | + /* | |
140 | + * For whatever reason, the first capability offset is from the | |
141 | + * capability register base, not from the HCCPARAMS register. | |
142 | + * See section 5.3.6 for offset calculation. | |
143 | + */ | |
144 | + addr = &xhci->cap_regs->hc_capbase + offset; | |
145 | + while (1) { | |
146 | + u32 cap_id; | |
147 | + | |
148 | + cap_id = xhci_readl(xhci, addr); | |
149 | + if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL) | |
150 | + xhci_add_in_port(xhci, num_ports, addr, | |
151 | + (u8) XHCI_EXT_PORT_MAJOR(cap_id)); | |
152 | + offset = XHCI_EXT_CAPS_NEXT(cap_id); | |
153 | + if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports) | |
154 | + == num_ports) | |
155 | + break; | |
156 | + /* | |
157 | + * Once you're into the Extended Capabilities, the offset is | |
158 | + * always relative to the register holding the offset. | |
159 | + */ | |
160 | + addr += offset; | |
161 | + } | |
162 | + | |
163 | + if (xhci->num_usb2_ports == 0 && xhci->num_usb3_ports == 0) { | |
164 | + xhci_warn(xhci, "No ports on the roothubs?\n"); | |
165 | + return -ENODEV; | |
166 | + } | |
167 | + xhci_dbg(xhci, "Found %u USB 2.0 ports and %u USB 3.0 ports.\n", | |
168 | + xhci->num_usb2_ports, xhci->num_usb3_ports); | |
169 | + /* | |
170 | + * Note we could have all USB 3.0 ports, or all USB 2.0 ports. | |
171 | + * Not sure how the USB core will handle a hub with no ports... | |
172 | + */ | |
173 | + if (xhci->num_usb2_ports) { | |
174 | + xhci->usb2_ports = kmalloc(sizeof(*xhci->usb2_ports)* | |
175 | + xhci->num_usb2_ports, flags); | |
176 | + if (!xhci->usb2_ports) | |
177 | + return -ENOMEM; | |
178 | + | |
179 | + port_index = 0; | |
180 | + for (i = 0; i < num_ports; i++) | |
181 | + if (xhci->port_array[i] != 0x03) { | |
182 | + xhci->usb2_ports[port_index] = | |
183 | + &xhci->op_regs->port_status_base + | |
184 | + NUM_PORT_REGS*i; | |
185 | + xhci_dbg(xhci, "USB 2.0 port at index %u, " | |
186 | + "addr = %p\n", i, | |
187 | + xhci->usb2_ports[port_index]); | |
188 | + port_index++; | |
189 | + } | |
190 | + } | |
191 | + if (xhci->num_usb3_ports) { | |
192 | + xhci->usb3_ports = kmalloc(sizeof(*xhci->usb3_ports)* | |
193 | + xhci->num_usb3_ports, flags); | |
194 | + if (!xhci->usb3_ports) | |
195 | + return -ENOMEM; | |
196 | + | |
197 | + port_index = 0; | |
198 | + for (i = 0; i < num_ports; i++) | |
199 | + if (xhci->port_array[i] == 0x03) { | |
200 | + xhci->usb3_ports[port_index] = | |
201 | + &xhci->op_regs->port_status_base + | |
202 | + NUM_PORT_REGS*i; | |
203 | + xhci_dbg(xhci, "USB 3.0 port at index %u, " | |
204 | + "addr = %p\n", i, | |
205 | + xhci->usb3_ports[port_index]); | |
206 | + port_index++; | |
207 | + } | |
208 | + } | |
209 | + return 0; | |
210 | +} | |
211 | ||
212 | int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) | |
213 | { | |
214 | @@ -1804,6 +1966,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, | |
215 | ||
216 | if (scratchpad_alloc(xhci, flags)) | |
217 | goto fail; | |
218 | + if (xhci_setup_port_arrays(xhci, flags)) | |
219 | + goto fail; | |
220 | ||
221 | return 0; | |
222 | ||
223 | --- a/drivers/usb/host/xhci.h | |
224 | +++ b/drivers/usb/host/xhci.h | |
225 | @@ -448,6 +448,24 @@ struct xhci_doorbell_array { | |
226 | ||
227 | ||
228 | /** | |
229 | + * struct xhci_protocol_caps | |
230 | + * @revision: major revision, minor revision, capability ID, | |
231 | + * and next capability pointer. | |
232 | + * @name_string: Four ASCII characters to say which spec this xHC | |
233 | + * follows, typically "USB ". | |
234 | + * @port_info: Port offset, count, and protocol-defined information. | |
235 | + */ | |
236 | +struct xhci_protocol_caps { | |
237 | + u32 revision; | |
238 | + u32 name_string; | |
239 | + u32 port_info; | |
240 | +}; | |
241 | + | |
242 | +#define XHCI_EXT_PORT_MAJOR(x) (((x) >> 24) & 0xff) | |
243 | +#define XHCI_EXT_PORT_OFF(x) ((x) & 0xff) | |
244 | +#define XHCI_EXT_PORT_COUNT(x) (((x) >> 8) & 0xff) | |
245 | + | |
246 | +/** | |
247 | * struct xhci_container_ctx | |
248 | * @type: Type of context. Used to calculated offsets to contained contexts. | |
249 | * @size: Size of the context data | |
250 | @@ -1204,6 +1222,15 @@ struct xhci_hcd { | |
251 | #define XHCI_LINK_TRB_QUIRK (1 << 0) | |
252 | #define XHCI_RESET_EP_QUIRK (1 << 1) | |
253 | #define XHCI_NEC_HOST (1 << 2) | |
254 | + | |
255 | + /* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */ | |
256 | + u8 *port_array; | |
257 | + /* Array of pointers to USB 3.0 PORTSC registers */ | |
258 | + u32 __iomem **usb3_ports; | |
259 | + unsigned int num_usb3_ports; | |
260 | + /* Array of pointers to USB 2.0 PORTSC registers */ | |
261 | + u32 __iomem **usb2_ports; | |
262 | + unsigned int num_usb2_ports; | |
263 | }; | |
264 | ||
265 | /* For testing purposes */ |