summaryrefslogtreecommitdiff
path: root/utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c
diff options
context:
space:
mode:
authorMarcin Bukat <marcin.bukat@gmail.com>2013-07-18 23:55:35 +0200
committerMarcin Bukat <marcin.bukat@gmail.com>2013-11-24 00:10:36 +0100
commit8e633385912494ff5e871ec4c264d3a7db46fb98 (patch)
treea8d6b23861969b7df72bb79695ad742082ce2b02 /utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c
parent1ed57aaa5049d2bbe4e94bed6674bd405e98a4a5 (diff)
downloadrockbox-8e633385912494ff5e871ec4c264d3a7db46fb98.tar.gz
rockbox-8e633385912494ff5e871ec4c264d3a7db46fb98.zip
hwstub rk27xx port
Change-Id: I85ac57117911544b65ccd56eb16303e30be67cab
Diffstat (limited to 'utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c')
-rw-r--r--utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c313
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
28typedef 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
36struct 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
46static 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
51volatile bool setup_data_valid = false;
52static volatile uint32_t setup_data[2];
53
54static volatile bool usb_drv_send_done = false;
55
56void 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
63int 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
73static 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 */
84static 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
107static 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
121static void udc_phy_reset(void)
122{
123 DEV_CTL |= SOFT_POR;
124 target_mdelay(10); /* min 10ms */
125 DEV_CTL &= ~SOFT_POR;
126}
127
128static 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 */
136int 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 */
147void usb_drv_set_address(int address)
148{
149 (void)address;
150 /* UDC sets this automaticaly */
151}
152
153int 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) */
173int 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 */
190void 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 */
212void 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 */
230void 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 */
239void 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}