i.MX31: Now that it matters because there's a debug screeen that allows changing...
[kugel-rb.git] / firmware / target / arm / usb-s3c6400x.c
blobeb7a0170bf4c84a30f492c3e2fdf32ce7df14501
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2009 by Michael Sparmann
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "config.h"
23 #include "usb.h"
24 #include "usb-target.h"
25 #include "usb_drv.h"
27 #include "cpu.h"
28 #include "system.h"
29 #include "kernel.h"
30 #include "panic.h"
32 #include "usb-s3c6400x.h"
34 #ifdef HAVE_USBSTACK
35 #include "usb_ch9.h"
36 #include "usb_core.h"
37 #include <inttypes.h>
38 #include "power.h"
40 struct ep_type
42 bool active;
43 bool busy;
44 bool done;
45 int rc;
46 int size;
47 struct wakeup complete;
48 } ;
50 static struct ep_type endpoints[USB_NUM_ENDPOINTS];
51 static struct usb_ctrlrequest ctrlreq USB_DEVBSS_ATTR;
53 int usb_drv_port_speed(void)
55 return (DSTS & 2) == 0 ? 1 : 0;
58 static void reset_endpoints(int reinit)
60 unsigned int i;
61 for (i = 0; i < sizeof(endpoints)/sizeof(struct ep_type); i++)
63 if (reinit) endpoints[i].active = false;
64 endpoints[i].busy = false;
65 endpoints[i].rc = -1;
66 endpoints[i].done = true;
67 wakeup_signal(&endpoints[i].complete);
69 DIEPCTL0 = 0x8800; /* EP0 IN ACTIVE NEXT=1 */
70 DOEPCTL0 = 0x8000; /* EP0 OUT ACTIVE */
71 DOEPTSIZ0 = 0x20080040; /* EP0 OUT Transfer Size:
72 64 Bytes, 1 Packet, 1 Setup Packet */
73 DOEPDMA0 = &ctrlreq;
74 DOEPCTL0 |= 0x84000000; /* EP0 OUT ENABLE CLEARNAK */
75 if (reinit)
77 /* The size is getting set to zero, because we don't know
78 whether we are Full Speed or High Speed at this stage */
79 /* EP1 IN INACTIVE DATA0 SIZE=0 NEXT=3 */
80 DIEPCTL1 = 0x10001800;
81 /* EP2 OUT INACTIVE DATA0 SIZE=0 */
82 DOEPCTL2 = 0x10000000;
83 /* EP3 IN INACTIVE DATA0 SIZE=0 NEXT=0 */
84 DIEPCTL3 = 0x10000000;
85 /* EP4 OUT INACTIVE DATA0 SIZE=0 */
86 DOEPCTL4 = 0x10000000;
88 else
90 /* INACTIVE DATA0 */
91 DIEPCTL1 = (DIEPCTL1 & ~0x00008000) | 0x10000000;
92 DOEPCTL2 = (DOEPCTL2 & ~0x00008000) | 0x10000000;
93 DIEPCTL3 = (DIEPCTL3 & ~0x00008000) | 0x10000000;
94 DOEPCTL4 = (DOEPCTL4 & ~0x00008000) | 0x10000000;
96 DAINTMSK = 0xFFFFFFFF; /* Enable interrupts on all EPs */
99 int usb_drv_request_endpoint(int type, int dir)
101 size_t ep;
102 int ret = -1;
104 if (dir == USB_DIR_IN) ep = 1;
105 else ep = 2;
107 while (ep < USB_NUM_ENDPOINTS)
109 if (!endpoints[ep].active)
111 endpoints[ep].active = true;
112 ret = ep | dir;
113 uint32_t newbits = (type << 18) | 0x10000000;
114 if (dir) DIEPCTL(ep) = (DIEPCTL(ep) & ~0x000C0000) | newbits;
115 else DOEPCTL(ep) = (DOEPCTL(ep) & ~0x000C0000) | newbits;
116 break;
118 ep += 2;
121 return ret;
124 void usb_drv_release_endpoint(int ep)
126 ep = ep & 0x7f;
128 if (ep < 1 || ep > USB_NUM_ENDPOINTS) return;
130 endpoints[ep].active = false;
133 static void usb_reset(void)
135 volatile int i;
137 DCTL = 0x802; /* Soft Disconnect */
139 OPHYPWR = 0; /* PHY: Power up */
140 OPHYUNK1 = 1;
141 OPHYUNK2 = 0xE3F;
142 OPHYCLK = SYNOPSYSOTG_CLOCK;
143 ORSTCON = 1; /* PHY: Assert Software Reset */
144 for (i = 0; i < 50; i++);
145 ORSTCON = 0; /* PHY: Deassert Software Reset */
146 OPHYUNK3 = 0x600;
148 GRSTCTL = 1; /* OTG: Assert Software Reset */
149 while (GRSTCTL & 1); /* Wait for OTG to ack reset */
150 while (!(GRSTCTL & 0x80000000)); /* Wait for OTG AHB master idle */
152 GRXFSIZ = 0x00000200; /* RX FIFO: 512 bytes */
153 GNPTXFSIZ = 0x02000200; /* Non-periodic TX FIFO: 512 bytes */
154 GAHBCFG = SYNOPSYSOTG_AHBCFG;
155 GUSBCFG = 0x1408; /* OTG: 16bit PHY and some reserved bits */
157 DCFG = 4; /* Address 0 */
158 DCTL = 0x800; /* Soft Reconnect */
159 DIEPMSK = 0x0D; /* IN EP interrupt mask */
160 DOEPMSK = 0x0D; /* IN EP interrupt mask */
161 DAINTMSK = 0xFFFFFFFF; /* Enable interrupts on all endpoints */
162 GINTMSK = 0xC3000; /* Interrupt mask: IN event, OUT event, bus reset */
164 reset_endpoints(1);
167 /* IRQ handler */
168 void INT_USB_FUNC(void)
170 int i;
171 uint32_t ints = GINTSTS;
172 uint32_t epints;
173 if (ints & 0x1000) /* bus reset */
175 DCFG = 4; /* Address 0 */
176 reset_endpoints(1);
177 usb_core_bus_reset();
180 if (ints & 0x2000) /* enumeration done, we now know the speed */
182 /* Set up the maximum packet sizes accordingly */
183 uint32_t maxpacket = usb_drv_port_speed() ? 512 : 64;
184 DIEPCTL1 = (DIEPCTL1 & ~0x000003FF) | maxpacket;
185 DOEPCTL2 = (DOEPCTL2 & ~0x000003FF) | maxpacket;
186 DIEPCTL3 = (DIEPCTL3 & ~0x000003FF) | maxpacket;
187 DOEPCTL4 = (DOEPCTL4 & ~0x000003FF) | maxpacket;
190 if (ints & 0x40000) /* IN EP event */
191 for (i = 0; i < 4; i += i + 1) // 0, 1, 3
192 if ((epints = DIEPINT(i)))
194 if (epints & 1) /* Transfer completed */
196 invalidate_dcache();
197 int bytes = endpoints[i].size - (DIEPTSIZ(i) & 0x3FFFF);
198 if (endpoints[i].busy)
200 endpoints[i].busy = false;
201 endpoints[i].rc = 0;
202 endpoints[i].done = true;
203 usb_core_transfer_complete(i, USB_DIR_IN, 0, bytes);
204 wakeup_signal(&endpoints[i].complete);
207 if (epints & 4) /* AHB error */
208 panicf("USB: AHB error on IN EP%d", i);
209 if (epints & 8) /* Timeout */
211 if (endpoints[i].busy)
213 endpoints[i].busy = false;
214 endpoints[i].rc = 1;
215 endpoints[i].done = true;
216 wakeup_signal(&endpoints[i].complete);
219 DIEPINT(i) = epints;
222 if (ints & 0x80000) /* OUT EP event */
223 for (i = 0; i < USB_NUM_ENDPOINTS; i += 2)
224 if ((epints = DOEPINT(i)))
226 if (epints & 1) /* Transfer completed */
228 invalidate_dcache();
229 int bytes = endpoints[i].size - (DOEPTSIZ(i) & 0x3FFFF);
230 if (endpoints[i].busy)
232 endpoints[i].busy = false;
233 endpoints[i].rc = 0;
234 endpoints[i].done = true;
235 usb_core_transfer_complete(i, USB_DIR_OUT, 0, bytes);
236 wakeup_signal(&endpoints[i].complete);
239 if (epints & 4) /* AHB error */
240 panicf("USB: AHB error on OUT EP%d", i);
241 if (epints & 8) /* SETUP phase done */
243 invalidate_dcache();
244 if (i == 0)
246 if (ctrlreq.bRequest == 5)
248 /* Already set the new address here,
249 before passing the packet to the core.
250 See below (usb_drv_set_address) for details. */
251 DCFG = (DCFG & ~0x7F0) | (ctrlreq.wValue << 4);
253 usb_core_control_request(&ctrlreq);
255 else panicf("USB: SETUP done on OUT EP%d!?", i);
257 /* Make sure EP0 OUT is set up to accept the next request */
258 if (!i)
260 DOEPTSIZ0 = 0x20080040;
261 DOEPDMA0 = &ctrlreq;
262 DOEPCTL0 |= 0x84000000;
264 DOEPINT(i) = epints;
267 GINTSTS = ints;
270 void usb_drv_set_address(int address)
272 (void)address;
273 /* Ignored intentionally, because the controller requires us to set the
274 new address before sending the response for some reason. So we'll
275 already set it when the control request arrives, before passing that
276 into the USB core, which will then call this dummy function. */
279 static void ep_send(int ep, const void *ptr, int length)
281 endpoints[ep].busy = true;
282 endpoints[ep].size = length;
283 DIEPCTL(ep) |= 0x8000; /* EPx OUT ACTIVE */
284 int blocksize = usb_drv_port_speed() ? 512 : 64;
285 int packets = (length + blocksize - 1) / blocksize;
286 if (!length)
288 DIEPTSIZ(ep) = 1 << 19; /* one empty packet */
289 DIEPDMA(ep) = NULL;
291 else
293 DIEPTSIZ(ep) = length | (packets << 19);
294 DIEPDMA(ep) = ptr;
296 clean_dcache();
297 DIEPCTL(ep) |= 0x84000000; /* EPx OUT ENABLE CLEARNAK */
300 static void ep_recv(int ep, void *ptr, int length)
302 endpoints[ep].busy = true;
303 endpoints[ep].size = length;
304 DOEPCTL(ep) &= ~0x20000; /* EPx UNSTALL */
305 DOEPCTL(ep) |= 0x8000; /* EPx OUT ACTIVE */
306 int blocksize = usb_drv_port_speed() ? 512 : 64;
307 int packets = (length + blocksize - 1) / blocksize;
308 if (!length)
310 DOEPTSIZ(ep) = 1 << 19; /* one empty packet */
311 DOEPDMA(ep) = NULL;
313 else
315 DOEPTSIZ(ep) = length | (packets << 19);
316 DOEPDMA(ep) = ptr;
318 clean_dcache();
319 DOEPCTL(ep) |= 0x84000000; /* EPx OUT ENABLE CLEARNAK */
322 int usb_drv_send(int endpoint, void *ptr, int length)
324 endpoint &= 0x7f;
325 endpoints[endpoint].done = false;
326 ep_send(endpoint, ptr, length);
327 while (!endpoints[endpoint].done && endpoints[endpoint].busy)
328 wakeup_wait(&endpoints[endpoint].complete, TIMEOUT_BLOCK);
329 return endpoints[endpoint].rc;
332 int usb_drv_send_nonblocking(int endpoint, void *ptr, int length)
334 ep_send(endpoint & 0x7f, ptr, length);
335 return 0;
338 int usb_drv_recv(int endpoint, void* ptr, int length)
340 ep_recv(endpoint & 0x7f, ptr, length);
341 return 0;
344 void usb_drv_cancel_all_transfers(void)
346 int flags = disable_irq_save();
347 reset_endpoints(0);
348 restore_irq(flags);
351 void usb_drv_set_test_mode(int mode)
353 (void)mode;
356 bool usb_drv_stalled(int endpoint, bool in)
358 if (in) return DIEPCTL(endpoint) & 0x00200000 ? true : false;
359 else return DOEPCTL(endpoint) & 0x00200000 ? true : false;
362 void usb_drv_stall(int endpoint, bool stall, bool in)
364 if (in)
366 if (stall) DIEPCTL(endpoint) |= 0x00200000;
367 else DIEPCTL(endpoint) &= ~0x00200000;
369 else
371 if (stall) DOEPCTL(endpoint) |= 0x00200000;
372 else DOEPCTL(endpoint) &= ~0x00200000;
376 void usb_drv_init(void)
378 /* Enable USB clock */
379 #if CONFIG_CPU==S5L8701
380 PWRCON &= ~0x4000;
381 PWRCONEXT &= ~0x800;
382 INTMSK |= INTMSK_USB_OTG;
383 #elif CONFIG_CPU==S5L8702
384 PWRCON(0) &= ~0x4;
385 PWRCON(1) &= ~0x8;
386 VIC0INTENABLE |= 1 << 19;
387 #endif
388 PCGCCTL = 0;
390 /* reset the beast */
391 usb_reset();
394 void usb_drv_exit(void)
396 DCTL = 0x802; /* Soft Disconnect */
398 ORSTCON = 1; /* Put the PHY into reset (needed to get current down) */
399 PCGCCTL = 1; /* Shut down PHY clock */
400 OPHYPWR = 0xF; /* PHY: Power down */
402 #if CONFIG_CPU==S5L8701
403 PWRCON |= 0x4000;
404 PWRCONEXT |= 0x800;
405 #elif CONFIG_CPU==S5L8702
406 PWRCON(0) |= 0x4;
407 PWRCON(1) |= 0x8;
408 #endif
411 void usb_init_device(void)
413 unsigned int i;
414 for (i = 0; i < sizeof(endpoints)/sizeof(struct ep_type); i++)
415 wakeup_init(&endpoints[i].complete);
417 /* Power up the core clocks to allow writing
418 to some registers needed to power it down */
419 PCGCCTL = 0;
420 #if CONFIG_CPU==S5L8701
421 PWRCON &= ~0x4000;
422 PWRCONEXT &= ~0x800;
423 INTMSK |= INTMSK_USB_OTG;
424 #elif CONFIG_CPU==S5L8702
425 PWRCON(0) &= ~0x4;
426 PWRCON(1) &= ~0x8;
427 VIC0INTENABLE |= 1 << 19;
428 #endif
430 usb_drv_exit();
433 void usb_enable(bool on)
435 if (on) usb_core_init();
436 else usb_core_exit();
439 void usb_attach(void)
441 usb_enable(true);
444 int usb_detect(void)
446 if (charger_inserted())
447 return USB_INSERTED;
448 return USB_EXTRACTED;
451 #else
452 void usb_init_device(void)
454 DCTL = 0x802; /* Soft Disconnect */
456 ORSTCON = 1; /* Put the PHY into reset (needed to get current down) */
457 PCGCCTL = 1; /* Shut down PHY clock */
458 OPHYPWR = 0xF; /* PHY: Power down */
460 #if CONFIG_CPU==S5L8701
461 PWRCON |= 0x4000;
462 PWRCONEXT |= 0x800;
463 #elif CONFIG_CPU==S5L8702
464 PWRCON(0) |= 0x4;
465 PWRCON(1) |= 0x8;
466 #endif
469 void usb_enable(bool on)
471 (void)on;
474 /* Always return false for now */
475 int usb_detect(void)
477 return USB_EXTRACTED;
479 #endif