diff options
Diffstat (limited to 'utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c')
-rw-r--r-- | utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c b/utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c new file mode 100644 index 0000000000..6a9293334a --- /dev/null +++ b/utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2011 by Marcin Bukat | ||
10 | * Copyright (C) 2012 by 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 "rk27xx.h" | ||
27 | |||
28 | typedef volatile uint32_t reg32; | ||
29 | |||
30 | #define USB_FULL_SPEED 0 | ||
31 | #define USB_HIGH_SPEED 1 | ||
32 | |||
33 | /* max allowed packet size definitions */ | ||
34 | #define CTL_MAX_SIZE 64 | ||
35 | |||
36 | struct endpoint_t { | ||
37 | const int type; /* EP type */ | ||
38 | const int dir; /* DIR_IN/DIR_OUT */ | ||
39 | const unsigned int intr_mask; | ||
40 | bool allocated; /* flag to mark EPs taken */ | ||
41 | volatile void *buf; /* tx/rx buffer address */ | ||
42 | volatile int len; /* size of the transfer (bytes) */ | ||
43 | volatile int cnt; /* number of bytes transfered/received */ | ||
44 | }; | ||
45 | |||
46 | static struct endpoint_t ctrlep[2] = { | ||
47 | {USB_ENDPOINT_XFER_CONTROL, DIR_OUT, 0, true, NULL, 0, 0}, | ||
48 | {USB_ENDPOINT_XFER_CONTROL, DIR_IN, 0, true, NULL, 0, 0} | ||
49 | }; | ||
50 | |||
51 | volatile bool setup_data_valid = false; | ||
52 | static volatile uint32_t setup_data[2]; | ||
53 | |||
54 | static volatile bool usb_drv_send_done = false; | ||
55 | |||
56 | void usb_drv_configure_endpoint(int ep_num, int type) | ||
57 | { | ||
58 | /* not needed as we use EP0 only */ | ||
59 | (void)ep_num; | ||
60 | (void)type; | ||
61 | } | ||
62 | |||
63 | int usb_drv_recv_setup(struct usb_ctrlrequest *req) | ||
64 | { | ||
65 | while (!setup_data_valid) | ||
66 | ; | ||
67 | |||
68 | memcpy(req, (void *)setup_data, sizeof(struct usb_ctrlrequest)); | ||
69 | setup_data_valid = false; | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static void setup_irq_handler(void) | ||
74 | { | ||
75 | /* copy setup data from packet */ | ||
76 | setup_data[0] = SETUP1; | ||
77 | setup_data[1] = SETUP2; | ||
78 | |||
79 | /* ack upper layer we have setup data */ | ||
80 | setup_data_valid = true; | ||
81 | } | ||
82 | |||
83 | /* service ep0 IN transaction */ | ||
84 | static void ctr_write(void) | ||
85 | { | ||
86 | int xfer_size = MIN(ctrlep[DIR_IN].cnt, CTL_MAX_SIZE); | ||
87 | |||
88 | while (TX0BUF & TXFULL) /* TX0FULL flag */ | ||
89 | ; | ||
90 | |||
91 | TX0STAT = xfer_size; /* size of the transfer */ | ||
92 | TX0DMALM_IADDR = (uint32_t)ctrlep[DIR_IN].buf; /* local buffer address */ | ||
93 | TX0DMAINCTL = DMA_START; /* start DMA */ | ||
94 | TX0CON &= ~TXNAK; /* clear NAK */ | ||
95 | |||
96 | /* Decrement by max packet size is intentional. | ||
97 | * This way if we have final packet short one we will get negative len | ||
98 | * after transfer, which in turn indicates we *don't* need to send | ||
99 | * zero length packet. If the final packet is max sized packet we will | ||
100 | * get zero len after transfer which indicates we need to send | ||
101 | * zero length packet to signal host end of the transfer. | ||
102 | */ | ||
103 | ctrlep[DIR_IN].cnt -= CTL_MAX_SIZE; | ||
104 | ctrlep[DIR_IN].buf += xfer_size; | ||
105 | } | ||
106 | |||
107 | static void ctr_read(void) | ||
108 | { | ||
109 | int xfer_size = RX0STAT & 0xffff; | ||
110 | |||
111 | /* clear NAK bit */ | ||
112 | RX0CON &= ~RXNAK; | ||
113 | |||
114 | ctrlep[DIR_OUT].cnt -= xfer_size; | ||
115 | ctrlep[DIR_OUT].buf += xfer_size; | ||
116 | |||
117 | RX0DMAOUTLMADDR = (uint32_t)ctrlep[DIR_OUT].buf; /* buffer address */ | ||
118 | RX0DMACTLO = DMA_START; /* start DMA */ | ||
119 | } | ||
120 | |||
121 | static void udc_phy_reset(void) | ||
122 | { | ||
123 | DEV_CTL |= SOFT_POR; | ||
124 | target_mdelay(10); /* min 10ms */ | ||
125 | DEV_CTL &= ~SOFT_POR; | ||
126 | } | ||
127 | |||
128 | static void udc_soft_connect(void) | ||
129 | { | ||
130 | DEV_CTL |= CSR_DONE | | ||
131 | DEV_SOFT_CN | | ||
132 | DEV_SELF_PWR; | ||
133 | } | ||
134 | |||
135 | /* return port speed */ | ||
136 | int usb_drv_port_speed(void) | ||
137 | { | ||
138 | return ((DEV_INFO & DEV_SPEED) ? USB_FULL_SPEED : USB_HIGH_SPEED); | ||
139 | } | ||
140 | |||
141 | /* Set the address (usually it's in a register). | ||
142 | * There is a problem here: some controller want the address to be set between | ||
143 | * control out and ack and some want to wait for the end of the transaction. | ||
144 | * In the first case, you need to write some code special code when getting | ||
145 | * setup packets and ignore this function (have a look at other drives) | ||
146 | */ | ||
147 | void usb_drv_set_address(int address) | ||
148 | { | ||
149 | (void)address; | ||
150 | /* UDC sets this automaticaly */ | ||
151 | } | ||
152 | |||
153 | int usb_drv_send(int endpoint, void *ptr, int length) | ||
154 | { | ||
155 | (void)endpoint; | ||
156 | struct endpoint_t *ep = &ctrlep[DIR_IN]; | ||
157 | |||
158 | ep->buf = ptr; | ||
159 | ep->len = ep->cnt = length; | ||
160 | |||
161 | ctr_write(); | ||
162 | |||
163 | /* wait for transfer to end */ | ||
164 | while(!usb_drv_send_done) | ||
165 | ; | ||
166 | |||
167 | usb_drv_send_done = false; | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | /* Setup a receive transfer. (non blocking) */ | ||
173 | int usb_drv_recv(int endpoint, void* ptr, int length) | ||
174 | { | ||
175 | (void)endpoint; | ||
176 | struct endpoint_t *ep = &ctrlep[DIR_OUT]; | ||
177 | |||
178 | ep->buf = ptr; | ||
179 | ep->len = ep->cnt = length; | ||
180 | |||
181 | /* clear NAK bit */ | ||
182 | RX0CON &= ~RXNAK; | ||
183 | RX0DMAOUTLMADDR = (uint32_t)ptr; /* buffer address */ | ||
184 | RX0DMACTLO = DMA_START; /* start DMA */ | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | /* Stall the endpoint. Usually set a flag in the controller */ | ||
190 | void usb_drv_stall(int endpoint, bool stall, bool in) | ||
191 | { | ||
192 | /* ctrl only anyway */ | ||
193 | (void)endpoint; | ||
194 | |||
195 | if(in) | ||
196 | { | ||
197 | if(stall) | ||
198 | TX0CON |= TXSTALL; | ||
199 | else | ||
200 | TX0CON &= ~TXSTALL; | ||
201 | } | ||
202 | else | ||
203 | { | ||
204 | if (stall) | ||
205 | RX0CON |= RXSTALL; | ||
206 | else | ||
207 | RX0CON &= ~RXSTALL; /* doc says Auto clear by UDC 2.0 */ | ||
208 | } | ||
209 | } | ||
210 | |||
211 | /* one time init (once per connection) - basicaly enable usb core */ | ||
212 | void usb_drv_init(void) | ||
213 | { | ||
214 | udc_phy_reset(); | ||
215 | target_mdelay(10); /* wait at least 10ms */ | ||
216 | udc_soft_connect(); | ||
217 | |||
218 | EN_INT = EN_SUSP_INTR | /* Enable Suspend Irq */ | ||
219 | EN_RESUME_INTR | /* Enable Resume Irq */ | ||
220 | EN_USBRST_INTR | /* Enable USB Reset Irq */ | ||
221 | EN_OUT0_INTR | /* Enable OUT Token receive Irq EP0 */ | ||
222 | EN_IN0_INTR | /* Enable IN Token transmit Irq EP0 */ | ||
223 | EN_SETUP_INTR; /* Enable SETUP Packet Receive Irq */ | ||
224 | |||
225 | INTCON = UDC_INTHIGH_ACT | /* interrupt high active */ | ||
226 | UDC_INTEN; /* enable EP0 irqs */ | ||
227 | } | ||
228 | |||
229 | /* turn off usb core */ | ||
230 | void usb_drv_exit(void) | ||
231 | { | ||
232 | /* udc module reset */ | ||
233 | SCU_RSTCFG |= (1<<1); | ||
234 | target_udelay(10); | ||
235 | SCU_RSTCFG &= ~(1<<1); | ||
236 | } | ||
237 | |||
238 | /* UDC ISR function */ | ||
239 | void INT_UDC(void) | ||
240 | { | ||
241 | uint32_t txstat, rxstat; | ||
242 | |||
243 | /* read what caused UDC irq */ | ||
244 | uint32_t intsrc = INT2FLAG & 0x7fffff; | ||
245 | |||
246 | if (intsrc & USBRST_INTR) /* usb reset */ | ||
247 | { | ||
248 | EN_INT = EN_SUSP_INTR | /* Enable Suspend Irq */ | ||
249 | EN_RESUME_INTR | /* Enable Resume Irq */ | ||
250 | EN_USBRST_INTR | /* Enable USB Reset Irq */ | ||
251 | EN_OUT0_INTR | /* Enable OUT Token receive Irq EP0 */ | ||
252 | EN_IN0_INTR | /* Enable IN Token transmit Irq EP0 */ | ||
253 | EN_SETUP_INTR; /* Enable SETUP Packet Receive Irq */ | ||
254 | |||
255 | TX0CON = TXACKINTEN | /* Set as one to enable the EP0 tx irq */ | ||
256 | TXNAK; /* Set as one to response NAK handshake */ | ||
257 | |||
258 | RX0CON = RXACKINTEN | | ||
259 | RXEPEN | /* Endpoint 0 Enable. When cleared the | ||
260 | * endpoint does not respond to an SETUP | ||
261 | * or OUT token */ | ||
262 | RXNAK; /* Set as one to response NAK handshake */ | ||
263 | } | ||
264 | |||
265 | if (intsrc & SETUP_INTR) /* setup interrupt */ | ||
266 | { | ||
267 | setup_irq_handler(); | ||
268 | } | ||
269 | |||
270 | if (intsrc & IN0_INTR) /* ep0 in interrupt */ | ||
271 | { | ||
272 | txstat = TX0STAT; /* read clears flags */ | ||
273 | |||
274 | /* TODO handle errors */ | ||
275 | if (txstat & TXACK) /* check TxACK flag */ | ||
276 | { | ||
277 | if (ctrlep[DIR_IN].cnt > 0) | ||
278 | { | ||
279 | /* we still have data to send */ | ||
280 | ctr_write(); | ||
281 | } | ||
282 | else | ||
283 | { | ||
284 | if (ctrlep[DIR_IN].cnt == 0) | ||
285 | ctr_write(); | ||
286 | |||
287 | /* final ack received */ | ||
288 | usb_drv_send_done = true; | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | if (intsrc & OUT0_INTR) /* ep0 out interrupt */ | ||
294 | { | ||
295 | rxstat = RX0STAT; | ||
296 | |||
297 | /* TODO handle errors */ | ||
298 | if (rxstat & RXACK) /* RxACK */ | ||
299 | { | ||
300 | if (ctrlep[DIR_OUT].cnt > 0) | ||
301 | ctr_read(); | ||
302 | } | ||
303 | } | ||
304 | |||
305 | if (intsrc & RESUME_INTR) /* usb resume */ | ||
306 | { | ||
307 | TX0CON |= TXCLR; /* TxClr */ | ||
308 | TX0CON &= ~TXCLR; | ||
309 | |||
310 | RX0CON |= RXCLR; /* RxClr */ | ||
311 | RX0CON &= ~RXCLR; | ||
312 | } | ||
313 | } | ||