Move c/h files implementing/defining standard library stuff into a new libc directory...
[kugel-rb.git] / firmware / target / arm / usb-s3c6400x.c
blob3f28e7f379e9a0dafbfedbae1fd2643b09be65cb
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"
25 #define OTGBASE 0x38800000
26 #define PHYBASE 0x3C400000
27 #include "usb-s3c6400x.h"
29 #include "cpu.h"
30 #include "system.h"
31 #include "kernel.h"
32 #include "panic.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[5];
51 static struct usb_ctrlrequest ctrlreq USB_DEVBSS_ATTR;
53 int usb_drv_port_speed(void)
55 return (DSTS & 2) == 0 ? 1 : 0;
58 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 = (uint32_t)&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 < 5)
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 ORSTCON = 1; /* PHY: Assert Software Reset */
141 for (i = 0; i < 50; i++);
142 ORSTCON = 0; /* PHY: Deassert Software Reset */
143 OPHYCLK = 0; /* PHY: 48MHz clock */
145 GRSTCTL = 1; /* OTG: Assert Software Reset */
146 while (GRSTCTL & 1); /* Wait for OTG to ack reset */
147 while (!(GRSTCTL & 0x80000000)); /* Wait for OTG AHB master idle */
149 GAHBCFG = 0x27; /* OTG AHB config: Unmask ints, burst length 4, DMA on */
150 GUSBCFG = 0x1408; /* OTG: 16bit PHY and some reserved bits */
152 DCFG = 4; /* Address 0 */
153 DCTL = 0x800; /* Soft Reconnect */
154 DIEPMSK = 0x0D; /* IN EP interrupt mask */
155 DOEPMSK = 0x0D; /* IN EP interrupt mask */
156 GINTMSK = 0xC3000; /* Interrupt mask: IN event, OUT event, bus reset */
158 reset_endpoints(1);
161 /* IRQ handler */
162 void INT_USB_FUNC(void)
164 int i;
165 if (GINTSTS & 0x1000) /* bus reset */
167 DCFG = 4; /* Address 0 */
168 reset_endpoints(1);
169 usb_core_bus_reset();
172 if (GINTSTS & 0x2000) /* enumeration done, we now know the speed */
174 /* Set up the maximum packet sizes accordingly */
175 uint32_t maxpacket = usb_drv_port_speed() ? 512 : 64;
176 DIEPCTL1 = (DIEPCTL1 & ~0x000003FF) | maxpacket;
177 DOEPCTL2 = (DOEPCTL2 & ~0x000003FF) | maxpacket;
178 DIEPCTL3 = (DIEPCTL3 & ~0x000003FF) | maxpacket;
179 DOEPCTL4 = (DOEPCTL4 & ~0x000003FF) | maxpacket;
182 if (GINTSTS & 0x40000) /* IN EP event */
183 for (i = 0; i < 5; i ++)
184 if (i != 2 && i != 4 && DIEPINT(i))
186 if (DIEPINT(i) & 1) /* Transfer completed */
188 invalidate_dcache();
189 int bytes = endpoints[i].size - (DIEPTSIZ(i) & 0x3FFFF);
190 if (endpoints[i].busy)
192 endpoints[i].busy = false;
193 endpoints[i].rc = 0;
194 endpoints[i].done = true;
195 usb_core_transfer_complete(i, USB_DIR_IN, 0, bytes);
196 wakeup_signal(&endpoints[i].complete);
199 if (DIEPINT(i) & 4) /* AHB error */
200 panicf("USB: AHB error on IN EP%d", i);
201 if (DIEPINT(i) & 8) /* Timeout */
203 if (endpoints[i].busy)
205 endpoints[i].busy = false;
206 endpoints[i].rc = 1;
207 endpoints[i].done = true;
208 wakeup_signal(&endpoints[i].complete);
211 DIEPINT(i) = DIEPINT(i);
214 if (GINTSTS & 0x80000) /* OUT EP event */
215 for (i = 0; i < 5; i += 2)
216 if (DOEPINT(i))
218 if (DOEPINT(i) & 1) /* Transfer completed */
220 invalidate_dcache();
221 int bytes = endpoints[i].size - (DOEPTSIZ(i) & 0x3FFFF);
222 if (endpoints[i].busy)
224 endpoints[i].busy = false;
225 endpoints[i].rc = 0;
226 endpoints[i].done = true;
227 usb_core_transfer_complete(i, USB_DIR_OUT, 0, bytes);
228 wakeup_signal(&endpoints[i].complete);
231 if (DOEPINT(i) & 4) /* AHB error */
232 panicf("USB: AHB error on OUT EP%d", i);
233 if (DOEPINT(i) & 8) /* SETUP phase done */
235 invalidate_dcache();
236 if (i == 0)
238 if (ctrlreq.bRequest == 5)
240 /* Already set the new address here,
241 before passing the packet to the core.
242 See below (usb_drv_set_address) for details. */
243 DCFG = (DCFG & ~0x7F0) | (ctrlreq.wValue << 4);
245 usb_core_control_request(&ctrlreq);
247 else panicf("USB: SETUP done on OUT EP%d!?", i);
249 /* Make sure EP0 OUT is set up to accept the next request */
250 if (!i)
252 DOEPTSIZ0 = 0x20080040;
253 DOEPDMA0 = (uint32_t)&ctrlreq;
254 DOEPCTL0 |= 0x84000000;
256 DOEPINT(i) = DOEPINT(i);
259 GINTSTS = GINTSTS;
262 void usb_drv_set_address(int address)
264 (void)address;
265 /* Ignored intentionally, because the controller requires us to set the
266 new address before sending the response for some reason. So we'll
267 already set it when the control request arrives, before passing that
268 into the USB core, which will then call this dummy function. */
271 void ep_send(int ep, void *ptr, int length)
273 endpoints[ep].busy = true;
274 endpoints[ep].size = length;
275 DIEPCTL(ep) |= 0x8000; /* EPx OUT ACTIVE */
276 int blocksize = usb_drv_port_speed() ? 512 : 64;
277 int packets = (length + blocksize - 1) / blocksize;
278 if (!length)
280 DIEPTSIZ(ep) = 1 << 19; /* one empty packet */
281 DIEPDMA(ep) = 0x10000000; /* dummy address */
283 else
285 DIEPTSIZ(ep) = length | (packets << 19);
286 DIEPDMA(ep) = (uint32_t)ptr;
288 clean_dcache();
289 DIEPCTL(ep) |= 0x84000000; /* EPx OUT ENABLE CLEARNAK */
292 void ep_recv(int ep, void *ptr, int length)
294 endpoints[ep].busy = true;
295 endpoints[ep].size = length;
296 DOEPCTL(ep) &= ~0x20000; /* EPx UNSTALL */
297 DOEPCTL(ep) |= 0x8000; /* EPx OUT ACTIVE */
298 int blocksize = usb_drv_port_speed() ? 512 : 64;
299 int packets = (length + blocksize - 1) / blocksize;
300 if (!length)
302 DOEPTSIZ(ep) = 1 << 19; /* one empty packet */
303 DOEPDMA(ep) = 0x10000000; /* dummy address */
305 else
307 DOEPTSIZ(ep) = length | (packets << 19);
308 DOEPDMA(ep) = (uint32_t)ptr;
310 clean_dcache();
311 DOEPCTL(ep) |= 0x84000000; /* EPx OUT ENABLE CLEARNAK */
314 int usb_drv_send(int endpoint, void *ptr, int length)
316 endpoint &= 0x7f;
317 endpoints[endpoint].done = false;
318 ep_send(endpoint, ptr, length);
319 while (!endpoints[endpoint].done && endpoints[endpoint].busy)
320 wakeup_wait(&endpoints[endpoint].complete, TIMEOUT_BLOCK);
321 return endpoints[endpoint].rc;
324 int usb_drv_send_nonblocking(int endpoint, void *ptr, int length)
326 ep_send(endpoint & 0x7f, ptr, length);
327 return 0;
330 int usb_drv_recv(int endpoint, void* ptr, int length)
332 ep_recv(endpoint & 0x7f, ptr, length);
333 return 0;
336 void usb_drv_cancel_all_transfers(void)
338 int flags = disable_irq_save();
339 reset_endpoints(0);
340 restore_irq(flags);
343 void usb_drv_set_test_mode(int mode)
345 (void)mode;
348 bool usb_drv_stalled(int endpoint, bool in)
350 if (in) return DIEPCTL(endpoint) & 0x00200000 ? true : false;
351 else return DOEPCTL(endpoint) & 0x00200000 ? true : false;
354 void usb_drv_stall(int endpoint, bool stall, bool in)
356 if (in)
358 if (stall) DIEPCTL(endpoint) |= 0x00200000;
359 else DIEPCTL(endpoint) &= ~0x00200000;
361 else
363 if (stall) DOEPCTL(endpoint) |= 0x00200000;
364 else DOEPCTL(endpoint) &= ~0x00200000;
368 void usb_drv_init(void)
370 /* Enable USB clock */
371 PWRCON &= ~0x4000;
372 PWRCONEXT &= ~0x800;
373 PCGCCTL = 0;
375 /* unmask irq */
376 INTMSK |= INTMSK_USB_OTG;
378 /* reset the beast */
379 usb_reset();
382 void usb_drv_exit(void)
384 DCTL = 0x802; /* Soft Disconnect */
386 ORSTCON = 1; /* Put the PHY into reset (needed to get current down) */
387 PCGCCTL = 1; /* Shut down PHY clock */
388 OPHYPWR = 0xF; /* PHY: Power down */
390 PWRCON |= 0x4000;
391 PWRCONEXT |= 0x800;
394 void usb_init_device(void)
396 unsigned int i;
397 for (i = 0; i < sizeof(endpoints)/sizeof(struct ep_type); i++)
398 wakeup_init(&endpoints[i].complete);
399 usb_drv_exit();
402 void usb_enable(bool on)
404 if (on) usb_core_init();
405 else usb_core_exit();
408 void usb_attach(void)
410 usb_enable(true);
413 int usb_detect(void)
415 if (charger_inserted())
416 return USB_INSERTED;
417 return USB_EXTRACTED;
420 #else
421 void usb_init_device(void)
423 DCTL = 0x802; /* Soft Disconnect */
425 ORSTCON = 1; /* Put the PHY into reset (needed to get current down) */
426 PCGCCTL = 1; /* Shut down PHY clock */
427 OPHYPWR = 0xF; /* PHY: Power down */
429 PWRCON |= 0x4000;
430 PWRCONEXT |= 0x800;
433 void usb_enable(bool on)
435 (void)on;
438 /* Always return false for now */
439 int usb_detect(void)
441 return USB_EXTRACTED;
443 #endif