blob: 5699499f26af87ebac0c17924b7fa04e0bb475bd [file] [log] [blame]
Nico Huber90292652013-06-13 14:37:15 +02001/*
2 * This file is part of the libpayload project.
3 *
4 * Copyright (C) 2013 secunet Security Networks AG
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30//#define XHCI_SPEW_DEBUG
31
32#include <arch/virtual.h>
Julius Wernere00ba212013-09-24 20:03:54 -070033#include <usb/usb.h>
Nico Huber90292652013-06-13 14:37:15 +020034#include "xhci_private.h"
35
36static u32
37xhci_gen_route(xhci_t *const xhci, const int hubport, const int hubaddr)
38{
39 if (!hubaddr)
40 return 0;
Julius Werner1f864342013-09-03 17:15:31 -070041 u32 route_string = SC_GET(ROUTE, xhci->dev[hubaddr].ctx.slot);
Nico Huber90292652013-06-13 14:37:15 +020042 int i;
43 for (i = 0; i < 20; i += 4) {
44 if (!(route_string & (0xf << i))) {
45 route_string |= (hubport & 0xf) << i;
46 break;
47 }
48 }
49 return route_string;
50}
51
52static int
53xhci_get_rh_port(xhci_t *const xhci, const int hubport, const int hubaddr)
54{
55 if (!hubaddr)
56 return hubport;
Julius Werner1f864342013-09-03 17:15:31 -070057 return SC_GET(RHPORT, xhci->dev[hubaddr].ctx.slot);
Nico Huber90292652013-06-13 14:37:15 +020058}
59
60static int
Julius Wernere00ba212013-09-24 20:03:54 -070061xhci_get_tt(xhci_t *const xhci, const usb_speed speed,
Nico Huber90292652013-06-13 14:37:15 +020062 const int hubport, const int hubaddr,
63 int *const tt, int *const tt_port)
64{
65 if (!hubaddr)
66 return 0;
Julius Werner1f864342013-09-03 17:15:31 -070067 const slotctx_t *const slot = xhci->dev[hubaddr].ctx.slot;
68 if ((*tt = SC_GET(TTID, slot))) {
69 *tt_port = SC_GET(TTPORT, slot);
Julius Wernere00ba212013-09-24 20:03:54 -070070 } else if (speed < HIGH_SPEED &&
71 SC_GET(SPEED1, slot) - 1 == HIGH_SPEED) {
Nico Huber90292652013-06-13 14:37:15 +020072 *tt = hubaddr;
73 *tt_port = hubport;
74 }
75 return *tt != 0;
76}
77
Julius Werner1f864342013-09-03 17:15:31 -070078static inputctx_t *
79xhci_make_inputctx(const size_t ctxsize)
80{
81 int i;
82 const size_t size = (1 + NUM_EPS) * ctxsize;
83 inputctx_t *const ic = malloc(sizeof(*ic));
84 void *dma_buffer = dma_memalign(64, size);
85
86 if (!ic || !dma_buffer) {
87 free(ic);
88 free(dma_buffer);
89 return NULL;
90 }
91
92 memset(dma_buffer, 0, size);
93 ic->drop = dma_buffer + 0;
94 ic->add = dma_buffer + 4;
95 dma_buffer += ctxsize;
96 for (i = 0; i < NUM_EPS; i++, dma_buffer += ctxsize)
97 ic->dev.ep[i] = dma_buffer;
98
99 return ic;
100}
101
Julius Wernerd13e2c42013-09-17 22:16:04 -0700102usbdev_t *
Julius Wernere00ba212013-09-24 20:03:54 -0700103xhci_set_address (hci_t *controller, usb_speed speed, int hubport, int hubaddr)
Nico Huber90292652013-06-13 14:37:15 +0200104{
105 xhci_t *const xhci = XHCI_INST(controller);
Julius Werner1f864342013-09-03 17:15:31 -0700106 const size_t ctxsize = CTXSIZE(xhci);
107 devinfo_t *di = NULL;
Julius Wernerd13e2c42013-09-17 22:16:04 -0700108 usbdev_t *dev = NULL;
109 int i;
Nico Huber90292652013-06-13 14:37:15 +0200110
Julius Werner1f864342013-09-03 17:15:31 -0700111 inputctx_t *const ic = xhci_make_inputctx(ctxsize);
Nico Huber90292652013-06-13 14:37:15 +0200112 transfer_ring_t *const tr = malloc(sizeof(*tr));
113 if (tr)
114 tr->ring = xhci_align(16, TRANSFER_RING_SIZE * sizeof(trb_t));
Julius Werner1f864342013-09-03 17:15:31 -0700115 if (!ic || !tr || !tr->ring) {
Nico Huber90292652013-06-13 14:37:15 +0200116 xhci_debug("Out of memory\n");
117 goto _free_return;
118 }
119
120 int slot_id;
121 int cc = xhci_cmd_enable_slot(xhci, &slot_id);
122 if (cc != CC_SUCCESS) {
123 xhci_debug("Enable slot failed: %d\n", cc);
124 goto _free_return;
125 } else {
126 xhci_debug("Enabled slot %d\n", slot_id);
127 }
128
Julius Werner1f864342013-09-03 17:15:31 -0700129 di = &xhci->dev[slot_id];
130 void *dma_buffer = dma_memalign(64, NUM_EPS * ctxsize);
131 if (!dma_buffer)
Julius Wernerd609e892013-09-25 12:30:07 -0700132 goto _disable_return;
Julius Werner1f864342013-09-03 17:15:31 -0700133 memset(dma_buffer, 0, NUM_EPS * ctxsize);
134 for (i = 0; i < NUM_EPS; i++, dma_buffer += ctxsize)
135 di->ctx.ep[i] = dma_buffer;
136
137 *ic->add = (1 << 0) /* Slot Context */ | (1 << 1) /* EP0 Context */ ;
Nico Huber90292652013-06-13 14:37:15 +0200138
139 SC_SET(ROUTE, ic->dev.slot, xhci_gen_route(xhci, hubport, hubaddr));
Julius Wernere00ba212013-09-24 20:03:54 -0700140 SC_SET(SPEED1, ic->dev.slot, speed + 1);
Nico Huber90292652013-06-13 14:37:15 +0200141 SC_SET(CTXENT, ic->dev.slot, 1); /* the endpoint 0 context */
142 SC_SET(RHPORT, ic->dev.slot, xhci_get_rh_port(xhci, hubport, hubaddr));
143
144 int tt, tt_port;
Julius Wernere00ba212013-09-24 20:03:54 -0700145 if (xhci_get_tt(xhci, speed, hubport, hubaddr, &tt, &tt_port)) {
Nico Huber90292652013-06-13 14:37:15 +0200146 xhci_debug("TT for %d: %d[%d]\n", slot_id, tt, tt_port);
Julius Werner1f864342013-09-03 17:15:31 -0700147 SC_SET(MTT, ic->dev.slot, SC_GET(MTT, xhci->dev[tt].ctx.slot));
Nico Huber90292652013-06-13 14:37:15 +0200148 SC_SET(TTID, ic->dev.slot, tt);
149 SC_SET(TTPORT, ic->dev.slot, tt_port);
150 }
151
Nico Huber90292652013-06-13 14:37:15 +0200152 di->transfer_rings[1] = tr;
153 xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE);
154
Julius Werner1f864342013-09-03 17:15:31 -0700155 ic->dev.ep0->tr_dq_low = virt_to_phys(tr->ring);
156 ic->dev.ep0->tr_dq_high = 0;
Nico Huber90292652013-06-13 14:37:15 +0200157 EC_SET(TYPE, ic->dev.ep0, EP_CONTROL);
158 EC_SET(AVRTRB, ic->dev.ep0, 8);
159 EC_SET(MPS, ic->dev.ep0, 8);
160 EC_SET(CERR, ic->dev.ep0, 3);
161 EC_SET(DCS, ic->dev.ep0, 1);
162
Julius Werner1f864342013-09-03 17:15:31 -0700163 xhci->dcbaa[slot_id] = virt_to_phys(di->ctx.raw);
Nico Huber90292652013-06-13 14:37:15 +0200164
165 cc = xhci_cmd_address_device(xhci, slot_id, ic);
166 if (cc != CC_SUCCESS) {
167 xhci_debug("Address device failed: %d\n", cc);
168 goto _disable_return;
169 } else {
170 xhci_debug("Addressed device %d (USB: %d)\n",
Julius Werner1f864342013-09-03 17:15:31 -0700171 slot_id, SC_GET(UADDR, di->ctx.slot));
Nico Huber90292652013-06-13 14:37:15 +0200172 }
Julius Wernere00ba212013-09-24 20:03:54 -0700173 mdelay(SET_ADDRESS_MDELAY);
Nico Huber90292652013-06-13 14:37:15 +0200174
Julius Wernerd13e2c42013-09-17 22:16:04 -0700175 dev = init_device_entry(controller, slot_id);
176 if (!dev)
Nico Huber90292652013-06-13 14:37:15 +0200177 goto _disable_return;
Julius Wernerd13e2c42013-09-17 22:16:04 -0700178
179 dev->address = slot_id;
180 dev->hub = hubaddr;
181 dev->port = hubport;
182 dev->speed = speed;
183 dev->endpoints[0].dev = dev;
184 dev->endpoints[0].endpoint = 0;
185 dev->endpoints[0].toggle = 0;
186 dev->endpoints[0].direction = SETUP;
187 dev->endpoints[0].type = CONTROL;
188
189 u8 buf[8];
190 if (get_descriptor(dev, gen_bmRequestType(device_to_host, standard_type,
191 dev_recp), DT_DEV, 0, buf, sizeof(buf)) != sizeof(buf)) {
192 usb_debug("first get_descriptor(DT_DEV) failed\n");
193 goto _disable_return;
194 }
195
196 dev->endpoints[0].maxpacketsize = usb_decode_mps0(speed, buf[7]);
197 if (dev->endpoints[0].maxpacketsize != 8) {
Julius Werner1f864342013-09-03 17:15:31 -0700198 memset((void *)ic->dev.ep0, 0x00, ctxsize);
199 *ic->add = (1 << 1); /* EP0 Context */
Julius Wernerd13e2c42013-09-17 22:16:04 -0700200 EC_SET(MPS, ic->dev.ep0, dev->endpoints[0].maxpacketsize);
Nico Huber90292652013-06-13 14:37:15 +0200201 cc = xhci_cmd_evaluate_context(xhci, slot_id, ic);
202 if (cc != CC_SUCCESS) {
203 xhci_debug("Context evaluation failed: %d\n", cc);
204 goto _disable_return;
Nico Huber90292652013-06-13 14:37:15 +0200205 }
206 }
207
Nico Huber90292652013-06-13 14:37:15 +0200208 goto _free_ic_return;
209
210_disable_return:
211 xhci_cmd_disable_slot(xhci, slot_id);
212 xhci->dcbaa[slot_id] = 0;
Julius Wernerd609e892013-09-25 12:30:07 -0700213 usb_detach_device(controller, slot_id);
Julius Wernerd13e2c42013-09-17 22:16:04 -0700214 dev = NULL;
Nico Huber90292652013-06-13 14:37:15 +0200215_free_return:
216 if (tr)
217 free((void *)tr->ring);
218 free(tr);
Julius Werner1f864342013-09-03 17:15:31 -0700219 if (di)
220 free(di->ctx.raw);
Nico Huber90292652013-06-13 14:37:15 +0200221 free((void *)di);
222_free_ic_return:
Julius Werner1f864342013-09-03 17:15:31 -0700223 if (ic)
224 free(ic->raw);
Nico Huber90292652013-06-13 14:37:15 +0200225 free(ic);
Julius Wernerd13e2c42013-09-17 22:16:04 -0700226 return dev;
Nico Huber90292652013-06-13 14:37:15 +0200227}
228
229static int
230xhci_finish_hub_config(usbdev_t *const dev, inputctx_t *const ic)
231{
Julius Wernerd13e2c42013-09-17 22:16:04 -0700232 hub_descriptor_t desc;
233
234 if (get_descriptor(dev, gen_bmRequestType(device_to_host, class_type,
235 dev_recp), 0x29, 0, &desc, sizeof(desc)) != sizeof(desc)) {
Nico Huber90292652013-06-13 14:37:15 +0200236 xhci_debug("Failed to fetch hub descriptor\n");
237 return COMMUNICATION_ERROR;
238 }
239
240 SC_SET(HUB, ic->dev.slot, 1);
241 SC_SET(MTT, ic->dev.slot, 0); /* No support for Multi-TT */
Julius Wernerd13e2c42013-09-17 22:16:04 -0700242 SC_SET(NPORTS, ic->dev.slot, desc.bNbrPorts);
Nico Huber90292652013-06-13 14:37:15 +0200243 if (dev->speed == HIGH_SPEED)
244 SC_SET(TTT, ic->dev.slot,
Julius Wernerd13e2c42013-09-17 22:16:04 -0700245 (desc.wHubCharacteristics >> 5) & 0x0003);
Nico Huber90292652013-06-13 14:37:15 +0200246
Nico Huber90292652013-06-13 14:37:15 +0200247 return 0;
248}
249
250static size_t
251xhci_bound_interval(const endpoint_t *const ep)
252{
253 if ( (ep->dev->speed == LOW_SPEED &&
254 (ep->type == ISOCHRONOUS ||
255 ep->type == INTERRUPT)) ||
256 (ep->dev->speed == FULL_SPEED &&
257 ep->type == INTERRUPT))
258 {
259 if (ep->interval < 3)
260 return 3;
261 else if (ep->interval > 11)
262 return 11;
263 else
264 return ep->interval;
265 } else {
266 if (ep->interval < 0)
267 return 0;
268 else if (ep->interval > 15)
269 return 15;
270 else
271 return ep->interval;
272 }
273}
274
275static int
276xhci_finish_ep_config(const endpoint_t *const ep, inputctx_t *const ic)
277{
278 xhci_t *const xhci = XHCI_INST(ep->dev->controller);
Nico Huber90292652013-06-13 14:37:15 +0200279 const int ep_id = xhci_ep_id(ep);
280 xhci_debug("ep_id: %d\n", ep_id);
281 if (ep_id <= 1 || 32 <= ep_id)
282 return DRIVER_ERROR;
283
284 transfer_ring_t *const tr = malloc(sizeof(*tr));
285 if (tr)
286 tr->ring = xhci_align(16, TRANSFER_RING_SIZE * sizeof(trb_t));
287 if (!tr || !tr->ring) {
288 free(tr);
289 xhci_debug("Out of memory\n");
290 return OUT_OF_MEMORY;
291 }
Julius Werner1f864342013-09-03 17:15:31 -0700292 xhci->dev[ep->dev->address].transfer_rings[ep_id] = tr;
Nico Huber90292652013-06-13 14:37:15 +0200293 xhci_init_cycle_ring(tr, TRANSFER_RING_SIZE);
294
Julius Werner1f864342013-09-03 17:15:31 -0700295 *ic->add |= (1 << ep_id);
Nico Huber90292652013-06-13 14:37:15 +0200296 if (SC_GET(CTXENT, ic->dev.slot) < ep_id)
297 SC_SET(CTXENT, ic->dev.slot, ep_id);
298
Julius Werner1f864342013-09-03 17:15:31 -0700299 epctx_t *const epctx = ic->dev.ep[ep_id];
Nico Huber90292652013-06-13 14:37:15 +0200300 xhci_debug("Filling epctx (@%p)\n", epctx);
301 epctx->tr_dq_low = virt_to_phys(tr->ring);
302 epctx->tr_dq_high = 0;
Julius Werner1f864342013-09-03 17:15:31 -0700303 EC_SET(INTVAL, epctx, xhci_bound_interval(ep));
304 EC_SET(CERR, epctx, 3);
305 EC_SET(TYPE, epctx, ep->type | ((ep->direction != OUT) << 2));
306 EC_SET(MPS, epctx, ep->maxpacketsize);
307 EC_SET(DCS, epctx, 1);
Nico Huber90292652013-06-13 14:37:15 +0200308 size_t avrtrb;
309 switch (ep->type) {
310 case BULK: case ISOCHRONOUS: avrtrb = 3 * 1024; break;
311 case INTERRUPT: avrtrb = 1024; break;
312 default: avrtrb = 8; break;
313 }
Julius Werner1f864342013-09-03 17:15:31 -0700314 EC_SET(AVRTRB, epctx, avrtrb);
315 EC_SET(MXESIT, epctx, EC_GET(MPS, epctx) * EC_GET(MBS, epctx));
Nico Huber90292652013-06-13 14:37:15 +0200316
317 return 0;
318}
319
320int
321xhci_finish_device_config(usbdev_t *const dev)
322{
323 xhci_t *const xhci = XHCI_INST(dev->controller);
Julius Werner1f864342013-09-03 17:15:31 -0700324 devinfo_t *const di = &xhci->dev[dev->address];
Nico Huber90292652013-06-13 14:37:15 +0200325
326 int i, ret = 0;
327
Julius Werner1f864342013-09-03 17:15:31 -0700328 inputctx_t *const ic = xhci_make_inputctx(CTXSIZE(xhci));
Nico Huber90292652013-06-13 14:37:15 +0200329 if (!ic) {
330 xhci_debug("Out of memory\n");
331 return OUT_OF_MEMORY;
332 }
Nico Huber90292652013-06-13 14:37:15 +0200333
Julius Werner1f864342013-09-03 17:15:31 -0700334 *ic->add = (1 << 0); /* Slot Context */
Nico Huber90292652013-06-13 14:37:15 +0200335
Julius Werner1f864342013-09-03 17:15:31 -0700336 xhci_dump_slotctx(di->ctx.slot);
337 ic->dev.slot->f1 = di->ctx.slot->f1;
338 ic->dev.slot->f2 = di->ctx.slot->f2;
339 ic->dev.slot->f3 = di->ctx.slot->f3;
Nico Huber90292652013-06-13 14:37:15 +0200340
Julius Wernerd13e2c42013-09-17 22:16:04 -0700341 if (dev->descriptor->bDeviceClass == 0x09 && dev->speed < SUPER_SPEED) {
Nico Huber90292652013-06-13 14:37:15 +0200342 ret = xhci_finish_hub_config(dev, ic);
343 if (ret)
344 goto _free_return;
345 }
346
347 for (i = 1; i < dev->num_endp; ++i) {
348 ret = xhci_finish_ep_config(&dev->endpoints[i], ic);
349 if (ret)
350 goto _free_ep_ctx_return;
351 }
352
353 xhci_dump_inputctx(ic);
354
Julius Wernerd13e2c42013-09-17 22:16:04 -0700355 const int config_id = dev->configuration->bConfigurationValue;
Nico Huber90292652013-06-13 14:37:15 +0200356 xhci_debug("config_id: %d\n", config_id);
357 const int cc =
358 xhci_cmd_configure_endpoint(xhci, dev->address, config_id, ic);
359 if (cc != CC_SUCCESS) {
360 xhci_debug("Configure endpoint failed: %d\n", cc);
361 ret = CONTROLLER_ERROR;
362 goto _free_ep_ctx_return;
363 } else {
364 xhci_debug("Endpoints configured\n");
365 }
366
367 goto _free_return;
368
369_free_ep_ctx_return:
370 for (i = 2; i < 31; ++i) {
371 if (di->transfer_rings[i])
372 free((void *)di->transfer_rings[i]->ring);
373 free(di->transfer_rings[i]);
374 di->transfer_rings[i] = NULL;
375 }
376_free_return:
Julius Werner1f864342013-09-03 17:15:31 -0700377 free(ic->raw);
Nico Huber90292652013-06-13 14:37:15 +0200378 free(ic);
379 return ret;
380}
381
382void
383xhci_destroy_dev(hci_t *const controller, const int slot_id)
384{
385 xhci_t *const xhci = XHCI_INST(controller);
386
Julius Wernerd609e892013-09-25 12:30:07 -0700387 if (slot_id <= 0 || slot_id > xhci->max_slots_en)
Nico Huber90292652013-06-13 14:37:15 +0200388 return;
389
390 int i;
391
392 const int cc = xhci_cmd_disable_slot(xhci, slot_id);
393 if (cc != CC_SUCCESS)
394 xhci_debug("Failed to disable slot %d: %d\n", slot_id, cc);
395
Julius Werner1f864342013-09-03 17:15:31 -0700396 devinfo_t *const di = &xhci->dev[slot_id];
Nico Huber90292652013-06-13 14:37:15 +0200397 for (i = 1; i < 31; ++i) {
398 if (di->transfer_rings[i])
399 free((void *)di->transfer_rings[i]->ring);
400 free(di->transfer_rings[i]);
401
402 free(di->interrupt_queues[i]);
403 }
Nico Huber90292652013-06-13 14:37:15 +0200404 xhci->dcbaa[slot_id] = 0;
405}