blob: 8bf8d75af529e3d6763de6754743e41e391458df [file] [log] [blame]
Kevin O'Connor59f02832009-10-12 10:09:15 -04001// Code for handling OHCI 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
7#include "util.h" // dprintf
8#include "pci.h" // pci_bdf_to_bus
9#include "config.h" // CONFIG_*
Kevin O'Connor1c46a542009-10-17 23:53:32 -040010#include "usb-ohci.h" // struct ohci_hcca
11#include "pci_regs.h" // PCI_BASE_ADDRESS_0
Kevin O'Connor59f02832009-10-12 10:09:15 -040012#include "usb.h" // struct usb_s
13#include "farptr.h" // GET_FLATPTR
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050014#include "usb-hub.h" // struct usbhub_s
Kevin O'Connor59f02832009-10-12 10:09:15 -040015
Kevin O'Connor1c46a542009-10-17 23:53:32 -040016#define FIT (1 << 31)
17
Kevin O'Connor406fad62010-02-28 02:23:19 -050018struct usb_ohci_s {
19 struct usb_s usb;
20 struct ohci_regs *regs;
21};
22
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050023
24/****************************************************************
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050025 * Root hub
26 ****************************************************************/
27
28static void
29init_ohci_port(void *data)
30{
31 struct usbhub_s *hub = data;
32 u32 port = hub->port; // XXX - find better way to pass port
Kevin O'Connor406fad62010-02-28 02:23:19 -050033 struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050034
Kevin O'Connor406fad62010-02-28 02:23:19 -050035 u32 sts = readl(&cntl->regs->roothub_portstatus[port]);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050036 if (!(sts & RH_PS_CCS))
37 // No device.
38 goto done;
39
40 // XXX - need to wait for USB_TIME_ATTDB if just powered up?
41
42 // Signal reset
Kevin O'Connor406fad62010-02-28 02:23:19 -050043 mutex_lock(&cntl->usb.resetlock);
44 writel(&cntl->regs->roothub_portstatus[port], RH_PS_PRS);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050045 u64 end = calc_future_tsc(USB_TIME_DRSTR * 2);
46 for (;;) {
Kevin O'Connor406fad62010-02-28 02:23:19 -050047 sts = readl(&cntl->regs->roothub_portstatus[port]);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050048 if (!(sts & RH_PS_PRS))
49 // XXX - need to ensure USB_TIME_DRSTR time in reset?
50 break;
51 if (check_time(end)) {
52 // Timeout.
53 warn_timeout();
54 goto resetfail;
55 }
56 yield();
57 }
58
59 if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES))
60 // Device no longer present
61 goto resetfail;
62
63 // Set address of port
Kevin O'Connor190cc622010-03-09 19:43:52 -050064 struct usb_pipe *pipe = usb_set_address(hub, port, !!(sts & RH_PS_LSDA));
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050065 if (!pipe)
66 goto resetfail;
Kevin O'Connor406fad62010-02-28 02:23:19 -050067 mutex_unlock(&cntl->usb.resetlock);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050068
69 // Configure the device
70 int count = configure_usb_device(pipe);
71 free_pipe(pipe);
72 if (! count)
73 // Shutdown port
Kevin O'Connor406fad62010-02-28 02:23:19 -050074 writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050075 hub->devcount += count;
76done:
77 hub->threads--;
78 return;
79
80resetfail:
81 // Shutdown port
Kevin O'Connor406fad62010-02-28 02:23:19 -050082 writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA);
83 mutex_unlock(&cntl->usb.resetlock);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050084 goto done;
85}
86
87// Find any devices connected to the root hub.
88static int
Kevin O'Connor406fad62010-02-28 02:23:19 -050089check_ohci_ports(struct usb_ohci_s *cntl)
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050090{
91 ASSERT32FLAT();
92 // Turn on power for all devices on roothub.
Kevin O'Connor406fad62010-02-28 02:23:19 -050093 u32 rha = readl(&cntl->regs->roothub_a);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050094 rha &= ~(RH_A_PSM | RH_A_OCPM);
Kevin O'Connor406fad62010-02-28 02:23:19 -050095 writel(&cntl->regs->roothub_status, RH_HS_LPSC);
96 writel(&cntl->regs->roothub_b, RH_B_PPCM);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050097 msleep((rha >> 24) * 2);
98 // XXX - need to sleep for USB_TIME_SIGATT if just powered up?
99
100 // Lanuch a thread per port.
101 struct usbhub_s hub;
102 memset(&hub, 0, sizeof(hub));
Kevin O'Connor406fad62010-02-28 02:23:19 -0500103 hub.cntl = &cntl->usb;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500104 int ports = rha & RH_A_NDP;
105 hub.threads = ports;
106 int i;
107 for (i=0; i<ports; i++) {
108 hub.port = i;
109 run_thread(init_ohci_port, &hub);
110 }
111
112 // Wait for threads to complete.
113 while (hub.threads)
114 yield();
115
116 return hub.devcount;
117}
118
119
120/****************************************************************
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500121 * Setup
122 ****************************************************************/
123
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400124static int
Kevin O'Connor406fad62010-02-28 02:23:19 -0500125start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400126{
Kevin O'Connor406fad62010-02-28 02:23:19 -0500127 u32 oldfminterval = readl(&cntl->regs->fminterval);
128 u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400129
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400130 // XXX - check if already running?
Kevin O'Connor59f02832009-10-12 10:09:15 -0400131
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400132 // Do reset
Kevin O'Connor406fad62010-02-28 02:23:19 -0500133 writel(&cntl->regs->control, OHCI_USB_RESET | oldrwc);
134 readl(&cntl->regs->control); // flush writes
Kevin O'Connor8bbc79c2010-02-14 12:16:32 -0500135 msleep(USB_TIME_DRSTR);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400136
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400137 // Do software init (min 10us, max 2ms)
138 u64 end = calc_future_tsc_usec(10);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500139 writel(&cntl->regs->cmdstatus, OHCI_HCR);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400140 for (;;) {
Kevin O'Connor406fad62010-02-28 02:23:19 -0500141 u32 status = readl(&cntl->regs->cmdstatus);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400142 if (! status & OHCI_HCR)
143 break;
Kevin O'Connor89eb6242009-10-22 22:30:37 -0400144 if (check_time(end)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500145 warn_timeout();
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400146 return -1;
147 }
Kevin O'Connor59f02832009-10-12 10:09:15 -0400148 }
149
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400150 // Init memory
Kevin O'Connor406fad62010-02-28 02:23:19 -0500151 writel(&cntl->regs->ed_controlhead, 0);
152 writel(&cntl->regs->ed_bulkhead, 0);
153 writel(&cntl->regs->hcca, (u32)hcca);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400154
155 // Init fminterval
156 u32 fi = oldfminterval & 0x3fff;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500157 writel(&cntl->regs->fminterval
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400158 , (((oldfminterval & FIT) ^ FIT)
159 | fi | (((6 * (fi - 210)) / 7) << 16)));
Kevin O'Connor406fad62010-02-28 02:23:19 -0500160 writel(&cntl->regs->periodicstart, ((9 * fi) / 10) & 0x3fff);
161 readl(&cntl->regs->control); // flush writes
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400162
163 // XXX - verify that fminterval was setup correctly.
164
165 // Go into operational state
Kevin O'Connor406fad62010-02-28 02:23:19 -0500166 writel(&cntl->regs->control
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400167 , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_PLE
168 | OHCI_USB_OPER | oldrwc));
Kevin O'Connor406fad62010-02-28 02:23:19 -0500169 readl(&cntl->regs->control); // flush writes
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400170
171 return 0;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400172}
173
174static void
Kevin O'Connor406fad62010-02-28 02:23:19 -0500175stop_ohci(struct usb_ohci_s *cntl)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400176{
Kevin O'Connor406fad62010-02-28 02:23:19 -0500177 u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC;
178 writel(&cntl->regs->control, oldrwc);
179 readl(&cntl->regs->control); // flush writes
Kevin O'Connor59f02832009-10-12 10:09:15 -0400180}
181
Kevin O'Connor406fad62010-02-28 02:23:19 -0500182static void
183configure_ohci(void *data)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400184{
Kevin O'Connor406fad62010-02-28 02:23:19 -0500185 struct usb_ohci_s *cntl = data;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400186
187 // Allocate memory
188 struct ohci_hcca *hcca = memalign_high(256, sizeof(*hcca));
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500189 struct ohci_ed *intr_ed = malloc_high(sizeof(*intr_ed));
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500190 if (!hcca || !intr_ed) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500191 warn_noalloc();
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500192 goto free;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400193 }
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400194 memset(hcca, 0, sizeof(*hcca));
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500195 memset(intr_ed, 0, sizeof(*intr_ed));
196 intr_ed->hwINFO = ED_SKIP;
197 int i;
198 for (i=0; i<ARRAY_SIZE(hcca->int_table); i++)
199 hcca->int_table[i] = (u32)intr_ed;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400200
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400201 int ret = start_ohci(cntl, hcca);
202 if (ret)
203 goto err;
204
205 int count = check_ohci_ports(cntl);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500206 free_pipe(cntl->usb.defaultpipe);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400207 if (! count)
208 goto err;
Kevin O'Connora5826b52009-10-24 17:57:29 -0400209 return;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400210
211err:
212 stop_ohci(cntl);
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500213free:
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400214 free(hcca);
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500215 free(intr_ed);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400216}
217
Kevin O'Connor406fad62010-02-28 02:23:19 -0500218void
219ohci_init(u16 bdf, int busid)
220{
221 if (! CONFIG_USB_OHCI)
222 return;
223 struct usb_ohci_s *cntl = malloc_tmphigh(sizeof(*cntl));
224 memset(cntl, 0, sizeof(*cntl));
225 cntl->usb.busid = busid;
226 cntl->usb.type = USB_TYPE_OHCI;
227
228 u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0);
229 cntl->regs = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK);
230
231 dprintf(3, "OHCI init on dev %02x:%02x.%x (regs=%p)\n"
232 , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)
233 , pci_bdf_to_fn(bdf), cntl->regs);
234
235 // Enable bus mastering and memory access.
236 pci_config_maskw(bdf, PCI_COMMAND
237 , 0, PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY);
238
239 // XXX - check for and disable SMM control?
240
241 // Disable interrupts
242 writel(&cntl->regs->intrdisable, ~0);
243 writel(&cntl->regs->intrstatus, ~0);
244
245 run_thread(configure_ohci, cntl);
246}
247
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500248
249/****************************************************************
250 * End point communication
251 ****************************************************************/
252
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400253static int
254wait_ed(struct ohci_ed *ed)
255{
256 // XXX - 500ms just a guess
257 u64 end = calc_future_tsc(500);
258 for (;;) {
259 if (ed->hwHeadP == ed->hwTailP)
260 return 0;
Kevin O'Connor89eb6242009-10-22 22:30:37 -0400261 if (check_time(end)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500262 warn_timeout();
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400263 return -1;
264 }
Kevin O'Connor229e3be2009-10-31 13:55:59 -0400265 yield();
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400266 }
267}
268
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500269// Wait for next USB frame to start - for ensuring safe memory release.
270static void
Kevin O'Connor406fad62010-02-28 02:23:19 -0500271ohci_waittick(struct usb_ohci_s *cntl)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500272{
273 barrier();
Kevin O'Connor406fad62010-02-28 02:23:19 -0500274 struct ohci_hcca *hcca = (void*)cntl->regs->hcca;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500275 u32 startframe = hcca->frame_no;
276 u64 end = calc_future_tsc(1000 * 5);
277 for (;;) {
278 if (hcca->frame_no != startframe)
279 break;
280 if (check_time(end)) {
281 warn_timeout();
282 return;
283 }
284 yield();
285 }
286}
287
288static void
Kevin O'Connor406fad62010-02-28 02:23:19 -0500289signal_freelist(struct usb_ohci_s *cntl)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500290{
Kevin O'Connor406fad62010-02-28 02:23:19 -0500291 u32 v = readl(&cntl->regs->control);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500292 if (v & OHCI_CTRL_CLE) {
Kevin O'Connor406fad62010-02-28 02:23:19 -0500293 writel(&cntl->regs->control, v & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE));
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500294 ohci_waittick(cntl);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500295 writel(&cntl->regs->ed_controlcurrent, 0);
296 writel(&cntl->regs->ed_bulkcurrent, 0);
297 writel(&cntl->regs->control, v);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500298 } else {
299 ohci_waittick(cntl);
300 }
301}
302
303struct ohci_pipe {
304 struct ohci_ed ed;
305 struct usb_pipe pipe;
306 void *data;
307 int count;
308 struct ohci_td *tds;
309};
310
311void
312ohci_free_pipe(struct usb_pipe *p)
313{
314 if (! CONFIG_USB_OHCI)
315 return;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500316 dprintf(7, "ohci_free_pipe %p\n", p);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500317 struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500318 struct usb_ohci_s *cntl = container_of(
319 pipe->pipe.cntl, struct usb_ohci_s, usb);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500320
Kevin O'Connor406fad62010-02-28 02:23:19 -0500321 u32 *pos = &cntl->regs->ed_controlhead;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500322 for (;;) {
323 struct ohci_ed *next = (void*)*pos;
324 if (!next) {
325 // Not found?! Exit without freeing.
326 warn_internalerror();
327 return;
328 }
329 if (next == &pipe->ed) {
330 *pos = next->hwNextED;
331 signal_freelist(cntl);
332 free(pipe);
333 return;
334 }
335 pos = &next->hwNextED;
336 }
337}
338
339struct usb_pipe *
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500340ohci_alloc_control_pipe(struct usb_pipe *dummy)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500341{
342 if (! CONFIG_USB_OHCI)
343 return NULL;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500344 struct usb_ohci_s *cntl = container_of(
345 dummy->cntl, struct usb_ohci_s, usb);
346 dprintf(7, "ohci_alloc_control_pipe %p\n", &cntl->usb);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500347
348 // Allocate a queue head.
349 struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe));
350 if (!pipe) {
351 warn_noalloc();
352 return NULL;
353 }
354 memset(pipe, 0, sizeof(*pipe));
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500355 memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
Kevin O'Connor0770d672010-03-09 19:33:22 -0500356 pipe->ed.hwINFO = ED_SKIP;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500357
358 // Add queue head to controller list.
Kevin O'Connor406fad62010-02-28 02:23:19 -0500359 pipe->ed.hwNextED = cntl->regs->ed_controlhead;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500360 barrier();
Kevin O'Connor406fad62010-02-28 02:23:19 -0500361 cntl->regs->ed_controlhead = (u32)&pipe->ed;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500362 return &pipe->pipe;
363}
364
Kevin O'Connor59f02832009-10-12 10:09:15 -0400365int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500366ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
Kevin O'Connor59f02832009-10-12 10:09:15 -0400367 , void *data, int datasize)
368{
369 if (! CONFIG_USB_OHCI)
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400370 return -1;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500371 dprintf(5, "ohci_control %p\n", p);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500372 if (datasize > 4096) {
373 // XXX - should support larger sizes.
374 warn_noalloc();
375 return -1;
376 }
377 struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500378 struct usb_ohci_s *cntl = container_of(
379 pipe->pipe.cntl, struct usb_ohci_s, usb);
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500380 int maxpacket = pipe->pipe.maxpacket;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500381 int lowspeed = pipe->pipe.speed;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500382 int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400383
384 // Setup transfer descriptors
385 struct ohci_td *tds = malloc_tmphigh(sizeof(*tds) * 3);
Kevin O'Connor0770d672010-03-09 19:33:22 -0500386 if (!tds) {
387 warn_noalloc();
388 return -1;
389 }
390 struct ohci_td *td = tds;
391 td->hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC;
392 td->hwCBP = (u32)cmd;
393 td->hwNextTD = (u32)&td[1];
394 td->hwBE = (u32)cmd + cmdsize - 1;
395 td++;
396 if (datasize) {
397 td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_T_DATA1 | TD_CC;
398 td->hwCBP = (u32)data;
399 td->hwNextTD = (u32)&td[1];
400 td->hwBE = (u32)data + datasize - 1;
401 td++;
402 }
403 td->hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC;
404 td->hwCBP = 0;
405 td->hwNextTD = (u32)&td[1];
406 td->hwBE = 0;
407 td++;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400408
409 // Transfer data
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500410 pipe->ed.hwINFO = ED_SKIP;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400411 barrier();
Kevin O'Connor0770d672010-03-09 19:33:22 -0500412 pipe->ed.hwHeadP = (u32)tds;
413 pipe->ed.hwTailP = (u32)td;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400414 barrier();
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500415 pipe->ed.hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500416 writel(&cntl->regs->cmdstatus, OHCI_CLF);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400417
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500418 int ret = wait_ed(&pipe->ed);
419 pipe->ed.hwINFO = ED_SKIP;
Kevin O'Connorad901592009-12-13 11:25:25 -0500420 if (ret)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500421 ohci_waittick(cntl);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400422 free(tds);
423 return ret;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400424}
425
426struct usb_pipe *
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500427ohci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400428{
429 if (! CONFIG_USB_OHCI)
430 return NULL;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500431 struct usb_ohci_s *cntl = container_of(
432 dummy->cntl, struct usb_ohci_s, usb);
433 dprintf(7, "ohci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400434
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500435 if (frameexp > 5)
436 frameexp = 5;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500437 int maxpacket = dummy->maxpacket;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500438 int lowspeed = dummy->speed;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500439 int devaddr = dummy->devaddr | (dummy->ep << 7);
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500440 // Determine number of entries needed for 2 timer ticks.
441 int ms = 1<<frameexp;
Kevin O'Connorbfe7ca72010-02-28 02:36:32 -0500442 int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms)+1;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400443 struct ohci_pipe *pipe = malloc_low(sizeof(*pipe));
444 struct ohci_td *tds = malloc_low(sizeof(*tds) * count);
445 void *data = malloc_low(maxpacket * count);
446 if (!pipe || !tds || !data)
447 goto err;
Kevin O'Connor0770d672010-03-09 19:33:22 -0500448 memset(pipe, 0, sizeof(*pipe));
449 memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
450 pipe->data = data;
451 pipe->count = count;
452 pipe->tds = tds;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400453
454 struct ohci_ed *ed = &pipe->ed;
455 ed->hwHeadP = (u32)&tds[0];
456 ed->hwTailP = (u32)&tds[count-1];
457 ed->hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400458
459 int i;
460 for (i=0; i<count-1; i++) {
461 tds[i].hwINFO = TD_DP_IN | TD_T_TOGGLE | TD_CC;
462 tds[i].hwCBP = (u32)data + maxpacket * i;
463 tds[i].hwNextTD = (u32)&tds[i+1];
464 tds[i].hwBE = tds[i].hwCBP + maxpacket - 1;
465 }
466
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500467 // Add to interrupt schedule.
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400468 barrier();
Kevin O'Connor406fad62010-02-28 02:23:19 -0500469 struct ohci_hcca *hcca = (void*)cntl->regs->hcca;
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500470 if (frameexp == 0) {
471 // Add to existing interrupt entry.
472 struct ohci_ed *intr_ed = (void*)hcca->int_table[0];
473 ed->hwNextED = intr_ed->hwNextED;
474 intr_ed->hwNextED = (u32)ed;
475 } else {
476 int startpos = 1<<(frameexp-1);
477 ed->hwNextED = hcca->int_table[startpos];
478 for (i=startpos; i<ARRAY_SIZE(hcca->int_table); i+=ms)
479 hcca->int_table[i] = (u32)ed;
480 }
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400481
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400482 return &pipe->pipe;
483
484err:
485 free(pipe);
486 free(tds);
487 free(data);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400488 return NULL;
489}
490
491int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500492ohci_poll_intr(struct usb_pipe *p, void *data)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400493{
494 ASSERT16();
495 if (! CONFIG_USB_OHCI)
496 return -1;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400497
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500498 struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
499 struct ohci_td *tds = GET_FLATPTR(pipe->tds);
500 struct ohci_td *head = (void*)GET_FLATPTR(pipe->ed.hwHeadP);
501 struct ohci_td *tail = (void*)GET_FLATPTR(pipe->ed.hwTailP);
502 int count = GET_FLATPTR(pipe->count);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400503 int pos = (tail - tds + 1) % count;
504 struct ohci_td *next = &tds[pos];
505 if (head == next)
506 // No intrs found.
507 return -1;
508 // XXX - check for errors.
509
510 // Copy data.
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500511 int maxpacket = GET_FLATPTR(pipe->pipe.maxpacket);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500512 void *pipedata = GET_FLATPTR(pipe->data);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400513 void *intrdata = pipedata + maxpacket * pos;
514 memcpy_far(GET_SEG(SS), data
515 , FLATPTR_TO_SEG(intrdata), (void*)FLATPTR_TO_OFFSET(intrdata)
516 , maxpacket);
517
518 // Reenable this td.
519 SET_FLATPTR(tail->hwINFO, TD_DP_IN | TD_T_TOGGLE | TD_CC);
520 intrdata = pipedata + maxpacket * (tail-tds);
521 SET_FLATPTR(tail->hwCBP, (u32)intrdata);
522 SET_FLATPTR(tail->hwNextTD, (u32)next);
523 SET_FLATPTR(tail->hwBE, (u32)intrdata + maxpacket - 1);
Kevin O'Connor0770d672010-03-09 19:33:22 -0500524 barrier();
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500525 SET_FLATPTR(pipe->ed.hwTailP, (u32)next);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400526
527 return 0;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400528}