blob: 386392b53f318f25542672900b269d8a90ce9292 [file] [log] [blame]
Patrick Georgid21f68b2008-09-02 16:06:22 +00001/*
2 * This file is part of the libpayload project.
3 *
Stefan Reinauerb56f2d02010-03-25 22:17:36 +00004 * Copyright (C) 2008-2010 coresystems GmbH
Patrick Georgid21f68b2008-09-02 16:06:22 +00005 *
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
Stefan Reinauerb56f2d02010-03-25 22:17:36 +000030//#define USB_DEBUG
31
Patrick Georgid78691d2010-06-07 13:58:17 +000032#include <arch/virtual.h>
Jordan Crouse29061a52008-09-11 17:29:00 +000033#include <usb/usb.h>
Patrick Georgid21f68b2008-09-02 16:06:22 +000034#include "uhci.h"
Patrick Georgid78691d2010-06-07 13:58:17 +000035#include "uhci_private.h"
Patrick Georgid21f68b2008-09-02 16:06:22 +000036
37static void uhci_start (hci_t *controller);
38static void uhci_stop (hci_t *controller);
39static void uhci_reset (hci_t *controller);
40static void uhci_shutdown (hci_t *controller);
Patrick Georgid21f68b2008-09-02 16:06:22 +000041static int uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
Patrick Georgid78691d2010-06-07 13:58:17 +000042static int uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq,
Patrick Georgid21f68b2008-09-02 16:06:22 +000043 int dalen, u8 *data);
Patrick Georgi4727c072008-10-16 19:20:51 +000044static void* uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
45static void uhci_destroy_intr_queue (endpoint_t *ep, void *queue);
46static u8* uhci_poll_intr_queue (void *queue);
Patrick Georgid21f68b2008-09-02 16:06:22 +000047
48#if 0
49/* dump uhci */
50static void
51uhci_dump (hci_t *controller)
52{
Gabe Black93ded592012-11-01 15:44:10 -070053 usb_debug ("dump:\nUSBCMD: %x\n", uhci_reg_read16 (controller, USBCMD));
54 usb_debug ("USBSTS: %x\n", uhci_reg_read16 (controller, USBSTS));
55 usb_debug ("USBINTR: %x\n", uhci_reg_read16 (controller, USBINTR));
56 usb_debug ("FRNUM: %x\n", uhci_reg_read16 (controller, FRNUM));
57 usb_debug ("FLBASEADD: %x\n", uhci_reg_read32 (controller, FLBASEADD));
58 usb_debug ("SOFMOD: %x\n", uhci_reg_read8 (controller, SOFMOD));
59 usb_debug ("PORTSC1: %x\n", uhci_reg_read16 (controller, PORTSC1));
60 usb_debug ("PORTSC2: %x\n", uhci_reg_read16 (controller, PORTSC2));
Patrick Georgid21f68b2008-09-02 16:06:22 +000061}
62#endif
63
64static void
65td_dump (td_t *td)
66{
Stefan Reinauerd233f362009-04-30 16:46:12 +000067 char td_value[3];
Patrick Georgi7f965832011-04-21 18:57:16 +020068 const char *td_type;
Patrick Georgib0b4a522011-11-24 11:55:46 +010069 switch (td->token & TD_PID_MASK) {
Patrick Georgid78691d2010-06-07 13:58:17 +000070 case UHCI_SETUP:
Stefan Reinauerd233f362009-04-30 16:46:12 +000071 td_type="SETUP";
72 break;
Patrick Georgid78691d2010-06-07 13:58:17 +000073 case UHCI_IN:
Stefan Reinauerd233f362009-04-30 16:46:12 +000074 td_type="IN";
75 break;
Patrick Georgid78691d2010-06-07 13:58:17 +000076 case UHCI_OUT:
Stefan Reinauerd233f362009-04-30 16:46:12 +000077 td_type="OUT";
78 break;
79 default:
Patrick Georgib0b4a522011-11-24 11:55:46 +010080 sprintf(td_value, "%x", td->token & TD_PID_MASK);
Stefan Reinauerd233f362009-04-30 16:46:12 +000081 td_type=td_value;
82 }
Gabe Black93ded592012-11-01 15:44:10 -070083 usb_debug ("%s packet (at %lx) to %x.%x failed\n", td_type,
Patrick Georgib0b4a522011-11-24 11:55:46 +010084 virt_to_phys (td), (td->token & TD_DEVADDR_MASK) >> TD_DEVADDR_SHIFT,
85 (td->token & TD_EP_MASK) >> TD_EP_SHIFT);
Gabe Black93ded592012-11-01 15:44:10 -070086 usb_debug ("td (counter at %x) returns: ", td->ctrlsts >> TD_COUNTER_SHIFT);
87 usb_debug (" bitstuff err: %x, ", !!(td->ctrlsts & TD_STATUS_BITSTUFF_ERR));
88 usb_debug (" CRC err: %x, ", !!(td->ctrlsts & TD_STATUS_CRC_ERR));
89 usb_debug (" NAK rcvd: %x, ", !!(td->ctrlsts & TD_STATUS_NAK_RCVD));
90 usb_debug (" Babble: %x, ", !!(td->ctrlsts & TD_STATUS_BABBLE));
91 usb_debug (" Data Buffer err: %x, ", !!(td->ctrlsts & TD_STATUS_DATABUF_ERR));
92 usb_debug (" Stalled: %x, ", !!(td->ctrlsts & TD_STATUS_STALLED));
93 usb_debug (" Active: %x\n", !!(td->ctrlsts & TD_STATUS_ACTIVE));
Patrick Georgib0b4a522011-11-24 11:55:46 +010094 if (td->ctrlsts & TD_STATUS_BABBLE)
Gabe Black93ded592012-11-01 15:44:10 -070095 usb_debug (" Babble because of %s\n",
Patrick Georgib0b4a522011-11-24 11:55:46 +010096 (td->ctrlsts & TD_STATUS_BITSTUFF_ERR) ? "host" : "device");
97 if (td->ctrlsts & TD_STATUS_ACTIVE)
Gabe Black93ded592012-11-01 15:44:10 -070098 usb_debug (" still active - timeout?\n");
Patrick Georgid21f68b2008-09-02 16:06:22 +000099}
100
101static void
102uhci_reset (hci_t *controller)
103{
104 /* reset */
Nico Huberbb1c42b2012-05-21 14:23:03 +0200105 uhci_reg_write16 (controller, USBCMD, 4); /* Global Reset */
106 mdelay (50); /* uhci spec 2.1.1: at least 10ms */
Patrick Georgid21f68b2008-09-02 16:06:22 +0000107 uhci_reg_write16 (controller, USBCMD, 0);
108 mdelay (10);
Nico Huberbb1c42b2012-05-21 14:23:03 +0200109 uhci_reg_write16 (controller, USBCMD, 2); /* Host Controller Reset */
110 /* wait for controller to finish reset */
111 /* TOTEST: how long to wait? 100ms for now */
112 int timeout = 200; /* time out after 200 * 500us == 100ms */
113 while (((uhci_reg_read16 (controller, USBCMD) & 2) != 0) && timeout--)
114 udelay (500);
115 if (timeout < 0)
Gabe Black93ded592012-11-01 15:44:10 -0700116 usb_debug ("Warning: uhci: host controller reset timed out.\n");
Nico Huber6e711c62012-11-12 16:20:32 +0100117}
Patrick Georgid21f68b2008-09-02 16:06:22 +0000118
Nico Huber6e711c62012-11-12 16:20:32 +0100119static void
120uhci_reinit (hci_t *controller)
121{
Patrick Georgid21f68b2008-09-02 16:06:22 +0000122 uhci_reg_write32 (controller, FLBASEADD,
123 (u32) virt_to_phys (UHCI_INST (controller)->
124 framelistptr));
Gabe Black93ded592012-11-01 15:44:10 -0700125 //usb_debug ("framelist at %p\n",UHCI_INST(controller)->framelistptr);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000126
127 /* disable irqs */
128 uhci_reg_write16 (controller, USBINTR, 0);
129
130 /* reset framelist index */
131 uhci_reg_write16 (controller, FRNUM, 0);
132
Patrick Georgif42fdab2011-11-18 14:44:16 +0100133 uhci_reg_write16(controller, USBCMD,
134 uhci_reg_read16(controller, USBCMD) | 0xc0); // max packets, configure flag
Patrick Georgid21f68b2008-09-02 16:06:22 +0000135
136 uhci_start (controller);
137}
138
139hci_t *
140uhci_init (pcidev_t addr)
141{
142 int i;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000143 u16 reg16;
144
Patrick Georgid21f68b2008-09-02 16:06:22 +0000145 hci_t *controller = new_controller ();
146
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000147 if (!controller)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100148 fatal("Could not create USB controller instance.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000149
Patrick Georgid21f68b2008-09-02 16:06:22 +0000150 controller->instance = malloc (sizeof (uhci_t));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000151 if(!controller->instance)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100152 fatal("Not enough memory creating USB controller instance.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000153
Anton Kochkov1c36ead2012-06-28 08:30:15 +0400154 controller->type = UHCI;
155
Patrick Georgid21f68b2008-09-02 16:06:22 +0000156 controller->start = uhci_start;
157 controller->stop = uhci_stop;
158 controller->reset = uhci_reset;
Nico Huber6e711c62012-11-12 16:20:32 +0100159 controller->init = uhci_reinit;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000160 controller->shutdown = uhci_shutdown;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000161 controller->bulk = uhci_bulk;
162 controller->control = uhci_control;
Patrick Georgi4727c072008-10-16 19:20:51 +0000163 controller->create_intr_queue = uhci_create_intr_queue;
164 controller->destroy_intr_queue = uhci_destroy_intr_queue;
165 controller->poll_intr_queue = uhci_poll_intr_queue;
Stefan Reinauer219cece2009-07-18 15:17:40 +0000166 for (i = 0; i < 128; i++) {
Patrick Georgi4727c072008-10-16 19:20:51 +0000167 controller->devices[i] = 0;
168 }
169 init_device_entry (controller, 0);
170 UHCI_INST (controller)->roothub = controller->devices[0];
Patrick Georgid21f68b2008-09-02 16:06:22 +0000171
172 controller->bus_address = addr;
173 controller->reg_base = pci_read_config32 (controller->bus_address, 0x20) & ~1; /* ~1 clears the register type indicator that is set to 1 for IO space */
174
175 /* kill legacy support handler */
176 uhci_stop (controller);
177 mdelay (1);
178 uhci_reg_write16 (controller, USBSTS, 0x3f);
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000179 reg16 = pci_read_config16(controller->bus_address, 0xc0);
180 reg16 &= 0xdf80;
181 pci_write_config16 (controller->bus_address, 0xc0, reg16);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000182
Anton Kochkovf6c80802012-09-20 10:24:01 +0200183 UHCI_INST (controller)->framelistptr = memalign (0x1000, 1024 * sizeof (flistp_t)); /* 4kb aligned to 4kb */
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000184 if (! UHCI_INST (controller)->framelistptr)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100185 fatal("Not enough memory for USB frame list pointer.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000186
Patrick Georgid21f68b2008-09-02 16:06:22 +0000187 memset (UHCI_INST (controller)->framelistptr, 0,
188 1024 * sizeof (flistp_t));
189
Patrick Georgi4727c072008-10-16 19:20:51 +0000190 /* According to the *BSD UHCI code, this one is needed on some
191 PIIX chips, because otherwise they misbehave. It must be
192 added to the last chain.
193
194 FIXME: this leaks, if the driver should ever be reinited
195 for some reason. Not a problem now.
196 */
197 td_t *antiberserk = memalign(16, sizeof(td_t));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000198 if (!antiberserk)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100199 fatal("Not enough memory for chipset workaround.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000200 memset(antiberserk, 0, sizeof(td_t));
201
202 UHCI_INST (controller)->qh_prei = memalign (16, sizeof (qh_t));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000203 UHCI_INST (controller)->qh_intr = memalign (16, sizeof (qh_t));
204 UHCI_INST (controller)->qh_data = memalign (16, sizeof (qh_t));
205 UHCI_INST (controller)->qh_last = memalign (16, sizeof (qh_t));
206
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000207 if (! UHCI_INST (controller)->qh_prei ||
208 ! UHCI_INST (controller)->qh_intr ||
209 ! UHCI_INST (controller)->qh_data ||
210 ! UHCI_INST (controller)->qh_last)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100211 fatal("Not enough memory for USB controller queues.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000212
Patrick Georgib0b4a522011-11-24 11:55:46 +0100213 UHCI_INST (controller)->qh_prei->headlinkptr =
214 virt_to_phys (UHCI_INST (controller)->qh_intr) | FLISTP_QH;
215 UHCI_INST (controller)->qh_prei->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000216
Patrick Georgib0b4a522011-11-24 11:55:46 +0100217 UHCI_INST (controller)->qh_intr->headlinkptr =
218 virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_QH;
219 UHCI_INST (controller)->qh_intr->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000220
Patrick Georgib0b4a522011-11-24 11:55:46 +0100221 UHCI_INST (controller)->qh_data->headlinkptr =
222 virt_to_phys (UHCI_INST (controller)->qh_last) | FLISTP_QH;
223 UHCI_INST (controller)->qh_data->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000224
Patrick Georgib0b4a522011-11-24 11:55:46 +0100225 UHCI_INST (controller)->qh_last->headlinkptr = virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_TERMINATE;
226 UHCI_INST (controller)->qh_last->elementlinkptr = virt_to_phys (antiberserk) | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000227
228 for (i = 0; i < 1024; i++) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100229 UHCI_INST (controller)->framelistptr[i] =
230 virt_to_phys (UHCI_INST (controller)->qh_prei) | FLISTP_QH;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000231 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000232 controller->devices[0]->controller = controller;
233 controller->devices[0]->init = uhci_rh_init;
234 controller->devices[0]->init (controller->devices[0]);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000235 uhci_reset (controller);
Nico Huber6e711c62012-11-12 16:20:32 +0100236 uhci_reinit (controller);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000237 return controller;
238}
239
240static void
241uhci_shutdown (hci_t *controller)
242{
243 if (controller == 0)
244 return;
245 detach_controller (controller);
246 UHCI_INST (controller)->roothub->destroy (UHCI_INST (controller)->
247 roothub);
Patrick Georgif42fdab2011-11-18 14:44:16 +0100248 uhci_reg_write16(controller, USBCMD,
249 uhci_reg_read16(controller, USBCMD) & 0); // stop work
Patrick Georgid21f68b2008-09-02 16:06:22 +0000250 free (UHCI_INST (controller)->framelistptr);
Patrick Georgi4727c072008-10-16 19:20:51 +0000251 free (UHCI_INST (controller)->qh_prei);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000252 free (UHCI_INST (controller)->qh_intr);
253 free (UHCI_INST (controller)->qh_data);
254 free (UHCI_INST (controller)->qh_last);
255 free (UHCI_INST (controller));
256 free (controller);
257}
258
259static void
260uhci_start (hci_t *controller)
261{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100262 uhci_reg_write16(controller, USBCMD,
263 uhci_reg_read16(controller, USBCMD) | 1); // start work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000264}
265
266static void
267uhci_stop (hci_t *controller)
268{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100269 uhci_reg_write16(controller, USBCMD,
270 uhci_reg_read16(controller, USBCMD) & ~1); // stop work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000271}
272
273#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))
274
275static td_t *
276wait_for_completed_qh (hci_t *controller, qh_t *qh)
277{
Mathias Krause7b7b5662012-05-29 16:19:19 +0200278 int timeout = 1000; /* max 30 ms. */
Patrick Georgib0b4a522011-11-24 11:55:46 +0100279 void *current = GET_TD (qh->elementlinkptr);
280 while (((qh->elementlinkptr & FLISTP_TERMINATE) == 0) && (timeout-- > 0)) {
281 if (current != GET_TD (qh->elementlinkptr)) {
282 current = GET_TD (qh->elementlinkptr);
Mathias Krause7b7b5662012-05-29 16:19:19 +0200283 timeout = 1000;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000284 }
Patrick Georgif42fdab2011-11-18 14:44:16 +0100285 uhci_reg_write16(controller, USBSTS,
286 uhci_reg_read16(controller, USBSTS) | 0); // clear resettable registers
Patrick Georgid21f68b2008-09-02 16:06:22 +0000287 udelay (30);
288 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100289 return (GET_TD (qh->elementlinkptr) ==
290 0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000291}
292
Patrick Georgid21f68b2008-09-02 16:06:22 +0000293static int
294maxlen (int size)
295{
296 return (size - 1) & 0x7ff;
297}
298
299static int
300min (int a, int b)
301{
302 if (a < b)
303 return a;
304 else
305 return b;
306}
307
308static int
Patrick Georgid78691d2010-06-07 13:58:17 +0000309uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
Patrick Georgid21f68b2008-09-02 16:06:22 +0000310 unsigned char *data)
311{
312 int endp = 0; /* this is control: always 0 */
313 int mlen = dev->endpoints[0].maxpacketsize;
314 int count = (2 + (dalen + mlen - 1) / mlen);
315 unsigned short req = ((unsigned short *) devreq)[0];
316 int i;
317 td_t *tds = memalign (16, sizeof (td_t) * count);
318 memset (tds, 0, sizeof (td_t) * count);
319 count--; /* to compensate for 0-indexed array */
320 for (i = 0; i < count; i++) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100321 tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000322 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100323 tds[count].ptr = 0 | TD_DEPTH_FIRST | TD_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000324
Patrick Georgib0b4a522011-11-24 11:55:46 +0100325 tds[0].token = UHCI_SETUP |
326 dev->address << TD_DEVADDR_SHIFT |
327 endp << TD_EP_SHIFT |
328 TD_TOGGLE_DATA0 |
329 maxlen(drlen) << TD_MAXLEN_SHIFT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000330 tds[0].bufptr = virt_to_phys (devreq);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100331 tds[0].ctrlsts = (3 << TD_COUNTER_SHIFT) |
332 (dev->speed?TD_LOWSPEED:0) |
333 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000334
335 int toggle = 1;
336 for (i = 1; i < count; i++) {
Patrick Georgid78691d2010-06-07 13:58:17 +0000337 switch (dir) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100338 case SETUP: tds[i].token = UHCI_SETUP; break;
339 case IN: tds[i].token = UHCI_IN; break;
340 case OUT: tds[i].token = UHCI_OUT; break;
Patrick Georgid78691d2010-06-07 13:58:17 +0000341 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100342 tds[i].token |= dev->address << TD_DEVADDR_SHIFT |
343 endp << TD_EP_SHIFT |
344 maxlen (min (mlen, dalen)) << TD_MAXLEN_SHIFT |
345 toggle << TD_TOGGLE_SHIFT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000346 tds[i].bufptr = virt_to_phys (data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100347 tds[i].ctrlsts = (3 << TD_COUNTER_SHIFT) |
348 (dev->speed?TD_LOWSPEED:0) |
349 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000350 toggle ^= 1;
351 dalen -= mlen;
352 data += mlen;
353 }
354
Nico Hubercefec0e2012-05-16 15:04:27 +0200355 tds[count].token = ((dir == OUT) ? UHCI_IN : UHCI_OUT) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100356 dev->address << TD_DEVADDR_SHIFT |
357 endp << TD_EP_SHIFT |
358 maxlen(0) << TD_MAXLEN_SHIFT |
359 TD_TOGGLE_DATA1;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000360 tds[count].bufptr = 0;
Nico Hubercefec0e2012-05-16 15:04:27 +0200361 tds[count].ctrlsts = (0 << TD_COUNTER_SHIFT) | /* as Linux 2.4.10 does */
Patrick Georgib0b4a522011-11-24 11:55:46 +0100362 (dev->speed?TD_LOWSPEED:0) |
363 TD_STATUS_ACTIVE;
364 UHCI_INST (dev->controller)->qh_data->elementlinkptr =
365 virt_to_phys (tds) & ~(FLISTP_QH | FLISTP_TERMINATE);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000366 td_t *td = wait_for_completed_qh (dev->controller,
367 UHCI_INST (dev->controller)->
368 qh_data);
369 int result;
370 if (td == 0) {
371 result = 0;
372 } else {
Gabe Black93ded592012-11-01 15:44:10 -0700373 usb_debug ("control packet, req %x\n", req);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000374 td_dump (td);
375 result = 1;
376 }
377 free (tds);
378 return result;
379}
380
Patrick Georgid21f68b2008-09-02 16:06:22 +0000381static td_t *
382create_schedule (int numpackets)
383{
384 if (numpackets == 0)
385 return 0;
386 td_t *tds = memalign (16, sizeof (td_t) * numpackets);
387 memset (tds, 0, sizeof (td_t) * numpackets);
388 int i;
389 for (i = 0; i < numpackets; i++) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100390 tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000391 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100392 tds[numpackets - 1].ptr = 0 | TD_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000393 return tds;
394}
395
396static void
397fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data,
398 int *toggle)
399{
Patrick Georgicfaa0812010-06-11 14:25:40 +0000400 switch (ep->direction) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100401 case IN: td->token = UHCI_IN; break;
402 case OUT: td->token = UHCI_OUT; break;
403 case SETUP: td->token = UHCI_SETUP; break;
Patrick Georgicfaa0812010-06-11 14:25:40 +0000404 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100405 td->token |= ep->dev->address << TD_DEVADDR_SHIFT |
406 (ep->endpoint & 0xf) << TD_EP_SHIFT |
407 maxlen (length) << TD_MAXLEN_SHIFT |
408 (*toggle & 1) << TD_TOGGLE_SHIFT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000409 td->bufptr = virt_to_phys (data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100410 td->ctrlsts = ((ep->direction == SETUP?3:0) << TD_COUNTER_SHIFT) |
Nico Hubercefec0e2012-05-16 15:04:27 +0200411 (ep->dev->speed?TD_LOWSPEED:0) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100412 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000413 *toggle ^= 1;
414}
415
416static int
417run_schedule (usbdev_t *dev, td_t *td)
418{
Patrick Georgib0b4a522011-11-24 11:55:46 +0100419 UHCI_INST (dev->controller)->qh_data->elementlinkptr =
Anton Kochkovefcb8de2012-10-02 00:04:29 +0400420 virt_to_phys (td) & ~(FLISTP_QH | FLISTP_TERMINATE);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000421 td = wait_for_completed_qh (dev->controller,
422 UHCI_INST (dev->controller)->qh_data);
423 if (td == 0) {
424 return 0;
425 } else {
426 td_dump (td);
427 return 1;
428 }
429}
430
431/* finalize == 1: if data is of packet aligned size, add a zero length packet */
432static int
433uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
434{
435 int maxpsize = ep->maxpacketsize;
436 if (maxpsize == 0)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100437 fatal("MaxPacketSize == 0!!!");
Patrick Georgid2cb7ea2012-10-03 08:23:56 +0200438 int numpackets = (size + maxpsize - 1) / maxpsize;
439 if (finalize && ((size % maxpsize) == 0)) {
440 numpackets++;
441 }
Patrick Georgid21f68b2008-09-02 16:06:22 +0000442 if (numpackets == 0)
443 return 0;
444 td_t *tds = create_schedule (numpackets);
445 int i = 0, toggle = ep->toggle;
446 while ((size > 0) || ((size == 0) && (finalize != 0))) {
447 fill_schedule (&tds[i], ep, min (size, maxpsize), data,
448 &toggle);
449 i++;
450 data += maxpsize;
451 size -= maxpsize;
452 }
453 if (run_schedule (ep->dev, tds) == 1) {
Gabe Black93ded592012-11-01 15:44:10 -0700454 usb_debug("Stalled. Trying to clean up.\n");
Patrick Georgid21f68b2008-09-02 16:06:22 +0000455 clear_stall (ep);
456 free (tds);
457 return 1;
458 }
459 ep->toggle = toggle;
460 free (tds);
461 return 0;
462}
463
Patrick Georgi4727c072008-10-16 19:20:51 +0000464typedef struct {
465 qh_t *qh;
466 td_t *tds;
467 td_t *last_td;
468 u8 *data;
469 int lastread;
470 int total;
471 int reqsize;
472} intr_q;
473
474/* create and hook-up an intr queue into device schedule */
475static void*
476uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
477{
478 u8 *data = malloc(reqsize*reqcount);
479 td_t *tds = memalign(16, sizeof(td_t) * reqcount);
480 qh_t *qh = memalign(16, sizeof(qh_t));
481
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000482 if (!data || !tds || !qh)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100483 fatal("Not enough memory to create USB intr queue prerequisites.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000484
Patrick Georgib0b4a522011-11-24 11:55:46 +0100485 qh->elementlinkptr = virt_to_phys(tds);
Patrick Georgi4727c072008-10-16 19:20:51 +0000486
487 intr_q *q = malloc(sizeof(intr_q));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000488 if (!q)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100489 fatal("Not enough memory to create USB intr queue.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000490 q->qh = qh;
491 q->tds = tds;
492 q->data = data;
493 q->lastread = 0;
494 q->total = reqcount;
495 q->reqsize = reqsize;
496 q->last_td = &tds[reqcount - 1];
497
498 memset (tds, 0, sizeof (td_t) * reqcount);
499 int i;
500 for (i = 0; i < reqcount; i++) {
501 tds[i].ptr = virt_to_phys (&tds[i + 1]);
Patrick Georgi4727c072008-10-16 19:20:51 +0000502
Patrick Georgicfaa0812010-06-11 14:25:40 +0000503 switch (ep->direction) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100504 case IN: tds[i].token = UHCI_IN; break;
505 case OUT: tds[i].token = UHCI_OUT; break;
506 case SETUP: tds[i].token = UHCI_SETUP; break;
Patrick Georgicfaa0812010-06-11 14:25:40 +0000507 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100508 tds[i].token |= ep->dev->address << TD_DEVADDR_SHIFT |
509 (ep->endpoint & 0xf) << TD_EP_SHIFT |
510 maxlen (reqsize) << TD_MAXLEN_SHIFT |
511 (ep->toggle & 1) << TD_TOGGLE_SHIFT;
Patrick Georgi4727c072008-10-16 19:20:51 +0000512 tds[i].bufptr = virt_to_phys (data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100513 tds[i].ctrlsts = (0 << TD_COUNTER_SHIFT) |
Nico Hubercefec0e2012-05-16 15:04:27 +0200514 (ep->dev->speed?TD_LOWSPEED:0) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100515 TD_STATUS_ACTIVE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000516 ep->toggle ^= 1;
517 data += reqsize;
518 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100519 tds[reqcount - 1].ptr = 0 | TD_TERMINATE;
Nico Huberce407e42012-11-20 17:27:46 +0100520
521 /* insert QH into framelist */
522 uhci_t *const uhcic = UHCI_INST(ep->dev->controller);
523 const u32 def_ptr = virt_to_phys(uhcic->qh_prei) | FLISTP_QH;
524 int nothing_placed = 1;
525 qh->headlinkptr = def_ptr;
526 for (i = 0; i < 1024; i += reqtiming) {
527 /* advance to the next free position */
528 while ((i < 1024) && (uhcic->framelistptr[i] != def_ptr)) ++i;
529 if (i < 1024) {
530 uhcic->framelistptr[i] = virt_to_phys(qh) | FLISTP_QH;
531 nothing_placed = 0;
532 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000533 }
Nico Huberce407e42012-11-20 17:27:46 +0100534 if (nothing_placed) {
535 printf("Error: Failed to place UHCI interrupt queue "
536 "head into framelist: no space left\n");
537 uhci_destroy_intr_queue(ep, q);
538 return NULL;
539 }
540
Patrick Georgi4727c072008-10-16 19:20:51 +0000541 return q;
542}
543
544/* remove queue from device schedule, dropping all data that came in */
545static void
546uhci_destroy_intr_queue (endpoint_t *ep, void *q_)
547{
Nico Huberce407e42012-11-20 17:27:46 +0100548 intr_q *const q = (intr_q*)q_;
549
550 /* remove QH from framelist */
551 uhci_t *const uhcic = UHCI_INST(ep->dev->controller);
552 const u32 qh_ptr = virt_to_phys(q->qh) | FLISTP_QH;
553 const u32 def_ptr = virt_to_phys(uhcic->qh_prei) | FLISTP_QH;
Patrick Georgi4727c072008-10-16 19:20:51 +0000554 int i;
Nico Huberce407e42012-11-20 17:27:46 +0100555 for (i = 0; i < 1024; ++i) {
556 if (uhcic->framelistptr[i] == qh_ptr)
557 uhcic->framelistptr[i] = def_ptr;
Patrick Georgi4727c072008-10-16 19:20:51 +0000558 }
Nico Huberce407e42012-11-20 17:27:46 +0100559
Patrick Georgi4727c072008-10-16 19:20:51 +0000560 free(q->data);
561 free(q->tds);
562 free(q->qh);
563 free(q);
564}
565
566/* read one intr-packet from queue, if available. extend the queue for new input.
567 return NULL if nothing new available.
568 Recommended use: while (data=poll_intr_queue(q)) process(data);
569 */
570static u8*
571uhci_poll_intr_queue (void *q_)
572{
573 intr_q *q = (intr_q*)q_;
Patrick Georgib0b4a522011-11-24 11:55:46 +0100574 if ((q->tds[q->lastread].ctrlsts & TD_STATUS_ACTIVE) == 0) {
Patrick Georgi4727c072008-10-16 19:20:51 +0000575 /* FIXME: handle errors */
576 int current = q->lastread;
577 int previous;
578 if (q->lastread == 0) {
579 previous = q->total - 1;
580 } else {
581 previous = q->lastread - 1;
582 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100583 q->tds[previous].ctrlsts &= ~TD_STATUS_MASK;
584 q->tds[previous].ptr = 0 | TD_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000585 if (q->last_td != &q->tds[previous]) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100586 q->last_td->ptr = virt_to_phys(&q->tds[previous]) & ~TD_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000587 q->last_td = &q->tds[previous];
588 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100589 q->tds[previous].ctrlsts |= TD_STATUS_ACTIVE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000590 q->lastread = (q->lastread + 1) % q->total;
591 return &q->data[current*q->reqsize];
592 }
Nico Huber8c4d2f32012-11-20 17:49:00 +0100593 /* reset queue if we fully processed it after underrun */
594 else if (q->qh->elementlinkptr & FLISTP_TERMINATE) {
595 usb_debug("resetting underrun uhci interrupt queue.\n");
596 q->qh->elementlinkptr = virt_to_phys(q->tds + q->lastread);
597 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000598 return NULL;
599}
600
Patrick Georgid21f68b2008-09-02 16:06:22 +0000601void
602uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value)
603{
604 outl (value, ctrl->reg_base + reg);
605}
606
607u32
608uhci_reg_read32 (hci_t *ctrl, usbreg reg)
609{
610 return inl (ctrl->reg_base + reg);
611}
612
613void
614uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value)
615{
616 outw (value, ctrl->reg_base + reg);
617}
618
619u16
620uhci_reg_read16 (hci_t *ctrl, usbreg reg)
621{
622 return inw (ctrl->reg_base + reg);
623}
624
625void
626uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value)
627{
628 outb (value, ctrl->reg_base + reg);
629}
630
631u8
632uhci_reg_read8 (hci_t *ctrl, usbreg reg)
633{
634 return inb (ctrl->reg_base + reg);
635}