blob: bfa1b57d4380af0703d374505e0e9771cbe42a32 [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
Anton Kochkovc62b69c2012-12-19 13:49:20 +040064static void td_dump(td_t *td)
Patrick Georgid21f68b2008-09-02 16:06:22 +000065{
Anton Kochkovc62b69c2012-12-19 13:49:20 +040066 usb_debug("+---------------------------------------------------+\n");
67 if ((td->token & TD_PID_MASK) == UHCI_SETUP)
68 usb_debug("|..[SETUP]..........................................|\n");
69 else if ((td->token & TD_PID_MASK) == UHCI_IN)
70 usb_debug("|..[IN].............................................|\n");
71 else if ((td->token & TD_PID_MASK) == UHCI_OUT)
72 usb_debug("|..[OUT]............................................|\n");
73 else
74 usb_debug("|..[]...............................................|\n");
75 usb_debug("|:|============ UHCI TD at [0x%08lx] ==========|:|\n", virt_to_phys(td));
76 usb_debug("|:+-----------------------------------------------+:|\n");
77 usb_debug("|:| Next TD/QH [0x%08lx] |:|\n", td->ptr & ~0xFUL);
78 usb_debug("|:+-----------------------------------------------+:|\n");
79 usb_debug("|:| Depth/Breath [%lx] | QH/TD [%lx] | TERMINATE [%lx] |:|\n",
80 (td->ptr & (1UL << 2)) >> 2, (td->ptr & (1UL << 1)) >> 1, td->ptr & 1UL);
81 usb_debug("|:+-----------------------------------------------+:|\n");
82 usb_debug("|:| T | Maximum Length | [%04lx] |:|\n", (td->token & (0x7FFUL << 21)) >> 21);
83 usb_debug("|:| O | PID CODE | [%04lx] |:|\n", td->token & 0xFF);
84 usb_debug("|:| K | Endpoint | [%04lx] |:|\n", (td->token & TD_EP_MASK) >> TD_EP_SHIFT);
85 usb_debug("|:| E | Device Address | [%04lx] |:|\n", (td->token & (0x7FUL << 8)) >> 8);
86 usb_debug("|:| N | Data Toggle | [%lx] |:|\n", (td->token & (1UL << 19)) >> 19);
87 usb_debug("|:+-----------------------------------------------+:|\n");
88 usb_debug("|:| C | Short Packet Detector | [%lx] |:|\n", (td->ctrlsts & (1UL << 29)) >> 29);
89 usb_debug("|:| O | Error Counter | [%lx] |:|\n",
90 (td->ctrlsts & (3UL << TD_COUNTER_SHIFT)) >> TD_COUNTER_SHIFT);
91 usb_debug("|:| N | Low Speed Device | [%lx] |:|\n", (td->ctrlsts & (1UL << 26)) >> 26);
92 usb_debug("|:| T | Isochronous Select | [%lx] |:|\n", (td->ctrlsts & (1UL << 25)) >> 25);
93 usb_debug("|:| R | Interrupt on Complete (IOC) | [%lx] |:|\n", (td->ctrlsts & (1UL << 24)) >> 24);
94 usb_debug("|:+ O ----------------------------------------+:|\n");
95 usb_debug("|:| L | Active | [%lx] |:|\n", (td->ctrlsts & (1UL << 23)) >> 23);
96 usb_debug("|:| & | Stalled | [%lx] |:|\n", (td->ctrlsts & (1UL << 22)) >> 22);
97 usb_debug("|:| S | Data Buffer Error | [%lx] |:|\n", (td->ctrlsts & (1UL << 21)) >> 21);
98 usb_debug("|:| T | Bubble Detected | [%lx] |:|\n", (td->ctrlsts & (1UL << 20)) >> 20);
99 usb_debug("|:| A | NAK Received | [%lx] |:|\n", (td->ctrlsts & (1UL << 19)) >> 19);
100 usb_debug("|:| T | CRC/Timeout Error | [%lx] |:|\n", (td->ctrlsts & (1UL << 18)) >> 18);
101 usb_debug("|:| U | Bitstuff Error | [%lx] |:|\n", (td->ctrlsts & (1UL << 17)) >> 17);
102 usb_debug("|:| S ----------------------------------------|:|\n");
103 usb_debug("|:| | Actual Length | [%04lx] |:|\n", td->ctrlsts & 0x7FFUL);
104 usb_debug("|:+-----------------------------------------------+:|\n");
105 usb_debug("|:| Buffer pointer [0x%08lx] |:|\n", td->bufptr);
106 usb_debug("|:|-----------------------------------------------|:|\n");
107 usb_debug("|...................................................|\n");
108 usb_debug("+---------------------------------------------------+\n");
Patrick Georgid21f68b2008-09-02 16:06:22 +0000109}
110
111static void
112uhci_reset (hci_t *controller)
113{
114 /* reset */
Nico Huberbb1c42b2012-05-21 14:23:03 +0200115 uhci_reg_write16 (controller, USBCMD, 4); /* Global Reset */
116 mdelay (50); /* uhci spec 2.1.1: at least 10ms */
Patrick Georgid21f68b2008-09-02 16:06:22 +0000117 uhci_reg_write16 (controller, USBCMD, 0);
118 mdelay (10);
Nico Huberbb1c42b2012-05-21 14:23:03 +0200119 uhci_reg_write16 (controller, USBCMD, 2); /* Host Controller Reset */
120 /* wait for controller to finish reset */
121 /* TOTEST: how long to wait? 100ms for now */
122 int timeout = 200; /* time out after 200 * 500us == 100ms */
123 while (((uhci_reg_read16 (controller, USBCMD) & 2) != 0) && timeout--)
124 udelay (500);
125 if (timeout < 0)
Gabe Black93ded592012-11-01 15:44:10 -0700126 usb_debug ("Warning: uhci: host controller reset timed out.\n");
Nico Huber6e711c62012-11-12 16:20:32 +0100127}
Patrick Georgid21f68b2008-09-02 16:06:22 +0000128
Nico Huber6e711c62012-11-12 16:20:32 +0100129static void
130uhci_reinit (hci_t *controller)
131{
Patrick Georgid21f68b2008-09-02 16:06:22 +0000132 uhci_reg_write32 (controller, FLBASEADD,
133 (u32) virt_to_phys (UHCI_INST (controller)->
134 framelistptr));
Gabe Black93ded592012-11-01 15:44:10 -0700135 //usb_debug ("framelist at %p\n",UHCI_INST(controller)->framelistptr);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000136
137 /* disable irqs */
138 uhci_reg_write16 (controller, USBINTR, 0);
139
140 /* reset framelist index */
141 uhci_reg_write16 (controller, FRNUM, 0);
142
Patrick Georgif42fdab2011-11-18 14:44:16 +0100143 uhci_reg_write16(controller, USBCMD,
144 uhci_reg_read16(controller, USBCMD) | 0xc0); // max packets, configure flag
Patrick Georgid21f68b2008-09-02 16:06:22 +0000145
146 uhci_start (controller);
147}
148
149hci_t *
150uhci_init (pcidev_t addr)
151{
152 int i;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000153 u16 reg16;
154
Patrick Georgid21f68b2008-09-02 16:06:22 +0000155 hci_t *controller = new_controller ();
156
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000157 if (!controller)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100158 fatal("Could not create USB controller instance.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000159
Patrick Georgid21f68b2008-09-02 16:06:22 +0000160 controller->instance = malloc (sizeof (uhci_t));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000161 if(!controller->instance)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100162 fatal("Not enough memory creating USB controller instance.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000163
Anton Kochkov1c36ead2012-06-28 08:30:15 +0400164 controller->type = UHCI;
165
Patrick Georgid21f68b2008-09-02 16:06:22 +0000166 controller->start = uhci_start;
167 controller->stop = uhci_stop;
168 controller->reset = uhci_reset;
Nico Huber6e711c62012-11-12 16:20:32 +0100169 controller->init = uhci_reinit;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000170 controller->shutdown = uhci_shutdown;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000171 controller->bulk = uhci_bulk;
172 controller->control = uhci_control;
Patrick Georgi4727c072008-10-16 19:20:51 +0000173 controller->create_intr_queue = uhci_create_intr_queue;
174 controller->destroy_intr_queue = uhci_destroy_intr_queue;
175 controller->poll_intr_queue = uhci_poll_intr_queue;
Stefan Reinauer219cece2009-07-18 15:17:40 +0000176 for (i = 0; i < 128; i++) {
Patrick Georgi4727c072008-10-16 19:20:51 +0000177 controller->devices[i] = 0;
178 }
179 init_device_entry (controller, 0);
180 UHCI_INST (controller)->roothub = controller->devices[0];
Patrick Georgid21f68b2008-09-02 16:06:22 +0000181
182 controller->bus_address = addr;
183 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 */
184
185 /* kill legacy support handler */
186 uhci_stop (controller);
187 mdelay (1);
188 uhci_reg_write16 (controller, USBSTS, 0x3f);
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000189 reg16 = pci_read_config16(controller->bus_address, 0xc0);
190 reg16 &= 0xdf80;
191 pci_write_config16 (controller->bus_address, 0xc0, reg16);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000192
Anton Kochkovf6c80802012-09-20 10:24:01 +0200193 UHCI_INST (controller)->framelistptr = memalign (0x1000, 1024 * sizeof (flistp_t)); /* 4kb aligned to 4kb */
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000194 if (! UHCI_INST (controller)->framelistptr)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100195 fatal("Not enough memory for USB frame list pointer.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000196
Patrick Georgid21f68b2008-09-02 16:06:22 +0000197 memset (UHCI_INST (controller)->framelistptr, 0,
198 1024 * sizeof (flistp_t));
199
Patrick Georgi4727c072008-10-16 19:20:51 +0000200 /* According to the *BSD UHCI code, this one is needed on some
201 PIIX chips, because otherwise they misbehave. It must be
202 added to the last chain.
203
204 FIXME: this leaks, if the driver should ever be reinited
205 for some reason. Not a problem now.
206 */
207 td_t *antiberserk = memalign(16, sizeof(td_t));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000208 if (!antiberserk)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100209 fatal("Not enough memory for chipset workaround.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000210 memset(antiberserk, 0, sizeof(td_t));
211
212 UHCI_INST (controller)->qh_prei = memalign (16, sizeof (qh_t));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000213 UHCI_INST (controller)->qh_intr = memalign (16, sizeof (qh_t));
214 UHCI_INST (controller)->qh_data = memalign (16, sizeof (qh_t));
215 UHCI_INST (controller)->qh_last = memalign (16, sizeof (qh_t));
216
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000217 if (! UHCI_INST (controller)->qh_prei ||
218 ! UHCI_INST (controller)->qh_intr ||
219 ! UHCI_INST (controller)->qh_data ||
220 ! UHCI_INST (controller)->qh_last)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100221 fatal("Not enough memory for USB controller queues.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000222
Patrick Georgib0b4a522011-11-24 11:55:46 +0100223 UHCI_INST (controller)->qh_prei->headlinkptr =
224 virt_to_phys (UHCI_INST (controller)->qh_intr) | FLISTP_QH;
225 UHCI_INST (controller)->qh_prei->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000226
Patrick Georgib0b4a522011-11-24 11:55:46 +0100227 UHCI_INST (controller)->qh_intr->headlinkptr =
228 virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_QH;
229 UHCI_INST (controller)->qh_intr->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000230
Patrick Georgib0b4a522011-11-24 11:55:46 +0100231 UHCI_INST (controller)->qh_data->headlinkptr =
232 virt_to_phys (UHCI_INST (controller)->qh_last) | FLISTP_QH;
233 UHCI_INST (controller)->qh_data->elementlinkptr = 0 | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000234
Patrick Georgib0b4a522011-11-24 11:55:46 +0100235 UHCI_INST (controller)->qh_last->headlinkptr = virt_to_phys (UHCI_INST (controller)->qh_data) | FLISTP_TERMINATE;
236 UHCI_INST (controller)->qh_last->elementlinkptr = virt_to_phys (antiberserk) | FLISTP_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000237
238 for (i = 0; i < 1024; i++) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100239 UHCI_INST (controller)->framelistptr[i] =
240 virt_to_phys (UHCI_INST (controller)->qh_prei) | FLISTP_QH;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000241 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000242 controller->devices[0]->controller = controller;
243 controller->devices[0]->init = uhci_rh_init;
244 controller->devices[0]->init (controller->devices[0]);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000245 uhci_reset (controller);
Nico Huber6e711c62012-11-12 16:20:32 +0100246 uhci_reinit (controller);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000247 return controller;
248}
249
250static void
251uhci_shutdown (hci_t *controller)
252{
253 if (controller == 0)
254 return;
255 detach_controller (controller);
256 UHCI_INST (controller)->roothub->destroy (UHCI_INST (controller)->
257 roothub);
Patrick Georgif42fdab2011-11-18 14:44:16 +0100258 uhci_reg_write16(controller, USBCMD,
259 uhci_reg_read16(controller, USBCMD) & 0); // stop work
Patrick Georgid21f68b2008-09-02 16:06:22 +0000260 free (UHCI_INST (controller)->framelistptr);
Patrick Georgi4727c072008-10-16 19:20:51 +0000261 free (UHCI_INST (controller)->qh_prei);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000262 free (UHCI_INST (controller)->qh_intr);
263 free (UHCI_INST (controller)->qh_data);
264 free (UHCI_INST (controller)->qh_last);
265 free (UHCI_INST (controller));
266 free (controller);
267}
268
269static void
270uhci_start (hci_t *controller)
271{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100272 uhci_reg_write16(controller, USBCMD,
273 uhci_reg_read16(controller, USBCMD) | 1); // start work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000274}
275
276static void
277uhci_stop (hci_t *controller)
278{
Patrick Georgif42fdab2011-11-18 14:44:16 +0100279 uhci_reg_write16(controller, USBCMD,
280 uhci_reg_read16(controller, USBCMD) & ~1); // stop work on schedule
Patrick Georgid21f68b2008-09-02 16:06:22 +0000281}
282
283#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))
284
285static td_t *
286wait_for_completed_qh (hci_t *controller, qh_t *qh)
287{
Mathias Krause7b7b5662012-05-29 16:19:19 +0200288 int timeout = 1000; /* max 30 ms. */
Patrick Georgib0b4a522011-11-24 11:55:46 +0100289 void *current = GET_TD (qh->elementlinkptr);
290 while (((qh->elementlinkptr & FLISTP_TERMINATE) == 0) && (timeout-- > 0)) {
291 if (current != GET_TD (qh->elementlinkptr)) {
292 current = GET_TD (qh->elementlinkptr);
Mathias Krause7b7b5662012-05-29 16:19:19 +0200293 timeout = 1000;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000294 }
Patrick Georgif42fdab2011-11-18 14:44:16 +0100295 uhci_reg_write16(controller, USBSTS,
296 uhci_reg_read16(controller, USBSTS) | 0); // clear resettable registers
Patrick Georgid21f68b2008-09-02 16:06:22 +0000297 udelay (30);
298 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100299 return (GET_TD (qh->elementlinkptr) ==
300 0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr));
Patrick Georgid21f68b2008-09-02 16:06:22 +0000301}
302
Patrick Georgid21f68b2008-09-02 16:06:22 +0000303static int
304maxlen (int size)
305{
306 return (size - 1) & 0x7ff;
307}
308
309static int
310min (int a, int b)
311{
312 if (a < b)
313 return a;
314 else
315 return b;
316}
317
318static int
Patrick Georgid78691d2010-06-07 13:58:17 +0000319uhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
Patrick Georgid21f68b2008-09-02 16:06:22 +0000320 unsigned char *data)
321{
322 int endp = 0; /* this is control: always 0 */
323 int mlen = dev->endpoints[0].maxpacketsize;
324 int count = (2 + (dalen + mlen - 1) / mlen);
325 unsigned short req = ((unsigned short *) devreq)[0];
326 int i;
327 td_t *tds = memalign (16, sizeof (td_t) * count);
328 memset (tds, 0, sizeof (td_t) * count);
329 count--; /* to compensate for 0-indexed array */
330 for (i = 0; i < count; i++) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100331 tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000332 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100333 tds[count].ptr = 0 | TD_DEPTH_FIRST | TD_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000334
Patrick Georgib0b4a522011-11-24 11:55:46 +0100335 tds[0].token = UHCI_SETUP |
336 dev->address << TD_DEVADDR_SHIFT |
337 endp << TD_EP_SHIFT |
338 TD_TOGGLE_DATA0 |
339 maxlen(drlen) << TD_MAXLEN_SHIFT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000340 tds[0].bufptr = virt_to_phys (devreq);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100341 tds[0].ctrlsts = (3 << TD_COUNTER_SHIFT) |
342 (dev->speed?TD_LOWSPEED:0) |
343 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000344
345 int toggle = 1;
346 for (i = 1; i < count; i++) {
Patrick Georgid78691d2010-06-07 13:58:17 +0000347 switch (dir) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100348 case SETUP: tds[i].token = UHCI_SETUP; break;
349 case IN: tds[i].token = UHCI_IN; break;
350 case OUT: tds[i].token = UHCI_OUT; break;
Patrick Georgid78691d2010-06-07 13:58:17 +0000351 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100352 tds[i].token |= dev->address << TD_DEVADDR_SHIFT |
353 endp << TD_EP_SHIFT |
354 maxlen (min (mlen, dalen)) << TD_MAXLEN_SHIFT |
355 toggle << TD_TOGGLE_SHIFT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000356 tds[i].bufptr = virt_to_phys (data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100357 tds[i].ctrlsts = (3 << TD_COUNTER_SHIFT) |
358 (dev->speed?TD_LOWSPEED:0) |
359 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000360 toggle ^= 1;
361 dalen -= mlen;
362 data += mlen;
363 }
364
Nico Hubercefec0e2012-05-16 15:04:27 +0200365 tds[count].token = ((dir == OUT) ? UHCI_IN : UHCI_OUT) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100366 dev->address << TD_DEVADDR_SHIFT |
367 endp << TD_EP_SHIFT |
368 maxlen(0) << TD_MAXLEN_SHIFT |
369 TD_TOGGLE_DATA1;
Stefan Reinauerb56f2d02010-03-25 22:17:36 +0000370 tds[count].bufptr = 0;
Nico Hubercefec0e2012-05-16 15:04:27 +0200371 tds[count].ctrlsts = (0 << TD_COUNTER_SHIFT) | /* as Linux 2.4.10 does */
Patrick Georgib0b4a522011-11-24 11:55:46 +0100372 (dev->speed?TD_LOWSPEED:0) |
373 TD_STATUS_ACTIVE;
374 UHCI_INST (dev->controller)->qh_data->elementlinkptr =
375 virt_to_phys (tds) & ~(FLISTP_QH | FLISTP_TERMINATE);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000376 td_t *td = wait_for_completed_qh (dev->controller,
377 UHCI_INST (dev->controller)->
378 qh_data);
379 int result;
380 if (td == 0) {
381 result = 0;
382 } else {
Gabe Black93ded592012-11-01 15:44:10 -0700383 usb_debug ("control packet, req %x\n", req);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000384 td_dump (td);
385 result = 1;
386 }
387 free (tds);
388 return result;
389}
390
Patrick Georgid21f68b2008-09-02 16:06:22 +0000391static td_t *
392create_schedule (int numpackets)
393{
394 if (numpackets == 0)
395 return 0;
396 td_t *tds = memalign (16, sizeof (td_t) * numpackets);
397 memset (tds, 0, sizeof (td_t) * numpackets);
398 int i;
399 for (i = 0; i < numpackets; i++) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100400 tds[i].ptr = virt_to_phys (&tds[i + 1]) | TD_DEPTH_FIRST;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000401 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100402 tds[numpackets - 1].ptr = 0 | TD_TERMINATE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000403 return tds;
404}
405
406static void
407fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data,
408 int *toggle)
409{
Patrick Georgicfaa0812010-06-11 14:25:40 +0000410 switch (ep->direction) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100411 case IN: td->token = UHCI_IN; break;
412 case OUT: td->token = UHCI_OUT; break;
413 case SETUP: td->token = UHCI_SETUP; break;
Patrick Georgicfaa0812010-06-11 14:25:40 +0000414 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100415 td->token |= ep->dev->address << TD_DEVADDR_SHIFT |
416 (ep->endpoint & 0xf) << TD_EP_SHIFT |
417 maxlen (length) << TD_MAXLEN_SHIFT |
418 (*toggle & 1) << TD_TOGGLE_SHIFT;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000419 td->bufptr = virt_to_phys (data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100420 td->ctrlsts = ((ep->direction == SETUP?3:0) << TD_COUNTER_SHIFT) |
Nico Hubercefec0e2012-05-16 15:04:27 +0200421 (ep->dev->speed?TD_LOWSPEED:0) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100422 TD_STATUS_ACTIVE;
Patrick Georgid21f68b2008-09-02 16:06:22 +0000423 *toggle ^= 1;
424}
425
426static int
427run_schedule (usbdev_t *dev, td_t *td)
428{
Patrick Georgib0b4a522011-11-24 11:55:46 +0100429 UHCI_INST (dev->controller)->qh_data->elementlinkptr =
Anton Kochkovefcb8de2012-10-02 00:04:29 +0400430 virt_to_phys (td) & ~(FLISTP_QH | FLISTP_TERMINATE);
Patrick Georgid21f68b2008-09-02 16:06:22 +0000431 td = wait_for_completed_qh (dev->controller,
432 UHCI_INST (dev->controller)->qh_data);
433 if (td == 0) {
434 return 0;
435 } else {
436 td_dump (td);
437 return 1;
438 }
439}
440
441/* finalize == 1: if data is of packet aligned size, add a zero length packet */
442static int
443uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
444{
445 int maxpsize = ep->maxpacketsize;
446 if (maxpsize == 0)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100447 fatal("MaxPacketSize == 0!!!");
Patrick Georgid2cb7ea2012-10-03 08:23:56 +0200448 int numpackets = (size + maxpsize - 1) / maxpsize;
449 if (finalize && ((size % maxpsize) == 0)) {
450 numpackets++;
451 }
Patrick Georgid21f68b2008-09-02 16:06:22 +0000452 if (numpackets == 0)
453 return 0;
454 td_t *tds = create_schedule (numpackets);
455 int i = 0, toggle = ep->toggle;
456 while ((size > 0) || ((size == 0) && (finalize != 0))) {
457 fill_schedule (&tds[i], ep, min (size, maxpsize), data,
458 &toggle);
459 i++;
460 data += maxpsize;
461 size -= maxpsize;
462 }
463 if (run_schedule (ep->dev, tds) == 1) {
Gabe Black93ded592012-11-01 15:44:10 -0700464 usb_debug("Stalled. Trying to clean up.\n");
Patrick Georgid21f68b2008-09-02 16:06:22 +0000465 clear_stall (ep);
466 free (tds);
467 return 1;
468 }
469 ep->toggle = toggle;
470 free (tds);
471 return 0;
472}
473
Patrick Georgi4727c072008-10-16 19:20:51 +0000474typedef struct {
475 qh_t *qh;
476 td_t *tds;
477 td_t *last_td;
478 u8 *data;
479 int lastread;
480 int total;
481 int reqsize;
482} intr_q;
483
484/* create and hook-up an intr queue into device schedule */
485static void*
486uhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
487{
488 u8 *data = malloc(reqsize*reqcount);
489 td_t *tds = memalign(16, sizeof(td_t) * reqcount);
490 qh_t *qh = memalign(16, sizeof(qh_t));
491
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000492 if (!data || !tds || !qh)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100493 fatal("Not enough memory to create USB intr queue prerequisites.\n");
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000494
Patrick Georgib0b4a522011-11-24 11:55:46 +0100495 qh->elementlinkptr = virt_to_phys(tds);
Patrick Georgi4727c072008-10-16 19:20:51 +0000496
497 intr_q *q = malloc(sizeof(intr_q));
Stefan Reinauer5fe6e232009-07-31 11:39:55 +0000498 if (!q)
Patrick Georgi2e768e72011-11-04 11:50:03 +0100499 fatal("Not enough memory to create USB intr queue.\n");
Patrick Georgi4727c072008-10-16 19:20:51 +0000500 q->qh = qh;
501 q->tds = tds;
502 q->data = data;
503 q->lastread = 0;
504 q->total = reqcount;
505 q->reqsize = reqsize;
506 q->last_td = &tds[reqcount - 1];
507
508 memset (tds, 0, sizeof (td_t) * reqcount);
509 int i;
510 for (i = 0; i < reqcount; i++) {
511 tds[i].ptr = virt_to_phys (&tds[i + 1]);
Patrick Georgi4727c072008-10-16 19:20:51 +0000512
Patrick Georgicfaa0812010-06-11 14:25:40 +0000513 switch (ep->direction) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100514 case IN: tds[i].token = UHCI_IN; break;
515 case OUT: tds[i].token = UHCI_OUT; break;
516 case SETUP: tds[i].token = UHCI_SETUP; break;
Patrick Georgicfaa0812010-06-11 14:25:40 +0000517 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100518 tds[i].token |= ep->dev->address << TD_DEVADDR_SHIFT |
519 (ep->endpoint & 0xf) << TD_EP_SHIFT |
520 maxlen (reqsize) << TD_MAXLEN_SHIFT |
521 (ep->toggle & 1) << TD_TOGGLE_SHIFT;
Patrick Georgi4727c072008-10-16 19:20:51 +0000522 tds[i].bufptr = virt_to_phys (data);
Patrick Georgib0b4a522011-11-24 11:55:46 +0100523 tds[i].ctrlsts = (0 << TD_COUNTER_SHIFT) |
Nico Hubercefec0e2012-05-16 15:04:27 +0200524 (ep->dev->speed?TD_LOWSPEED:0) |
Patrick Georgib0b4a522011-11-24 11:55:46 +0100525 TD_STATUS_ACTIVE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000526 ep->toggle ^= 1;
527 data += reqsize;
528 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100529 tds[reqcount - 1].ptr = 0 | TD_TERMINATE;
Nico Huberce407e42012-11-20 17:27:46 +0100530
531 /* insert QH into framelist */
532 uhci_t *const uhcic = UHCI_INST(ep->dev->controller);
533 const u32 def_ptr = virt_to_phys(uhcic->qh_prei) | FLISTP_QH;
534 int nothing_placed = 1;
535 qh->headlinkptr = def_ptr;
536 for (i = 0; i < 1024; i += reqtiming) {
537 /* advance to the next free position */
538 while ((i < 1024) && (uhcic->framelistptr[i] != def_ptr)) ++i;
539 if (i < 1024) {
540 uhcic->framelistptr[i] = virt_to_phys(qh) | FLISTP_QH;
541 nothing_placed = 0;
542 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000543 }
Nico Huberce407e42012-11-20 17:27:46 +0100544 if (nothing_placed) {
Dave Frodin6bf11cf2012-12-11 13:08:07 -0700545 usb_debug("Error: Failed to place UHCI interrupt queue "
Nico Huberce407e42012-11-20 17:27:46 +0100546 "head into framelist: no space left\n");
547 uhci_destroy_intr_queue(ep, q);
548 return NULL;
549 }
550
Patrick Georgi4727c072008-10-16 19:20:51 +0000551 return q;
552}
553
554/* remove queue from device schedule, dropping all data that came in */
555static void
556uhci_destroy_intr_queue (endpoint_t *ep, void *q_)
557{
Nico Huberce407e42012-11-20 17:27:46 +0100558 intr_q *const q = (intr_q*)q_;
559
560 /* remove QH from framelist */
561 uhci_t *const uhcic = UHCI_INST(ep->dev->controller);
562 const u32 qh_ptr = virt_to_phys(q->qh) | FLISTP_QH;
563 const u32 def_ptr = virt_to_phys(uhcic->qh_prei) | FLISTP_QH;
Patrick Georgi4727c072008-10-16 19:20:51 +0000564 int i;
Nico Huberce407e42012-11-20 17:27:46 +0100565 for (i = 0; i < 1024; ++i) {
566 if (uhcic->framelistptr[i] == qh_ptr)
567 uhcic->framelistptr[i] = def_ptr;
Patrick Georgi4727c072008-10-16 19:20:51 +0000568 }
Nico Huberce407e42012-11-20 17:27:46 +0100569
Patrick Georgi4727c072008-10-16 19:20:51 +0000570 free(q->data);
571 free(q->tds);
572 free(q->qh);
573 free(q);
574}
575
576/* read one intr-packet from queue, if available. extend the queue for new input.
577 return NULL if nothing new available.
578 Recommended use: while (data=poll_intr_queue(q)) process(data);
579 */
580static u8*
581uhci_poll_intr_queue (void *q_)
582{
583 intr_q *q = (intr_q*)q_;
Patrick Georgib0b4a522011-11-24 11:55:46 +0100584 if ((q->tds[q->lastread].ctrlsts & TD_STATUS_ACTIVE) == 0) {
Patrick Georgi4727c072008-10-16 19:20:51 +0000585 int current = q->lastread;
586 int previous;
587 if (q->lastread == 0) {
588 previous = q->total - 1;
589 } else {
590 previous = q->lastread - 1;
591 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100592 q->tds[previous].ctrlsts &= ~TD_STATUS_MASK;
593 q->tds[previous].ptr = 0 | TD_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000594 if (q->last_td != &q->tds[previous]) {
Patrick Georgib0b4a522011-11-24 11:55:46 +0100595 q->last_td->ptr = virt_to_phys(&q->tds[previous]) & ~TD_TERMINATE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000596 q->last_td = &q->tds[previous];
597 }
Patrick Georgib0b4a522011-11-24 11:55:46 +0100598 q->tds[previous].ctrlsts |= TD_STATUS_ACTIVE;
Patrick Georgi4727c072008-10-16 19:20:51 +0000599 q->lastread = (q->lastread + 1) % q->total;
Nico Huberbe58fee2012-11-22 11:12:13 +0100600 if (!(q->tds[current].ctrlsts & TD_STATUS_MASK))
601 return &q->data[current*q->reqsize];
Patrick Georgi4727c072008-10-16 19:20:51 +0000602 }
Nico Huber8c4d2f32012-11-20 17:49:00 +0100603 /* reset queue if we fully processed it after underrun */
604 else if (q->qh->elementlinkptr & FLISTP_TERMINATE) {
605 usb_debug("resetting underrun uhci interrupt queue.\n");
606 q->qh->elementlinkptr = virt_to_phys(q->tds + q->lastread);
607 }
Patrick Georgi4727c072008-10-16 19:20:51 +0000608 return NULL;
609}
610
Patrick Georgid21f68b2008-09-02 16:06:22 +0000611void
612uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value)
613{
614 outl (value, ctrl->reg_base + reg);
615}
616
617u32
618uhci_reg_read32 (hci_t *ctrl, usbreg reg)
619{
620 return inl (ctrl->reg_base + reg);
621}
622
623void
624uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value)
625{
626 outw (value, ctrl->reg_base + reg);
627}
628
629u16
630uhci_reg_read16 (hci_t *ctrl, usbreg reg)
631{
632 return inw (ctrl->reg_base + reg);
633}
634
635void
636uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value)
637{
638 outb (value, ctrl->reg_base + reg);
639}
640
641u8
642uhci_reg_read8 (hci_t *ctrl, usbreg reg)
643{
644 return inb (ctrl->reg_base + reg);
645}