blob: d9a1a106244230d31caadc43d0cfda7c06e6c0af [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;
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 }
Mathias Krausec4716b42011-06-08 15:36:55 +020083 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);
86 debug ("td (counter at %x) returns: ", td->ctrlsts >> TD_COUNTER_SHIFT);
87 debug (" bitstuff err: %x, ", !!(td->ctrlsts & TD_STATUS_BITSTUFF_ERR));
88 debug (" CRC err: %x, ", !!(td->ctrlsts & TD_STATUS_CRC_ERR));
89 debug (" NAK rcvd: %x, ", !!(td->ctrlsts & TD_STATUS_NAK_RCVD));
90 debug (" Babble: %x, ", !!(td->ctrlsts & TD_STATUS_BABBLE));
91 debug (" Data Buffer err: %x, ", !!(td->ctrlsts & TD_STATUS_DATABUF_ERR));
92 debug (" Stalled: %x, ", !!(td->ctrlsts & TD_STATUS_STALLED));
93 debug (" Active: %x\n", !!(td->ctrlsts & TD_STATUS_ACTIVE));
94 if (td->ctrlsts & TD_STATUS_BABBLE)
Mathias Krausec4716b42011-06-08 15:36:55 +020095 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)
Mathias Krausec4716b42011-06-08 15:36:55 +020098 debug (" still active - timeout?\n");
Patrick Georgid21f68b2008-09-02 16:06:22 +000099}
100
101static void
102uhci_reset (hci_t *controller)
103{
104 /* reset */
105 uhci_reg_write16 (controller, USBCMD, 4);
106 mdelay (50);
107 uhci_reg_write16 (controller, USBCMD, 0);
108 mdelay (10);
109 uhci_reg_write16 (controller, USBCMD, 2);
110 while ((uhci_reg_read16 (controller, USBCMD) & 2) != 0)
111 mdelay (1);
112
113 uhci_reg_write32 (controller, FLBASEADD,
114 (u32) virt_to_phys (UHCI_INST (controller)->
115 framelistptr));
Mathias Krausec4716b42011-06-08 15:36:55 +0200116 //debug ("framelist at %p\n",UHCI_INST(controller)->framelistptr);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000117
118 /* disable irqs */
119 uhci_reg_write16 (controller, USBINTR, 0);
120
121 /* reset framelist index */
122 uhci_reg_write16 (controller, FRNUM, 0);
123
Patrick Georgif42fdab2011-11-18 14:44:16 +0100124 uhci_reg_write16(controller, USBCMD,
125 uhci_reg_read16(controller, USBCMD) | 0xc0); // max packets, configure flag
Patrick Georgid21f68b2008-09-02 16:06:22 +0000126
127 uhci_start (controller);
128}
129
130hci_t *
131uhci_init (pcidev_t addr)
132{
133 int i;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000134 u16 reg16;
135
Patrick Georgid21f68b2008-09-02 16:06:22 +0000136 hci_t *controller = new_controller ();
137
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000138 if (!controller)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100139 fatal("Could not create USB controller instance.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000140
Patrick Georgid21f68b2008-09-02 16:06:22 +0000141 controller->instance = malloc (sizeof (uhci_t));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000142 if(!controller->instance)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100143 fatal("Not enough memory creating USB controller instance.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000144
Patrick Georgid21f68b2008-09-02 16:06:22 +0000145 controller->start = uhci_start;
146 controller->stop = uhci_stop;
147 controller->reset = uhci_reset;
148 controller->shutdown = uhci_shutdown;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000149 controller->bulk = uhci_bulk;
150 controller->control = uhci_control;
Patrick Georgi4727c072008-10-16 19:20:51 +0000151 controller->create_intr_queue = uhci_create_intr_queue;
152 controller->destroy_intr_queue = uhci_destroy_intr_queue;
153 controller->poll_intr_queue = uhci_poll_intr_queue;
Stefan Reinauer219cece2009-07-18 15:17:40 +0000154 for (i = 0; i < 128; i++) {
Patrick Georgi4727c072008-10-16 19:20:51 +0000155 controller->devices[i] = 0;
156 }
157 init_device_entry (controller, 0);
158 UHCI_INST (controller)->roothub = controller->devices[0];
Patrick Georgid21f68b2008-09-02 16:06:22 +0000159
160 controller->bus_address = addr;
161 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 */
162
163 /* kill legacy support handler */
164 uhci_stop (controller);
165 mdelay (1);
166 uhci_reg_write16 (controller, USBSTS, 0x3f);
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000167 reg16 = pci_read_config16(controller->bus_address, 0xc0);
168 reg16 &= 0xdf80;
169 pci_write_config16 (controller->bus_address, 0xc0, reg16);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000170
171 UHCI_INST (controller)->framelistptr = memalign (0x1000, 1024 * sizeof (flistp_t *)); /* 4kb aligned to 4kb */
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000172 if (! UHCI_INST (controller)->framelistptr)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100173 fatal("Not enough memory for USB frame list pointer.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000174
Patrick Georgid21f68b2008-09-02 16:06:22 +0000175 memset (UHCI_INST (controller)->framelistptr, 0,
176 1024 * sizeof (flistp_t));
177
Patrick Georgi4727c072008-10-16 19:20:51 +0000178 /* According to the *BSD UHCI code, this one is needed on some
179 PIIX chips, because otherwise they misbehave. It must be
180 added to the last chain.
181
182 FIXME: this leaks, if the driver should ever be reinited
183 for some reason. Not a problem now.
184 */
185 td_t *antiberserk = memalign(16, sizeof(td_t));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000186 if (!antiberserk)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100187 fatal("Not enough memory for chipset workaround.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000188 memset(antiberserk, 0, sizeof(td_t));
189
190 UHCI_INST (controller)->qh_prei = memalign (16, sizeof (qh_t));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000191 UHCI_INST (controller)->qh_intr = memalign (16, sizeof (qh_t));
192 UHCI_INST (controller)->qh_data = memalign (16, sizeof (qh_t));
193 UHCI_INST (controller)->qh_last = memalign (16, sizeof (qh_t));
194
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000195 if (! UHCI_INST (controller)->qh_prei ||
196 ! UHCI_INST (controller)->qh_intr ||
197 ! UHCI_INST (controller)->qh_data ||
198 ! UHCI_INST (controller)->qh_last)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100199 fatal("Not enough memory for USB controller queues.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000200
Patrick Georgib0b4a522011-11-24 11:55:46 +0100201 UHCI_INST (controller)->qh_prei->headlinkptr =
202 virt_to_phys (UHCI_INST (controller)->qh_intr) | FLISTP_QH;
203 UHCI_INST (controller)->qh_prei->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000204
Patrick Georgib0b4a522011-11-24 11:55:46 +0100205 UHCI_INST (controller)->qh_intr->headlinkptr =
206 virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_QH;
207 UHCI_INST (controller)->qh_intr->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000208
Patrick Georgib0b4a522011-11-24 11:55:46 +0100209 UHCI_INST (controller)->qh_data->headlinkptr =
210 virt_to_phys (UHCI_INST (controller)->qh_last) | FLISTP_QH;
211 UHCI_INST (controller)->qh_data->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000212
Patrick Georgib0b4a522011-11-24 11:55:46 +0100213 UHCI_INST (controller)->qh_last->headlinkptr = virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_TERMINATE;
214 UHCI_INST (controller)->qh_last->elementlinkptr = virt_to_phys (antiberserk) | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000215
216 for (i = 0; i < 1024; i++) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100217 UHCI_INST (controller)->framelistptr[i] =
218 virt_to_phys (UHCI_INST (controller)->qh_prei) | FLISTP_QH;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000219 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000220 controller->devices[0]->controller = controller;
221 controller->devices[0]->init = uhci_rh_init;
222 controller->devices[0]->init (controller->devices[0]);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000223 uhci_reset (controller);
224 return controller;
225}
226
227static void
228uhci_shutdown (hci_t *controller)
229{
230 if (controller == 0)
231 return;
232 detach_controller (controller);
233 UHCI_INST (controller)->roothub->destroy (UHCI_INST (controller)->
234 roothub);
Patrick Georgif42fdab2011-11-18 14:44:16 +0100235 uhci_reg_write16(controller, USBCMD,
236 uhci_reg_read16(controller, USBCMD) & 0); // stop work
Patrick Georgid21f68b2008-09-02 16:06:22 +0000237 free (UHCI_INST (controller)->framelistptr);
Patrick Georgi4727c072008-10-16 19:20:51 +0000238 free (UHCI_INST (controller)->qh_prei);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000239 free (UHCI_INST (controller)->qh_intr);
240 free (UHCI_INST (controller)->qh_data);
241 free (UHCI_INST (controller)->qh_last);
242 free (UHCI_INST (controller));
243 free (controller);
244}
245
246static void
247uhci_start (hci_t *controller)
248{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100249 uhci_reg_write16(controller, USBCMD,
250 uhci_reg_read16(controller, USBCMD) | 1); // start work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000251}
252
253static void
254uhci_stop (hci_t *controller)
255{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100256 uhci_reg_write16(controller, USBCMD,
257 uhci_reg_read16(controller, USBCMD) & ~1); // stop work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000258}
259
260#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))
261
262static td_t *
263wait_for_completed_qh (hci_t *controller, qh_t *qh)
264{
Patrick Georgi4727c072008-10-16 19:20:51 +0000265 int timeout = 1000000; /* max 30 ms. */
Patrick Georgib0b4a522011-11-24 11:55:46 +0100266 void *current = GET_TD (qh->elementlinkptr);
267 while (((qh->elementlinkptr & FLISTP_TERMINATE) == 0) && (timeout-- > 0)) {
268 if (current != GET_TD (qh->elementlinkptr)) {
269 current = GET_TD (qh->elementlinkptr);
Patrick Georgi4727c072008-10-16 19:20:51 +0000270 timeout = 1000000;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000271 }
Patrick Georgif42fdab2011-11-18 14:44:16 +0100272 uhci_reg_write16(controller, USBSTS,
273 uhci_reg_read16(controller, USBSTS) | 0); // clear resettable registers
Patrick Georgid21f68b2008-09-02 16:06:22 +0000274 udelay (30);
275 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100276 return (GET_TD (qh->elementlinkptr) ==
277 0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000278}
279
Patrick Georgid21f68b2008-09-02 16:06:22 +0000280static int
281maxlen (int size)
282{
283 return (size - 1) & 0x7ff;
284}
285
286static int
287min (int a, int b)
288{
289 if (a < b)
290 return a;
291 else
292 return b;
293}
294
295static int
Patrick Georgid78691d2010-06-07 13:58:17 +0000296uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
Patrick Georgid21f68b2008-09-02 16:06:22 +0000297 unsigned char *data)
298{
299 int endp = 0; /* this is control: always 0 */
300 int mlen = dev->endpoints[0].maxpacketsize;
301 int count = (2 + (dalen + mlen - 1) / mlen);
302 unsigned short req = ((unsigned short *) devreq)[0];
303 int i;
304 td_t *tds = memalign (16, sizeof (td_t) * count);
305 memset (tds, 0, sizeof (td_t) * count);
306 count--; /* to compensate for 0-indexed array */
307 for (i = 0; i < count; i++) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100308 tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000309 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100310 tds[count].ptr = 0 | TD_DEPTH_FIRST | TD_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000311
Patrick Georgib0b4a522011-11-24 11:55:46 +0100312 tds[0].token = UHCI_SETUP |
313 dev->address << TD_DEVADDR_SHIFT |
314 endp << TD_EP_SHIFT |
315 TD_TOGGLE_DATA0 |
316 maxlen(drlen) << TD_MAXLEN_SHIFT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000317 tds[0].bufptr = virt_to_phys (devreq);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100318 tds[0].ctrlsts = (3 << TD_COUNTER_SHIFT) |
319 (dev->speed?TD_LOWSPEED:0) |
320 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000321
322 int toggle = 1;
323 for (i = 1; i < count; i++) {
Patrick Georgid78691d2010-06-07 13:58:17 +0000324 switch (dir) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100325 case SETUP: tds[i].token = UHCI_SETUP; break;
326 case IN: tds[i].token = UHCI_IN; break;
327 case OUT: tds[i].token = UHCI_OUT; break;
Patrick Georgid78691d2010-06-07 13:58:17 +0000328 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100329 tds[i].token |= dev->address << TD_DEVADDR_SHIFT |
330 endp << TD_EP_SHIFT |
331 maxlen (min (mlen, dalen)) << TD_MAXLEN_SHIFT |
332 toggle << TD_TOGGLE_SHIFT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000333 tds[i].bufptr = virt_to_phys (data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100334 tds[i].ctrlsts = (3 << TD_COUNTER_SHIFT) |
335 (dev->speed?TD_LOWSPEED:0) |
336 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000337 toggle ^= 1;
338 dalen -= mlen;
339 data += mlen;
340 }
341
Nico Hubercefec0e2012-05-16 15:04:27 +0200342 tds[count].token = ((dir == OUT) ? UHCI_IN : UHCI_OUT) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100343 dev->address << TD_DEVADDR_SHIFT |
344 endp << TD_EP_SHIFT |
345 maxlen(0) << TD_MAXLEN_SHIFT |
346 TD_TOGGLE_DATA1;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000347 tds[count].bufptr = 0;
Nico Hubercefec0e2012-05-16 15:04:27 +0200348 tds[count].ctrlsts = (0 << TD_COUNTER_SHIFT) | /* as Linux 2.4.10 does */
Patrick Georgib0b4a522011-11-24 11:55:46 +0100349 (dev->speed?TD_LOWSPEED:0) |
350 TD_STATUS_ACTIVE;
351 UHCI_INST (dev->controller)->qh_data->elementlinkptr =
352 virt_to_phys (tds) & ~(FLISTP_QH | FLISTP_TERMINATE);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000353 td_t *td = wait_for_completed_qh (dev->controller,
354 UHCI_INST (dev->controller)->
355 qh_data);
356 int result;
357 if (td == 0) {
358 result = 0;
359 } else {
Mathias Krausec4716b42011-06-08 15:36:55 +0200360 debug ("control packet, req %x\n", req);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000361 td_dump (td);
362 result = 1;
363 }
364 free (tds);
365 return result;
366}
367
Patrick Georgid21f68b2008-09-02 16:06:22 +0000368static td_t *
369create_schedule (int numpackets)
370{
371 if (numpackets == 0)
372 return 0;
373 td_t *tds = memalign (16, sizeof (td_t) * numpackets);
374 memset (tds, 0, sizeof (td_t) * numpackets);
375 int i;
376 for (i = 0; i < numpackets; i++) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100377 tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000378 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100379 tds[numpackets - 1].ptr = 0 | TD_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000380 return tds;
381}
382
383static void
384fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data,
385 int *toggle)
386{
Patrick Georgicfaa0812010-06-11 14:25:40 +0000387 switch (ep->direction) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100388 case IN: td->token = UHCI_IN; break;
389 case OUT: td->token = UHCI_OUT; break;
390 case SETUP: td->token = UHCI_SETUP; break;
Patrick Georgicfaa0812010-06-11 14:25:40 +0000391 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100392 td->token |= ep->dev->address << TD_DEVADDR_SHIFT |
393 (ep->endpoint & 0xf) << TD_EP_SHIFT |
394 maxlen (length) << TD_MAXLEN_SHIFT |
395 (*toggle & 1) << TD_TOGGLE_SHIFT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000396 td->bufptr = virt_to_phys (data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100397 td->ctrlsts = ((ep->direction == SETUP?3:0) << TD_COUNTER_SHIFT) |
Nico Hubercefec0e2012-05-16 15:04:27 +0200398 (ep->dev->speed?TD_LOWSPEED:0) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100399 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000400 *toggle ^= 1;
401}
402
403static int
404run_schedule (usbdev_t *dev, td_t *td)
405{
Patrick Georgib0b4a522011-11-24 11:55:46 +0100406 UHCI_INST (dev->controller)->qh_data->elementlinkptr =
407 virt_to_phys (td) | ~(FLISTP_QH | FLISTP_TERMINATE);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000408 td = wait_for_completed_qh (dev->controller,
409 UHCI_INST (dev->controller)->qh_data);
410 if (td == 0) {
411 return 0;
412 } else {
413 td_dump (td);
414 return 1;
415 }
416}
417
418/* finalize == 1: if data is of packet aligned size, add a zero length packet */
419static int
420uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
421{
422 int maxpsize = ep->maxpacketsize;
423 if (maxpsize == 0)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100424 fatal("MaxPacketSize == 0!!!");
Patrick Georgid21f68b2008-09-02 16:06:22 +0000425 int numpackets = (size + maxpsize - 1 + finalize) / maxpsize;
426 if (numpackets == 0)
427 return 0;
428 td_t *tds = create_schedule (numpackets);
429 int i = 0, toggle = ep->toggle;
430 while ((size > 0) || ((size == 0) && (finalize != 0))) {
431 fill_schedule (&tds[i], ep, min (size, maxpsize), data,
432 &toggle);
433 i++;
434 data += maxpsize;
435 size -= maxpsize;
436 }
437 if (run_schedule (ep->dev, tds) == 1) {
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000438 debug("Stalled. Trying to clean up.\n");
Patrick Georgid21f68b2008-09-02 16:06:22 +0000439 clear_stall (ep);
440 free (tds);
441 return 1;
442 }
443 ep->toggle = toggle;
444 free (tds);
445 return 0;
446}
447
Patrick Georgi4727c072008-10-16 19:20:51 +0000448typedef struct {
449 qh_t *qh;
450 td_t *tds;
451 td_t *last_td;
452 u8 *data;
453 int lastread;
454 int total;
455 int reqsize;
456} intr_q;
457
458/* create and hook-up an intr queue into device schedule */
459static void*
460uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
461{
462 u8 *data = malloc(reqsize*reqcount);
463 td_t *tds = memalign(16, sizeof(td_t) * reqcount);
464 qh_t *qh = memalign(16, sizeof(qh_t));
465
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000466 if (!data || !tds || !qh)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100467 fatal("Not enough memory to create USB intr queue prerequisites.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000468
Patrick Georgib0b4a522011-11-24 11:55:46 +0100469 qh->elementlinkptr = virt_to_phys(tds);
Patrick Georgi4727c072008-10-16 19:20:51 +0000470
471 intr_q *q = malloc(sizeof(intr_q));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000472 if (!q)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100473 fatal("Not enough memory to create USB intr queue.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000474 q->qh = qh;
475 q->tds = tds;
476 q->data = data;
477 q->lastread = 0;
478 q->total = reqcount;
479 q->reqsize = reqsize;
480 q->last_td = &tds[reqcount - 1];
481
482 memset (tds, 0, sizeof (td_t) * reqcount);
483 int i;
484 for (i = 0; i < reqcount; i++) {
485 tds[i].ptr = virt_to_phys (&tds[i + 1]);
Patrick Georgi4727c072008-10-16 19:20:51 +0000486
Patrick Georgicfaa0812010-06-11 14:25:40 +0000487 switch (ep->direction) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100488 case IN: tds[i].token = UHCI_IN; break;
489 case OUT: tds[i].token = UHCI_OUT; break;
490 case SETUP: tds[i].token = UHCI_SETUP; break;
Patrick Georgicfaa0812010-06-11 14:25:40 +0000491 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100492 tds[i].token |= ep->dev->address << TD_DEVADDR_SHIFT |
493 (ep->endpoint & 0xf) << TD_EP_SHIFT |
494 maxlen (reqsize) << TD_MAXLEN_SHIFT |
495 (ep->toggle & 1) << TD_TOGGLE_SHIFT;
Patrick Georgi4727c072008-10-16 19:20:51 +0000496 tds[i].bufptr = virt_to_phys (data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100497 tds[i].ctrlsts = (0 << TD_COUNTER_SHIFT) |
Nico Hubercefec0e2012-05-16 15:04:27 +0200498 (ep->dev->speed?TD_LOWSPEED:0) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100499 TD_STATUS_ACTIVE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000500 ep->toggle ^= 1;
501 data += reqsize;
502 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100503 tds[reqcount - 1].ptr = 0 | TD_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000504 for (i = reqtiming; i < 1024; i += reqtiming) {
505 /* FIXME: wrap in another qh, one for each occurance of the qh in the framelist */
Patrick Georgib0b4a522011-11-24 11:55:46 +0100506 qh->headlinkptr = UHCI_INST (ep->dev->controller)->framelistptr[i] & ~FLISTP_TERMINATE;
507 UHCI_INST (ep->dev->controller)->framelistptr[i] = virt_to_phys(qh) | FLISTP_QH;
Patrick Georgi4727c072008-10-16 19:20:51 +0000508 }
509 return q;
510}
511
512/* remove queue from device schedule, dropping all data that came in */
513static void
514uhci_destroy_intr_queue (endpoint_t *ep, void *q_)
515{
516 intr_q *q = (intr_q*)q_;
517 u32 val = virt_to_phys (q->qh);
518 u32 end = virt_to_phys (UHCI_INST (ep->dev->controller)->qh_intr);
519 int i;
520 for (i=0; i<1024; i++) {
521 u32 oldptr = 0;
Patrick Georgib0b4a522011-11-24 11:55:46 +0100522 u32 ptr = UHCI_INST (ep->dev->controller)->framelistptr[i];
Patrick Georgi4727c072008-10-16 19:20:51 +0000523 while (ptr != end) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100524 if (((qh_t*)phys_to_virt(ptr))->elementlinkptr == val) {
525 ((qh_t*)phys_to_virt(oldptr))->headlinkptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr;
Patrick Georgi4727c072008-10-16 19:20:51 +0000526 free(phys_to_virt(ptr));
527 break;
528 }
529 oldptr = ptr;
Patrick Georgib0b4a522011-11-24 11:55:46 +0100530 ptr = ((qh_t*)phys_to_virt(ptr))->headlinkptr;
Patrick Georgi4727c072008-10-16 19:20:51 +0000531 }
532 }
533 free(q->data);
534 free(q->tds);
535 free(q->qh);
536 free(q);
537}
538
539/* read one intr-packet from queue, if available. extend the queue for new input.
540 return NULL if nothing new available.
541 Recommended use: while (data=poll_intr_queue(q)) process(data);
542 */
543static u8*
544uhci_poll_intr_queue (void *q_)
545{
546 intr_q *q = (intr_q*)q_;
Patrick Georgib0b4a522011-11-24 11:55:46 +0100547 if ((q->tds[q->lastread].ctrlsts & TD_STATUS_ACTIVE) == 0) {
Patrick Georgi4727c072008-10-16 19:20:51 +0000548 /* FIXME: handle errors */
549 int current = q->lastread;
550 int previous;
551 if (q->lastread == 0) {
552 previous = q->total - 1;
553 } else {
554 previous = q->lastread - 1;
555 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100556 q->tds[previous].ctrlsts &= ~TD_STATUS_MASK;
557 q->tds[previous].ptr = 0 | TD_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000558 if (q->last_td != &q->tds[previous]) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100559 q->last_td->ptr = virt_to_phys(&q->tds[previous]) & ~TD_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000560 q->last_td = &q->tds[previous];
561 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100562 q->tds[previous].ctrlsts |= TD_STATUS_ACTIVE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000563 q->lastread = (q->lastread + 1) % q->total;
564 return &q->data[current*q->reqsize];
565 }
566 return NULL;
567}
568
Patrick Georgid21f68b2008-09-02 16:06:22 +0000569void
570uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value)
571{
572 outl (value, ctrl->reg_base + reg);
573}
574
575u32
576uhci_reg_read32 (hci_t *ctrl, usbreg reg)
577{
578 return inl (ctrl->reg_base + reg);
579}
580
581void
582uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value)
583{
584 outw (value, ctrl->reg_base + reg);
585}
586
587u16
588uhci_reg_read16 (hci_t *ctrl, usbreg reg)
589{
590 return inw (ctrl->reg_base + reg);
591}
592
593void
594uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value)
595{
596 outb (value, ctrl->reg_base + reg);
597}
598
599u8
600uhci_reg_read8 (hci_t *ctrl, usbreg reg)
601{
602 return inb (ctrl->reg_base + reg);
603}