diff options
Diffstat (limited to 'utils/hwstub/stub/atj213x/usb_drv_atj213x.c')
-rw-r--r-- | utils/hwstub/stub/atj213x/usb_drv_atj213x.c | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/utils/hwstub/stub/atj213x/usb_drv_atj213x.c b/utils/hwstub/stub/atj213x/usb_drv_atj213x.c new file mode 100644 index 0000000000..ef66766527 --- /dev/null +++ b/utils/hwstub/stub/atj213x/usb_drv_atj213x.c | |||
@@ -0,0 +1,267 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2014 by Marcin Bukat | ||
10 | Amaury Pouly | ||
11 | * | ||
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. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "usb_drv.h" | ||
23 | #include "config.h" | ||
24 | #include "memory.h" | ||
25 | #include "target.h" | ||
26 | #include "atj213x.h" | ||
27 | |||
28 | #define USB_FULL_SPEED 0 | ||
29 | #define USB_HIGH_SPEED 1 | ||
30 | |||
31 | volatile bool setup_data_valid = false; | ||
32 | volatile int udc_speed = USB_FULL_SPEED; | ||
33 | |||
34 | static void usb_copy_from(void *ptr, volatile void *reg, size_t sz) | ||
35 | { | ||
36 | uint32_t *p = ptr; | ||
37 | volatile uint32_t *rp = reg; | ||
38 | /* do not overflow the destination buffer ! */ | ||
39 | while(sz >= 4) | ||
40 | { | ||
41 | *p++ = *rp++; | ||
42 | sz -= 4; | ||
43 | } | ||
44 | |||
45 | if(sz == 0) | ||
46 | return; | ||
47 | |||
48 | /* reminder */ | ||
49 | uint32_t cache = *rp; | ||
50 | uint8_t *p8 = (void *)p; | ||
51 | while(sz-- > 0) | ||
52 | { | ||
53 | *p8++ = cache; | ||
54 | cache >>= 8; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | static void usb_copy_to(volatile void *reg, void *ptr, size_t sz) | ||
59 | { | ||
60 | uint32_t *p = ptr; | ||
61 | volatile uint32_t *rp = reg; | ||
62 | sz = (sz + 3) / 4; | ||
63 | /* read may overflow the source buffer but | ||
64 | * it will not overwrite anything | ||
65 | */ | ||
66 | while(sz-- > 0) | ||
67 | *rp++ = *p++; | ||
68 | } | ||
69 | |||
70 | void INT_UDC(void) | ||
71 | { | ||
72 | /* get possible sources */ | ||
73 | unsigned int usbirq = OTG_USBIRQ; | ||
74 | unsigned int otgirq = OTG_OTGIRQ; | ||
75 | #if 0 | ||
76 | unsigned int usbeirq = OTG_USBEIRQ; | ||
77 | unsigned int epinirq = OTG_IN04IRQ; | ||
78 | unsigned int epoutirq = OTG_OUT04IRQ; | ||
79 | #endif | ||
80 | |||
81 | /* HS, Reset, Setup */ | ||
82 | if (usbirq) | ||
83 | { | ||
84 | |||
85 | if (usbirq & (1<<5)) | ||
86 | { | ||
87 | /* HS irq */ | ||
88 | udc_speed = USB_HIGH_SPEED; | ||
89 | } | ||
90 | else if (usbirq & (1<<4)) | ||
91 | { | ||
92 | /* Reset */ | ||
93 | udc_speed = USB_FULL_SPEED; | ||
94 | |||
95 | /* clear all pending irqs */ | ||
96 | OTG_OUT04IRQ = 0xff; | ||
97 | OTG_IN04IRQ = 0xff; | ||
98 | } | ||
99 | else if (usbirq & (1<<0)) | ||
100 | { | ||
101 | /* Setup data valid */ | ||
102 | setup_data_valid = true; | ||
103 | } | ||
104 | |||
105 | /* clear irq flags */ | ||
106 | OTG_USBIRQ = usbirq; | ||
107 | } | ||
108 | |||
109 | #if 0 | ||
110 | if (epoutirq) | ||
111 | { | ||
112 | OTG_OUT04IRQ = epoutirq; | ||
113 | } | ||
114 | |||
115 | if (epinirq) | ||
116 | { | ||
117 | OTG_IN04IRQ = epinirq; | ||
118 | } | ||
119 | #endif | ||
120 | |||
121 | if (otgirq) | ||
122 | { | ||
123 | OTG_OTGIRQ = otgirq; | ||
124 | } | ||
125 | |||
126 | OTG_USBEIRQ = 0x50; | ||
127 | } | ||
128 | |||
129 | void usb_drv_init(void) | ||
130 | { | ||
131 | OTG_USBCS |= 0x40; /* soft disconnect */ | ||
132 | |||
133 | OTG_ENDPRST = 0x10; /* reset all ep fifos */ | ||
134 | OTG_ENDPRST = 0x70; | ||
135 | OTG_ENDPRST = 0x00; | ||
136 | OTG_ENDPRST = 0x60; | ||
137 | |||
138 | OTG_USBIRQ = 0xff; /* clear all pending interrupts */ | ||
139 | OTG_OTGIRQ = 0xff; | ||
140 | OTG_IN04IRQ = 0xff; | ||
141 | OTG_OUT04IRQ = 0xff; | ||
142 | OTG_USBEIRQ = 0x50; /* UDC ? with 0x40 there is irq storm */ | ||
143 | |||
144 | OTG_USBIEN = (1<<5) | (1<<4) | (1<<0); /* HS, Reset, Setup_data */ | ||
145 | OTG_OTGIEN = 0; | ||
146 | |||
147 | /* disable interrupts from ep0 */ | ||
148 | OTG_IN04IEN = 0; | ||
149 | OTG_OUT04IEN = 0; | ||
150 | |||
151 | /* unmask UDC interrupt in interrupt controller */ | ||
152 | INTC_MSK = (1<<4); | ||
153 | |||
154 | target_mdelay(100); | ||
155 | |||
156 | OTG_USBCS &= ~0x40; /* soft connect */ | ||
157 | } | ||
158 | |||
159 | int usb_drv_recv_setup(struct usb_ctrlrequest *req) | ||
160 | { | ||
161 | while (!setup_data_valid) | ||
162 | ; | ||
163 | |||
164 | usb_copy_from(req, &OTG_SETUPDAT, sizeof(struct usb_ctrlrequest)); | ||
165 | setup_data_valid = false; | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | int usb_drv_port_speed(void) | ||
170 | { | ||
171 | return (int)udc_speed; | ||
172 | } | ||
173 | |||
174 | /* Set the address (usually it's in a register). | ||
175 | * There is a problem here: some controller want the address to be set between | ||
176 | * control out and ack and some want to wait for the end of the transaction. | ||
177 | * In the first case, you need to write some code special code when getting | ||
178 | * setup packets and ignore this function (have a look at other drives) | ||
179 | */ | ||
180 | void usb_drv_set_address(int address) | ||
181 | { | ||
182 | (void)address; | ||
183 | /* UDC sets this automaticaly */ | ||
184 | } | ||
185 | |||
186 | /* TODO: Maybe adapt to irq scheme */ | ||
187 | int usb_drv_send(int endpoint, void *ptr, int length) | ||
188 | { | ||
189 | (void)endpoint; | ||
190 | |||
191 | int xfer_size, cnt = length; | ||
192 | |||
193 | while (cnt) | ||
194 | { | ||
195 | xfer_size = MIN(cnt, 64); | ||
196 | |||
197 | /* copy data to ep0in buffer */ | ||
198 | usb_copy_to(&OTG_EP0INDAT, ptr, xfer_size); | ||
199 | |||
200 | /* this marks data as ready to send */ | ||
201 | OTG_IN0BC = xfer_size; | ||
202 | |||
203 | /* wait for the transfer end */ | ||
204 | while(OTG_EP0CS & 0x04) | ||
205 | ; | ||
206 | |||
207 | cnt -= xfer_size; | ||
208 | ptr += xfer_size; | ||
209 | } | ||
210 | |||
211 | /* ZLP stage */ | ||
212 | if((length % 64) == 0) | ||
213 | OTG_EP0CS = 2; | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | /* TODO: Maybe adapt to irq scheme */ | ||
219 | int usb_drv_recv(int endpoint, void* ptr, int length) | ||
220 | { | ||
221 | (void)endpoint; | ||
222 | int xfer_size, cnt = 0; | ||
223 | |||
224 | while (cnt < length) | ||
225 | { | ||
226 | /* Arm receiving buffer by writing | ||
227 | * any value to OUT0BC. This sets | ||
228 | * OUT_BUSY bit in EP0CS until the data | ||
229 | * are correctly received and ACK'd | ||
230 | */ | ||
231 | OTG_OUT0BC = 0; | ||
232 | |||
233 | while (OTG_EP0CS & 0x08) | ||
234 | ; | ||
235 | |||
236 | xfer_size = OTG_OUT0BC; | ||
237 | |||
238 | usb_copy_from(ptr, &OTG_EP0OUTDAT, xfer_size); | ||
239 | cnt += xfer_size; | ||
240 | ptr += xfer_size; | ||
241 | |||
242 | if (xfer_size < 64) | ||
243 | break; | ||
244 | } | ||
245 | |||
246 | /* ZLP stage */ | ||
247 | if (length == 0) | ||
248 | OTG_EP0CS = 2; | ||
249 | |||
250 | return cnt; | ||
251 | } | ||
252 | |||
253 | void usb_drv_stall(int endpoint, bool stall, bool in) | ||
254 | { | ||
255 | (void)endpoint; | ||
256 | (void)in; | ||
257 | |||
258 | /* only EP0 in hwstub */ | ||
259 | if (stall) | ||
260 | OTG_EP0CS |= 1; | ||
261 | else | ||
262 | OTG_EP0CS &= ~1; | ||
263 | } | ||
264 | |||
265 | void usb_drv_exit(void) | ||
266 | { | ||
267 | } | ||