blob: ac1eb3775169b0b0a80f836fe57fc048655b554a [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'Connor59f02832009-10-12 10:09:15 -040014
Kevin O'Connor1c46a542009-10-17 23:53:32 -040015#define FIT (1 << 31)
16
Kevin O'Connor406fad62010-02-28 02:23:19 -050017struct usb_ohci_s {
18 struct usb_s usb;
19 struct ohci_regs *regs;
20};
21
Kevin O'Connor98af3532012-02-20 12:54:49 -050022struct ohci_pipe {
23 struct ohci_ed ed;
24 struct usb_pipe pipe;
25 void *data;
26 int count;
27 struct ohci_td *tds;
28};
29
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050030
31/****************************************************************
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050032 * Root hub
33 ****************************************************************/
34
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040035// Check if device attached to port
36static int
37ohci_hub_detect(struct usbhub_s *hub, u32 port)
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050038{
Kevin O'Connor406fad62010-02-28 02:23:19 -050039 struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb);
Kevin O'Connor406fad62010-02-28 02:23:19 -050040 u32 sts = readl(&cntl->regs->roothub_portstatus[port]);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050041 if (!(sts & RH_PS_CCS))
42 // No device.
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040043 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050044
45 // XXX - need to wait for USB_TIME_ATTDB if just powered up?
46
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040047 return 0;
48}
49
50// Disable port
51static void
52ohci_hub_disconnect(struct usbhub_s *hub, u32 port)
53{
54 struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb);
55 writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA);
56}
57
58// Reset device on port
59static int
60ohci_hub_reset(struct usbhub_s *hub, u32 port)
61{
62 struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb);
Kevin O'Connor406fad62010-02-28 02:23:19 -050063 writel(&cntl->regs->roothub_portstatus[port], RH_PS_PRS);
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040064 u32 sts;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050065 u64 end = calc_future_tsc(USB_TIME_DRSTR * 2);
66 for (;;) {
Kevin O'Connor406fad62010-02-28 02:23:19 -050067 sts = readl(&cntl->regs->roothub_portstatus[port]);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050068 if (!(sts & RH_PS_PRS))
69 // XXX - need to ensure USB_TIME_DRSTR time in reset?
70 break;
Kevin O'Connor144817b2010-05-23 10:46:49 -040071 if (check_tsc(end)) {
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050072 // Timeout.
73 warn_timeout();
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040074 ohci_hub_disconnect(hub, port);
75 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050076 }
77 yield();
78 }
79
80 if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES))
81 // Device no longer present
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040082 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050083
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040084 return !!(sts & RH_PS_LSDA);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050085}
86
Kevin O'Connord28b0fe2010-03-28 15:11:19 -040087static struct usbhub_op_s ohci_HubOp = {
88 .detect = ohci_hub_detect,
89 .reset = ohci_hub_reset,
90 .disconnect = ohci_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_ohci_ports(struct usb_ohci_s *cntl)
Kevin O'Connor8ebcac02010-03-09 19:58:23 -050096{
97 ASSERT32FLAT();
98 // Turn on power for all devices on roothub.
Kevin O'Connor406fad62010-02-28 02:23:19 -050099 u32 rha = readl(&cntl->regs->roothub_a);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500100 rha &= ~(RH_A_PSM | RH_A_OCPM);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500101 writel(&cntl->regs->roothub_status, RH_HS_LPSC);
102 writel(&cntl->regs->roothub_b, RH_B_PPCM);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500103 msleep((rha >> 24) * 2);
104 // XXX - need to sleep for USB_TIME_SIGATT if just powered up?
105
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500106 struct usbhub_s hub;
107 memset(&hub, 0, sizeof(hub));
Kevin O'Connor406fad62010-02-28 02:23:19 -0500108 hub.cntl = &cntl->usb;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400109 hub.portcount = rha & RH_A_NDP;
110 hub.op = &ohci_HubOp;
111 usb_enumerate(&hub);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500112 return hub.devcount;
113}
114
115
116/****************************************************************
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500117 * Setup
118 ****************************************************************/
119
Kevin O'Connor98af3532012-02-20 12:54:49 -0500120// Wait for next USB frame to start - for ensuring safe memory release.
121static void
122ohci_waittick(struct usb_ohci_s *cntl)
123{
124 barrier();
125 struct ohci_hcca *hcca = (void*)cntl->regs->hcca;
126 u32 startframe = hcca->frame_no;
127 u64 end = calc_future_tsc(1000 * 5);
128 for (;;) {
129 if (hcca->frame_no != startframe)
130 break;
131 if (check_tsc(end)) {
132 warn_timeout();
133 return;
134 }
135 yield();
136 }
137}
138
139static void
140ohci_free_pipes(struct usb_ohci_s *cntl)
141{
142 dprintf(7, "ohci_free_pipes %p\n", cntl);
143
144 u32 creg = readl(&cntl->regs->control);
145 if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) {
146 writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE));
147 ohci_waittick(cntl);
148 }
149
150 u32 *pos = &cntl->regs->ed_controlhead;
151 for (;;) {
152 struct ohci_ed *next = (void*)*pos;
153 if (!next)
154 break;
155 struct ohci_pipe *pipe = container_of(next, struct ohci_pipe, ed);
156 if (pipe->pipe.cntl != &cntl->usb) {
157 *pos = next->hwNextED;
158 free(pipe);
159 } else {
160 pos = &next->hwNextED;
161 }
162 }
163
164 writel(&cntl->regs->ed_controlcurrent, 0);
165 writel(&cntl->regs->ed_bulkcurrent, 0);
166 writel(&cntl->regs->control, creg);
167 cntl->usb.freelist = NULL;
168}
169
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400170static int
Kevin O'Connor406fad62010-02-28 02:23:19 -0500171start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400172{
Kevin O'Connor406fad62010-02-28 02:23:19 -0500173 u32 oldfminterval = readl(&cntl->regs->fminterval);
174 u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400175
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400176 // XXX - check if already running?
Kevin O'Connor59f02832009-10-12 10:09:15 -0400177
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400178 // Do reset
Kevin O'Connor406fad62010-02-28 02:23:19 -0500179 writel(&cntl->regs->control, OHCI_USB_RESET | oldrwc);
180 readl(&cntl->regs->control); // flush writes
Kevin O'Connor8bbc79c2010-02-14 12:16:32 -0500181 msleep(USB_TIME_DRSTR);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400182
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400183 // Do software init (min 10us, max 2ms)
184 u64 end = calc_future_tsc_usec(10);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500185 writel(&cntl->regs->cmdstatus, OHCI_HCR);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400186 for (;;) {
Kevin O'Connor406fad62010-02-28 02:23:19 -0500187 u32 status = readl(&cntl->regs->cmdstatus);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400188 if (! status & OHCI_HCR)
189 break;
Kevin O'Connor144817b2010-05-23 10:46:49 -0400190 if (check_tsc(end)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500191 warn_timeout();
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400192 return -1;
193 }
Kevin O'Connor59f02832009-10-12 10:09:15 -0400194 }
195
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400196 // Init memory
Kevin O'Connor406fad62010-02-28 02:23:19 -0500197 writel(&cntl->regs->ed_controlhead, 0);
198 writel(&cntl->regs->ed_bulkhead, 0);
199 writel(&cntl->regs->hcca, (u32)hcca);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400200
201 // Init fminterval
202 u32 fi = oldfminterval & 0x3fff;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500203 writel(&cntl->regs->fminterval
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400204 , (((oldfminterval & FIT) ^ FIT)
205 | fi | (((6 * (fi - 210)) / 7) << 16)));
Kevin O'Connor406fad62010-02-28 02:23:19 -0500206 writel(&cntl->regs->periodicstart, ((9 * fi) / 10) & 0x3fff);
207 readl(&cntl->regs->control); // flush writes
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400208
209 // XXX - verify that fminterval was setup correctly.
210
211 // Go into operational state
Kevin O'Connor406fad62010-02-28 02:23:19 -0500212 writel(&cntl->regs->control
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400213 , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_PLE
214 | OHCI_USB_OPER | oldrwc));
Kevin O'Connor406fad62010-02-28 02:23:19 -0500215 readl(&cntl->regs->control); // flush writes
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400216
217 return 0;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400218}
219
220static void
Kevin O'Connor406fad62010-02-28 02:23:19 -0500221stop_ohci(struct usb_ohci_s *cntl)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400222{
Kevin O'Connor406fad62010-02-28 02:23:19 -0500223 u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC;
224 writel(&cntl->regs->control, oldrwc);
225 readl(&cntl->regs->control); // flush writes
Kevin O'Connor59f02832009-10-12 10:09:15 -0400226}
227
Kevin O'Connor406fad62010-02-28 02:23:19 -0500228static void
229configure_ohci(void *data)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400230{
Kevin O'Connor406fad62010-02-28 02:23:19 -0500231 struct usb_ohci_s *cntl = data;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400232
233 // Allocate memory
234 struct ohci_hcca *hcca = memalign_high(256, sizeof(*hcca));
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500235 struct ohci_ed *intr_ed = malloc_high(sizeof(*intr_ed));
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500236 if (!hcca || !intr_ed) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500237 warn_noalloc();
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500238 goto free;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400239 }
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400240 memset(hcca, 0, sizeof(*hcca));
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500241 memset(intr_ed, 0, sizeof(*intr_ed));
242 intr_ed->hwINFO = ED_SKIP;
243 int i;
244 for (i=0; i<ARRAY_SIZE(hcca->int_table); i++)
245 hcca->int_table[i] = (u32)intr_ed;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400246
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400247 int ret = start_ohci(cntl, hcca);
248 if (ret)
249 goto err;
250
251 int count = check_ohci_ports(cntl);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500252 free_pipe(cntl->usb.defaultpipe);
Kevin O'Connor98af3532012-02-20 12:54:49 -0500253 ohci_free_pipes(cntl);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400254 if (! count)
255 goto err;
Kevin O'Connora5826b52009-10-24 17:57:29 -0400256 return;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400257
258err:
259 stop_ohci(cntl);
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500260free:
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400261 free(hcca);
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500262 free(intr_ed);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400263}
264
Kevin O'Connor406fad62010-02-28 02:23:19 -0500265void
Kevin O'Connor8ff8e012011-07-09 14:11:21 -0400266ohci_init(struct pci_device *pci, int busid)
Kevin O'Connor406fad62010-02-28 02:23:19 -0500267{
268 if (! CONFIG_USB_OHCI)
269 return;
270 struct usb_ohci_s *cntl = malloc_tmphigh(sizeof(*cntl));
Kevin O'Connorffdcd3a2011-07-10 15:48:00 -0400271 if (!cntl) {
272 warn_noalloc();
273 return;
274 }
Kevin O'Connor406fad62010-02-28 02:23:19 -0500275 memset(cntl, 0, sizeof(*cntl));
276 cntl->usb.busid = busid;
Kevin O'Connor8ff8e012011-07-09 14:11:21 -0400277 cntl->usb.pci = pci;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500278 cntl->usb.type = USB_TYPE_OHCI;
279
Kevin O'Connor8ff8e012011-07-09 14:11:21 -0400280 u16 bdf = pci->bdf;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500281 u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0);
282 cntl->regs = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK);
283
Kevin O'Connor9dc243e2010-03-20 17:53:03 -0400284 dprintf(1, "OHCI init on dev %02x:%02x.%x (regs=%p)\n"
Kevin O'Connor406fad62010-02-28 02:23:19 -0500285 , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)
286 , pci_bdf_to_fn(bdf), cntl->regs);
287
288 // Enable bus mastering and memory access.
289 pci_config_maskw(bdf, PCI_COMMAND
290 , 0, PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY);
291
292 // XXX - check for and disable SMM control?
293
294 // Disable interrupts
295 writel(&cntl->regs->intrdisable, ~0);
296 writel(&cntl->regs->intrstatus, ~0);
297
298 run_thread(configure_ohci, cntl);
299}
300
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500301
302/****************************************************************
303 * End point communication
304 ****************************************************************/
305
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400306static int
307wait_ed(struct ohci_ed *ed)
308{
309 // XXX - 500ms just a guess
310 u64 end = calc_future_tsc(500);
311 for (;;) {
312 if (ed->hwHeadP == ed->hwTailP)
313 return 0;
Kevin O'Connor144817b2010-05-23 10:46:49 -0400314 if (check_tsc(end)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500315 warn_timeout();
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400316 return -1;
317 }
Kevin O'Connor229e3be2009-10-31 13:55:59 -0400318 yield();
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400319 }
320}
321
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500322struct usb_pipe *
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500323ohci_alloc_control_pipe(struct usb_pipe *dummy)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500324{
325 if (! CONFIG_USB_OHCI)
326 return NULL;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500327 struct usb_ohci_s *cntl = container_of(
328 dummy->cntl, struct usb_ohci_s, usb);
329 dprintf(7, "ohci_alloc_control_pipe %p\n", &cntl->usb);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500330
Kevin O'Connor98af3532012-02-20 12:54:49 -0500331 if (cntl->usb.freelist) {
332 // Use previously allocated queue head.
333 struct ohci_pipe *pipe = container_of(cntl->usb.freelist
334 , struct ohci_pipe, pipe);
335 cntl->usb.freelist = pipe->pipe.freenext;
336
337 memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
338 return &pipe->pipe;
339 }
340
341 // Allocate a new queue head.
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500342 struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe));
343 if (!pipe) {
344 warn_noalloc();
345 return NULL;
346 }
347 memset(pipe, 0, sizeof(*pipe));
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500348 memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
Kevin O'Connor0770d672010-03-09 19:33:22 -0500349 pipe->ed.hwINFO = ED_SKIP;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500350
351 // Add queue head to controller list.
Kevin O'Connor406fad62010-02-28 02:23:19 -0500352 pipe->ed.hwNextED = cntl->regs->ed_controlhead;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500353 barrier();
Kevin O'Connor406fad62010-02-28 02:23:19 -0500354 cntl->regs->ed_controlhead = (u32)&pipe->ed;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500355 return &pipe->pipe;
356}
357
Kevin O'Connor59f02832009-10-12 10:09:15 -0400358int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500359ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize
Kevin O'Connor59f02832009-10-12 10:09:15 -0400360 , void *data, int datasize)
361{
362 if (! CONFIG_USB_OHCI)
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400363 return -1;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500364 dprintf(5, "ohci_control %p\n", p);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500365 if (datasize > 4096) {
366 // XXX - should support larger sizes.
367 warn_noalloc();
368 return -1;
369 }
370 struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500371 struct usb_ohci_s *cntl = container_of(
372 pipe->pipe.cntl, struct usb_ohci_s, usb);
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500373 int maxpacket = pipe->pipe.maxpacket;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500374 int lowspeed = pipe->pipe.speed;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500375 int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400376
377 // Setup transfer descriptors
378 struct ohci_td *tds = malloc_tmphigh(sizeof(*tds) * 3);
Kevin O'Connor0770d672010-03-09 19:33:22 -0500379 if (!tds) {
380 warn_noalloc();
381 return -1;
382 }
383 struct ohci_td *td = tds;
384 td->hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC;
385 td->hwCBP = (u32)cmd;
386 td->hwNextTD = (u32)&td[1];
387 td->hwBE = (u32)cmd + cmdsize - 1;
388 td++;
389 if (datasize) {
390 td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_T_DATA1 | TD_CC;
391 td->hwCBP = (u32)data;
392 td->hwNextTD = (u32)&td[1];
393 td->hwBE = (u32)data + datasize - 1;
394 td++;
395 }
396 td->hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC;
397 td->hwCBP = 0;
398 td->hwNextTD = (u32)&td[1];
399 td->hwBE = 0;
400 td++;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400401
402 // Transfer data
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500403 pipe->ed.hwINFO = ED_SKIP;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400404 barrier();
Kevin O'Connor0770d672010-03-09 19:33:22 -0500405 pipe->ed.hwHeadP = (u32)tds;
406 pipe->ed.hwTailP = (u32)td;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400407 barrier();
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500408 pipe->ed.hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0);
Kevin O'Connor406fad62010-02-28 02:23:19 -0500409 writel(&cntl->regs->cmdstatus, OHCI_CLF);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400410
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500411 int ret = wait_ed(&pipe->ed);
412 pipe->ed.hwINFO = ED_SKIP;
Kevin O'Connorad901592009-12-13 11:25:25 -0500413 if (ret)
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500414 ohci_waittick(cntl);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400415 free(tds);
416 return ret;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400417}
418
419struct usb_pipe *
Kevin O'Connor2f968002010-09-19 22:10:08 -0400420ohci_alloc_bulk_pipe(struct usb_pipe *dummy)
421{
422 if (! CONFIG_USB_OHCI)
423 return NULL;
424 dprintf(1, "OHCI Bulk transfers not supported.\n");
425 return NULL;
426}
427
428int
429ohci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize)
430{
431 return -1;
432}
433
434struct usb_pipe *
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500435ohci_alloc_intr_pipe(struct usb_pipe *dummy, int frameexp)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400436{
437 if (! CONFIG_USB_OHCI)
438 return NULL;
Kevin O'Connor406fad62010-02-28 02:23:19 -0500439 struct usb_ohci_s *cntl = container_of(
440 dummy->cntl, struct usb_ohci_s, usb);
441 dprintf(7, "ohci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400442
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500443 if (frameexp > 5)
444 frameexp = 5;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500445 int maxpacket = dummy->maxpacket;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500446 int lowspeed = dummy->speed;
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500447 int devaddr = dummy->devaddr | (dummy->ep << 7);
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500448 // Determine number of entries needed for 2 timer ticks.
449 int ms = 1<<frameexp;
Kevin O'Connorbfe7ca72010-02-28 02:36:32 -0500450 int count = DIV_ROUND_UP(PIT_TICK_INTERVAL * 1000 * 2, PIT_TICK_RATE * ms)+1;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400451 struct ohci_pipe *pipe = malloc_low(sizeof(*pipe));
452 struct ohci_td *tds = malloc_low(sizeof(*tds) * count);
453 void *data = malloc_low(maxpacket * count);
454 if (!pipe || !tds || !data)
455 goto err;
Kevin O'Connor0770d672010-03-09 19:33:22 -0500456 memset(pipe, 0, sizeof(*pipe));
457 memcpy(&pipe->pipe, dummy, sizeof(pipe->pipe));
458 pipe->data = data;
459 pipe->count = count;
460 pipe->tds = tds;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400461
462 struct ohci_ed *ed = &pipe->ed;
463 ed->hwHeadP = (u32)&tds[0];
464 ed->hwTailP = (u32)&tds[count-1];
465 ed->hwINFO = devaddr | (maxpacket << 16) | (lowspeed ? ED_LOWSPEED : 0);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400466
467 int i;
468 for (i=0; i<count-1; i++) {
469 tds[i].hwINFO = TD_DP_IN | TD_T_TOGGLE | TD_CC;
470 tds[i].hwCBP = (u32)data + maxpacket * i;
471 tds[i].hwNextTD = (u32)&tds[i+1];
472 tds[i].hwBE = tds[i].hwCBP + maxpacket - 1;
473 }
474
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500475 // Add to interrupt schedule.
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400476 barrier();
Kevin O'Connor406fad62010-02-28 02:23:19 -0500477 struct ohci_hcca *hcca = (void*)cntl->regs->hcca;
Kevin O'Connor991eaff2010-02-13 21:51:47 -0500478 if (frameexp == 0) {
479 // Add to existing interrupt entry.
480 struct ohci_ed *intr_ed = (void*)hcca->int_table[0];
481 ed->hwNextED = intr_ed->hwNextED;
482 intr_ed->hwNextED = (u32)ed;
483 } else {
484 int startpos = 1<<(frameexp-1);
485 ed->hwNextED = hcca->int_table[startpos];
486 for (i=startpos; i<ARRAY_SIZE(hcca->int_table); i+=ms)
487 hcca->int_table[i] = (u32)ed;
488 }
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400489
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400490 return &pipe->pipe;
491
492err:
493 free(pipe);
494 free(tds);
495 free(data);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400496 return NULL;
497}
498
499int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500500ohci_poll_intr(struct usb_pipe *p, void *data)
Kevin O'Connor59f02832009-10-12 10:09:15 -0400501{
502 ASSERT16();
503 if (! CONFIG_USB_OHCI)
504 return -1;
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400505
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500506 struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe);
507 struct ohci_td *tds = GET_FLATPTR(pipe->tds);
Kevin O'Connor3cabf782011-05-29 09:48:28 -0400508 struct ohci_td *head = (void*)(GET_FLATPTR(pipe->ed.hwHeadP) & ~(ED_C|ED_H));
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500509 struct ohci_td *tail = (void*)GET_FLATPTR(pipe->ed.hwTailP);
510 int count = GET_FLATPTR(pipe->count);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400511 int pos = (tail - tds + 1) % count;
512 struct ohci_td *next = &tds[pos];
513 if (head == next)
514 // No intrs found.
515 return -1;
516 // XXX - check for errors.
517
518 // Copy data.
Kevin O'Connor4547eb92010-02-28 02:17:28 -0500519 int maxpacket = GET_FLATPTR(pipe->pipe.maxpacket);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500520 void *pipedata = GET_FLATPTR(pipe->data);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400521 void *intrdata = pipedata + maxpacket * pos;
522 memcpy_far(GET_SEG(SS), data
523 , FLATPTR_TO_SEG(intrdata), (void*)FLATPTR_TO_OFFSET(intrdata)
524 , maxpacket);
525
526 // Reenable this td.
527 SET_FLATPTR(tail->hwINFO, TD_DP_IN | TD_T_TOGGLE | TD_CC);
528 intrdata = pipedata + maxpacket * (tail-tds);
529 SET_FLATPTR(tail->hwCBP, (u32)intrdata);
530 SET_FLATPTR(tail->hwNextTD, (u32)next);
531 SET_FLATPTR(tail->hwBE, (u32)intrdata + maxpacket - 1);
Kevin O'Connor0770d672010-03-09 19:33:22 -0500532 barrier();
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500533 SET_FLATPTR(pipe->ed.hwTailP, (u32)next);
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400534
535 return 0;
Kevin O'Connor59f02832009-10-12 10:09:15 -0400536}