blob: 10c92feef5e919e0c6b850bd0f4081ccb6100c97 [file] [log] [blame]
Kevin O'Connor190cc622010-03-09 19:43:52 -05001// Code for handling EHCI USB controllers.
2//
Kevin O'Connorec443ff2013-12-05 18:43:20 -05003// Copyright (C) 2010-2013 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connor190cc622010-03-09 19:43:52 -05004//
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'Connor190cc622010-03-09 19:43:52 -05008#include "config.h" // CONFIG_*
Kevin O'Connor2d2fa312013-09-14 21:55:26 -04009#include "output.h" // dprintf
10#include "malloc.h" // free
11#include "pci.h" // pci_bdf_to_bus
Kevin O'Connor190cc622010-03-09 19:43:52 -050012#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI
13#include "pci_regs.h" // PCI_BASE_ADDRESS_0
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-ehci.h" // struct ehci_qh
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040017#include "util.h" // msleep
Kevin O'Connorb9c6a962013-09-14 13:01:30 -040018#include "x86.h" // readl
Kevin O'Connor190cc622010-03-09 19:43:52 -050019
Kevin O'Connor190cc622010-03-09 19:43:52 -050020struct usb_ehci_s {
21 struct usb_s usb;
22 struct ehci_caps *caps;
23 struct ehci_regs *regs;
24 struct ehci_qh *async_qh;
Kevin O'Connor190cc622010-03-09 19:43:52 -050025 int checkports;
Kevin O'Connor190cc622010-03-09 19:43:52 -050026};
27
Kevin O'Connor402f9a02012-02-20 12:54:49 -050028struct ehci_pipe {
29 struct ehci_qh qh;
30 struct ehci_qtd *next_td, *tds;
31 void *data;
32 struct usb_pipe pipe;
Kevin O'Connor402f9a02012-02-20 12:54:49 -050033};
34
Kevin O'Connorec443ff2013-12-05 18:43:20 -050035static int PendingEHCIPorts;
36
Kevin O'Connor190cc622010-03-09 19:43:52 -050037
38/****************************************************************
39 * Root hub
40 ****************************************************************/
41
42#define EHCI_TIME_POSTPOWER 20
43#define EHCI_TIME_POSTRESET 2
44
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040045// Check if device attached to port
46static int
47ehci_hub_detect(struct usbhub_s *hub, u32 port)
Kevin O'Connor190cc622010-03-09 19:43:52 -050048{
Kevin O'Connor190cc622010-03-09 19:43:52 -050049 struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb);
Kevin O'Connor190cc622010-03-09 19:43:52 -050050 u32 *portreg = &cntl->regs->portsc[port];
51 u32 portsc = readl(portreg);
52
53 // Power up port.
54 if (!(portsc & PORT_POWER)) {
55 portsc |= PORT_POWER;
56 writel(portreg, portsc);
57 msleep(EHCI_TIME_POSTPOWER);
Kevin O'Connor87ab2fb2010-03-20 23:25:11 -040058 } else {
Kevin O'Connorc0207882012-08-01 20:40:03 -040059 // Port is already powered up, but we don't know how long it
60 // has been powered up, so wait the 20ms.
61 msleep(EHCI_TIME_POSTPOWER);
Kevin O'Connor190cc622010-03-09 19:43:52 -050062 }
Kevin O'Connor87ab2fb2010-03-20 23:25:11 -040063 portsc = readl(portreg);
Kevin O'Connor190cc622010-03-09 19:43:52 -050064
65 if (!(portsc & PORT_CONNECT))
66 // No device present
Kevin O'Connor87ab2fb2010-03-20 23:25:11 -040067 goto doneearly;
Kevin O'Connor190cc622010-03-09 19:43:52 -050068
69 if ((portsc & PORT_LINESTATUS_MASK) == PORT_LINESTATUS_KSTATE) {
70 // low speed device
Kevin O'Connor190cc622010-03-09 19:43:52 -050071 writel(portreg, portsc | PORT_OWNER);
Kevin O'Connor87ab2fb2010-03-20 23:25:11 -040072 goto doneearly;
Kevin O'Connor190cc622010-03-09 19:43:52 -050073 }
74
75 // XXX - if just powered up, need to wait for USB_TIME_ATTDB?
76
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040077 // Begin reset on port
Kevin O'Connor190cc622010-03-09 19:43:52 -050078 portsc = (portsc & ~PORT_PE) | PORT_RESET;
79 writel(portreg, portsc);
80 msleep(USB_TIME_DRSTR);
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040081 return 0;
82
83doneearly:
Kevin O'Connorec443ff2013-12-05 18:43:20 -050084 PendingEHCIPorts--;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040085 return -1;
86}
87
88// Reset device on port
89static int
90ehci_hub_reset(struct usbhub_s *hub, u32 port)
91{
92 struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb);
93 u32 *portreg = &cntl->regs->portsc[port];
94 u32 portsc = readl(portreg);
95
96 // Finish reset on port
Kevin O'Connor190cc622010-03-09 19:43:52 -050097 portsc &= ~PORT_RESET;
98 writel(portreg, portsc);
99 msleep(EHCI_TIME_POSTRESET);
100
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400101 int rv = -1;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500102 portsc = readl(portreg);
103 if (!(portsc & PORT_CONNECT))
104 // No longer connected
105 goto resetfail;
106 if (!(portsc & PORT_PE)) {
107 // full speed device
Kevin O'Connor190cc622010-03-09 19:43:52 -0500108 writel(portreg, portsc | PORT_OWNER);
109 goto resetfail;
110 }
Kevin O'Connor87ab2fb2010-03-20 23:25:11 -0400111
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400112 rv = USB_HIGHSPEED;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500113resetfail:
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500114 PendingEHCIPorts--;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400115 return rv;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500116}
117
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400118// Disable port
119static void
120ehci_hub_disconnect(struct usbhub_s *hub, u32 port)
121{
122 struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb);
123 u32 *portreg = &cntl->regs->portsc[port];
124 u32 portsc = readl(portreg);
125 writel(portreg, portsc & ~PORT_PE);
126}
127
128static struct usbhub_op_s ehci_HubOp = {
129 .detect = ehci_hub_detect,
130 .reset = ehci_hub_reset,
131 .disconnect = ehci_hub_disconnect,
132};
133
Kevin O'Connor190cc622010-03-09 19:43:52 -0500134// Find any devices connected to the root hub.
135static int
136check_ehci_ports(struct usb_ehci_s *cntl)
137{
138 ASSERT32FLAT();
Kevin O'Connor190cc622010-03-09 19:43:52 -0500139 struct usbhub_s hub;
140 memset(&hub, 0, sizeof(hub));
141 hub.cntl = &cntl->usb;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400142 hub.portcount = cntl->checkports;
143 hub.op = &ehci_HubOp;
144 usb_enumerate(&hub);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500145 return hub.devcount;
146}
147
148
149/****************************************************************
150 * Setup
151 ****************************************************************/
152
Kevin O'Connor402f9a02012-02-20 12:54:49 -0500153// Wait for next USB async frame to start - for ensuring safe memory release.
154static void
155ehci_waittick(struct usb_ehci_s *cntl)
156{
157 if (MODE16) {
158 msleep(10);
159 return;
160 }
161 // Wait for access to "doorbell"
162 barrier();
163 u32 cmd, sts;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400164 u32 end = timer_calc(100);
Kevin O'Connor402f9a02012-02-20 12:54:49 -0500165 for (;;) {
166 sts = readl(&cntl->regs->usbsts);
167 if (!(sts & STS_IAA)) {
168 cmd = readl(&cntl->regs->usbcmd);
169 if (!(cmd & CMD_IAAD))
170 break;
171 }
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400172 if (timer_check(end)) {
Kevin O'Connor402f9a02012-02-20 12:54:49 -0500173 warn_timeout();
174 return;
175 }
176 yield();
177 }
178 // Ring "doorbell"
179 writel(&cntl->regs->usbcmd, cmd | CMD_IAAD);
180 // Wait for completion
181 for (;;) {
182 sts = readl(&cntl->regs->usbsts);
183 if (sts & STS_IAA)
184 break;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400185 if (timer_check(end)) {
Kevin O'Connor402f9a02012-02-20 12:54:49 -0500186 warn_timeout();
187 return;
188 }
189 yield();
190 }
191 // Ack completion
192 writel(&cntl->regs->usbsts, STS_IAA);
193}
194
195static void
196ehci_free_pipes(struct usb_ehci_s *cntl)
197{
198 dprintf(7, "ehci_free_pipes %p\n", cntl);
199
200 struct ehci_qh *start = cntl->async_qh;
201 struct ehci_qh *pos = start;
202 for (;;) {
203 struct ehci_qh *next = (void*)(pos->next & ~EHCI_PTR_BITS);
204 if (next == start)
205 break;
206 struct ehci_pipe *pipe = container_of(next, struct ehci_pipe, qh);
207 if (pipe->pipe.cntl != &cntl->usb)
208 pos->next = next->next;
209 else
210 pos = next;
211 }
212 ehci_waittick(cntl);
213 for (;;) {
214 struct usb_pipe *usbpipe = cntl->usb.freelist;
215 if (!usbpipe)
216 break;
217 cntl->usb.freelist = usbpipe->freenext;
218 struct ehci_pipe *pipe = container_of(usbpipe, struct ehci_pipe, pipe);
219 free(pipe);
220 }
221}
222
Kevin O'Connor190cc622010-03-09 19:43:52 -0500223static void
224configure_ehci(void *data)
225{
226 struct usb_ehci_s *cntl = data;
227
228 // Allocate ram for schedule storage
229 struct ehci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl));
230 struct ehci_qh *intr_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*intr_qh));
231 struct ehci_qh *async_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*async_qh));
232 if (!fl || !intr_qh || !async_qh) {
233 warn_noalloc();
234 goto fail;
235 }
236
237 // XXX - check for halted?
238
239 // Reset the HC
240 u32 cmd = readl(&cntl->regs->usbcmd);
241 writel(&cntl->regs->usbcmd, (cmd & ~(CMD_ASE | CMD_PSE)) | CMD_HCRESET);
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400242 u32 end = timer_calc(250);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500243 for (;;) {
244 cmd = readl(&cntl->regs->usbcmd);
245 if (!(cmd & CMD_HCRESET))
246 break;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400247 if (timer_check(end)) {
Kevin O'Connor190cc622010-03-09 19:43:52 -0500248 warn_timeout();
249 goto fail;
250 }
Kevin O'Connor698d3f92010-04-17 16:59:12 -0400251 yield();
Kevin O'Connor190cc622010-03-09 19:43:52 -0500252 }
253
254 // Disable interrupts (just to be safe).
255 writel(&cntl->regs->usbintr, 0);
256
257 // Set schedule to point to primary intr queue head
258 memset(intr_qh, 0, sizeof(*intr_qh));
259 intr_qh->next = EHCI_PTR_TERM;
260 intr_qh->info2 = (0x01 << QH_SMASK_SHIFT);
261 intr_qh->token = QTD_STS_HALT;
262 intr_qh->qtd_next = intr_qh->alt_next = EHCI_PTR_TERM;
263 int i;
264 for (i=0; i<ARRAY_SIZE(fl->links); i++)
265 fl->links[i] = (u32)intr_qh | EHCI_PTR_QH;
266 writel(&cntl->regs->periodiclistbase, (u32)fl);
267
268 // Set async list to point to primary async queue head
269 memset(async_qh, 0, sizeof(*async_qh));
270 async_qh->next = (u32)async_qh | EHCI_PTR_QH;
271 async_qh->info1 = QH_HEAD;
272 async_qh->token = QTD_STS_HALT;
273 async_qh->qtd_next = async_qh->alt_next = EHCI_PTR_TERM;
274 cntl->async_qh = async_qh;
275 writel(&cntl->regs->asynclistbase, (u32)async_qh);
276
277 // Enable queues
278 writel(&cntl->regs->usbcmd, cmd | CMD_ASE | CMD_PSE | CMD_RUN);
279
280 // Set default of high speed for root hub.
281 writel(&cntl->regs->configflag, 1);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500282
283 // Find devices
284 int count = check_ehci_ports(cntl);
Kevin O'Connor402f9a02012-02-20 12:54:49 -0500285 ehci_free_pipes(cntl);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500286 if (count)
287 // Success
288 return;
289
290 // No devices found - shutdown and free controller.
291 writel(&cntl->regs->usbcmd, cmd & ~CMD_RUN);
292 msleep(4); // 2ms to stop reading memory - XXX
293fail:
294 free(fl);
295 free(intr_qh);
296 free(async_qh);
297 free(cntl);
298}
299
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500300static void
301ehci_controller_setup(struct pci_device *pci)
Kevin O'Connor190cc622010-03-09 19:43:52 -0500302{
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500303 wait_preempt(); // Avoid pci_config_readl when preempting
Kevin O'Connor8ff8e012011-07-09 14:11:21 -0400304 u16 bdf = pci->bdf;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500305 u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0);
306 struct ehci_caps *caps = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK);
307 u32 hcc_params = readl(&caps->hccparams);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500308
309 struct usb_ehci_s *cntl = malloc_tmphigh(sizeof(*cntl));
Kevin O'Connorffdcd3a2011-07-10 15:48:00 -0400310 if (!cntl) {
311 warn_noalloc();
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500312 return;
Kevin O'Connorffdcd3a2011-07-10 15:48:00 -0400313 }
Kevin O'Connor190cc622010-03-09 19:43:52 -0500314 memset(cntl, 0, sizeof(*cntl));
Kevin O'Connor8ff8e012011-07-09 14:11:21 -0400315 cntl->usb.pci = pci;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500316 cntl->usb.type = USB_TYPE_EHCI;
317 cntl->caps = caps;
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500318 cntl->checkports = readl(&cntl->caps->hcsparams) & HCS_N_PORTS_MASK;
Avik Sil7cac6002013-02-14 10:54:57 +0530319 cntl->regs = (void*)caps + readb(&caps->caplength);
Sven Schnellea3fa66c2012-06-12 09:20:23 +0200320 if (hcc_params & HCC_64BIT_ADDR)
321 cntl->regs->ctrldssegment = 0;
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500322 PendingEHCIPorts += cntl->checkports;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500323
Kevin O'Connor9dc243e2010-03-20 17:53:03 -0400324 dprintf(1, "EHCI init on dev %02x:%02x.%x (regs=%p)\n"
Kevin O'Connor190cc622010-03-09 19:43:52 -0500325 , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)
326 , pci_bdf_to_fn(bdf), cntl->regs);
327
328 pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
329
330 // XXX - check for and disable SMM control?
331
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500332 run_thread(configure_ehci, cntl);
333}
334
335void
336ehci_setup(void)
337{
338 if (! CONFIG_USB_EHCI)
339 return;
340 struct pci_device *pci;
341 foreachpci(pci) {
342 if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_EHCI)
343 ehci_controller_setup(pci);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500344 }
345
Kevin O'Connorec443ff2013-12-05 18:43:20 -0500346 // Wait for all EHCI ports to initialize. This forces OHCI/UHCI
347 // setup to always be after any EHCI ports are set to low speed.
348 while (PendingEHCIPorts)
349 yield();
Kevin O'Connor190cc622010-03-09 19:43:52 -0500350}
351
352
353/****************************************************************
354 * End point communication
355 ****************************************************************/
356
Kevin O'Connor8915ed22012-03-10 10:09:56 -0500357// Setup fields in qh
358static void
359ehci_desc2pipe(struct ehci_pipe *pipe, struct usbdevice_s *usbdev
360 , struct usb_endpoint_descriptor *epdesc)
361{
362 usb_desc2pipe(&pipe->pipe, usbdev, epdesc);
363
Kevin O'Connor32d68ab2012-03-10 21:07:26 -0500364 pipe->qh.info1 = ((pipe->pipe.maxpacket << QH_MAXPACKET_SHIFT)
365 | (pipe->pipe.speed << QH_SPEED_SHIFT)
366 | (pipe->pipe.ep << QH_EP_SHIFT)
367 | (pipe->pipe.devaddr << QH_DEVADDR_SHIFT));
Kevin O'Connor8915ed22012-03-10 10:09:56 -0500368
369 pipe->qh.info2 = (1 << QH_MULT_SHIFT);
370 struct usbdevice_s *hubdev = usbdev->hub->usbdev;
371 if (hubdev) {
372 struct ehci_pipe *hpipe = container_of(
373 hubdev->defpipe, struct ehci_pipe, pipe);
374 if (hpipe->pipe.speed == USB_HIGHSPEED)
375 pipe->qh.info2 |= ((usbdev->port << QH_HUBPORT_SHIFT)
376 | (hpipe->pipe.devaddr << QH_HUBADDR_SHIFT));
377 else
378 pipe->qh.info2 = hpipe->qh.info2;
379 }
380
381 u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
382 if (eptype == USB_ENDPOINT_XFER_CONTROL)
383 pipe->qh.info1 |= ((pipe->pipe.speed != USB_HIGHSPEED ? QH_CONTROL : 0)
384 | QH_TOGGLECONTROL);
385 else if (eptype == USB_ENDPOINT_XFER_INT)
386 pipe->qh.info2 |= (0x01 << QH_SMASK_SHIFT) | (0x1c << QH_CMASK_SHIFT);
387}
388
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500389static struct usb_pipe *
Kevin O'Connor6152c322012-03-10 08:53:58 -0500390ehci_alloc_intr_pipe(struct usbdevice_s *usbdev
391 , struct usb_endpoint_descriptor *epdesc)
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500392{
Kevin O'Connor6152c322012-03-10 08:53:58 -0500393 struct usb_ehci_s *cntl = container_of(
394 usbdev->hub->cntl, struct usb_ehci_s, usb);
395 int frameexp = usb_getFrameExp(usbdev, epdesc);
396 dprintf(7, "ehci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp);
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500397
Kevin O'Connor6152c322012-03-10 08:53:58 -0500398 if (frameexp > 10)
399 frameexp = 10;
400 int maxpacket = epdesc->wMaxPacketSize;
401 // Determine number of entries needed for 2 timer ticks.
402 int ms = 1<<frameexp;
Kevin O'Connor69013372013-07-20 12:08:48 -0400403 int count = DIV_ROUND_UP(ticks_to_ms(2), ms);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500404 struct ehci_pipe *pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe));
405 struct ehci_qtd *tds = memalign_low(EHCI_QTD_ALIGN, sizeof(*tds) * count);
406 void *data = malloc_low(maxpacket * count);
407 if (!pipe || !tds || !data) {
408 warn_noalloc();
409 goto fail;
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500410 }
Kevin O'Connor6152c322012-03-10 08:53:58 -0500411 memset(pipe, 0, sizeof(*pipe));
Kevin O'Connor8915ed22012-03-10 10:09:56 -0500412 ehci_desc2pipe(pipe, usbdev, epdesc);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500413 pipe->next_td = pipe->tds = tds;
414 pipe->data = data;
Kevin O'Connor6152c322012-03-10 08:53:58 -0500415 pipe->qh.qtd_next = (u32)tds;
416
417 int i;
418 for (i=0; i<count; i++) {
419 struct ehci_qtd *td = &tds[i];
420 td->qtd_next = (i==count-1 ? (u32)tds : (u32)&td[1]);
421 td->alt_next = EHCI_PTR_TERM;
422 td->token = (ehci_explen(maxpacket) | QTD_STS_ACTIVE
423 | QTD_PID_IN | ehci_maxerr(3));
424 td->buf[0] = (u32)data + maxpacket * i;
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500425 }
Kevin O'Connor6152c322012-03-10 08:53:58 -0500426
427 // Add to interrupt schedule.
428 struct ehci_framelist *fl = (void*)readl(&cntl->regs->periodiclistbase);
429 if (frameexp == 0) {
430 // Add to existing interrupt entry.
431 struct ehci_qh *intr_qh = (void*)(fl->links[0] & ~EHCI_PTR_BITS);
432 pipe->qh.next = intr_qh->next;
433 barrier();
434 intr_qh->next = (u32)&pipe->qh | EHCI_PTR_QH;
435 } else {
436 int startpos = 1<<(frameexp-1);
437 pipe->qh.next = fl->links[startpos];
438 barrier();
439 for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms)
440 fl->links[i] = (u32)&pipe->qh | EHCI_PTR_QH;
441 }
442
443 return &pipe->pipe;
444fail:
445 free(pipe);
446 free(tds);
447 free(data);
448 return NULL;
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500449}
Kevin O'Connor190cc622010-03-09 19:43:52 -0500450
Kevin O'Connor01de9bd2012-03-05 22:41:21 -0500451struct usb_pipe *
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500452ehci_alloc_pipe(struct usbdevice_s *usbdev
453 , struct usb_endpoint_descriptor *epdesc)
Kevin O'Connor190cc622010-03-09 19:43:52 -0500454{
455 if (! CONFIG_USB_EHCI)
456 return NULL;
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500457 u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
458 if (eptype == USB_ENDPOINT_XFER_INT)
459 return ehci_alloc_intr_pipe(usbdev, epdesc);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500460 struct usb_ehci_s *cntl = container_of(
Kevin O'Connor54bf2072012-03-09 07:52:33 -0500461 usbdev->hub->cntl, struct usb_ehci_s, usb);
Kevin O'Connor54bf2072012-03-09 07:52:33 -0500462 dprintf(7, "ehci_alloc_async_pipe %p %d\n", &cntl->usb, eptype);
463
464 struct usb_pipe *usbpipe = usb_getFreePipe(&cntl->usb, eptype);
465 if (usbpipe) {
466 // Use previously allocated pipe.
Kevin O'Connor8915ed22012-03-10 10:09:56 -0500467 struct ehci_pipe *pipe = container_of(usbpipe, struct ehci_pipe, pipe);
468 ehci_desc2pipe(pipe, usbdev, epdesc);
Kevin O'Connor54bf2072012-03-09 07:52:33 -0500469 return usbpipe;
470 }
Kevin O'Connor402f9a02012-02-20 12:54:49 -0500471
472 // Allocate a new queue head.
473 struct ehci_pipe *pipe;
Kevin O'Connor54bf2072012-03-09 07:52:33 -0500474 if (eptype == USB_ENDPOINT_XFER_CONTROL)
Kevin O'Connor402f9a02012-02-20 12:54:49 -0500475 pipe = memalign_tmphigh(EHCI_QH_ALIGN, sizeof(*pipe));
476 else
477 pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe));
Kevin O'Connor190cc622010-03-09 19:43:52 -0500478 if (!pipe) {
479 warn_noalloc();
480 return NULL;
481 }
482 memset(pipe, 0, sizeof(*pipe));
Kevin O'Connor8915ed22012-03-10 10:09:56 -0500483 ehci_desc2pipe(pipe, usbdev, epdesc);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500484 pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500485
486 // Add queue head to controller list.
487 struct ehci_qh *async_qh = cntl->async_qh;
488 pipe->qh.next = async_qh->next;
489 barrier();
490 async_qh->next = (u32)&pipe->qh | EHCI_PTR_QH;
491 return &pipe->pipe;
492}
493
Kevin O'Connor6152c322012-03-10 08:53:58 -0500494static void
495ehci_reset_pipe(struct ehci_pipe *pipe)
496{
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400497 SET_LOWFLAT(pipe->qh.qtd_next, EHCI_PTR_TERM);
498 SET_LOWFLAT(pipe->qh.alt_next, EHCI_PTR_TERM);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500499 barrier();
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400500 SET_LOWFLAT(pipe->qh.token, GET_LOWFLAT(pipe->qh.token) & QTD_TOGGLE);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500501}
502
503static int
504ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, int timeout)
505{
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400506 u32 end = timer_calc(timeout);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500507 u32 status;
508 for (;;) {
509 status = td->token;
510 if (!(status & QTD_STS_ACTIVE))
511 break;
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400512 if (timer_check(end)) {
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400513 u32 cur = GET_LOWFLAT(pipe->qh.current);
514 u32 tok = GET_LOWFLAT(pipe->qh.token);
515 u32 next = GET_LOWFLAT(pipe->qh.qtd_next);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500516 warn_timeout();
517 dprintf(1, "ehci pipe=%p cur=%08x tok=%08x next=%x td=%p status=%x\n"
518 , pipe, cur, tok, next, td, status);
519 ehci_reset_pipe(pipe);
520 struct usb_ehci_s *cntl = container_of(
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400521 GET_LOWFLAT(pipe->pipe.cntl), struct usb_ehci_s, usb);
Kevin O'Connor6152c322012-03-10 08:53:58 -0500522 ehci_waittick(cntl);
523 return -1;
524 }
525 yield();
526 }
527 if (status & QTD_STS_HALT) {
528 dprintf(1, "ehci_wait_td error - status=%x\n", status);
529 ehci_reset_pipe(pipe);
530 return -2;
531 }
532 return 0;
533}
534
Kevin O'Connor190cc622010-03-09 19:43:52 -0500535static int
536fillTDbuffer(struct ehci_qtd *td, u16 maxpacket, const void *buf, int bytes)
537{
538 u32 dest = (u32)buf;
539 u32 *pos = td->buf;
540 while (bytes) {
541 if (pos >= &td->buf[ARRAY_SIZE(td->buf)])
542 // More data than can transfer in a single qtd - only use
543 // full packets to prevent a babble error.
544 return ALIGN_DOWN(dest - (u32)buf, maxpacket);
545 u32 count = bytes;
546 u32 max = 0x1000 - (dest & 0xfff);
547 if (count > max)
548 count = max;
549 *pos = dest;
550 bytes -= count;
551 dest += count;
552 pos++;
553 }
554 return dest - (u32)buf;
555}
556
557int
558ehci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
559 , void *data, int datasize)
560{
561 ASSERT32FLAT();
562 if (! CONFIG_USB_EHCI)
563 return -1;
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500564 dprintf(5, "ehci_control %p (dir=%d cmd=%d data=%d)\n"
565 , p, dir, cmdsize, datasize);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500566 if (datasize > 4*4096 || cmdsize > 4*4096) {
567 // XXX - should support larger sizes.
568 warn_noalloc();
569 return -1;
570 }
571 struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500572
Kevin O'Connor190cc622010-03-09 19:43:52 -0500573 // Setup transfer descriptors
574 struct ehci_qtd *tds = memalign_tmphigh(EHCI_QTD_ALIGN, sizeof(*tds) * 3);
575 if (!tds) {
576 warn_noalloc();
577 return -1;
578 }
579 memset(tds, 0, sizeof(*tds) * 3);
580 struct ehci_qtd *td = tds;
581
582 td->qtd_next = (u32)&td[1];
583 td->alt_next = EHCI_PTR_TERM;
584 td->token = (ehci_explen(cmdsize) | QTD_STS_ACTIVE
585 | QTD_PID_SETUP | ehci_maxerr(3));
Kevin O'Connor8915ed22012-03-10 10:09:56 -0500586 u16 maxpacket = pipe->pipe.maxpacket;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500587 fillTDbuffer(td, maxpacket, cmd, cmdsize);
588 td++;
589
590 if (datasize) {
591 td->qtd_next = (u32)&td[1];
592 td->alt_next = EHCI_PTR_TERM;
593 td->token = (QTD_TOGGLE | ehci_explen(datasize) | QTD_STS_ACTIVE
594 | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3));
595 fillTDbuffer(td, maxpacket, data, datasize);
596 td++;
597 }
598
599 td->qtd_next = EHCI_PTR_TERM;
600 td->alt_next = EHCI_PTR_TERM;
601 td->token = (QTD_TOGGLE | QTD_STS_ACTIVE
602 | (dir ? QTD_PID_OUT : QTD_PID_IN) | ehci_maxerr(3));
603
604 // Transfer data
605 barrier();
606 pipe->qh.qtd_next = (u32)tds;
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500607 int i, ret=0;
608 for (i=0; i<3; i++) {
609 struct ehci_qtd *td = &tds[i];
610 ret = ehci_wait_td(pipe, td, 500);
611 if (ret)
612 break;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500613 }
614 free(tds);
615 return ret;
616}
617
Kevin O'Connor190cc622010-03-09 19:43:52 -0500618#define STACKQTDS 4
619
620int
621ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize)
622{
623 if (! CONFIG_USB_EHCI)
624 return -1;
625 struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe);
626 dprintf(7, "ehci_send_bulk qh=%p dir=%d data=%p size=%d\n"
627 , &pipe->qh, dir, data, datasize);
628
Kevin O'Connor8915ed22012-03-10 10:09:56 -0500629 // Allocate 4 tds on stack (with required alignment)
Kevin O'Connor190cc622010-03-09 19:43:52 -0500630 u8 tdsbuf[sizeof(struct ehci_qtd) * STACKQTDS + EHCI_QTD_ALIGN - 1];
631 struct ehci_qtd *tds = (void*)ALIGN((u32)tdsbuf, EHCI_QTD_ALIGN);
632 memset(tds, 0, sizeof(*tds) * STACKQTDS);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500633 barrier();
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400634 SET_LOWFLAT(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds));
Kevin O'Connor190cc622010-03-09 19:43:52 -0500635
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400636 u16 maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500637 int tdpos = 0;
638 while (datasize) {
639 struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS];
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500640 int ret = ehci_wait_td(pipe, td, 5000);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500641 if (ret)
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500642 return -1;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500643
644 struct ehci_qtd *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS)
645 , &tds[tdpos % STACKQTDS]);
646
647 int transfer = fillTDbuffer(td, maxpacket, data, datasize);
648 td->qtd_next = (transfer==datasize ? EHCI_PTR_TERM : (u32)nexttd_fl);
649 td->alt_next = EHCI_PTR_TERM;
650 barrier();
651 td->token = (ehci_explen(transfer) | QTD_STS_ACTIVE
652 | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3));
653
654 data += transfer;
655 datasize -= transfer;
656 }
657 int i;
658 for (i=0; i<STACKQTDS; i++) {
659 struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS];
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500660 int ret = ehci_wait_td(pipe, td, 5000);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500661 if (ret)
Kevin O'Connor49ecf692011-11-19 14:21:51 -0500662 return -1;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500663 }
664
665 return 0;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500666}
667
Kevin O'Connor190cc622010-03-09 19:43:52 -0500668int
669ehci_poll_intr(struct usb_pipe *p, void *data)
670{
671 ASSERT16();
672 if (! CONFIG_USB_EHCI)
673 return -1;
674 struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe);
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400675 struct ehci_qtd *td = GET_LOWFLAT(pipe->next_td);
676 u32 token = GET_LOWFLAT(td->token);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500677 if (token & QTD_STS_ACTIVE)
678 // No intrs found.
679 return -1;
680 // XXX - check for errors.
681
682 // Copy data.
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400683 int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket);
684 int pos = td - GET_LOWFLAT(pipe->tds);
685 void *tddata = GET_LOWFLAT(pipe->data) + maxpacket * pos;
686 memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(tddata), maxpacket);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500687
688 // Reenable this td.
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400689 struct ehci_qtd *next = (void*)(GET_LOWFLAT(td->qtd_next) & ~EHCI_PTR_BITS);
690 SET_LOWFLAT(pipe->next_td, next);
691 SET_LOWFLAT(td->buf[0], (u32)tddata);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500692 barrier();
Kevin O'Connor4f6563e2012-05-19 22:42:51 -0400693 SET_LOWFLAT(td->token, (ehci_explen(maxpacket) | QTD_STS_ACTIVE
Kevin O'Connor190cc622010-03-09 19:43:52 -0500694 | QTD_PID_IN | ehci_maxerr(3)));
695
696 return 0;
697}