diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/config.h | 4 | ||||
-rw-r--r-- | firmware/export/rk27xx.h | 5 | ||||
-rw-r--r-- | firmware/target/arm/rk27xx/usb-drv-rk27xx.c | 714 | ||||
-rw-r--r-- | firmware/target/arm/rk27xx/usb-rk27xx.c | 14 |
4 files changed, 449 insertions, 288 deletions
diff --git a/firmware/export/config.h b/firmware/export/config.h index 7252d62c5e..c252b44f4e 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h | |||
@@ -834,7 +834,6 @@ Lyre prototype 1 */ | |||
834 | #define USB_STATUS_BY_EVENT | 834 | #define USB_STATUS_BY_EVENT |
835 | #define USB_DETECT_BY_REQUEST | 835 | #define USB_DETECT_BY_REQUEST |
836 | #elif CONFIG_USBOTG == USBOTG_RK27XX | 836 | #elif CONFIG_USBOTG == USBOTG_RK27XX |
837 | #define USB_STATUS_BY_EVENT | ||
838 | #define USB_DETECT_BY_REQUEST | 837 | #define USB_DETECT_BY_REQUEST |
839 | #endif /* CONFIG_USB == */ | 838 | #endif /* CONFIG_USB == */ |
840 | #endif /* HAVE_USBSTACK */ | 839 | #endif /* HAVE_USBSTACK */ |
@@ -1069,7 +1068,8 @@ Lyre prototype 1 */ | |||
1069 | #elif (CONFIG_USBOTG == USBOTG_ARC) || \ | 1068 | #elif (CONFIG_USBOTG == USBOTG_ARC) || \ |
1070 | (CONFIG_USBOTG == USBOTG_JZ4740) || \ | 1069 | (CONFIG_USBOTG == USBOTG_JZ4740) || \ |
1071 | (CONFIG_USBOTG == USBOTG_M66591) || \ | 1070 | (CONFIG_USBOTG == USBOTG_M66591) || \ |
1072 | (CONFIG_USBOTG == USBOTG_AS3525) | 1071 | (CONFIG_USBOTG == USBOTG_AS3525) || \ |
1072 | (CONFIG_USBOTG == USBOTG_RK27XX) | ||
1073 | #define USB_HAS_BULK | 1073 | #define USB_HAS_BULK |
1074 | #define USB_HAS_INTERRUPT | 1074 | #define USB_HAS_INTERRUPT |
1075 | #elif defined(CPU_TCC780X) || defined(CPU_TCC77X) | 1075 | #elif defined(CPU_TCC780X) || defined(CPU_TCC77X) |
diff --git a/firmware/export/rk27xx.h b/firmware/export/rk27xx.h index 58b3fe8166..dc6bca7cbd 100644 --- a/firmware/export/rk27xx.h +++ b/firmware/export/rk27xx.h | |||
@@ -8,7 +8,8 @@ | |||
8 | #define FLASH_BANK1 0x11000000 | 8 | #define FLASH_BANK1 0x11000000 |
9 | 9 | ||
10 | #define USB_NUM_ENDPOINTS 16 | 10 | #define USB_NUM_ENDPOINTS 16 |
11 | #define USB_DEVBSS_ATTR | 11 | /* cache aligned */ |
12 | #define USB_DEVBSS_ATTR __attribute__((aligned(CACHEALIGN_SIZE))) | ||
12 | 13 | ||
13 | /* Timers */ | 14 | /* Timers */ |
14 | #define APB0_TIMER (ARM_BUS0_BASE + 0x00000000) | 15 | #define APB0_TIMER (ARM_BUS0_BASE + 0x00000000) |
@@ -811,6 +812,7 @@ | |||
811 | #define RXVOIDINTEN (1<<5) | 812 | #define RXVOIDINTEN (1<<5) |
812 | #define RXERRINTEN (1<<6) | 813 | #define RXERRINTEN (1<<6) |
813 | #define RXACKINTEN (1<<7) | 814 | #define RXACKINTEN (1<<7) |
815 | #define RXCFINTE (1<<12) | ||
814 | /* bits 31:8 reserved for EP0 */ | 816 | /* bits 31:8 reserved for EP0 */ |
815 | /* bits 31:14 reserved for others */ | 817 | /* bits 31:14 reserved for others */ |
816 | 818 | ||
@@ -833,6 +835,7 @@ | |||
833 | #define TXERRINTEN (1<<5) | 835 | #define TXERRINTEN (1<<5) |
834 | #define TXACKINTEN (1<<6) | 836 | #define TXACKINTEN (1<<6) |
835 | #define TXDMADNEN (1<<7) /* reserved for EP0 */ | 837 | #define TXDMADNEN (1<<7) /* reserved for EP0 */ |
838 | #define TXCFINTE (1<<12) | ||
836 | /* bits 31:8 reserved */ | 839 | /* bits 31:8 reserved */ |
837 | 840 | ||
838 | /* TXnBUF bits */ | 841 | /* TXnBUF bits */ |
diff --git a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c index 9380f54193..af613e7024 100644 --- a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c +++ b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c | |||
@@ -5,9 +5,9 @@ | |||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | ||
9 | * | 8 | * |
10 | * Copyright (C) 2011 by Marcin Bukat | 9 | * Copyright (C) 2011 by Marcin Bukat |
10 | * Copyright (C) 2012 by Amaury Pouly | ||
11 | * | 11 | * |
12 | * This program is free software; you can redistribute it and/or | 12 | * This program is free software; you can redistribute it and/or |
13 | * modify it under the terms of the GNU General Public License | 13 | * modify it under the terms of the GNU General Public License |
@@ -28,13 +28,12 @@ | |||
28 | #include "kernel.h" | 28 | #include "kernel.h" |
29 | #include "panic.h" | 29 | #include "panic.h" |
30 | 30 | ||
31 | //#include "usb-s3c6400x.h" | ||
32 | |||
33 | #include "usb_ch9.h" | 31 | #include "usb_ch9.h" |
34 | #include "usb_core.h" | 32 | #include "usb_core.h" |
35 | #include <inttypes.h> | 33 | #include <inttypes.h> |
36 | #include "power.h" | 34 | #include "power.h" |
37 | 35 | ||
36 | /*#define LOGF_ENABLE*/ | ||
38 | #include "logf.h" | 37 | #include "logf.h" |
39 | 38 | ||
40 | typedef volatile uint32_t reg32; | 39 | typedef volatile uint32_t reg32; |
@@ -59,6 +58,16 @@ typedef volatile uint32_t reg32; | |||
59 | #define IIN_DMAINCTL(ep_num) (*(reg32*)(AHB0_UDC+0x84+0x38*((ep_num/3)-1))) | 58 | #define IIN_DMAINCTL(ep_num) (*(reg32*)(AHB0_UDC+0x84+0x38*((ep_num/3)-1))) |
60 | #define IIN_DMAINLMADDR(ep_num) (*(reg32*)(AHB0_UDC+0x88+0x38*((ep_num/3)-1))) | 59 | #define IIN_DMAINLMADDR(ep_num) (*(reg32*)(AHB0_UDC+0x88+0x38*((ep_num/3)-1))) |
61 | 60 | ||
61 | #define USB_FULL_SPEED 0 | ||
62 | #define USB_HIGH_SPEED 1 | ||
63 | |||
64 | /* max allowed packet size definitions */ | ||
65 | #define CTL_MAX_SIZE 64 | ||
66 | #define BLK_HS_MAX_SIZE 512 | ||
67 | #define BLK_FS_MAX_SIZE 64 | ||
68 | #define INT_HS_MAX_SIZE 1024 | ||
69 | #define INT_FS_MAX_SIZE 64 | ||
70 | |||
62 | #ifdef LOGF_ENABLE | 71 | #ifdef LOGF_ENABLE |
63 | #define XFER_DIR_STR(dir) ((dir) ? "IN" : "OUT") | 72 | #define XFER_DIR_STR(dir) ((dir) ? "IN" : "OUT") |
64 | #define XFER_TYPE_STR(type) \ | 73 | #define XFER_TYPE_STR(type) \ |
@@ -71,6 +80,7 @@ typedef volatile uint32_t reg32; | |||
71 | struct endpoint_t { | 80 | struct endpoint_t { |
72 | const int type; /* EP type */ | 81 | const int type; /* EP type */ |
73 | const int dir; /* DIR_IN/DIR_OUT */ | 82 | const int dir; /* DIR_IN/DIR_OUT */ |
83 | const unsigned int intr_mask; | ||
74 | bool allocated; /* flag to mark EPs taken */ | 84 | bool allocated; /* flag to mark EPs taken */ |
75 | volatile void *buf; /* tx/rx buffer address */ | 85 | volatile void *buf; /* tx/rx buffer address */ |
76 | volatile int len; /* size of the transfer (bytes) */ | 86 | volatile int len; /* size of the transfer (bytes) */ |
@@ -80,30 +90,33 @@ struct endpoint_t { | |||
80 | }; | 90 | }; |
81 | 91 | ||
82 | static struct endpoint_t ctrlep[2] = { | 92 | static struct endpoint_t ctrlep[2] = { |
83 | {USB_ENDPOINT_XFER_CONTROL, DIR_OUT, true, NULL, 0, 0, true, {0, 0, 0}}, | 93 | {USB_ENDPOINT_XFER_CONTROL, DIR_OUT, 0, true, NULL, 0, 0, true, {0, 0, 0}}, |
84 | {USB_ENDPOINT_XFER_CONTROL, DIR_IN, true, NULL, 0, 0, true, {0, 0, 0}} | 94 | {USB_ENDPOINT_XFER_CONTROL, DIR_IN, 0, true, NULL, 0, 0, true, {0, 0, 0}} |
85 | }; | 95 | }; |
86 | 96 | ||
87 | static struct endpoint_t endpoints[16] = { | 97 | static struct endpoint_t endpoints[16] = { |
88 | {USB_ENDPOINT_XFER_CONTROL, 3, true, NULL, 0, 0, true, {0, 0, 0}}, /* stub */ | 98 | {USB_ENDPOINT_XFER_CONTROL, 3, 0, true, NULL, 0, 0, true, {0, 0, 0}}, /* stub */ |
89 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT1 */ | 99 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT1_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT1 */ |
90 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN2 */ | 100 | {USB_ENDPOINT_XFER_BULK, DIR_IN, BIN2_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN2 */ |
91 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN3 */ | 101 | {USB_ENDPOINT_XFER_INT, DIR_IN, IIN3_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN3 */ |
92 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT4 */ | 102 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT4_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT4 */ |
93 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN5 */ | 103 | {USB_ENDPOINT_XFER_BULK, DIR_IN, BIN5_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN5 */ |
94 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN6 */ | 104 | {USB_ENDPOINT_XFER_INT, DIR_IN, IIN6_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN6 */ |
95 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT7 */ | 105 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT7_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT7 */ |
96 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN8 */ | 106 | {USB_ENDPOINT_XFER_BULK, DIR_IN, BIN8_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN8 */ |
97 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN9 */ | 107 | {USB_ENDPOINT_XFER_INT, DIR_IN, IIN9_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN9 */ |
98 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT10 */ | 108 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT10_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT10 */ |
99 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN11 */ | 109 | {USB_ENDPOINT_XFER_BULK, DIR_IN, BIN11_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN11 */ |
100 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN12 */ | 110 | {USB_ENDPOINT_XFER_INT, DIR_IN, IIN12_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN12 */ |
101 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT13 */ | 111 | {USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT13_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT13 */ |
102 | {USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN14 */ | 112 | {USB_ENDPOINT_XFER_BULK, DIR_IN, BIN14_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN14 */ |
103 | {USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN15 */ | 113 | {USB_ENDPOINT_XFER_INT, DIR_IN, IIN15_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN15 */ |
104 | }; | 114 | }; |
105 | 115 | ||
106 | static void setup_received(void) | 116 | static volatile bool set_address = false; |
117 | static volatile bool set_configuration = false; | ||
118 | |||
119 | static void setup_irq_handler(void) | ||
107 | { | 120 | { |
108 | static uint32_t setup_data[2]; | 121 | static uint32_t setup_data[2]; |
109 | 122 | ||
@@ -111,10 +124,6 @@ static void setup_received(void) | |||
111 | setup_data[0] = SETUP1; | 124 | setup_data[0] = SETUP1; |
112 | setup_data[1] = SETUP2; | 125 | setup_data[1] = SETUP2; |
113 | 126 | ||
114 | /* clear all pending control transfers | ||
115 | * do we need this here? | ||
116 | */ | ||
117 | |||
118 | /* pass setup data to the upper layer */ | 127 | /* pass setup data to the upper layer */ |
119 | usb_core_control_request((struct usb_ctrlrequest*)setup_data); | 128 | usb_core_control_request((struct usb_ctrlrequest*)setup_data); |
120 | } | 129 | } |
@@ -122,12 +131,12 @@ static void setup_received(void) | |||
122 | /* service ep0 IN transaction */ | 131 | /* service ep0 IN transaction */ |
123 | static void ctr_write(void) | 132 | static void ctr_write(void) |
124 | { | 133 | { |
125 | int xfer_size = (ctrlep[DIR_IN].cnt > 64) ? 64 : ctrlep[DIR_IN].cnt; | 134 | int xfer_size = MIN(ctrlep[DIR_IN].cnt, CTL_MAX_SIZE); |
126 | unsigned int timeout = current_tick + HZ/10; | 135 | unsigned int timeout = current_tick + HZ/10; |
127 | 136 | ||
128 | while (TX0BUF & TXFULL) /* TX0FULL flag */ | 137 | while (TX0BUF & TXFULL) /* TX0FULL flag */ |
129 | { | 138 | { |
130 | if(TIME_AFTER(current_tick, timeout)) | 139 | if (TIME_AFTER(current_tick, timeout)) |
131 | break; | 140 | break; |
132 | } | 141 | } |
133 | 142 | ||
@@ -143,7 +152,7 @@ static void ctr_write(void) | |||
143 | * get zero len after transfer which indicates we need to send | 152 | * get zero len after transfer which indicates we need to send |
144 | * zero length packet to signal host end of the transfer. | 153 | * zero length packet to signal host end of the transfer. |
145 | */ | 154 | */ |
146 | ctrlep[DIR_IN].cnt -= 64; | 155 | ctrlep[DIR_IN].cnt -= CTL_MAX_SIZE; |
147 | ctrlep[DIR_IN].buf += xfer_size; | 156 | ctrlep[DIR_IN].buf += xfer_size; |
148 | } | 157 | } |
149 | 158 | ||
@@ -161,60 +170,147 @@ static void ctr_read(void) | |||
161 | RX0DMACTLO = DMA_START; /* start DMA */ | 170 | RX0DMACTLO = DMA_START; /* start DMA */ |
162 | } | 171 | } |
163 | 172 | ||
164 | static void blk_write(int ep) | 173 | static void blk_write(int ep_num) |
165 | { | 174 | { |
166 | int ep_num = EP_NUM(ep); | ||
167 | int max = usb_drv_port_speed() ? 512 : 64; | ||
168 | int xfer_size = (endpoints[ep_num].cnt > max) ? max : endpoints[ep_num].cnt; | ||
169 | unsigned int timeout = current_tick + HZ/10; | 175 | unsigned int timeout = current_tick + HZ/10; |
170 | 176 | int max = usb_drv_port_speed() ? BLK_HS_MAX_SIZE : BLK_FS_MAX_SIZE; | |
177 | int xfer_size = MIN(endpoints[ep_num].cnt, max); | ||
178 | |||
171 | while (BIN_TXBUF(ep_num) & TXFULL) /* TXFULL flag */ | 179 | while (BIN_TXBUF(ep_num) & TXFULL) /* TXFULL flag */ |
172 | { | 180 | { |
173 | if(TIME_AFTER(current_tick, timeout)) | 181 | if (TIME_AFTER(current_tick, timeout)) |
174 | break; | 182 | break; |
175 | } | 183 | } |
176 | 184 | ||
177 | BIN_TXSTAT(ep_num) = xfer_size; /* size */ | 185 | BIN_TXSTAT(ep_num) = xfer_size; /* size */ |
178 | BIN_DMAINLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; /* buf address */ | 186 | BIN_DMAINLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; /* buf address */ |
179 | BIN_DMAINCTL(ep_num) = DMA_START; /* start DMA */ | 187 | BIN_DMAINCTL(ep_num) = DMA_START; /* start DMA */ |
180 | BIN_TXCON(ep_num) &= ~TXNAK; /* clear NAK */ | 188 | BIN_TXCON(ep_num) &= ~TXNAK; /* clear NAK */ |
181 | 189 | ||
182 | /* Decrement by max packet size is intentional. | 190 | /* Decrement by max packet size is intentional. |
183 | * This way if we have final packet short one we will get negative len | 191 | * This way if we have final packet short one we will get negative len |
184 | * after transfer, which in turn indicates we *don't* need to send | 192 | * after transfer, which in turn indicates we *don't* need to send |
185 | * zero length packet. If the final packet is max sized packet we will | 193 | * zero length packet. If the final packet is max sized packet we will |
186 | * get zero len after transfer which indicates we need to send | 194 | * get zero len after transfer which indicates we need to send |
187 | * zero length packet to signal host end of the transfer. | 195 | * zero length packet to signal host end of the transfer. |
188 | */ | 196 | */ |
189 | endpoints[ep_num].cnt -= max; | 197 | endpoints[ep_num].cnt -= max; |
190 | endpoints[ep_num].buf += xfer_size; | 198 | endpoints[ep_num].buf += xfer_size; |
191 | } | 199 | } |
192 | 200 | ||
193 | static void blk_read(int ep) | 201 | static void blk_in_irq_handler(int ep_num) |
194 | { | 202 | { |
195 | int ep_num = EP_NUM(ep); | 203 | uint32_t txstat = BIN_TXSTAT(ep_num); |
196 | int xfer_size = BOUT_RXSTAT(ep_num) & 0xffff; | 204 | struct endpoint_t *endp = &endpoints[ep_num]; |
197 | 205 | ||
198 | /* clear NAK bit */ | 206 | if (txstat & TXERR) |
199 | BOUT_RXCON(ep_num) &= ~RXNAK; | 207 | { |
200 | 208 | panicf("error condition ep%d, %ld", ep_num, current_tick); | |
201 | endpoints[ep_num].cnt -= xfer_size; | 209 | } |
202 | endpoints[ep_num].buf += xfer_size; | 210 | |
203 | 211 | if (txstat & TXCFINT) | |
204 | BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; | 212 | { |
205 | BOUT_DMAOUTCTL(ep_num) = DMA_START; | 213 | logf("blk_write: cf(0x%x), %ld", ep_num, current_tick); |
214 | /* bit cleared by read */ | ||
215 | usb_drv_stall(ep_num, false, true); | ||
216 | } | ||
217 | |||
218 | if (txstat & TXACK) /* check TxACK flag */ | ||
219 | { | ||
220 | if (endp->cnt > 0) | ||
221 | { | ||
222 | logf("blk_write: ack(0x%x), %ld", ep_num, current_tick); | ||
223 | /* we still have data to send (or ZLP) */ | ||
224 | blk_write(ep_num); | ||
225 | } | ||
226 | else | ||
227 | { | ||
228 | logf("udc_intr: usb_core_transfer_complete(0x%x, USB_DIR_IN, 0, 0x%x), %ld", | ||
229 | ep_num, endp->len, current_tick); | ||
230 | |||
231 | /* final ack received */ | ||
232 | usb_core_transfer_complete(ep_num, /* ep */ | ||
233 | USB_DIR_IN, /* dir */ | ||
234 | 0, /* status */ | ||
235 | endp->len); /* length */ | ||
236 | |||
237 | /* release semaphore for blocking transfer */ | ||
238 | if (endp->block) | ||
239 | { | ||
240 | logf("udc_intr: ep=0x%x, semaphore_release(), %ld", | ||
241 | ep_num, current_tick); | ||
242 | |||
243 | semaphore_release(&endp->complete); | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | |||
249 | static void blk_out_irq_handler(int ep_num) | ||
250 | { | ||
251 | uint32_t rxstat = BOUT_RXSTAT(ep_num); | ||
252 | int xfer_size = rxstat & 0xffff; | ||
253 | int max = usb_drv_port_speed() ? BLK_HS_MAX_SIZE : BLK_FS_MAX_SIZE; | ||
254 | struct endpoint_t *endp = &endpoints[ep_num]; | ||
255 | |||
256 | if (rxstat & RXERR) | ||
257 | { | ||
258 | panicf("error condition ep%d, %ld", ep_num, current_tick); | ||
259 | } | ||
260 | |||
261 | if (rxstat & RXOVF) | ||
262 | { | ||
263 | panicf("rxovf ep%d, %ld", ep_num, current_tick); | ||
264 | } | ||
265 | |||
266 | if (rxstat & RXCFINT) | ||
267 | { | ||
268 | logf("blk_read:cf(0x%x), %ld", ep_num, current_tick); | ||
269 | usb_drv_stall(ep_num, false, false); | ||
270 | } | ||
271 | |||
272 | if ((rxstat & RXACK) && endp->cnt > 0) | ||
273 | { | ||
274 | logf("blk_read:ack(0x%x,0x%x), %ld", ep_num, xfer_size, current_tick); | ||
275 | |||
276 | /* clear NAK bit */ | ||
277 | BOUT_RXCON(ep_num) &= ~RXNAK; | ||
278 | |||
279 | endp->cnt -= xfer_size; | ||
280 | endp->buf += xfer_size; | ||
281 | |||
282 | /* if the transfer was short or if the total count is zero, | ||
283 | * transfer is complete | ||
284 | */ | ||
285 | if (endp->cnt == 0 || xfer_size < max) | ||
286 | { | ||
287 | logf("udc_intr: usb_core_transfer_complete(0x%x, USB_DIR_OUT, 0, 0x%x), %ld", | ||
288 | ep_num, endp->len, current_tick); | ||
289 | |||
290 | usb_core_transfer_complete(ep_num, /* ep */ | ||
291 | USB_DIR_OUT, /* dir */ | ||
292 | 0, /* status */ | ||
293 | endp->len - endp->cnt); /* length */ | ||
294 | endp->cnt = 0; | ||
295 | } | ||
296 | else | ||
297 | { | ||
298 | BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)endp->buf; | ||
299 | BOUT_DMAOUTCTL(ep_num) = DMA_START; | ||
300 | } | ||
301 | } | ||
206 | } | 302 | } |
207 | 303 | ||
208 | static void int_write(int ep) | 304 | static void int_write(int ep) |
209 | { | 305 | { |
210 | int ep_num = EP_NUM(ep); | 306 | int ep_num = EP_NUM(ep); |
211 | int max = usb_drv_port_speed() ? 1024 : 64; | 307 | int max = usb_drv_port_speed() ? INT_HS_MAX_SIZE : INT_FS_MAX_SIZE; |
212 | int xfer_size = (endpoints[ep_num].cnt > max) ? max : endpoints[ep_num].cnt; | 308 | int xfer_size = MIN(endpoints[ep_num].cnt, max); |
213 | unsigned int timeout = current_tick + HZ/10; | 309 | unsigned int timeout = current_tick + HZ/10; |
214 | 310 | ||
215 | while (IIN_TXBUF(ep_num) & TXFULL) /* TXFULL flag */ | 311 | while (IIN_TXBUF(ep_num) & TXFULL) |
216 | { | 312 | { |
217 | if(TIME_AFTER(current_tick, timeout)) | 313 | if (TIME_AFTER(current_tick, timeout)) |
218 | break; | 314 | break; |
219 | } | 315 | } |
220 | 316 | ||
@@ -234,167 +330,99 @@ static void int_write(int ep) | |||
234 | endpoints[ep_num].buf += xfer_size; | 330 | endpoints[ep_num].buf += xfer_size; |
235 | } | 331 | } |
236 | 332 | ||
237 | /* UDC ISR function */ | 333 | static void int_in_irq_handler(int ep_num) |
238 | void INT_UDC(void) | ||
239 | { | 334 | { |
240 | uint32_t txstat, rxstat; | 335 | uint32_t txstat = IIN_TXSTAT(ep_num); |
241 | int tmp, ep_num; | 336 | struct endpoint_t *endp = &endpoints[ep_num]; |
242 | |||
243 | /* read what caused UDC irq */ | ||
244 | uint32_t intsrc = INT2FLAG & 0x7fffff; | ||
245 | |||
246 | if (intsrc & SETUP_INTR) /* setup interrupt */ | ||
247 | { | ||
248 | setup_received(); | ||
249 | } | ||
250 | else if (intsrc & IN0_INTR) /* ep0 in interrupt */ | ||
251 | { | ||
252 | txstat = TX0STAT; /* read clears flags */ | ||
253 | |||
254 | /* TODO handle errors */ | ||
255 | if (txstat & TXACK) /* check TxACK flag */ | ||
256 | { | ||
257 | if (ctrlep[DIR_IN].cnt >= 0) | ||
258 | { | ||
259 | /* we still have data to send (or ZLP) */ | ||
260 | ctr_write(); | ||
261 | } | ||
262 | else | ||
263 | { | ||
264 | /* final ack received */ | ||
265 | usb_core_transfer_complete(0, /* ep */ | ||
266 | USB_DIR_IN, /* dir */ | ||
267 | 0, /* status */ | ||
268 | ctrlep[DIR_IN].len); /* length */ | ||
269 | |||
270 | /* release semaphore for blocking transfer */ | ||
271 | if (ctrlep[DIR_IN].block) | ||
272 | semaphore_release(&ctrlep[DIR_IN].complete); | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | else if (intsrc & OUT0_INTR) /* ep0 out interrupt */ | ||
277 | { | ||
278 | rxstat = RX0STAT; | ||
279 | 337 | ||
280 | /* TODO handle errors */ | 338 | if (txstat & TXCFINT) |
281 | if (rxstat & RXACK) /* RxACK */ | ||
282 | { | ||
283 | if (ctrlep[DIR_OUT].cnt > 0) | ||
284 | ctr_read(); | ||
285 | else | ||
286 | usb_core_transfer_complete(0, /* ep */ | ||
287 | USB_DIR_OUT, /* dir */ | ||
288 | 0, /* status */ | ||
289 | ctrlep[DIR_OUT].len); /* length */ | ||
290 | } | ||
291 | } | ||
292 | else if (intsrc & USBRST_INTR) /* usb reset */ | ||
293 | { | ||
294 | usb_drv_init(); | ||
295 | } | ||
296 | else if (intsrc & RESUME_INTR) /* usb resume */ | ||
297 | { | 339 | { |
298 | TX0CON |= TXCLR; /* TxClr */ | 340 | logf("int_write: cf(0x%x), %ld", ep_num, current_tick); |
299 | TX0CON &= ~TXCLR; | 341 | /* bit cleared by read */ |
300 | RX0CON |= RXCLR; /* RxClr */ | 342 | usb_drv_stall(ep_num, false, true); |
301 | RX0CON &= ~RXCLR; | ||
302 | } | 343 | } |
303 | else if (intsrc & SUSP_INTR) /* usb suspend */ | 344 | |
304 | { | 345 | if (txstat & TXERR) |
305 | } | ||
306 | else if (intsrc & CONN_INTR) /* usb connect */ | ||
307 | { | 346 | { |
347 | panicf("error condition ep%d, %ld", ep_num, current_tick); | ||
308 | } | 348 | } |
309 | else | 349 | |
350 | if (txstat & TXACK) /* check TxACK flag */ | ||
310 | { | 351 | { |
311 | /* lets figure out which ep generated irq */ | 352 | if (endp->cnt > 0) |
312 | tmp = intsrc >> 7; | ||
313 | for (ep_num=1; ep_num < 15; ep_num++) | ||
314 | { | 353 | { |
315 | tmp >>= ep_num; | 354 | logf("int_write: ack(0x%x), %ld", ep_num, current_tick); |
316 | if (tmp & 0x01) | 355 | /* we still have data to send (or ZLP) */ |
317 | break; | 356 | int_write(ep_num); |
318 | } | 357 | } |
319 | 358 | else | |
320 | if (intsrc & ((1<<8)|(1<<11)|(1<<14)|(1<<17)|(1<<20))) | ||
321 | { | 359 | { |
322 | /* bulk out */ | 360 | logf("udc_intr: usb_core_transfer_complete(0x%x, USB_DIR_IN, 0, 0x%x), %ld", |
323 | rxstat = BOUT_RXSTAT(ep_num); | 361 | ep_num, endp->len, current_tick); |
324 | 362 | ||
325 | /* TODO handle errors */ | 363 | /* final ack received */ |
326 | if (rxstat & (1<<18)) /* RxACK */ | 364 | usb_core_transfer_complete(ep_num, /* ep */ |
365 | USB_DIR_IN, /* dir */ | ||
366 | 0, /* status */ | ||
367 | endp->len); /* length */ | ||
368 | |||
369 | /* release semaphore for blocking transfer */ | ||
370 | if (endp->block) | ||
327 | { | 371 | { |
328 | if (endpoints[ep_num].cnt > 0) | 372 | logf("udc_intr: ep=0x%x, semaphore_release(), %ld", |
329 | blk_read(ep_num); | 373 | ep_num, current_tick); |
330 | else | 374 | |
331 | usb_core_transfer_complete(ep_num, /* ep */ | 375 | semaphore_release(&endp->complete); |
332 | USB_DIR_OUT, /* dir */ | ||
333 | 0, /* status */ | ||
334 | endpoints[ep_num].len); /* length */ | ||
335 | } | 376 | } |
336 | } | 377 | } |
337 | else if (intsrc & ((1<<9)|(1<<12)|(1<<15)|(1<<18)|(1<<21))) | 378 | } |
379 | } | ||
380 | |||
381 | static void udc_phy_reset(void) | ||
382 | { | ||
383 | DEV_CTL |= SOFT_POR; | ||
384 | udelay(10000); /* min 10ms */ | ||
385 | DEV_CTL &= ~SOFT_POR; | ||
386 | } | ||
387 | |||
388 | static void udc_soft_connect(void) | ||
389 | { | ||
390 | DEV_CTL |= CSR_DONE | | ||
391 | DEV_SOFT_CN | | ||
392 | DEV_SELF_PWR; | ||
393 | } | ||
394 | |||
395 | static void udc_helper(void) | ||
396 | { | ||
397 | uint32_t dev_info = DEV_INFO; | ||
398 | |||
399 | /* This polls for DEV_EN bit set in DEV_INFO register | ||
400 | * as well as tracks current requested configuration | ||
401 | * (DEV_INFO [11:8]). On state change it notifies usb stack | ||
402 | * about it. | ||
403 | */ | ||
404 | |||
405 | /* SET ADDRESS request */ | ||
406 | if (set_address == false) | ||
407 | if ((dev_info & 0x7f)) | ||
338 | { | 408 | { |
339 | /* bulk in */ | 409 | set_address = true; |
340 | txstat = BIN_TXSTAT(ep_num); | 410 | usb_core_notify_set_address(dev_info & 0x7f); |
341 | |||
342 | /* TODO handle errors */ | ||
343 | if (txstat & (1<<18)) /* check TxACK flag */ | ||
344 | { | ||
345 | if (endpoints[ep_num].cnt >= 0) | ||
346 | { | ||
347 | /* we still have data to send (or ZLP) */ | ||
348 | blk_write(ep_num); | ||
349 | } | ||
350 | else | ||
351 | { | ||
352 | /* final ack received */ | ||
353 | usb_core_transfer_complete(ep_num, /* ep */ | ||
354 | USB_DIR_IN, /* dir */ | ||
355 | 0, /* status */ | ||
356 | endpoints[ep_num].len); /* length */ | ||
357 | |||
358 | /* release semaphore for blocking transfer */ | ||
359 | if (endpoints[ep_num].block) | ||
360 | semaphore_release(&endpoints[ep_num].complete); | ||
361 | } | ||
362 | } | ||
363 | } | 411 | } |
364 | else if (intsrc & ((1<<10)|(1<13)|(1<<16)|(1<<19)|(1<<22))) | ||
365 | { | ||
366 | /* int in */ | ||
367 | txstat = IIN_TXSTAT(ep_num); | ||
368 | 412 | ||
369 | /* TODO handle errors */ | 413 | /* SET CONFIGURATION request */ |
370 | if (txstat & TXACK) /* check TxACK flag */ | 414 | if (set_configuration == false) |
371 | { | 415 | if (dev_info & DEV_EN) |
372 | if (endpoints[ep_num].cnt >= 0) | 416 | { |
373 | { | 417 | set_configuration = true; |
374 | /* we still have data to send (or ZLP) */ | 418 | usb_core_notify_set_config(((dev_info >> 7) & 0xf) + 1); |
375 | int_write(ep_num); | ||
376 | } | ||
377 | else | ||
378 | { | ||
379 | /* final ack received */ | ||
380 | usb_core_transfer_complete(ep_num, /* ep */ | ||
381 | USB_DIR_IN, /* dir */ | ||
382 | 0, /* status */ | ||
383 | endpoints[ep_num].len); /* length */ | ||
384 | |||
385 | /* release semaphore for blocking transfer */ | ||
386 | if (endpoints[ep_num].block) | ||
387 | semaphore_release(&endpoints[ep_num].complete); | ||
388 | } | ||
389 | } | ||
390 | } | 419 | } |
391 | } | ||
392 | } | 420 | } |
393 | 421 | ||
394 | /* return port speed FS=0, HS=1 */ | 422 | /* return port speed */ |
395 | int usb_drv_port_speed(void) | 423 | int usb_drv_port_speed(void) |
396 | { | 424 | { |
397 | return ((DEV_INFO & DEV_SPEED) == 0) ? 0 : 1; | 425 | return ((DEV_INFO & DEV_SPEED) ? USB_FULL_SPEED : USB_HIGH_SPEED); |
398 | } | 426 | } |
399 | 427 | ||
400 | /* Reserve endpoint */ | 428 | /* Reserve endpoint */ |
@@ -410,7 +438,7 @@ int usb_drv_request_endpoint(int type, int dir) | |||
410 | logf("req: %s %s", XFER_DIR_STR(ep_dir), XFER_TYPE_STR(ep_type)); | 438 | logf("req: %s %s", XFER_DIR_STR(ep_dir), XFER_TYPE_STR(ep_type)); |
411 | 439 | ||
412 | /* Find an available ep/dir pair */ | 440 | /* Find an available ep/dir pair */ |
413 | for (ep_num=1;ep_num<USB_NUM_ENDPOINTS;ep_num++) | 441 | for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++) |
414 | { | 442 | { |
415 | struct endpoint_t* endpoint = &endpoints[ep_num]; | 443 | struct endpoint_t* endpoint = &endpoints[ep_num]; |
416 | 444 | ||
@@ -420,7 +448,35 @@ int usb_drv_request_endpoint(int type, int dir) | |||
420 | { | 448 | { |
421 | /* mark endpoint as taken */ | 449 | /* mark endpoint as taken */ |
422 | endpoint->allocated = true; | 450 | endpoint->allocated = true; |
423 | 451 | ||
452 | if (ep_num%3 == 0) /* IIN 3, 6, 9, 12, 15 */ | ||
453 | { | ||
454 | IIN_TXCON(ep_num) = (ep_num<<8) | /* set ep number */ | ||
455 | TXERRINTEN | | ||
456 | TXEPEN | /* endpoint enable */ | ||
457 | TXNAK | /* respond with NAK */ | ||
458 | TXACKINTEN | /* irq on ACK */ | ||
459 | TXCFINTE; /* irq on Clear feature */ | ||
460 | } | ||
461 | else if (ep_num%3 == 1) /* BOUT 1, 4, 7, 10, 13 */ | ||
462 | { | ||
463 | BOUT_RXCON(ep_num) = (ep_num<<8) | /* set ep number */ | ||
464 | RXERRINTEN | | ||
465 | RXEPEN | /* endpoint enable */ | ||
466 | RXNAK | /* respond with NAK */ | ||
467 | RXACKINTEN | /* irq on ACK */ | ||
468 | RXCFINTE; /* irq on Clear feature */ | ||
469 | } | ||
470 | else if (ep_num%3 == 2) /* BIN 2, 5, 8, 11, 14 */ | ||
471 | { | ||
472 | BIN_TXCON(ep_num) = (ep_num<<8) | /* set ep number */ | ||
473 | TXERRINTEN | | ||
474 | TXEPEN | /* endpoint enable */ | ||
475 | TXNAK | /* respond with NAK */ | ||
476 | TXACKINTEN | /* irq on ACK */ | ||
477 | TXCFINTE; /* irq on Clear feature */ | ||
478 | } | ||
479 | |||
424 | /* enable interrupt from this endpoint */ | 480 | /* enable interrupt from this endpoint */ |
425 | EN_INT |= (1<<(ep_num+7)); | 481 | EN_INT |= (1<<(ep_num+7)); |
426 | 482 | ||
@@ -435,10 +491,7 @@ int usb_drv_request_endpoint(int type, int dir) | |||
435 | void usb_drv_release_endpoint(int ep) | 491 | void usb_drv_release_endpoint(int ep) |
436 | { | 492 | { |
437 | int ep_num = EP_NUM(ep); | 493 | int ep_num = EP_NUM(ep); |
438 | int ep_dir = EP_DIR(ep); | 494 | logf("rel: ep%d %s", ep_num, XFER_DIR_STR(EP_DIR(ep))); |
439 | (void) ep_dir; | ||
440 | |||
441 | logf("rel: ep%d %s", ep_num, XFER_DIR_STR(ep_dir)); | ||
442 | endpoints[ep_num].allocated = false; | 495 | endpoints[ep_num].allocated = false; |
443 | 496 | ||
444 | /* disable interrupt from this endpoint */ | 497 | /* disable interrupt from this endpoint */ |
@@ -454,14 +507,20 @@ void usb_drv_release_endpoint(int ep) | |||
454 | void usb_drv_set_address(int address) | 507 | void usb_drv_set_address(int address) |
455 | { | 508 | { |
456 | (void)address; | 509 | (void)address; |
457 | /* UDC seems to set this automaticaly */ | 510 | /* UDC sets this automaticaly */ |
458 | } | 511 | } |
459 | 512 | ||
460 | static int _usb_drv_send(int endpoint, void *ptr, int length, bool block) | 513 | static int _usb_drv_send(int endpoint, void *ptr, int length, bool block) |
461 | { | 514 | { |
462 | struct endpoint_t *ep; | 515 | struct endpoint_t *ep; |
463 | int ep_num = EP_NUM(endpoint); | 516 | int ep_num = EP_NUM(endpoint); |
464 | 517 | ||
518 | /* for send transfers, make sure the data is committed */ | ||
519 | commit_discard_dcache_range(ptr, length); | ||
520 | |||
521 | logf("_usb_drv_send: endpt=0x%x, len=0x%x, block=%d", | ||
522 | endpoint, length, block); | ||
523 | |||
465 | if (ep_num == 0) | 524 | if (ep_num == 0) |
466 | ep = &ctrlep[DIR_IN]; | 525 | ep = &ctrlep[DIR_IN]; |
467 | else | 526 | else |
@@ -477,7 +536,7 @@ static int _usb_drv_send(int endpoint, void *ptr, int length, bool block) | |||
477 | 536 | ||
478 | switch (ep->type) | 537 | switch (ep->type) |
479 | { | 538 | { |
480 | case USB_ENDPOINT_XFER_CONTROL: | 539 | case USB_ENDPOINT_XFER_CONTROL: |
481 | ctr_write(); | 540 | ctr_write(); |
482 | break; | 541 | break; |
483 | 542 | ||
@@ -515,30 +574,40 @@ int usb_drv_recv(int endpoint, void* ptr, int length) | |||
515 | struct endpoint_t *ep; | 574 | struct endpoint_t *ep; |
516 | int ep_num = EP_NUM(endpoint); | 575 | int ep_num = EP_NUM(endpoint); |
517 | 576 | ||
577 | logf("usb_drv_recv: endpt=0x%x, len=0x%x, %ld", | ||
578 | endpoint, length, current_tick); | ||
579 | |||
580 | /* for recv, discard the cache lines related to the buffer */ | ||
581 | commit_discard_dcache_range(ptr, length); | ||
582 | |||
518 | if (ep_num == 0) | 583 | if (ep_num == 0) |
519 | { | 584 | { |
520 | ep = &ctrlep[DIR_OUT]; | 585 | ep = &ctrlep[DIR_OUT]; |
521 | 586 | ep->buf = ptr; | |
522 | ctr_read(); | 587 | ep->len = ep->cnt = length; |
588 | |||
589 | /* clear NAK bit */ | ||
590 | RX0CON &= ~RXNAK; | ||
591 | RX0DMAOUTLMADDR = (uint32_t)ptr; /* buffer address */ | ||
592 | RX0DMACTLO = DMA_START; /* start DMA */ | ||
523 | } | 593 | } |
524 | else | 594 | else |
525 | { | 595 | { |
526 | ep = &endpoints[ep_num]; | 596 | ep = &endpoints[ep_num]; |
527 | 597 | ep->buf = ptr; | |
598 | ep->len = ep->cnt = length; | ||
599 | |||
528 | /* clear NAK bit */ | 600 | /* clear NAK bit */ |
529 | BOUT_RXCON(ep_num) &= ~RXNAK; | 601 | BOUT_RXCON(ep_num) &= ~RXNAK; |
530 | BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)ptr; | 602 | BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)ptr; |
531 | BOUT_DMAOUTCTL(ep_num) = DMA_START; | 603 | BOUT_DMAOUTCTL(ep_num) = DMA_START; |
532 | } | 604 | } |
533 | |||
534 | ep->buf = ptr; | ||
535 | ep->len = ep->cnt = length; | ||
536 | 605 | ||
537 | return 0; | 606 | return 0; |
538 | } | 607 | } |
539 | 608 | ||
540 | /* Kill all transfers. Usually you need to set a bit for each endpoint | 609 | /* Kill all transfers. Usually you need to set a bit for each endpoint |
541 | * and flush fifos. You should also call the completion handler with | 610 | * and flush fifos. You should also call the completion handler with |
542 | * error status for everything | 611 | * error status for everything |
543 | */ | 612 | */ |
544 | void usb_drv_cancel_all_transfers(void) | 613 | void usb_drv_cancel_all_transfers(void) |
@@ -592,6 +661,8 @@ bool usb_drv_stalled(int endpoint, bool in) | |||
592 | void usb_drv_stall(int endpoint, bool stall, bool in) | 661 | void usb_drv_stall(int endpoint, bool stall, bool in) |
593 | { | 662 | { |
594 | int ep_num = EP_NUM(endpoint); | 663 | int ep_num = EP_NUM(endpoint); |
664 | logf("usb_drv: %sstall EP%d %s", | ||
665 | stall ? "": "un", ep_num, in ? "IN" : "OUT"); | ||
595 | 666 | ||
596 | switch (endpoints[ep_num].type) | 667 | switch (endpoints[ep_num].type) |
597 | { | 668 | { |
@@ -645,87 +716,23 @@ void usb_drv_stall(int endpoint, bool stall, bool in) | |||
645 | void usb_drv_init(void) | 716 | void usb_drv_init(void) |
646 | { | 717 | { |
647 | int ep_num; | 718 | int ep_num; |
648 | |||
649 | /* enable USB clock */ | ||
650 | SCU_CLKCFG &= ~CLKCFG_UDC; | ||
651 | |||
652 | /* 1. do soft disconnect */ | ||
653 | DEV_CTL = DEV_SELF_PWR; | ||
654 | |||
655 | /* 2. do power on reset to PHY */ | ||
656 | DEV_CTL = DEV_SELF_PWR | | ||
657 | SOFT_POR; | ||
658 | |||
659 | /* 3. wait more than 10ms */ | ||
660 | udelay(20000); | ||
661 | |||
662 | /* 4. clear SOFT_POR bit */ | ||
663 | DEV_CTL &= ~SOFT_POR; | ||
664 | |||
665 | /* 5. configure minimal EN_INT */ | ||
666 | EN_INT = EN_SUSP_INTR | /* Enable Suspend Interrupt */ | ||
667 | EN_RESUME_INTR | /* Enable Resume Interrupt */ | ||
668 | EN_USBRST_INTR | /* Enable USB Reset Interrupt */ | ||
669 | EN_OUT0_INTR | /* Enable OUT Token receive Interrupt EP0 */ | ||
670 | EN_IN0_INTR | /* Enable IN Token transmits Interrupt EP0 */ | ||
671 | EN_SETUP_INTR; /* Enable SETUP Packet Receive Interrupt */ | ||
672 | |||
673 | /* 6. configure INTCON */ | ||
674 | INTCON = UDC_INTHIGH_ACT | /* interrupt high active */ | ||
675 | UDC_INTEN; /* enable EP0 interrupts */ | ||
676 | |||
677 | /* 7. configure EP0 control registers */ | ||
678 | TX0CON = TXACKINTEN | /* Set as one to enable the EP0 tx irq */ | ||
679 | TXNAK; /* Set as one to response NAK handshake */ | ||
680 | |||
681 | RX0CON = RXACKINTEN | | ||
682 | RXEPEN | /* Endpoint 0 Enable. When cleared the endpoint does | ||
683 | * not respond to an SETUP or OUT token | ||
684 | */ | ||
685 | |||
686 | RXNAK; /* Set as one to response NAK handshake */ | ||
687 | |||
688 | /* 8. write final bits to DEV_CTL */ | ||
689 | DEV_CTL = CSR_DONE | /* Configure CSR done */ | ||
690 | DEV_PHY16BIT | /* 16-bit data path enabled. udc_clk = 30MHz */ | ||
691 | DEV_SOFT_CN | /* Device soft connect */ | ||
692 | DEV_SELF_PWR; /* Device self power */ | ||
693 | 719 | ||
694 | /* init semaphore of ep0 */ | 720 | /* init semaphore of ep0 */ |
695 | semaphore_init(&ctrlep[DIR_OUT].complete, 1, 0); | 721 | semaphore_init(&ctrlep[DIR_OUT].complete, 1, 0); |
696 | semaphore_init(&ctrlep[DIR_IN].complete, 1, 0); | 722 | semaphore_init(&ctrlep[DIR_IN].complete, 1, 0); |
697 | 723 | ||
724 | /* init semaphores for other endpoints */ | ||
698 | for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++) | 725 | for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++) |
699 | { | ||
700 | semaphore_init(&endpoints[ep_num].complete, 1, 0); | 726 | semaphore_init(&endpoints[ep_num].complete, 1, 0); |
701 | |||
702 | if (ep_num%3 == 0) /* IIN 3, 6, 9, 12, 15 */ | ||
703 | { | ||
704 | IIN_TXCON(ep_num) |= (ep_num<<8)|TXEPEN|TXNAK; /* ep_num, enable, NAK */ | ||
705 | } | ||
706 | else if (ep_num%3 == 1) /* BOUT 1, 4, 7, 10, 13 */ | ||
707 | { | ||
708 | BOUT_RXCON(ep_num) |= (ep_num<<8)|RXEPEN|RXNAK; /* ep_num, NAK, enable */ | ||
709 | } | ||
710 | else if (ep_num%3 == 2) /* BIN 2, 5, 8, 11, 14 */ | ||
711 | { | ||
712 | BIN_TXCON(ep_num) |= (ep_num<<8)|TXEPEN|TXNAK; /* ep_num, enable, NAK */ | ||
713 | } | ||
714 | } | ||
715 | } | 727 | } |
716 | 728 | ||
717 | /* turn off usb core */ | 729 | /* turn off usb core */ |
718 | void usb_drv_exit(void) | 730 | void usb_drv_exit(void) |
719 | { | 731 | { |
720 | DEV_CTL = DEV_SELF_PWR; | 732 | /* udc module reset */ |
721 | 733 | SCU_RSTCFG |= (1<<1); | |
722 | /* disable USB interrupts in interrupt controller */ | 734 | udelay(10); |
723 | INTC_IMR &= ~IRQ_ARM_UDC; | 735 | SCU_RSTCFG &= ~(1<<1); |
724 | INTC_IECR &= ~IRQ_ARM_UDC; | ||
725 | |||
726 | /* we cannot disable UDC clock since this causes data abort | ||
727 | * when reading DEV_INFO in order to check usb connect event | ||
728 | */ | ||
729 | } | 736 | } |
730 | 737 | ||
731 | int usb_detect(void) | 738 | int usb_detect(void) |
@@ -736,3 +743,140 @@ int usb_detect(void) | |||
736 | return USB_EXTRACTED; | 743 | return USB_EXTRACTED; |
737 | } | 744 | } |
738 | 745 | ||
746 | /* UDC ISR function */ | ||
747 | void INT_UDC(void) | ||
748 | { | ||
749 | uint32_t txstat, rxstat; | ||
750 | int ep_num; | ||
751 | |||
752 | /* read what caused UDC irq */ | ||
753 | uint32_t intsrc = INT2FLAG & 0x7fffff; | ||
754 | |||
755 | if (intsrc & USBRST_INTR) /* usb reset */ | ||
756 | { | ||
757 | logf("udc_int: reset, %ld", current_tick); | ||
758 | |||
759 | EN_INT = EN_SUSP_INTR | /* Enable Suspend Irq */ | ||
760 | EN_RESUME_INTR | /* Enable Resume Irq */ | ||
761 | EN_USBRST_INTR | /* Enable USB Reset Irq */ | ||
762 | EN_OUT0_INTR | /* Enable OUT Token receive Irq EP0 */ | ||
763 | EN_IN0_INTR | /* Enable IN Token transmit Irq EP0 */ | ||
764 | EN_SETUP_INTR; /* Enable SETUP Packet Receive Irq */ | ||
765 | |||
766 | INTCON = UDC_INTHIGH_ACT | /* interrupt high active */ | ||
767 | UDC_INTEN; /* enable EP0 irqs */ | ||
768 | |||
769 | TX0CON = TXACKINTEN | /* Set as one to enable the EP0 tx irq */ | ||
770 | TXNAK; /* Set as one to response NAK handshake */ | ||
771 | |||
772 | RX0CON = RXACKINTEN | | ||
773 | RXEPEN | /* Endpoint 0 Enable. When cleared the | ||
774 | * endpoint does not respond to an SETUP | ||
775 | * or OUT token */ | ||
776 | RXNAK; /* Set as one to response NAK handshake */ | ||
777 | |||
778 | set_address = false; | ||
779 | set_configuration = false; | ||
780 | } | ||
781 | |||
782 | /* This needs to be processed AFTER usb reset */ | ||
783 | udc_helper(); | ||
784 | |||
785 | if (intsrc & SETUP_INTR) /* setup interrupt */ | ||
786 | { | ||
787 | setup_irq_handler(); | ||
788 | } | ||
789 | |||
790 | if (intsrc & IN0_INTR) /* ep0 in interrupt */ | ||
791 | { | ||
792 | txstat = TX0STAT; /* read clears flags */ | ||
793 | |||
794 | /* TODO handle errors */ | ||
795 | if (txstat & TXACK) /* check TxACK flag */ | ||
796 | { | ||
797 | if (ctrlep[DIR_IN].cnt >= 0) | ||
798 | { | ||
799 | /* we still have data to send (or ZLP) */ | ||
800 | ctr_write(); | ||
801 | } | ||
802 | else | ||
803 | { | ||
804 | /* final ack received */ | ||
805 | usb_core_transfer_complete(0, /* ep */ | ||
806 | USB_DIR_IN, /* dir */ | ||
807 | 0, /* status */ | ||
808 | ctrlep[DIR_IN].len); /* length */ | ||
809 | |||
810 | /* release semaphore for blocking transfer */ | ||
811 | if (ctrlep[DIR_IN].block) | ||
812 | semaphore_release(&ctrlep[DIR_IN].complete); | ||
813 | } | ||
814 | } | ||
815 | } | ||
816 | |||
817 | if (intsrc & OUT0_INTR) /* ep0 out interrupt */ | ||
818 | { | ||
819 | rxstat = RX0STAT; | ||
820 | |||
821 | /* TODO handle errors */ | ||
822 | if (rxstat & RXACK) /* RxACK */ | ||
823 | { | ||
824 | if (ctrlep[DIR_OUT].cnt > 0) | ||
825 | ctr_read(); | ||
826 | else | ||
827 | usb_core_transfer_complete(0, /* ep */ | ||
828 | USB_DIR_OUT, /* dir */ | ||
829 | 0, /* status */ | ||
830 | ctrlep[DIR_OUT].len); /* length */ | ||
831 | } | ||
832 | } | ||
833 | |||
834 | if (intsrc & RESUME_INTR) /* usb resume */ | ||
835 | { | ||
836 | TX0CON |= TXCLR; /* TxClr */ | ||
837 | TX0CON &= ~TXCLR; | ||
838 | |||
839 | RX0CON |= RXCLR; /* RxClr */ | ||
840 | RX0CON &= ~RXCLR; | ||
841 | } | ||
842 | |||
843 | if (intsrc & SUSP_INTR) /* usb suspend */ | ||
844 | { | ||
845 | } | ||
846 | |||
847 | if (intsrc & CONN_INTR) /* usb connect */ | ||
848 | { | ||
849 | udc_phy_reset(); | ||
850 | udelay(10000); /* wait at least 10ms */ | ||
851 | udc_soft_connect(); | ||
852 | } | ||
853 | |||
854 | /* endpoints other then EP0 */ | ||
855 | if (intsrc & 0x7fff00) | ||
856 | { | ||
857 | /* lets figure out which ep generated irq */ | ||
858 | for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++) | ||
859 | { | ||
860 | struct endpoint_t *ep = &endpoints[ep_num]; | ||
861 | |||
862 | if ((intsrc & ep->intr_mask) && ep->allocated) | ||
863 | { | ||
864 | switch (ep->type) | ||
865 | { | ||
866 | case USB_ENDPOINT_XFER_BULK: | ||
867 | if (ep->dir == DIR_OUT) | ||
868 | blk_out_irq_handler(ep_num); | ||
869 | else | ||
870 | blk_in_irq_handler(ep_num); | ||
871 | |||
872 | break; | ||
873 | |||
874 | case USB_ENDPOINT_XFER_INT: | ||
875 | int_in_irq_handler(ep_num); | ||
876 | |||
877 | break; | ||
878 | } | ||
879 | } | ||
880 | } | ||
881 | } | ||
882 | } | ||
diff --git a/firmware/target/arm/rk27xx/usb-rk27xx.c b/firmware/target/arm/rk27xx/usb-rk27xx.c index 20bf867c8d..09c9090a3b 100644 --- a/firmware/target/arm/rk27xx/usb-rk27xx.c +++ b/firmware/target/arm/rk27xx/usb-rk27xx.c | |||
@@ -32,6 +32,20 @@ int usb_status = USB_EXTRACTED; | |||
32 | 32 | ||
33 | void usb_init_device(void) | 33 | void usb_init_device(void) |
34 | { | 34 | { |
35 | /* enable UDC interrupt */ | ||
36 | INTC_IMR |= (1<<16); | ||
37 | INTC_IECR |= (1<<16); | ||
38 | |||
39 | EN_INT = EN_SUSP_INTR | /* Enable Suspend Interrupt */ | ||
40 | EN_RESUME_INTR | /* Enable Resume Interrupt */ | ||
41 | EN_USBRST_INTR | /* Enable USB Reset Interrupt */ | ||
42 | EN_OUT0_INTR | /* Enable OUT Token receive Interrupt EP0 */ | ||
43 | EN_IN0_INTR | /* Enable IN Token transmits Interrupt EP0 */ | ||
44 | EN_SETUP_INTR; /* Enable SETUP Packet Receive Interrupt */ | ||
45 | |||
46 | /* configure INTCON */ | ||
47 | INTCON = UDC_INTHIGH_ACT | /* interrupt high active */ | ||
48 | UDC_INTEN; /* enable EP0 interrupts */ | ||
35 | } | 49 | } |
36 | 50 | ||
37 | void usb_attach(void) | 51 | void usb_attach(void) |