blob: 2321e21de0b768e5cfa82ad4863d4414d0d69184 [file] [log] [blame]
Kevin O'Connor114592f2009-09-28 21:32:08 -04001// Code for handling UHCI USB controllers.
2//
3// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
4//
5// This file may be distributed under the terms of the GNU LGPLv3 license.
6
Kevin O'Connor2d2fa312013-09-14 21:55:26 -04007#include "biosvar.h" // GET_LOWFLAT
Kevin O'Connor114592f2009-09-28 21:32:08 -04008#include "config.h" // CONFIG_*
Kevin O'Connor9dea5902013-09-14 20:23:54 -04009#include "malloc.h" // free
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "output.h" // dprintf
11#include "pci.h" // pci_bdf_to_bus
Kevin O'Connorec443ff2013-12-05 18:43:20 -050012#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040013#include "pci_regs.h" // PCI_BASE_ADDRESS_4
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040014#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040015#include "usb.h" // struct usb_s
16#include "usb-uhci.h" // USBLEGSUP
17#include "util.h" // msleep
Kevin O'Connor4ade5232013-09-18 21:41:48 -040018#include "x86.h" // outw
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050019
Kevin O'Connor406fad62010-02-28 02:23:19 -050020struct usb_uhci_s {
21 struct usb_s usb;
22 u16 iobase;
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -050023 struct uhci_qh *control_qh;
Kevin O'Connor406fad62010-02-28 02:23:19 -050024 struct uhci_framelist *framelist;
25};
26
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -050027struct uhci_pipe {
28 struct uhci_qh qh;
29 struct uhci_td *next_td;
30 struct usb_pipe pipe;
31 u16 iobase;
32 u8 toggle;
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -050033};
34
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050035
36/****************************************************************
37 * Root hub
38 ****************************************************************/
39
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040040// Check if device attached to a given port
41static int
42uhci_hub_detect(struct usbhub_s *hub, u32 port)
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050043{
Kevin O'Connor406fad62010-02-28 02:23:19 -050044 struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb);
45 u16 ioport = cntl->iobase + USBPORTSC1 + port * 2;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050046
47 u16 status = inw(ioport);
48 if (!(status & USBPORTSC_CCS))
49 // No device
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040050 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050051
52 // XXX - if just powered up, need to wait for USB_TIME_ATTDB?
53
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040054 // Begin reset on port
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050055 outw(USBPORTSC_PR, ioport);
56 msleep(USB_TIME_DRSTR);
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040057 return 0;
58}
59
60// Reset device on port
61static int
62uhci_hub_reset(struct usbhub_s *hub, u32 port)
63{
64 struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb);
65 u16 ioport = cntl->iobase + USBPORTSC1 + port * 2;
66
67 // Finish reset on port
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050068 outw(0, ioport);
69 udelay(6); // 64 high-speed bit times
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040070 u16 status = inw(ioport);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050071 if (!(status & USBPORTSC_CCS))
72 // No longer connected
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040073 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050074 outw(USBPORTSC_PE, ioport);
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040075 return !!(status & USBPORTSC_LSDA);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050076}
77
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040078// Disable port
79static void
80uhci_hub_disconnect(struct usbhub_s *hub, u32 port)
81{
82 struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb);
83 u16 ioport = cntl->iobase + USBPORTSC1 + port * 2;
84 outw(0, ioport);
85}
86
87static struct usbhub_op_s uhci_HubOp = {
88 .detect = uhci_hub_detect,
89 .reset = uhci_hub_reset,
90 .disconnect = uhci_hub_disconnect,
91};
92
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050093// Find any devices connected to the root hub.
94static int
Kevin O'Connor406fad62010-02-28 02:23:19 -050095check_uhci_ports(struct usb_uhci_s *cntl)
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050096{
97 ASSERT32FLAT();
98 struct usbhub_s hub;
99 memset(&hub, 0, sizeof(hub));
Kevin O'Connor406fad62010-02-28 02:23:19 -0500100 hub.cntl = &cntl->usb;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400101 hub.portcount = 2;
102 hub.op = &uhci_HubOp;
103 usb_enumerate(&hub);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500104 return hub.devcount;
105}
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500106
107
108/****************************************************************
109 * Setup
110 ****************************************************************/
Kevin O'Connor114592f2009-09-28 21:32:08 -0400111
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500112// Wait for next USB frame to start - for ensuring safe memory release.
113static void
114uhci_waittick(u16 iobase)
115{
116 barrier();
117 u16 startframe = inw(iobase + USBFRNUM);
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400118 u32 end = timer_calc(1000 * 5);
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500119 for (;;) {
120 if (inw(iobase + USBFRNUM) != startframe)
121 break;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400122 if (timer_check(end)) {
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500123 warn_timeout();
124 return;
125 }
126 yield();
127 }
128}
129
130static void
131uhci_free_pipes(struct usb_uhci_s *cntl)
132{
133 dprintf(7, "uhci_free_pipes %p\n", cntl);
134
135 struct uhci_qh *pos = (void*)(cntl->framelist->links[0] & ~UHCI_PTR_BITS);
136 for (;;) {
137 u32 link = pos->link;
138 if (link == UHCI_PTR_TERM)
139 break;
140 struct uhci_qh *next = (void*)(link & ~UHCI_PTR_BITS);
141 struct uhci_pipe *pipe = container_of(next, struct uhci_pipe, qh);
142 if (pipe->pipe.cntl != &cntl->usb)
143 pos->link = next->link;
144 else
145 pos = next;
146 }
147 uhci_waittick(cntl->iobase);
148 for (;;) {
149 struct usb_pipe *usbpipe = cntl->usb.freelist;
150 if (!usbpipe)
151 break;
152 cntl->usb.freelist = usbpipe->freenext;
153 struct uhci_pipe *pipe = container_of(usbpipe, struct uhci_pipe, pipe);
154 free(pipe);
155 }
156}
157
Kevin O'Connor114592f2009-09-28 21:32:08 -0400158static void
Kevin O'Connor406fad62010-02-28 02:23:19 -0500159reset_uhci(struct usb_uhci_s *cntl, u16 bdf)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400160{
161 // XXX - don't reset if not needed.
162
163 // Reset PIRQ and SMI
Kevin O'Connor406fad62010-02-28 02:23:19 -0500164 pci_config_writew(bdf, USBLEGSUP, USBLEGSUP_RWC);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400165
166 // Reset the HC
Kevin O'Connor406fad62010-02-28 02:23:19 -0500167 outw(USBCMD_HCRESET, cntl->iobase + USBCMD);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400168 udelay(5);
169
170 // Disable interrupts and commands (just to be safe).
Kevin O'Connor406fad62010-02-28 02:23:19 -0500171 outw(0, cntl->iobase + USBINTR);
172 outw(0, cntl->iobase + USBCMD);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400173}
174
175static void
Kevin O'Connor406fad62010-02-28 02:23:19 -0500176configure_uhci(void *data)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400177{
Kevin O'Connor406fad62010-02-28 02:23:19 -0500178 struct usb_uhci_s *cntl = data;
179
Kevin O'Connor114592f2009-09-28 21:32:08 -0400180 // Allocate ram for schedule storage
181 struct uhci_td *term_td = malloc_high(sizeof(*term_td));
182 struct uhci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl));
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500183 struct uhci_pipe *intr_pipe = malloc_high(sizeof(*intr_pipe));
184 struct uhci_pipe *term_pipe = malloc_high(sizeof(*term_pipe));
185 if (!term_td || !fl || !intr_pipe || !term_pipe) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500186 warn_noalloc();
Kevin O'Connor406fad62010-02-28 02:23:19 -0500187 goto fail;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400188 }
189
190 // Work around for PIIX errata
191 memset(term_td, 0, sizeof(*term_td));
192 term_td->link = UHCI_PTR_TERM;
193 term_td->token = (uhci_explen(0) | (0x7f << TD_TOKEN_DEVADDR_SHIFT)
194 | USB_PID_IN);
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500195 memset(term_pipe, 0, sizeof(*term_pipe));
196 term_pipe->qh.element = (u32)term_td;
197 term_pipe->qh.link = UHCI_PTR_TERM;
198 term_pipe->pipe.cntl = &cntl->usb;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400199
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500200 // Set schedule to point to primary intr queue head
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500201 memset(intr_pipe, 0, sizeof(*intr_pipe));
202 intr_pipe->qh.element = UHCI_PTR_TERM;
203 intr_pipe->qh.link = (u32)&term_pipe->qh | UHCI_PTR_QH;
204 intr_pipe->pipe.cntl = &cntl->usb;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400205 int i;
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500206 for (i=0; i<ARRAY_SIZE(fl->links); i++)
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500207 fl->links[i] = (u32)&intr_pipe->qh | UHCI_PTR_QH;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500208 cntl->framelist = fl;
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500209 cntl->control_qh = &intr_pipe->qh;
Kevin O'Connor95714392010-02-17 22:59:53 -0500210 barrier();
Kevin O'Connor114592f2009-09-28 21:32:08 -0400211
212 // Set the frame length to the default: 1 ms exactly
Kevin O'Connor406fad62010-02-28 02:23:19 -0500213 outb(USBSOF_DEFAULT, cntl->iobase + USBSOF);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400214
215 // Store the frame list base address
Kevin O'Connor406fad62010-02-28 02:23:19 -0500216 outl((u32)fl->links, cntl->iobase + USBFLBASEADD);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400217
218 // Set the current frame number
Kevin O'Connor406fad62010-02-28 02:23:19 -0500219 outw(0, cntl->iobase + USBFRNUM);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400220
Kevin O'Connor114592f2009-09-28 21:32:08 -0400221 // Mark as configured and running with a 64-byte max packet.
Kevin O'Connor406fad62010-02-28 02:23:19 -0500222 outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, cntl->iobase + USBCMD);
223
224 // Find devices
225 int count = check_uhci_ports(cntl);
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500226 uhci_free_pipes(cntl);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500227 if (count)
228 // Success
229 return;
230
231 // No devices found - shutdown and free controller.
232 outw(0, cntl->iobase + USBCMD);
233fail:
234 free(term_td);
235 free(fl);
Kevin O'Connor1e4dc8a2012-02-20 12:54:49 -0500236 free(intr_pipe);
237 free(term_pipe);
Kevin O'Connor0770d672010-03-09 19:33:22 -0500238 free(cntl);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400239}
240
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500241static void
242uhci_controller_setup(struct pci_device *pci)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400243{
Kevin O'Connor8ff8e012011-07-09 14:11:21 -0400244 u16 bdf = pci->bdf;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500245 struct usb_uhci_s *cntl = malloc_tmphigh(sizeof(*cntl));
Kevin O'Connorffdcd3a2011-07-10 15:48:00 -0400246 if (!cntl) {
247 warn_noalloc();
248 return;
249 }
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500250 wait_preempt(); // Avoid pci_config_readl when preempting
Kevin O'Connor406fad62010-02-28 02:23:19 -0500251 memset(cntl, 0, sizeof(*cntl));
Kevin O'Connor8ff8e012011-07-09 14:11:21 -0400252 cntl->usb.pci = pci;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500253 cntl->usb.type = USB_TYPE_UHCI;
254 cntl->iobase = (pci_config_readl(bdf, PCI_BASE_ADDRESS_4)
255 & PCI_BASE_ADDRESS_IO_MASK);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400256
Kevin O'Connor9dc243e2010-03-20 17:53:03 -0400257 dprintf(1, "UHCI init on dev %02x:%02x.%x (io=%x)\n"
Kevin O'Connor406fad62010-02-28 02:23:19 -0500258 , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)
259 , pci_bdf_to_fn(bdf), cntl->iobase);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400260
Kevin O'Connor406fad62010-02-28 02:23:19 -0500261 pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400262
Kevin O'Connor406fad62010-02-28 02:23:19 -0500263 reset_uhci(cntl, bdf);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400264
Kevin O'Connor406fad62010-02-28 02:23:19 -0500265 run_thread(configure_uhci, cntl);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400266}
267
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500268void
269uhci_setup(void)
270{
271 if (! CONFIG_USB_UHCI)
272 return;
273 struct pci_device *pci;
274 foreachpci(pci) {
275 if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI)
276 uhci_controller_setup(pci);
277 }
278}
279
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500280
281/****************************************************************
282 * End point communication
283 ****************************************************************/
284
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500285static struct usb_pipe *
Kevin O'Connoreb50eae2012-03-10 08:48:31 -0500286uhci_alloc_intr_pipe(struct usbdevice_s *usbdev
287 , struct usb_endpoint_descriptor *epdesc)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400288{
Kevin O'Connor406fad62010-02-28 02:23:19 -0500289 struct usb_uhci_s *cntl = container_of(
Kevin O'Connoreb50eae2012-03-10 08:48:31 -0500290 usbdev->hub->cntl, struct usb_uhci_s, usb);
291 int frameexp = usb_getFrameExp(usbdev, epdesc);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500292 dprintf(7, "uhci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400293
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500294 if (frameexp > 10)
295 frameexp = 10;
Kevin O'Connoreb50eae2012-03-10 08:48:31 -0500296 int maxpacket = epdesc->wMaxPacketSize;
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500297 // Determine number of entries needed for 2 timer ticks.
298 int ms = 1<<frameexp;
Kevin O'Connor69013372013-07-20 12:08:48 -0400299 int count = DIV_ROUND_UP(ticks_to_ms(2), ms);
Kevin O'Connor0770d672010-03-09 19:33:22 -0500300 count = ALIGN(count, 2);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500301 struct uhci_pipe *pipe = malloc_low(sizeof(*pipe));
Kevin O'Connor114592f2009-09-28 21:32:08 -0400302 struct uhci_td *tds = malloc_low(sizeof(*tds) * count);
Kevin O'Connor09e2f7c2010-02-27 23:50:48 -0500303 void *data = malloc_low(maxpacket * count);
304 if (!pipe || !tds || !data) {
Kevin O'Connor95714392010-02-17 22:59:53 -0500305 warn_noalloc();
306 goto fail;
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500307 }
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500308 memset(pipe, 0, sizeof(*pipe));
Kevin O'Connoreb50eae2012-03-10 08:48:31 -0500309 usb_desc2pipe(&pipe->pipe, usbdev, epdesc);
310 int lowspeed = pipe->pipe.speed;
311 int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500312 pipe->qh.element = (u32)tds;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500313 pipe->next_td = &tds[0];
Kevin O'Connor406fad62010-02-28 02:23:19 -0500314 pipe->iobase = cntl->iobase;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500315
Kevin O'Connor114592f2009-09-28 21:32:08 -0400316 int toggle = 0;
317 int i;
318 for (i=0; i<count; i++) {
319 tds[i].link = (i==count-1 ? (u32)&tds[0] : (u32)&tds[i+1]);
320 tds[i].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0)
321 | TD_CTRL_ACTIVE);
322 tds[i].token = (uhci_explen(maxpacket) | toggle
323 | (devaddr << TD_TOKEN_DEVADDR_SHIFT)
324 | USB_PID_IN);
Kevin O'Connor09e2f7c2010-02-27 23:50:48 -0500325 tds[i].buffer = data + maxpacket * i;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400326 toggle ^= TD_TOKEN_TOGGLE;
327 }
328
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500329 // Add to interrupt schedule.
Kevin O'Connor406fad62010-02-28 02:23:19 -0500330 struct uhci_framelist *fl = cntl->framelist;
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500331 if (frameexp == 0) {
332 // Add to existing interrupt entry.
333 struct uhci_qh *intr_qh = (void*)(fl->links[0] & ~UHCI_PTR_BITS);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500334 pipe->qh.link = intr_qh->link;
Kevin O'Connor95714392010-02-17 22:59:53 -0500335 barrier();
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500336 intr_qh->link = (u32)&pipe->qh | UHCI_PTR_QH;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500337 if (cntl->control_qh == intr_qh)
338 cntl->control_qh = &pipe->qh;
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500339 } else {
340 int startpos = 1<<(frameexp-1);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500341 pipe->qh.link = fl->links[startpos];
Kevin O'Connor95714392010-02-17 22:59:53 -0500342 barrier();
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500343 for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500344 fl->links[i] = (u32)&pipe->qh | UHCI_PTR_QH;
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500345 }
Kevin O'Connor114592f2009-09-28 21:32:08 -0400346
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500347 return &pipe->pipe;
Kevin O'Connor95714392010-02-17 22:59:53 -0500348fail:
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500349 free(pipe);
Kevin O'Connor95714392010-02-17 22:59:53 -0500350 free(tds);
Kevin O'Connor09e2f7c2010-02-27 23:50:48 -0500351 free(data);
Kevin O'Connor95714392010-02-17 22:59:53 -0500352 return NULL;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400353}
354
Kevin O'Connor6152c322012-03-10 08:53:58 -0500355struct usb_pipe *
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500356uhci_alloc_pipe(struct usbdevice_s *usbdev
357 , struct usb_endpoint_descriptor *epdesc)
Kevin O'Connor6152c322012-03-10 08:53:58 -0500358{
359 if (! CONFIG_USB_UHCI)
360 return NULL;
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500361 u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
362 if (eptype == USB_ENDPOINT_XFER_INT)
363 return uhci_alloc_intr_pipe(usbdev, epdesc);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500364 struct usb_uhci_s *cntl = container_of(
365 usbdev->hub->cntl, struct usb_uhci_s, usb);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500366 dprintf(7, "uhci_alloc_async_pipe %p %d\n", &cntl->usb, eptype);
367
368 struct usb_pipe *usbpipe = usb_getFreePipe(&cntl->usb, eptype);
369 if (usbpipe) {
370 // Use previously allocated pipe.
371 usb_desc2pipe(usbpipe, usbdev, epdesc);
372 return usbpipe;
373 }
374
375 // Allocate a new queue head.
376 struct uhci_pipe *pipe;
377 if (eptype == USB_ENDPOINT_XFER_CONTROL)
378 pipe = malloc_tmphigh(sizeof(*pipe));
379 else
380 pipe = malloc_low(sizeof(*pipe));
381 if (!pipe) {
382 warn_noalloc();
383 return NULL;
384 }
385 memset(pipe, 0, sizeof(*pipe));
386 usb_desc2pipe(&pipe->pipe, usbdev, epdesc);
387 pipe->qh.element = UHCI_PTR_TERM;
388 pipe->iobase = cntl->iobase;
389
390 // Add queue head to controller list.
391 struct uhci_qh *control_qh = cntl->control_qh;
392 pipe->qh.link = control_qh->link;
393 barrier();
394 control_qh->link = (u32)&pipe->qh | UHCI_PTR_QH;
395 if (eptype == USB_ENDPOINT_XFER_CONTROL)
396 cntl->control_qh = &pipe->qh;
397 return &pipe->pipe;
398}
399
400static int
401wait_pipe(struct uhci_pipe *pipe, int timeout)
402{
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400403 u32 end = timer_calc(timeout);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500404 for (;;) {
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400405 u32 el_link = GET_LOWFLAT(pipe->qh.element);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500406 if (el_link & UHCI_PTR_TERM)
407 return 0;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400408 if (timer_check(end)) {
Kevin O'Connor6152c322012-03-10 08:53:58 -0500409 warn_timeout();
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400410 u16 iobase = GET_LOWFLAT(pipe->iobase);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500411 struct uhci_td *td = (void*)(el_link & ~UHCI_PTR_BITS);
412 dprintf(1, "Timeout on wait_pipe %p (td=%p s=%x c=%x/%x)\n"
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400413 , pipe, (void*)el_link, GET_LOWFLAT(td->status)
Kevin O'Connor6152c322012-03-10 08:53:58 -0500414 , inw(iobase + USBCMD)
415 , inw(iobase + USBSTS));
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400416 SET_LOWFLAT(pipe->qh.element, UHCI_PTR_TERM);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500417 uhci_waittick(iobase);
418 return -1;
419 }
420 yield();
421 }
422}
423
424static int
425wait_td(struct uhci_td *td)
426{
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400427 u32 end = timer_calc(5000); // XXX - lookup real time.
Kevin O'Connor6152c322012-03-10 08:53:58 -0500428 u32 status;
429 for (;;) {
430 status = td->status;
431 if (!(status & TD_CTRL_ACTIVE))
432 break;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400433 if (timer_check(end)) {
Kevin O'Connor6152c322012-03-10 08:53:58 -0500434 warn_timeout();
435 return -1;
436 }
437 yield();
438 }
439 if (status & TD_CTRL_ANY_ERROR) {
440 dprintf(1, "wait_td error - status=%x\n", status);
441 return -2;
442 }
443 return 0;
444}
445
446int
447uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
448 , void *data, int datasize)
449{
450 ASSERT32FLAT();
451 if (! CONFIG_USB_UHCI)
452 return -1;
453 dprintf(5, "uhci_control %p\n", p);
454 struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe);
455
456 int maxpacket = pipe->pipe.maxpacket;
457 int lowspeed = pipe->pipe.speed;
458 int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7);
459
460 // Setup transfer descriptors
461 int count = 2 + DIV_ROUND_UP(datasize, maxpacket);
462 struct uhci_td *tds = malloc_tmphigh(sizeof(*tds) * count);
463 if (!tds) {
464 warn_noalloc();
465 return -1;
466 }
467
468 tds[0].link = (u32)&tds[1] | UHCI_PTR_DEPTH;
469 tds[0].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0)
470 | TD_CTRL_ACTIVE);
471 tds[0].token = (uhci_explen(cmdsize) | (devaddr << TD_TOKEN_DEVADDR_SHIFT)
472 | USB_PID_SETUP);
473 tds[0].buffer = (void*)cmd;
474 int toggle = TD_TOKEN_TOGGLE;
475 int i;
476 for (i=1; i<count-1; i++) {
477 tds[i].link = (u32)&tds[i+1] | UHCI_PTR_DEPTH;
478 tds[i].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0)
479 | TD_CTRL_ACTIVE);
480 int len = (i == count-2 ? (datasize - (i-1)*maxpacket) : maxpacket);
481 tds[i].token = (uhci_explen(len) | toggle
482 | (devaddr << TD_TOKEN_DEVADDR_SHIFT)
483 | (dir ? USB_PID_IN : USB_PID_OUT));
484 tds[i].buffer = data + (i-1) * maxpacket;
485 toggle ^= TD_TOKEN_TOGGLE;
486 }
487 tds[i].link = UHCI_PTR_TERM;
488 tds[i].status = (uhci_maxerr(0) | (lowspeed ? TD_CTRL_LS : 0)
489 | TD_CTRL_ACTIVE);
490 tds[i].token = (uhci_explen(0) | TD_TOKEN_TOGGLE
491 | (devaddr << TD_TOKEN_DEVADDR_SHIFT)
492 | (dir ? USB_PID_OUT : USB_PID_IN));
493 tds[i].buffer = 0;
494
495 // Transfer data
496 barrier();
497 pipe->qh.element = (u32)&tds[0];
498 int ret = wait_pipe(pipe, 500);
499 free(tds);
500 return ret;
501}
502
503#define STACKTDS 4
504#define TDALIGN 16
505
506int
507uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize)
508{
509 if (! CONFIG_USB_UHCI)
510 return -1;
511 struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe);
512 dprintf(7, "uhci_send_bulk qh=%p dir=%d data=%p size=%d\n"
513 , &pipe->qh, dir, data, datasize);
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400514 int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket);
515 int lowspeed = GET_LOWFLAT(pipe->pipe.speed);
516 int devaddr = (GET_LOWFLAT(pipe->pipe.devaddr)
517 | (GET_LOWFLAT(pipe->pipe.ep) << 7));
518 int toggle = GET_LOWFLAT(pipe->toggle) ? TD_TOKEN_TOGGLE : 0;
Kevin O'Connor6152c322012-03-10 08:53:58 -0500519
520 // Allocate 4 tds on stack (16byte aligned)
521 u8 tdsbuf[sizeof(struct uhci_td) * STACKTDS + TDALIGN - 1];
522 struct uhci_td *tds = (void*)ALIGN((u32)tdsbuf, TDALIGN);
523 memset(tds, 0, sizeof(*tds) * STACKTDS);
524
525 // Enable tds
526 barrier();
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400527 SET_LOWFLAT(pipe->qh.element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds));
Kevin O'Connor6152c322012-03-10 08:53:58 -0500528
529 int tdpos = 0;
530 while (datasize) {
531 struct uhci_td *td = &tds[tdpos++ % STACKTDS];
532 int ret = wait_td(td);
533 if (ret)
534 goto fail;
535
536 int transfer = datasize;
537 if (transfer > maxpacket)
538 transfer = maxpacket;
539 struct uhci_td *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS)
540 , &tds[tdpos % STACKTDS]);
541 td->link = (transfer==datasize ? UHCI_PTR_TERM : (u32)nexttd_fl);
542 td->token = (uhci_explen(transfer) | toggle
543 | (devaddr << TD_TOKEN_DEVADDR_SHIFT)
544 | (dir ? USB_PID_IN : USB_PID_OUT));
545 td->buffer = data;
546 barrier();
547 td->status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0)
548 | TD_CTRL_ACTIVE);
549 toggle ^= TD_TOKEN_TOGGLE;
550
551 data += transfer;
552 datasize -= transfer;
553 }
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400554 SET_LOWFLAT(pipe->toggle, !!toggle);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500555 return wait_pipe(pipe, 5000);
556fail:
557 dprintf(1, "uhci_send_bulk failed\n");
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400558 SET_LOWFLAT(pipe->qh.element, UHCI_PTR_TERM);
559 uhci_waittick(GET_LOWFLAT(pipe->iobase));
Kevin O'Connor6152c322012-03-10 08:53:58 -0500560 return -1;
561}
562
Kevin O'Connor114592f2009-09-28 21:32:08 -0400563int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500564uhci_poll_intr(struct usb_pipe *p, void *data)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400565{
566 ASSERT16();
567 if (! CONFIG_USB_UHCI)
568 return -1;
569
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500570 struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe);
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400571 struct uhci_td *td = GET_LOWFLAT(pipe->next_td);
572 u32 status = GET_LOWFLAT(td->status);
573 u32 token = GET_LOWFLAT(td->token);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400574 if (status & TD_CTRL_ACTIVE)
575 // No intrs found.
576 return -1;
577 // XXX - check for errors.
578
579 // Copy data.
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400580 void *tddata = GET_LOWFLAT(td->buffer);
581 memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(tddata)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400582 , uhci_expected_length(token));
583
584 // Reenable this td.
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400585 struct uhci_td *next = (void*)(GET_LOWFLAT(td->link) & ~UHCI_PTR_BITS);
586 SET_LOWFLAT(pipe->next_td, next);
Kevin O'Connor95714392010-02-17 22:59:53 -0500587 barrier();
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400588 SET_LOWFLAT(td->status, (uhci_maxerr(0) | (status & TD_CTRL_LS)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400589 | TD_CTRL_ACTIVE));
Kevin O'Connor114592f2009-09-28 21:32:08 -0400590
591 return 0;
592}