blob: b964ee7a0b168245ce4ea2830ef27de981de8c42 [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{
Mathias Krausec4716b42011-06-08 15:36:55 +020053 debug ("dump:\nUSBCMD: %x\n", uhci_reg_read16 (controller, USBCMD));
54 debug ("USBSTS: %x\n", uhci_reg_read16 (controller, USBSTS));
55 debug ("USBINTR: %x\n", uhci_reg_read16 (controller, USBINTR));
56 debug ("FRNUM: %x\n", uhci_reg_read16 (controller, FRNUM));
57 debug ("FLBASEADD: %x\n", uhci_reg_read32 (controller, FLBASEADD));
58 debug ("SOFMOD: %x\n", uhci_reg_read8 (controller, SOFMOD));
59 debug ("PORTSC1: %x\n", uhci_reg_read16 (controller, PORTSC1));
60 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;
Stefan Reinauerd233f362009-04-30 16:46:12 +000069 switch (td->pid) {
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:
80 sprintf(td_value, "%x", td->pid);
81 td_type=td_value;
82 }
Mathias Krausec4716b42011-06-08 15:36:55 +020083 debug ("%s packet (at %lx) to %x.%x failed\n", td_type,
Patrick Georgid21f68b2008-09-02 16:06:22 +000084 virt_to_phys (td), td->dev_addr, td->endp);
Mathias Krausec4716b42011-06-08 15:36:55 +020085 debug ("td (counter at %x) returns: ", td->counter);
86 debug (" bitstuff err: %x, ", td->status_bitstuff_err);
87 debug (" CRC err: %x, ", td->status_crc_err);
88 debug (" NAK rcvd: %x, ", td->status_nakrcvd);
89 debug (" Babble: %x, ", td->status_babble);
90 debug (" Data Buffer err: %x, ", td->status_databuf_err);
91 debug (" Stalled: %x, ", td->status_stalled);
92 debug (" Active: %x\n", td->status_active);
Patrick Georgid21f68b2008-09-02 16:06:22 +000093 if (td->status_babble)
Mathias Krausec4716b42011-06-08 15:36:55 +020094 debug (" Babble because of %s\n",
Patrick Georgid21f68b2008-09-02 16:06:22 +000095 td->status_bitstuff_err ? "host" : "device");
96 if (td->status_active)
Mathias Krausec4716b42011-06-08 15:36:55 +020097 debug (" still active - timeout?\n");
Patrick Georgid21f68b2008-09-02 16:06:22 +000098}
99
100static void
101uhci_reset (hci_t *controller)
102{
103 /* reset */
104 uhci_reg_write16 (controller, USBCMD, 4);
105 mdelay (50);
106 uhci_reg_write16 (controller, USBCMD, 0);
107 mdelay (10);
108 uhci_reg_write16 (controller, USBCMD, 2);
109 while ((uhci_reg_read16 (controller, USBCMD) & 2) != 0)
110 mdelay (1);
111
112 uhci_reg_write32 (controller, FLBASEADD,
113 (u32) virt_to_phys (UHCI_INST (controller)->
114 framelistptr));
Mathias Krausec4716b42011-06-08 15:36:55 +0200115 //debug ("framelist at %p\n",UHCI_INST(controller)->framelistptr);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000116
117 /* disable irqs */
118 uhci_reg_write16 (controller, USBINTR, 0);
119
120 /* reset framelist index */
121 uhci_reg_write16 (controller, FRNUM, 0);
122
Patrick Georgif42fdab2011-11-18 14:44:16 +0100123 uhci_reg_write16(controller, USBCMD,
124 uhci_reg_read16(controller, USBCMD) | 0xc0); // max packets, configure flag
Patrick Georgid21f68b2008-09-02 16:06:22 +0000125
126 uhci_start (controller);
127}
128
129hci_t *
130uhci_init (pcidev_t addr)
131{
132 int i;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000133 u16 reg16;
134
Patrick Georgid21f68b2008-09-02 16:06:22 +0000135 hci_t *controller = new_controller ();
136
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000137 if (!controller)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100138 fatal("Could not create USB controller instance.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000139
Patrick Georgid21f68b2008-09-02 16:06:22 +0000140 controller->instance = malloc (sizeof (uhci_t));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000141 if(!controller->instance)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100142 fatal("Not enough memory creating USB controller instance.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000143
Patrick Georgid21f68b2008-09-02 16:06:22 +0000144 controller->start = uhci_start;
145 controller->stop = uhci_stop;
146 controller->reset = uhci_reset;
147 controller->shutdown = uhci_shutdown;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000148 controller->bulk = uhci_bulk;
149 controller->control = uhci_control;
Patrick Georgi4727c072008-10-16 19:20:51 +0000150 controller->create_intr_queue = uhci_create_intr_queue;
151 controller->destroy_intr_queue = uhci_destroy_intr_queue;
152 controller->poll_intr_queue = uhci_poll_intr_queue;
Stefan Reinauer219cece2009-07-18 15:17:40 +0000153 for (i = 0; i < 128; i++) {
Patrick Georgi4727c072008-10-16 19:20:51 +0000154 controller->devices[i] = 0;
155 }
156 init_device_entry (controller, 0);
157 UHCI_INST (controller)->roothub = controller->devices[0];
Patrick Georgid21f68b2008-09-02 16:06:22 +0000158
159 controller->bus_address = addr;
160 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 */
161
162 /* kill legacy support handler */
163 uhci_stop (controller);
164 mdelay (1);
165 uhci_reg_write16 (controller, USBSTS, 0x3f);
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000166 reg16 = pci_read_config16(controller->bus_address, 0xc0);
167 reg16 &= 0xdf80;
168 pci_write_config16 (controller->bus_address, 0xc0, reg16);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000169
170 UHCI_INST (controller)->framelistptr = memalign (0x1000, 1024 * sizeof (flistp_t *)); /* 4kb aligned to 4kb */
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000171 if (! UHCI_INST (controller)->framelistptr)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100172 fatal("Not enough memory for USB frame list pointer.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000173
Patrick Georgid21f68b2008-09-02 16:06:22 +0000174 memset (UHCI_INST (controller)->framelistptr, 0,
175 1024 * sizeof (flistp_t));
176
Patrick Georgi4727c072008-10-16 19:20:51 +0000177 /* According to the *BSD UHCI code, this one is needed on some
178 PIIX chips, because otherwise they misbehave. It must be
179 added to the last chain.
180
181 FIXME: this leaks, if the driver should ever be reinited
182 for some reason. Not a problem now.
183 */
184 td_t *antiberserk = memalign(16, sizeof(td_t));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000185 if (!antiberserk)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100186 fatal("Not enough memory for chipset workaround.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000187 memset(antiberserk, 0, sizeof(td_t));
188
189 UHCI_INST (controller)->qh_prei = memalign (16, sizeof (qh_t));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000190 UHCI_INST (controller)->qh_intr = memalign (16, sizeof (qh_t));
191 UHCI_INST (controller)->qh_data = memalign (16, sizeof (qh_t));
192 UHCI_INST (controller)->qh_last = memalign (16, sizeof (qh_t));
193
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000194 if (! UHCI_INST (controller)->qh_prei ||
195 ! UHCI_INST (controller)->qh_intr ||
196 ! UHCI_INST (controller)->qh_data ||
197 ! UHCI_INST (controller)->qh_last)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100198 fatal("Not enough memory for USB controller queues.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000199
Patrick Georgi4727c072008-10-16 19:20:51 +0000200 UHCI_INST (controller)->qh_prei->headlinkptr.ptr =
201 virt_to_phys (UHCI_INST (controller)->qh_intr);
202 UHCI_INST (controller)->qh_prei->headlinkptr.queue_head = 1;
203 UHCI_INST (controller)->qh_prei->elementlinkptr.ptr = 0;
204 UHCI_INST (controller)->qh_prei->elementlinkptr.terminate = 1;
205
Patrick Georgid21f68b2008-09-02 16:06:22 +0000206 UHCI_INST (controller)->qh_intr->headlinkptr.ptr =
207 virt_to_phys (UHCI_INST (controller)->qh_data);
208 UHCI_INST (controller)->qh_intr->headlinkptr.queue_head = 1;
209 UHCI_INST (controller)->qh_intr->elementlinkptr.ptr = 0;
210 UHCI_INST (controller)->qh_intr->elementlinkptr.terminate = 1;
211
212 UHCI_INST (controller)->qh_data->headlinkptr.ptr =
213 virt_to_phys (UHCI_INST (controller)->qh_last);
214 UHCI_INST (controller)->qh_data->headlinkptr.queue_head = 1;
215 UHCI_INST (controller)->qh_data->elementlinkptr.ptr = 0;
216 UHCI_INST (controller)->qh_data->elementlinkptr.terminate = 1;
217
Patrick Georgi4727c072008-10-16 19:20:51 +0000218 UHCI_INST (controller)->qh_last->headlinkptr.ptr = virt_to_phys (UHCI_INST (controller)->qh_data);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000219 UHCI_INST (controller)->qh_last->headlinkptr.terminate = 1;
Patrick Georgi4727c072008-10-16 19:20:51 +0000220 UHCI_INST (controller)->qh_last->elementlinkptr.ptr = virt_to_phys (antiberserk);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000221 UHCI_INST (controller)->qh_last->elementlinkptr.terminate = 1;
222
223 for (i = 0; i < 1024; i++) {
224 UHCI_INST (controller)->framelistptr[i].ptr =
Patrick Georgi4727c072008-10-16 19:20:51 +0000225 virt_to_phys (UHCI_INST (controller)->qh_prei);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000226 UHCI_INST (controller)->framelistptr[i].terminate = 0;
227 UHCI_INST (controller)->framelistptr[i].queue_head = 1;
228 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000229 controller->devices[0]->controller = controller;
230 controller->devices[0]->init = uhci_rh_init;
231 controller->devices[0]->init (controller->devices[0]);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000232 uhci_reset (controller);
233 return controller;
234}
235
236static void
237uhci_shutdown (hci_t *controller)
238{
239 if (controller == 0)
240 return;
241 detach_controller (controller);
242 UHCI_INST (controller)->roothub->destroy (UHCI_INST (controller)->
243 roothub);
Patrick Georgif42fdab2011-11-18 14:44:16 +0100244 uhci_reg_write16(controller, USBCMD,
245 uhci_reg_read16(controller, USBCMD) & 0); // stop work
Patrick Georgid21f68b2008-09-02 16:06:22 +0000246 free (UHCI_INST (controller)->framelistptr);
Patrick Georgi4727c072008-10-16 19:20:51 +0000247 free (UHCI_INST (controller)->qh_prei);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000248 free (UHCI_INST (controller)->qh_intr);
249 free (UHCI_INST (controller)->qh_data);
250 free (UHCI_INST (controller)->qh_last);
251 free (UHCI_INST (controller));
252 free (controller);
253}
254
255static void
256uhci_start (hci_t *controller)
257{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100258 uhci_reg_write16(controller, USBCMD,
259 uhci_reg_read16(controller, USBCMD) | 1); // start work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000260}
261
262static void
263uhci_stop (hci_t *controller)
264{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100265 uhci_reg_write16(controller, USBCMD,
266 uhci_reg_read16(controller, USBCMD) & ~1); // stop work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000267}
268
269#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))
270
271static td_t *
272wait_for_completed_qh (hci_t *controller, qh_t *qh)
273{
Patrick Georgi4727c072008-10-16 19:20:51 +0000274 int timeout = 1000000; /* max 30 ms. */
Patrick Georgid21f68b2008-09-02 16:06:22 +0000275 void *current = GET_TD (qh->elementlinkptr.ptr);
276 while ((qh->elementlinkptr.terminate == 0) && (timeout-- > 0)) {
277 if (current != GET_TD (qh->elementlinkptr.ptr)) {
278 current = GET_TD (qh->elementlinkptr.ptr);
Patrick Georgi4727c072008-10-16 19:20:51 +0000279 timeout = 1000000;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000280 }
Patrick Georgif42fdab2011-11-18 14:44:16 +0100281 uhci_reg_write16(controller, USBSTS,
282 uhci_reg_read16(controller, USBSTS) | 0); // clear resettable registers
Patrick Georgid21f68b2008-09-02 16:06:22 +0000283 udelay (30);
284 }
285 return (GET_TD (qh->elementlinkptr.ptr) ==
286 0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr.ptr));
287}
288
Patrick Georgid21f68b2008-09-02 16:06:22 +0000289static int
290maxlen (int size)
291{
292 return (size - 1) & 0x7ff;
293}
294
295static int
296min (int a, int b)
297{
298 if (a < b)
299 return a;
300 else
301 return b;
302}
303
304static int
Patrick Georgid78691d2010-06-07 13:58:17 +0000305uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
Patrick Georgid21f68b2008-09-02 16:06:22 +0000306 unsigned char *data)
307{
308 int endp = 0; /* this is control: always 0 */
309 int mlen = dev->endpoints[0].maxpacketsize;
310 int count = (2 + (dalen + mlen - 1) / mlen);
311 unsigned short req = ((unsigned short *) devreq)[0];
312 int i;
313 td_t *tds = memalign (16, sizeof (td_t) * count);
314 memset (tds, 0, sizeof (td_t) * count);
315 count--; /* to compensate for 0-indexed array */
316 for (i = 0; i < count; i++) {
317 tds[i].ptr = virt_to_phys (&tds[i + 1]);
318 tds[i].depth_first = 1;
319 tds[i].terminate = 0;
320 }
321 tds[count].ptr = 0;
322 tds[count].depth_first = 1;
323 tds[count].terminate = 1;
324
Patrick Georgid78691d2010-06-07 13:58:17 +0000325 tds[0].pid = UHCI_SETUP;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000326 tds[0].dev_addr = dev->address;
327 tds[0].endp = endp;
328 tds[0].maxlen = maxlen (drlen);
329 tds[0].counter = 3;
330 tds[0].data_toggle = 0;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000331 tds[0].lowspeed = dev->speed;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000332 tds[0].bufptr = virt_to_phys (devreq);
333 tds[0].status_active = 1;
334
335 int toggle = 1;
336 for (i = 1; i < count; i++) {
Patrick Georgid78691d2010-06-07 13:58:17 +0000337 switch (dir) {
338 case SETUP: tds[i].pid = UHCI_SETUP; break;
339 case IN: tds[i].pid = UHCI_IN; break;
340 case OUT: tds[i].pid = UHCI_OUT; break;
341 }
Patrick Georgid21f68b2008-09-02 16:06:22 +0000342 tds[i].dev_addr = dev->address;
343 tds[i].endp = endp;
344 tds[i].maxlen = maxlen (min (mlen, dalen));
345 tds[i].counter = 3;
346 tds[i].data_toggle = toggle;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000347 tds[i].lowspeed = dev->speed;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000348 tds[i].bufptr = virt_to_phys (data);
349 tds[i].status_active = 1;
350 toggle ^= 1;
351 dalen -= mlen;
352 data += mlen;
353 }
354
Patrick Georgid78691d2010-06-07 13:58:17 +0000355 tds[count].pid = (dir == OUT) ? UHCI_IN : UHCI_OUT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000356 tds[count].dev_addr = dev->address;
357 tds[count].endp = endp;
358 tds[count].maxlen = maxlen (0);
359 tds[count].counter = 0; /* as per linux 2.4.10 */
360 tds[count].data_toggle = 1;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000361 tds[count].lowspeed = dev->speed;
362 tds[count].bufptr = 0;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000363 tds[count].status_active = 1;
364 UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
365 virt_to_phys (tds);
366 UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
367 UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
368 td_t *td = wait_for_completed_qh (dev->controller,
369 UHCI_INST (dev->controller)->
370 qh_data);
371 int result;
372 if (td == 0) {
373 result = 0;
374 } else {
Mathias Krausec4716b42011-06-08 15:36:55 +0200375 debug ("control packet, req %x\n", req);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000376 td_dump (td);
377 result = 1;
378 }
379 free (tds);
380 return result;
381}
382
Patrick Georgid21f68b2008-09-02 16:06:22 +0000383static td_t *
384create_schedule (int numpackets)
385{
386 if (numpackets == 0)
387 return 0;
388 td_t *tds = memalign (16, sizeof (td_t) * numpackets);
389 memset (tds, 0, sizeof (td_t) * numpackets);
390 int i;
391 for (i = 0; i < numpackets; i++) {
392 tds[i].ptr = virt_to_phys (&tds[i + 1]);
393 tds[i].terminate = 0;
394 tds[i].queue_head = 0;
395 tds[i].depth_first = 1;
396 }
397 tds[numpackets - 1].ptr = 0;
398 tds[numpackets - 1].terminate = 1;
399 tds[numpackets - 1].queue_head = 0;
400 tds[numpackets - 1].depth_first = 0;
401 return tds;
402}
403
404static void
405fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data,
406 int *toggle)
407{
Patrick Georgicfaa0812010-06-11 14:25:40 +0000408 switch (ep->direction) {
409 case IN: td->pid = UHCI_IN; break;
410 case OUT: td->pid = UHCI_OUT; break;
411 case SETUP: td->pid = UHCI_SETUP; break;
412 }
Patrick Georgid21f68b2008-09-02 16:06:22 +0000413 td->dev_addr = ep->dev->address;
414 td->endp = ep->endpoint & 0xf;
415 td->maxlen = maxlen (length);
416 if (ep->direction == SETUP)
417 td->counter = 3;
418 else
419 td->counter = 0;
420 td->data_toggle = *toggle & 1;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000421 td->lowspeed = ep->dev->speed;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000422 td->bufptr = virt_to_phys (data);
423
424 td->status_active = 1;
425 *toggle ^= 1;
426}
427
428static int
429run_schedule (usbdev_t *dev, td_t *td)
430{
431 UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
432 virt_to_phys (td);
433 UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
434 UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
435 td = wait_for_completed_qh (dev->controller,
436 UHCI_INST (dev->controller)->qh_data);
437 if (td == 0) {
438 return 0;
439 } else {
440 td_dump (td);
441 return 1;
442 }
443}
444
445/* finalize == 1: if data is of packet aligned size, add a zero length packet */
446static int
447uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
448{
449 int maxpsize = ep->maxpacketsize;
450 if (maxpsize == 0)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100451 fatal("MaxPacketSize == 0!!!");
Patrick Georgid21f68b2008-09-02 16:06:22 +0000452 int numpackets = (size + maxpsize - 1 + finalize) / maxpsize;
453 if (numpackets == 0)
454 return 0;
455 td_t *tds = create_schedule (numpackets);
456 int i = 0, toggle = ep->toggle;
457 while ((size > 0) || ((size == 0) && (finalize != 0))) {
458 fill_schedule (&tds[i], ep, min (size, maxpsize), data,
459 &toggle);
460 i++;
461 data += maxpsize;
462 size -= maxpsize;
463 }
464 if (run_schedule (ep->dev, tds) == 1) {
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000465 debug("Stalled. Trying to clean up.\n");
Patrick Georgid21f68b2008-09-02 16:06:22 +0000466 clear_stall (ep);
467 free (tds);
468 return 1;
469 }
470 ep->toggle = toggle;
471 free (tds);
472 return 0;
473}
474
Patrick Georgi4727c072008-10-16 19:20:51 +0000475typedef struct {
476 qh_t *qh;
477 td_t *tds;
478 td_t *last_td;
479 u8 *data;
480 int lastread;
481 int total;
482 int reqsize;
483} intr_q;
484
485/* create and hook-up an intr queue into device schedule */
486static void*
487uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
488{
489 u8 *data = malloc(reqsize*reqcount);
490 td_t *tds = memalign(16, sizeof(td_t) * reqcount);
491 qh_t *qh = memalign(16, sizeof(qh_t));
492
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000493 if (!data || !tds || !qh)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100494 fatal("Not enough memory to create USB intr queue prerequisites.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000495
Patrick Georgi4727c072008-10-16 19:20:51 +0000496 qh->elementlinkptr.ptr = virt_to_phys(tds);
Stefan Reinauerd233f362009-04-30 16:46:12 +0000497 qh->elementlinkptr.queue_head = 0;
Patrick Georgi4727c072008-10-16 19:20:51 +0000498 qh->elementlinkptr.terminate = 0;
499
500 intr_q *q = malloc(sizeof(intr_q));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000501 if (!q)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100502 fatal("Not enough memory to create USB intr queue.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000503 q->qh = qh;
504 q->tds = tds;
505 q->data = data;
506 q->lastread = 0;
507 q->total = reqcount;
508 q->reqsize = reqsize;
509 q->last_td = &tds[reqcount - 1];
510
511 memset (tds, 0, sizeof (td_t) * reqcount);
512 int i;
513 for (i = 0; i < reqcount; i++) {
514 tds[i].ptr = virt_to_phys (&tds[i + 1]);
515 tds[i].terminate = 0;
516 tds[i].queue_head = 0;
517 tds[i].depth_first = 0;
518
Patrick Georgicfaa0812010-06-11 14:25:40 +0000519 switch (ep->direction) {
520 case IN: tds[i].pid = UHCI_IN; break;
521 case OUT: tds[i].pid = UHCI_OUT; break;
522 case SETUP: tds[i].pid = UHCI_SETUP; break;
523 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000524 tds[i].dev_addr = ep->dev->address;
525 tds[i].endp = ep->endpoint & 0xf;
526 tds[i].maxlen = maxlen (reqsize);
527 tds[i].counter = 0;
528 tds[i].data_toggle = ep->toggle & 1;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000529 tds[i].lowspeed = ep->dev->speed;
Patrick Georgi4727c072008-10-16 19:20:51 +0000530 tds[i].bufptr = virt_to_phys (data);
531 tds[i].status_active = 1;
532 ep->toggle ^= 1;
533 data += reqsize;
534 }
535 tds[reqcount - 1].ptr = 0;
536 tds[reqcount - 1].terminate = 1;
537 tds[reqcount - 1].queue_head = 0;
538 tds[reqcount - 1].depth_first = 0;
539 for (i = reqtiming; i < 1024; i += reqtiming) {
540 /* FIXME: wrap in another qh, one for each occurance of the qh in the framelist */
541 qh->headlinkptr.ptr = UHCI_INST (ep->dev->controller)->framelistptr[i].ptr;
542 qh->headlinkptr.terminate = 0;
543 UHCI_INST (ep->dev->controller)->framelistptr[i].ptr = virt_to_phys(qh);
544 UHCI_INST (ep->dev->controller)->framelistptr[i].terminate = 0;
545 UHCI_INST (ep->dev->controller)->framelistptr[i].queue_head = 1;
546 }
547 return q;
548}
549
550/* remove queue from device schedule, dropping all data that came in */
551static void
552uhci_destroy_intr_queue (endpoint_t *ep, void *q_)
553{
554 intr_q *q = (intr_q*)q_;
555 u32 val = virt_to_phys (q->qh);
556 u32 end = virt_to_phys (UHCI_INST (ep->dev->controller)->qh_intr);
557 int i;
558 for (i=0; i<1024; i++) {
559 u32 oldptr = 0;
560 u32 ptr = UHCI_INST (ep->dev->controller)->framelistptr[i].ptr;
561 while (ptr != end) {
562 if (((qh_t*)phys_to_virt(ptr))->elementlinkptr.ptr == val) {
563 ((qh_t*)phys_to_virt(oldptr))->headlinkptr.ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr.ptr;
564 free(phys_to_virt(ptr));
565 break;
566 }
567 oldptr = ptr;
568 ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr.ptr;
569 }
570 }
571 free(q->data);
572 free(q->tds);
573 free(q->qh);
574 free(q);
575}
576
577/* read one intr-packet from queue, if available. extend the queue for new input.
578 return NULL if nothing new available.
579 Recommended use: while (data=poll_intr_queue(q)) process(data);
580 */
581static u8*
582uhci_poll_intr_queue (void *q_)
583{
584 intr_q *q = (intr_q*)q_;
585 if (q->tds[q->lastread].status_active == 0) {
586 /* FIXME: handle errors */
587 int current = q->lastread;
588 int previous;
589 if (q->lastread == 0) {
590 previous = q->total - 1;
591 } else {
592 previous = q->lastread - 1;
593 }
594 q->tds[previous].status = 0;
595 q->tds[previous].ptr = 0;
596 q->tds[previous].terminate = 1;
597 if (q->last_td != &q->tds[previous]) {
598 q->last_td->ptr = virt_to_phys(&q->tds[previous]);
599 q->last_td->terminate = 0;
600 q->last_td = &q->tds[previous];
601 }
602 q->tds[previous].status_active = 1;
603 q->lastread = (q->lastread + 1) % q->total;
604 return &q->data[current*q->reqsize];
605 }
606 return NULL;
607}
608
Patrick Georgid21f68b2008-09-02 16:06:22 +0000609void
610uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value)
611{
612 outl (value, ctrl->reg_base + reg);
613}
614
615u32
616uhci_reg_read32 (hci_t *ctrl, usbreg reg)
617{
618 return inl (ctrl->reg_base + reg);
619}
620
621void
622uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value)
623{
624 outw (value, ctrl->reg_base + reg);
625}
626
627u16
628uhci_reg_read16 (hci_t *ctrl, usbreg reg)
629{
630 return inw (ctrl->reg_base + reg);
631}
632
633void
634uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value)
635{
636 outb (value, ctrl->reg_base + reg);
637}
638
639u8
640uhci_reg_read8 (hci_t *ctrl, usbreg reg)
641{
642 return inb (ctrl->reg_base + reg);
643}