summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2016-07-31 03:00:43 +0200
committerCástor Muñoz <cmvidal@gmail.com>2016-08-02 04:57:49 +0200
commit5e305d35c94199241f71a994cf6a691aec49688c (patch)
tree51b52755a57017b3270720e303193e2daac3d3cf
parent0f89b041c0803e7e1cee2c9bcfc08b209d1c88fd (diff)
downloadrockbox-5e305d35c94199241f71a994cf6a691aec49688c.tar.gz
rockbox-5e305d35c94199241f71a994cf6a691aec49688c.zip
Introduce new USB driver for Synopsys DesignWare USB OTG core.
Based on g#844 and g#949, it is intended as a replacement for the current s3c6400x USB driver. The DesignWare USB OTG core is integrated into many SoC's, however HW core version and capabilities (mainly DMA mode, Tx FIFO mode, FIFO size and number of available IN/OUT endpoins) may differ: CPU targets HW ver DMA NPTX FIFO FIFO sz #IN/OUT -------- ------------- ------ --- --------- ------- ------- as3525v2 sansaclipplus 2.60a Yes Dedicated 0x535 4/4 sansaclipv2 sansaclipzip sansafuzev2 s5l8701 ipodnano2g 2.20a Yes Shared 0x500 4/5 s5l8702 ipod6g 2.60a Yes Dedicated 0x820 7/7 ipodnano3g s5l8720 ipodnano4g ? ? ? ? ? Functionality supported by this driver: - Device mode, compatible with USB 1.1/2.0 hosts. - Shared FIFO (USB_DW_SHARED_FIFO) or dedicated FIFOs. - No DMA (USB_DW_ARCH_SLAVE) or internal DMA mode. - Concurrent transfers: control, bulk (usb_storage, usb_serial) and interrupt (usb_hid). Actually this driver is not used by any CPU, it will be enabled for each individual CPU/target in next patches. Change-Id: I74a1e836d18927a31f6977d71115fb442477dd5f
-rw-r--r--firmware/SOURCES2
-rw-r--r--firmware/export/config.h7
-rw-r--r--firmware/export/usb-designware.h287
-rw-r--r--firmware/target/arm/usb-designware.c1381
-rw-r--r--firmware/usbstack/usb_serial.c11
5 files changed, 1687 insertions, 1 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index db3f09ea8e..9aab3c1115 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -766,6 +766,8 @@ target/arm/usb-drv-arc.c
766target/arm/as3525/usb-drv-as3525.c 766target/arm/as3525/usb-drv-as3525.c
767#elif CONFIG_USBOTG == USBOTG_S3C6400X 767#elif CONFIG_USBOTG == USBOTG_S3C6400X
768target/arm/usb-s3c6400x.c 768target/arm/usb-s3c6400x.c
769#elif CONFIG_USBOTG == USBOTG_DESIGNWARE
770target/arm/usb-designware.c
769#elif CONFIG_USBOTG == USBOTG_ISP1583 771#elif CONFIG_USBOTG == USBOTG_ISP1583
770drivers/isp1583.c 772drivers/isp1583.c
771#elif CONFIG_USBOTG == USBOTG_RK27XX 773#elif CONFIG_USBOTG == USBOTG_RK27XX
diff --git a/firmware/export/config.h b/firmware/export/config.h
index bdbc1c3f44..b769b63c32 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -363,6 +363,7 @@ Lyre prototype 1 */
363#define USBOTG_JZ4740 4740 /* Ingenic Jz4740/Jz4732 */ 363#define USBOTG_JZ4740 4740 /* Ingenic Jz4740/Jz4732 */
364#define USBOTG_AS3525 3525 /* AMS AS3525 */ 364#define USBOTG_AS3525 3525 /* AMS AS3525 */
365#define USBOTG_S3C6400X 6400 /* Samsung S3C6400X, also used in the S5L8701/S5L8702/S5L8720 */ 365#define USBOTG_S3C6400X 6400 /* Samsung S3C6400X, also used in the S5L8701/S5L8702/S5L8720 */
366#define USBOTG_DESIGNWARE 6401 /* Synopsys DesignWare OTG, used in S5L8701/S5L8702/S5L8720/AS3252v2 */
366#define USBOTG_RK27XX 2700 /* Rockchip rk27xx */ 367#define USBOTG_RK27XX 2700 /* Rockchip rk27xx */
367#define USBOTG_TNETV105 105 /* TI TNETV105 */ 368#define USBOTG_TNETV105 105 /* TI TNETV105 */
368 369
@@ -908,6 +909,9 @@ Lyre prototype 1 */
908#elif CONFIG_USBOTG == USBOTG_S3C6400X /* FIXME */ && CONFIG_CPU != S5L8701 909#elif CONFIG_USBOTG == USBOTG_S3C6400X /* FIXME */ && CONFIG_CPU != S5L8701
909#define USB_STATUS_BY_EVENT 910#define USB_STATUS_BY_EVENT
910#define USB_DETECT_BY_REQUEST 911#define USB_DETECT_BY_REQUEST
912#elif CONFIG_USBOTG == USBOTG_DESIGNWARE /* FIXME */ && CONFIG_CPU != S5L8701
913#define USB_STATUS_BY_EVENT
914#define USB_DETECT_BY_REQUEST
911#elif CONFIG_USBOTG == USBOTG_RK27XX 915#elif CONFIG_USBOTG == USBOTG_RK27XX
912#define USB_STATUS_BY_EVENT 916#define USB_STATUS_BY_EVENT
913#define USB_DETECT_BY_REQUEST 917#define USB_DETECT_BY_REQUEST
@@ -1147,6 +1151,7 @@ Lyre prototype 1 */
1147#elif (CONFIG_USBOTG == USBOTG_ARC) || \ 1151#elif (CONFIG_USBOTG == USBOTG_ARC) || \
1148 (CONFIG_USBOTG == USBOTG_JZ4740) || \ 1152 (CONFIG_USBOTG == USBOTG_JZ4740) || \
1149 (CONFIG_USBOTG == USBOTG_M66591) || \ 1153 (CONFIG_USBOTG == USBOTG_M66591) || \
1154 (CONFIG_USBOTG == USBOTG_DESIGNWARE) || \
1150 (CONFIG_USBOTG == USBOTG_AS3525) 1155 (CONFIG_USBOTG == USBOTG_AS3525)
1151#define USB_HAS_BULK 1156#define USB_HAS_BULK
1152#define USB_HAS_INTERRUPT 1157#define USB_HAS_INTERRUPT
@@ -1169,7 +1174,7 @@ Lyre prototype 1 */
1169#if defined(HAVE_BOOTLOADER_USB_MODE) || \ 1174#if defined(HAVE_BOOTLOADER_USB_MODE) || \
1170 defined(CREATIVE_ZVx) || defined(CPU_TCC77X) || defined(CPU_TCC780X) || \ 1175 defined(CREATIVE_ZVx) || defined(CPU_TCC77X) || defined(CPU_TCC780X) || \
1171 CONFIG_USBOTG == USBOTG_JZ4740 || CONFIG_USBOTG == USBOTG_AS3525 || \ 1176 CONFIG_USBOTG == USBOTG_JZ4740 || CONFIG_USBOTG == USBOTG_AS3525 || \
1172 CONFIG_USBOTG == USBOTG_S3C6400X 1177 CONFIG_USBOTG == USBOTG_S3C6400X || CONFIG_USBOTG == USBOTG_DESIGNWARE
1173#define USB_ENABLE_STORAGE 1178#define USB_ENABLE_STORAGE
1174#endif 1179#endif
1175 1180
diff --git a/firmware/export/usb-designware.h b/firmware/export/usb-designware.h
new file mode 100644
index 0000000000..428733b4f5
--- /dev/null
+++ b/firmware/export/usb-designware.h
@@ -0,0 +1,287 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 Michael Sparmann
11 * Copyright (C) 2014 by Marcin Bukat
12 * Copyright (C) 2016 by Cástor Muñoz
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23#ifndef __USB_DESIGNWARE_H__
24#define __USB_DESIGNWARE_H__
25
26#include <inttypes.h>
27#include "config.h"
28
29#ifndef REG32_PTR_T
30#define REG32_PTR_T volatile uint32_t *
31#endif
32
33/* Global registers */
34#define DWC_GOTGCTL (*((REG32_PTR_T)(OTGBASE + 0x00)))
35#define DWC_GOTGINT (*((REG32_PTR_T)(OTGBASE + 0x04)))
36#define DWC_GAHBCFG (*((REG32_PTR_T)(OTGBASE + 0x08)))
37 #define PTXFELVL (1<<8)
38 #define TXFELVL (1<<7)
39 #define DMAEN (1<<5)
40 #define HBSTLEN(x) ((x)<<1)
41 #define HBSTLEN_SINGLE 0
42 #define HBSTLEN_INCR 1
43 #define HBSTLEN_INCR4 3
44 #define HBSTLEN_INCR8 5
45 #define HBSTLEN_INCR16 7
46 #define GINT (1<<0)
47
48#define DWC_GUSBCFG (*((REG32_PTR_T)(OTGBASE + 0x0c)))
49 #define FDMOD (1<<30)
50 #define TRDT(x) ((x)<<10)
51 #define DDRSEL (1<<7)
52 #define PHSEL (1<<6)
53 #define FSINTF (1<<5)
54 #define ULPI_UTMI_SEL (1<<4)
55 #define PHYIF16 (1<<3)
56
57#define DWC_GRSTCTL (*((REG32_PTR_T)(OTGBASE + 0x10)))
58 #define AHBIDL (1<<31)
59 #define TXFNUM(x) ((x)<<6)
60 #define TXFFLSH (1<<5)
61 #define RXFFLSH (1<<4)
62 #define CSRST (1<<0)
63
64#define DWC_GINTSTS (*((REG32_PTR_T)(OTGBASE + 0x14)))
65 #define WKUINT (1<<31)
66 #define SRQINT (1<<30)
67 #define DISCINT (1<<29)
68 #define CIDSCHG (1<<28)
69 #define PTXFE (1<<26)
70 #define HCINT (1<<25)
71 #define HPRTINT (1<<24)
72 #define FETSUSP (1<<22)
73 #define IPXFR (1<<21)
74 #define IISOIXFR (1<<20)
75 #define OEPINT (1<<19)
76 #define IEPINT (1<<18)
77 #define EPMIS (1<<17)
78 #define EOPF (1<<15)
79 #define ISOODPR (1<<14)
80 #define ENUMDNE (1<<13)
81 #define USBRST (1<<12)
82 #define USBSUSP (1<<11)
83 #define ESUSP (1<<10)
84 #define GOUTNAKEFF (1<<7)
85 #define GINAKEFF (1<<6)
86 #define NPTXFE (1<<5)
87 #define RXFLVL (1<<4)
88 #define SOF (1<<3)
89 #define OTGINT (1<<2)
90 #define MMIS (1<<1)
91 #define CMOD (1<<0)
92
93#define DWC_GINTMSK (*((REG32_PTR_T)(OTGBASE + 0x18)))
94#define DWC_GRXSTSR (*((REG32_PTR_T)(OTGBASE + 0x1c)))
95#define DWC_GRXSTSP (*((REG32_PTR_T)(OTGBASE + 0x20)))
96 #define PKTSTS_GLOBALOUTNAK 1
97 #define PKTSTS_OUTRX 2
98 #define PKTSTS_HCHIN 2
99 #define PKTSTS_OUTDONE 3
100 #define PKTSTS_HCHIN_XFER_COMP 3
101 #define PKTSTS_SETUPDONE 4
102 #define PKTSTS_DATATOGGLEERR 5
103 #define PKTSTS_SETUPRX 6
104 #define PKTSTS_HCHHALTED 7
105
106#define DWC_GRXFSIZ (*((REG32_PTR_T)(OTGBASE + 0x24)))
107#ifdef USB_DW_SHARED_FIFO
108#define DWC_GNPTXFSIZ (*((REG32_PTR_T)(OTGBASE + 0x28)))
109#else
110#define DWC_TX0FSIZ (*((REG32_PTR_T)(OTGBASE + 0x28)))
111#endif
112#define DWC_GNPTXSTS (*((REG32_PTR_T)(OTGBASE + 0x2c)))
113#define DWC_GI2CCTL (*((REG32_PTR_T)(OTGBASE + 0x30)))
114/* reserved */
115#define DWC_GCCFG (*((REG32_PTR_T)(OTGBASE + 0x38)))
116 #define NOVBUSSENS (1<<21)
117 #define SOFOUTEN (1<<20)
118 #define VBUSBSEN (1<<19)
119 #define VBUSASEN (1<<18)
120 #define I2CPADEN (1<<17)
121 #define PWRDWN (1<<16)
122
123#define DWC_CID (*((REG32_PTR_T)(OTGBASE + 0x3c)))
124#define DWC_GSNPSID (*((REG32_PTR_T)(OTGBASE + 0x40)))
125#define DWC_GHWCFG1 (*((REG32_PTR_T)(OTGBASE + 0x44)))
126#define DWC_GHWCFG2 (*((REG32_PTR_T)(OTGBASE + 0x48)))
127#define DWC_GHWCFG3 (*((REG32_PTR_T)(OTGBASE + 0x4c)))
128#define DWC_GHWCFG4 (*((REG32_PTR_T)(OTGBASE + 0x50)))
129#define DWC_GLPMCFG (*((REG32_PTR_T)(OTGBASE + 0x54)))
130#define DWC_HPTXFSIZ (*((REG32_PTR_T)(OTGBASE + 0x100)))
131#define DWC_DIEPTXF(x) (*((REG32_PTR_T)(OTGBASE + 0x104 + 4*(x)))) /*0..15*/
132
133/* Host mode registers */
134#define DWC_HCFG (*((REG32_PTR_T)(OTGBASE + 0x400)))
135#define DWC_HFIR (*((REG32_PTR_T)(OTGBASE + 0x404)))
136#define DWC_HFNUM (*((REG32_PTR_T)(OTGBASE + 0x408)))
137/* reserved */
138#define DWC_HPTXSTS (*((REG32_PTR_T)(OTGBASE + 0x410)))
139#define DWC_HAINT (*((REG32_PTR_T)(OTGBASE + 0x414)))
140#define DWC_HAINTMSK (*((REG32_PTR_T)(OTGBASE + 0x418)))
141#define DWC_HPRT (*((REG32_PTR_T)(OTGBASE + 0x440)))
142#define DWC_HCCHAR(x) (*((REG32_PTR_T)(OTGBASE + 0x500 + 0x20*(x))))
143#define DWC_HCSPLT(x) (*((REG32_PTR_T)(OTGBASE + 0x504 + 0x20*(x))))
144#define DWC_HCINT(x) (*((REG32_PTR_T)(OTGBASE + 0x508 + 0x20*(x))))
145#define DWC_HCINTMSK(x) (*((REG32_PTR_T)(OTGBASE + 0x50c + 0x20*(x))))
146#define DWC_HCTSIZ(x) (*((REG32_PTR_T)(OTGBASE + 0x510 + 0x20*(x))))
147#define DWC_HCDMA(x) (*((REG32_PTR_T)(OTGBASE + 0x514 + 0x20*(x))))
148
149/* Device mode registers */
150#define DWC_DCFG (*((REG32_PTR_T)(OTGBASE + 0x800)))
151 #define EPMISCNT(x) ((x)<<18)
152 #define DAD(x) ((x)<<4)
153 #define NZLSOHSK (1<<2)
154
155#define DWC_DCTL (*((REG32_PTR_T)(OTGBASE + 0x804)))
156 #define POPRGDNE (1<<11)
157 #define CGONAK (1<<10)
158 #define SGONAK (1<<9)
159 #define CGINAK (1<<8)
160 #define SGINAK (1<<7)
161 #define TCTL(x) ((x)<<4)
162 #define GONSTS (1<<3)
163 #define GINSTS (1<<2)
164 #define SDIS (1<<1)
165 #define RWUSIG (1<<0)
166
167#define DWC_DSTS (*((REG32_PTR_T)(OTGBASE + 0x808)))
168/* reserved */
169#define DWC_DIEPMSK (*((REG32_PTR_T)(OTGBASE + 0x810)))
170#define DWC_DOEPMSK (*((REG32_PTR_T)(OTGBASE + 0x814)))
171#define DWC_DAINT (*((REG32_PTR_T)(OTGBASE + 0x818)))
172#define DWC_DAINTMSK (*((REG32_PTR_T)(OTGBASE + 0x81c)))
173/* reserved */
174#define DWC_DVBUSDIS (*((REG32_PTR_T)(OTGBASE + 0x828)))
175#define DWC_DVBUSPULSE (*((REG32_PTR_T)(OTGBASE + 0x82c)))
176
177#ifdef USB_DW_SHARED_FIFO
178#define DWC_DTKNQR1 (*((REG32_PTR_T)(OTGBASE + 0x820)))
179#define DWC_DTKNQR2 (*((REG32_PTR_T)(OTGBASE + 0x824)))
180#define DWC_DTKNQR3 (*((REG32_PTR_T)(OTGBASE + 0x830)))
181#define DWC_DTKNQR4 (*((REG32_PTR_T)(OTGBASE + 0x834)))
182
183#else /* !USB_DW_SHARED_FIFO */
184#define DWC_DTHRCTL (*((REG32_PTR_T)(OTGBASE + 0x830)))
185 #define ARPEN (1<<27)
186 #define RXTHRLEN(x) ((x)<<17)
187 #define RXTHREN (1<<16)
188 #define TXTHRLEN(x) ((x)<<2)
189 #define ISOTHREN (1<<1)
190 #define NONISOTHREN (1<<0)
191
192#define DWC_DIEPEMPMSK (*((REG32_PTR_T)(OTGBASE + 0x834)))
193#define DWC_DEACHINT (*((REG32_PTR_T)(OTGBASE + 0x838)))
194#define DWC_DEACHINTMSK (*((REG32_PTR_T)(OTGBASE + 0x83c)))
195#define DWC_DIEPEACHMSK(x) (*((REG32_PTR_T)(OTGBASE + 0x840 + 4*(x))))
196#define DWC_DOEPEACHMSK(x) (*((REG32_PTR_T)(OTGBASE + 0x880 + 4*(x))))
197#endif
198
199#define DWC_DIEPCTL(x) (*((REG32_PTR_T)(OTGBASE + 0x900 + 0x20*(x))))
200#define DWC_DOEPCTL(x) (*((REG32_PTR_T)(OTGBASE + 0xb00 + 0x20*(x))))
201 #define EPENA (1<<31)
202 #define EPDIS (1<<30)
203 #define SD0PID (1<<28)
204 #define SNAK (1<<27)
205 #define CNAK (1<<26)
206 #define DTXFNUM(x) ((x)<<22)
207 #define STALL (1<<21)
208 #define EPTYP(x) ((x)<<18)
209 #define EPTYP_CONTROL 0
210 #define EPTYP_ISOCHRONOUS 1
211 #define EPTYP_BULK 2
212 #define EPTYP_INTERRUPT 3
213 #define NAKSTS (1<<17)
214 #define USBAEP (1<<15)
215 #define NEXTEP(x) ((x)<<11)
216
217#define DWC_DIEPINT(x) (*((REG32_PTR_T)(OTGBASE + 0x908 + 0x20*(x))))
218#define DWC_DOEPINT(x) (*((REG32_PTR_T)(OTGBASE + 0xb08 + 0x20*(x))))
219 #define TXFE (1<<7) /* IN */
220 #define INEPNE (1<<6) /* IN */
221 #define ITEPMIS (1<<5) /* IN */
222 #define ITTXFE (1<<4) /* IN */
223 #define OTEPDIS (1<<4) /* OUT */
224 #define TOC (1<<3) /* control IN */
225 #define STUP (1<<3) /* control OUT */
226 #define AHBERR (1<<2)
227 #define EPDISD (1<<1)
228 #define XFRC (1<<0)
229
230#define DWC_DIEPTSIZ(x) (*((REG32_PTR_T)(OTGBASE + 0x910 + 0x20*(x))))
231#define DWC_DOEPTSIZ(x) (*((REG32_PTR_T)(OTGBASE + 0xb10 + 0x20*(x))))
232 #define MCCNT(x) ((x)<<29) /* IN */
233 #define STUPCNT(x) ((x)<<29) /* control OUT */
234 #define RXDPID(x) ((x)<<29) /* isochronous OUT */
235 #define PKTCNT(x) ((x)<<19)
236 #define XFERSIZE(x) ((x)<<0)
237
238#define DWC_DIEPDMA(x) (*((REG32_PTR_T)(OTGBASE + 0x914 + 0x20*(x))))
239#define DWC_DOEPDMA(x) (*((REG32_PTR_T)(OTGBASE + 0xb14 + 0x20*(x))))
240
241#define DWC_DTXFSTS(x) (*((REG32_PTR_T)(OTGBASE + 0x918 + 0x20*(x))))
242#define DWC_PCGCCTL (*((REG32_PTR_T)(OTGBASE + 0xe00)))
243#define DWC_DFIFO(x) (*((REG32_PTR_T)(OTGBASE + 0x1000 + 0x1000*(x))))
244
245/* Device mode registers by (epnum,epdir), d==0 -> IN */
246#define DWC_EPCTL(n,d) (*((REG32_PTR_T)(OTGBASE + 0x900 + 0x200*(d) + 0x20*(n))))
247#define DWC_EPINT(n,d) (*((REG32_PTR_T)(OTGBASE + 0x908 + 0x200*(d) + 0x20*(n))))
248#define DWC_EPTSIZ(n,d) (*((REG32_PTR_T)(OTGBASE + 0x910 + 0x200*(d) + 0x20*(n))))
249#define DWC_EPDMA(n,d) (*((REG32_PTR_T)(OTGBASE + 0x914 + 0x200*(d) + 0x20*(n))))
250
251
252/* HS PHY/interface configuration */
253#define DWC_PHYTYPE_UTMI_8 (0)
254#define DWC_PHYTYPE_UTMI_16 (PHYIF16)
255#define DWC_PHYTYPE_ULPI_SDR (ULPI_UTMI_SEL)
256#define DWC_PHYTYPE_ULPI_DDR (ULPI_UTMI_SEL|DDRSEL)
257
258/* configure USB OTG capabilities on SoC */
259struct usb_dw_config
260{
261 uint8_t phytype; /* DWC_PHYTYPE_ */
262
263 uint16_t rx_fifosz;
264 uint16_t nptx_fifosz;
265 uint16_t ptx_fifosz;
266#ifdef USB_DW_SHARED_FIFO
267 bool use_ptxfifo_as_plain_buffer;
268#endif
269#ifdef USB_DW_ARCH_SLAVE
270 bool disable_double_buffering;
271#else
272 uint8_t ahb_burst_len; /* HBSTLEN_ */
273#ifndef USB_DW_SHARED_FIFO
274 uint8_t ahb_threshold;
275#endif
276#endif
277};
278
279extern const struct usb_dw_config usb_dw_config;
280
281extern void usb_dw_target_enable_clocks(void);
282extern void usb_dw_target_disable_clocks(void);
283extern void usb_dw_target_enable_irq(void);
284extern void usb_dw_target_disable_irq(void);
285extern void usb_dw_target_clear_irq(void);
286
287#endif /* __USB_DESIGNWARE_H__ */
diff --git a/firmware/target/arm/usb-designware.c b/firmware/target/arm/usb-designware.c
new file mode 100644
index 0000000000..24c6055434
--- /dev/null
+++ b/firmware/target/arm/usb-designware.c
@@ -0,0 +1,1381 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2009-2014 by Michael Sparmann
11 * Copyright © 2010 Amaury Pouly
12 * Copyright (C) 2014 by Marcin Bukat
13 * Copyright (C) 2016 by Cástor Muñoz
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
19 *
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
22 *
23 ****************************************************************************/
24#include <inttypes.h>
25#include <string.h>
26
27#include "config.h"
28#include "cpu.h"
29#include "system.h"
30#include "kernel.h"
31#include "panic.h"
32#include "power.h"
33#include "usb.h"
34#include "usb_drv.h"
35#include "usb_ch9.h"
36#include "usb_core.h"
37
38#include "usb-designware.h"
39
40/* Define LOGF_ENABLE to enable logf output in this file */
41/*#define LOGF_ENABLE*/
42#include "logf.h"
43
44
45/* The ARM940T uses a subset of the ARMv4 functions, not
46 * supporting clean/invalidate cache entries using MVA.
47 */
48#if CONFIG_CPU == S5L8701
49#define DISCARD_DCACHE_RANGE(b,s) commit_discard_dcache()
50#define COMMIT_DCACHE_RANGE(b,s) commit_dcache()
51#else
52#define DISCARD_DCACHE_RANGE(b,s) discard_dcache_range(b,s)
53#define COMMIT_DCACHE_RANGE(b,s) commit_dcache_range(b,s)
54#endif
55
56#ifndef USB_DW_TOUTCAL
57#define USB_DW_TOUTCAL 0
58#endif
59
60#define GET_DTXFNUM(ep) ((DWC_DIEPCTL(ep)>>22) & 0xf)
61
62#define USB_DW_NUM_DIRS 2
63#define USB_DW_DIR_OFF(dir) (((dir) == USB_DW_EPDIR_IN) ? 0 : 16)
64
65enum usb_dw_epdir
66{
67 USB_DW_EPDIR_IN = 0,
68 USB_DW_EPDIR_OUT = 1,
69};
70
71union usb_ep0_buffer
72{
73 struct usb_ctrlrequest setup;
74 uint8_t raw[64];
75};
76
77static union usb_ep0_buffer ep0_buffer USB_DEVBSS_ATTR;
78
79/* Internal EP state/info */
80struct usb_dw_ep
81{
82 struct semaphore complete;
83 uint32_t* req_addr;
84 uint32_t req_size;
85 uint32_t* addr;
86 uint32_t sizeleft;
87 uint32_t size;
88 int8_t status;
89 uint8_t active;
90 uint8_t busy;
91};
92
93static struct usb_dw_ep usb_dw_ep_list[USB_NUM_ENDPOINTS][USB_DW_NUM_DIRS];
94
95static uint32_t usb_endpoints; /* available EPs mask */
96
97/* For SHARED_FIFO mode this is the number of periodic Tx FIFOs
98 (usually 1), otherwise it is the number of dedicated Tx FIFOs
99 (not counting NPTX FIFO that is always dedicated for IN0). */
100static int n_ptxfifos;
101static uint16_t ptxfifo_usage;
102
103static uint32_t hw_maxbytes;
104static uint32_t hw_maxpackets;
105#ifdef USB_DW_SHARED_FIFO
106static uint8_t hw_nptxqdepth;
107static uint32_t epmis_msk;
108static uint32_t ep_periodic_msk;
109#endif
110
111static const char *dw_dir_str[USB_DW_NUM_DIRS] =
112{
113 [USB_DW_EPDIR_IN] = "IN",
114 [USB_DW_EPDIR_OUT] = "OUT",
115};
116
117
118static struct usb_dw_ep *usb_dw_get_ep(int epnum, enum usb_dw_epdir epdir)
119{
120 return &usb_dw_ep_list[epnum][epdir];
121}
122
123static int usb_dw_maxpktsize(int epnum, enum usb_dw_epdir epdir)
124{
125 return epnum ? DWC_EPCTL(epnum, epdir) & 0x3ff : 64;
126}
127
128static int usb_dw_maxxfersize(int epnum, enum usb_dw_epdir epdir)
129{
130 return epnum ? ALIGN_DOWN_P2(MIN(hw_maxbytes,
131 hw_maxpackets*usb_dw_maxpktsize(epnum, epdir)), CACHEALIGN_BITS) : 64;
132}
133
134/* Calculate number of packets (if size == 0 an empty packet will be sent) */
135static int usb_dw_calc_packets(uint32_t size, uint32_t maxpktsize)
136{
137 int packets = (size + maxpktsize - 1) / maxpktsize;
138 if (!packets) packets = 1;
139 return packets;
140}
141
142static int usb_dw_get_stall(int epnum, enum usb_dw_epdir epdir)
143{
144 return !!(DWC_EPCTL(epnum, epdir) & STALL);
145}
146
147static void usb_dw_set_stall(int epnum, enum usb_dw_epdir epdir, int stall)
148{
149 if (stall)
150 {
151 DWC_EPCTL(epnum, epdir) |= STALL;
152 }
153 else
154 {
155 DWC_EPCTL(epnum, epdir) &= ~STALL;
156 DWC_EPCTL(epnum, epdir) |= SD0PID;
157 }
158}
159
160static void usb_dw_set_address(uint8_t address)
161{
162 DWC_DCFG = (DWC_DCFG & ~(0x7f0)) | DAD(address);
163}
164
165static void usb_dw_wait_for_ahb_idle(void)
166{
167 while (!(DWC_GRSTCTL & AHBIDL));
168}
169
170#ifdef USB_DW_SHARED_FIFO
171static unsigned usb_dw_bytes_in_txfifo(int epnum, uint32_t *sentbytes)
172{
173 uint32_t size = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN)->size;
174 if (sentbytes) *sentbytes = size;
175 uint32_t dieptsiz = DWC_DIEPTSIZ(epnum);
176 uint32_t packetsleft = (dieptsiz >> 19) & 0x3ff;
177 if (!packetsleft) return 0;
178 int maxpktsize = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN);
179 int packets = usb_dw_calc_packets(size, maxpktsize);
180 uint32_t bytesleft = dieptsiz & 0x7ffff;
181 uint32_t bytespushed = size - bytesleft;
182 uint32_t bytespulled = (packets - packetsleft) * maxpktsize;
183
184 if (sentbytes) *sentbytes = bytespulled;
185 return bytespushed - bytespulled;
186}
187#endif
188
189#ifdef USB_DW_ARCH_SLAVE
190/* Read one packet/token from Rx FIFO */
191static void usb_dw_handle_rxfifo(void)
192{
193 uint32_t rxsts = DWC_GRXSTSP;
194 int pktsts = (rxsts >> 17) & 0xf;
195
196 switch (pktsts)
197 {
198 case PKTSTS_OUTRX:
199 case PKTSTS_SETUPRX:
200 {
201 int ep = rxsts & 0xf;
202 int words = (((rxsts >> 4) & 0x7ff) + 3) >> 2;
203 struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, USB_DW_EPDIR_OUT);
204 if (dw_ep->busy)
205 {
206 while (words--)
207 *dw_ep->addr++ = DWC_DFIFO(0);
208 }
209 else
210 {
211 /* Discard data */
212 while (words--)
213 (void) DWC_DFIFO(0);
214 }
215 break;
216 }
217 case PKTSTS_OUTDONE:
218 case PKTSTS_SETUPDONE:
219 case PKTSTS_GLOBALOUTNAK:
220 default:
221 break;
222 }
223}
224
225#ifdef USB_DW_SHARED_FIFO
226static void usb_dw_try_push(int epnum)
227{
228 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN);
229
230 if (!dw_ep->busy)
231 return;
232
233 if (epmis_msk & (1 << epnum))
234 return;
235
236 uint32_t wordsleft = ((DWC_DIEPTSIZ(epnum) & 0x7ffff) + 3) >> 2;
237 if (!wordsleft) return;
238
239 /* Get fifo space for NPTXFIFO or PTXFIFO */
240 uint32_t fifospace;
241 int dtxfnum = GET_DTXFNUM(epnum);
242 if (dtxfnum)
243 {
244 uint32_t fifosize = DWC_DIEPTXF(dtxfnum - 1) >> 16;
245 fifospace = fifosize - ((usb_dw_bytes_in_txfifo(epnum, NULL) + 3) >> 2);
246 }
247 else
248 {
249 uint32_t gnptxsts = DWC_GNPTXSTS;
250 fifospace = ((gnptxsts >> 16) & 0xff) ? (gnptxsts & 0xffff) : 0;
251 }
252
253 uint32_t maxpktsize = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN);
254 uint32_t words = MIN((maxpktsize + 3) >> 2, wordsleft);
255
256 if (fifospace >= words)
257 {
258 wordsleft -= words;
259 while (words--)
260 DWC_DFIFO(epnum) = *dw_ep->addr++;
261 }
262
263 if (wordsleft)
264 DWC_GINTMSK |= (dtxfnum ? PTXFE : NPTXFE);
265}
266
267#else /* !USB_DW_SHARED_FIFO */
268static void usb_dw_handle_dtxfifo(int epnum)
269{
270 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN);
271
272 if (!dw_ep->busy)
273 return;
274
275 uint32_t wordsleft = ((DWC_DIEPTSIZ(epnum) & 0x7ffff) + 3) >> 2;
276
277 while (wordsleft)
278 {
279 uint32_t words = wordsleft;
280 uint32_t fifospace = DWC_DTXFSTS(epnum) & 0xffff;
281
282 if (fifospace < words)
283 {
284 /* We push whole packets to read consistent info on DIEPTSIZ
285 (i.e. when FIFO size is not maxpktsize multiplo). */
286 int maxpktwords = usb_dw_maxpktsize(epnum, USB_DW_EPDIR_IN) >> 2;
287 words = (fifospace / maxpktwords) * maxpktwords;
288 }
289
290 if (!words)
291 break;
292
293 wordsleft -= words;
294 while (words--)
295 DWC_DFIFO(epnum) = *dw_ep->addr++;
296 }
297
298 if (!wordsleft)
299 DWC_DIEPEMPMSK &= ~(1 << GET_DTXFNUM(epnum));
300}
301#endif /* !USB_DW_SHARED_FIFO */
302#endif /* USB_DW_ARCH_SLAVE */
303
304static void usb_dw_flush_fifo(uint32_t fflsh, int fnum)
305{
306#ifdef USB_DW_ARCH_SLAVE
307 /* Rx queue must be emptied before flushing Rx FIFO */
308 if (fflsh & RXFFLSH)
309 while (DWC_GINTSTS & RXFLVL)
310 usb_dw_handle_rxfifo();
311#else
312 /* Wait for any DMA activity to stop */
313 usb_dw_wait_for_ahb_idle();
314#endif
315 DWC_GRSTCTL = TXFNUM(fnum) | fflsh;
316 while (DWC_GRSTCTL & fflsh);
317 udelay(1); /* Wait 3 PHY cycles */
318}
319
320/* These are the conditions that must be met so that the application can
321 * disable an endpoint avoiding race conditions:
322 *
323 * 1) The endpoint must be enabled when EPDIS is written, otherwise the
324 * core will never raise EPDISD interrupt (thus EPDIS remains enabled).
325 *
326 * 2) - Periodic (SHARED_FIFO) or dedicated (!SHARED_FIFO) IN endpoints:
327 * IN NAK must be effective, to ensure that the core is not going
328 * to disable the EP just before EPDIS is written.
329 * - Non-periodic (SHARED_FIFO) IN endpoints: use usb_dw_nptx_unqueue().
330 * - OUT endpoints: GONAK must be effective, this also ensures that the
331 * core is not going to disable the EP.
332 */
333static void usb_dw_disable_ep(int epnum, enum usb_dw_epdir epdir)
334{
335 if (!epnum && (epdir == USB_DW_EPDIR_OUT))
336 return; /* The application cannot disable OUT0 */
337
338 if (DWC_EPCTL(epnum, epdir) & EPENA)
339 {
340 int tmo = 50;
341 DWC_EPCTL(epnum, epdir) |= EPDIS;
342 while (DWC_EPCTL(epnum, epdir) & EPDIS)
343 {
344 if (!tmo--)
345 panicf("%s: %s%d failed!", __func__, dw_dir_str[epdir], epnum);
346 udelay(1);
347 }
348 }
349}
350
351static void usb_dw_gonak_effective(bool enable)
352{
353 if (enable)
354 {
355 if (!(DWC_DCTL & GONSTS))
356 DWC_DCTL |= SGONAK;
357
358 /* Wait for global IN NAK effective */
359 int tmo = 50;
360 while (~DWC_GINTSTS & GOUTNAKEFF)
361 {
362 if (!tmo--) panicf("%s: failed!", __func__);
363#ifdef USB_DW_ARCH_SLAVE
364 /* Pull Rx queue until GLOBALOUTNAK token is received. */
365 if (DWC_GINTSTS & RXFLVL)
366 usb_dw_handle_rxfifo();
367 else
368#endif
369 udelay(1);
370 }
371 }
372 else
373 {
374 if (DWC_DCTL & GONSTS)
375 DWC_DCTL |= CGONAK;
376 }
377}
378
379static void usb_dw_set_innak_effective(int epnum)
380{
381 if (~DWC_DIEPCTL(epnum) & NAKSTS)
382 {
383 /* Wait for IN NAK effective avoiding race conditions, if the
384 * endpoint is disabled by the core (or it was already disabled)
385 * then INEPNE is never raised.
386 */
387 int tmo = 50;
388 DWC_DIEPCTL(epnum) |= SNAK;
389 while ((DWC_DIEPCTL(epnum) & EPENA) && !(DWC_DIEPINT(epnum) & INEPNE))
390 {
391 if (!tmo--) panicf("%s: IN%d failed!", __func__, epnum);
392 udelay(1);
393 }
394 }
395}
396
397#ifdef USB_DW_SHARED_FIFO
398static void usb_dw_ginak_effective(bool enable)
399{
400 if (enable)
401 {
402 if (!(DWC_DCTL & GINSTS))
403 DWC_DCTL |= SGINAK;
404
405 /* Wait for global IN NAK effective */
406 int tmo = 50;
407 while (~DWC_GINTSTS & GINAKEFF)
408 {
409 if (!tmo--) panicf("%s: failed!", __func__);
410 udelay(1);
411 }
412#ifndef USB_DW_ARCH_SLAVE
413 /* Wait for any DMA activity to stop. */
414 usb_dw_wait_for_ahb_idle();
415#endif
416 }
417 else
418 {
419 if (DWC_DCTL & GINSTS)
420 DWC_DCTL |= CGINAK;
421 }
422}
423
424static void usb_dw_nptx_unqueue(int epnum)
425{
426 uint32_t reenable_msk = 0;
427
428 usb_dw_ginak_effective(true);
429
430 /* Disable EPs */
431 for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++)
432 {
433 if (usb_endpoints & ~ep_periodic_msk & (1 << ep))
434 {
435 /* Disable */
436 if (~DWC_DIEPCTL(ep) & EPENA)
437 continue;
438 DWC_DIEPCTL(ep) |= EPDIS|SNAK;
439
440 /* Adjust */
441 uint32_t packetsleft = (DWC_DIEPTSIZ(ep) >> 19) & 0x3ff;
442 if (!packetsleft) continue;
443
444 struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, USB_DW_EPDIR_IN);
445 uint32_t sentbytes;
446 uint32_t bytesinfifo = usb_dw_bytes_in_txfifo(ep, &sentbytes);
447
448#ifdef USB_DW_ARCH_SLAVE
449 dw_ep->addr -= (bytesinfifo + 3) >> 2;
450#else
451 (void) bytesinfifo;
452 DWC_DIEPDMA(ep) = (uint32_t)(dw_ep->addr) + sentbytes;
453#endif
454 DWC_DIEPTSIZ(ep) = PKTCNT(packetsleft) | (dw_ep->size - sentbytes);
455
456 /* Do not re-enable the EP we are going to unqueue */
457 if (ep == epnum)
458 continue;
459
460 /* Mark EP to be re-enabled later */
461 reenable_msk |= (1 << ep);
462 }
463 }
464
465 /* Flush NPTXFIFO */
466 usb_dw_flush_fifo(TXFFLSH, 0);
467
468 /* Re-enable EPs */
469 for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++)
470 if (reenable_msk & (1 << ep))
471 DWC_DIEPCTL(ep) |= EPENA|CNAK;
472
473#ifdef USB_DW_ARCH_SLAVE
474 if (reenable_msk)
475 DWC_GINTMSK |= NPTXFE;
476#endif
477
478 usb_dw_ginak_effective(false);
479}
480#endif /* USB_DW_SHARED_FIFO */
481
482static void usb_dw_flush_endpoint(int epnum, enum usb_dw_epdir epdir)
483{
484 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir);
485 dw_ep->busy = false;
486 dw_ep->status = -1;
487 semaphore_release(&dw_ep->complete);
488
489 if (DWC_EPCTL(epnum, epdir) & EPENA)
490 {
491 if (epdir == USB_DW_EPDIR_IN)
492 {
493 /* We are shutting down an endpoint that might still have IN
494 * packets in the FIFO. Disable the endpoint, wait for things
495 * to settle, and flush the relevant FIFO.
496 */
497 int dtxfnum = GET_DTXFNUM(epnum);
498
499#ifdef USB_DW_SHARED_FIFO
500 if (!dtxfnum)
501 {
502 usb_dw_nptx_unqueue(epnum);
503 }
504 else
505#endif
506 {
507 /* Wait for IN NAK effective to avoid race conditions
508 while shutting down the endpoint. */
509 usb_dw_set_innak_effective(epnum);
510
511 /* Disable the EP we are going to flush */
512 usb_dw_disable_ep(epnum, epdir);
513
514 /* Flush it all the way down! */
515 usb_dw_flush_fifo(TXFFLSH, dtxfnum);
516
517#if !defined(USB_DW_SHARED_FIFO) && defined(USB_DW_ARCH_SLAVE)
518 DWC_DIEPEMPMSK &= ~(1 << dtxfnum);
519#endif
520 }
521 }
522 else
523 {
524 /* We are waiting for an OUT packet on this endpoint, which
525 * might arrive any moment. Assert a global output NAK to
526 * avoid race conditions while shutting down the endpoint.
527 * Global output NAK also flushes the Rx FIFO.
528 */
529 usb_dw_gonak_effective(true);
530 usb_dw_disable_ep(epnum, epdir);
531 usb_dw_gonak_effective(false);
532 }
533 }
534
535 /* At this point the endpoint is disabled, SNAK it (in case it is not
536 * already done), it is needed for Tx shared FIFOs (to not to raise
537 * unwanted EPMIS interrupts) and recomended for dedicated FIFOs.
538 */
539 DWC_EPCTL(epnum, epdir) |= SNAK;
540
541#ifdef USB_DW_SHARED_FIFO
542 if (epdir == USB_DW_EPDIR_IN)
543 {
544 epmis_msk &= ~(1 << epnum);
545 if (!epmis_msk)
546 DWC_DIEPMSK &= ~ITTXFE;
547 }
548#endif
549
550 /* Clear all channel interrupts to avoid to process
551 pending tokens for the flushed EP. */
552 DWC_EPINT(epnum, epdir) = DWC_EPINT(epnum, epdir);
553}
554
555static void usb_dw_unconfigure_ep(int epnum, enum usb_dw_epdir epdir)
556{
557 uint32_t epctl = 0;
558
559 if (epdir == USB_DW_EPDIR_IN)
560 {
561#ifdef USB_DW_SHARED_FIFO
562#ifndef USB_DW_ARCH_SLAVE
563 int next;
564 for (next = epnum + 1; next < USB_NUM_ENDPOINTS; next++)
565 if (usb_endpoints & (1 << next))
566 break;
567 epctl = NEXTEP(next % USB_NUM_ENDPOINTS);
568#endif
569 ep_periodic_msk &= ~(1 << epnum);
570#endif
571 ptxfifo_usage &= ~(1 << GET_DTXFNUM(epnum));
572 }
573
574 usb_dw_flush_endpoint(epnum, epdir);
575 DWC_EPCTL(epnum, epdir) = epctl;
576}
577
578static int usb_dw_configure_ep(int epnum,
579 enum usb_dw_epdir epdir, int type, int maxpktsize)
580{
581 uint32_t epctl = SD0PID|EPTYP(type)|USBAEP|maxpktsize;
582
583 if (epdir == USB_DW_EPDIR_IN)
584 {
585 /*
586 * If the hardware has dedicated fifos, we must give each
587 * IN EP a unique tx-fifo even if it is non-periodic.
588 */
589#ifdef USB_DW_SHARED_FIFO
590#ifndef USB_DW_ARCH_SLAVE
591 epctl |= DWC_DIEPCTL(epnum) & NEXTEP(0xf);
592#endif
593 if (type == USB_ENDPOINT_XFER_INT)
594#endif
595 {
596 int fnum;
597 for (fnum = 1; fnum <= n_ptxfifos; fnum++)
598 if (~ptxfifo_usage & (1 << fnum))
599 break;
600 if (fnum > n_ptxfifos)
601 return -1; /* no available fifos */
602 ptxfifo_usage |= (1 << fnum);
603 epctl |= DTXFNUM(fnum);
604#ifdef USB_DW_SHARED_FIFO
605 ep_periodic_msk |= (1 << epnum);
606#endif
607 }
608 }
609
610 DWC_EPCTL(epnum, epdir) = epctl;
611 return 0; /* ok */
612}
613
614static void usb_dw_reset_endpoints(void)
615{
616 /* Initial state for all endpoints, setting OUT EPs as not busy
617 * will discard all pending data (if any) on the flush stage.
618 */
619 for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++)
620 {
621 for (int dir = 0; dir < USB_DW_NUM_DIRS; dir++)
622 {
623 struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, dir);
624 dw_ep->active = !ep;
625 dw_ep->busy = false;
626 dw_ep->status = -1;
627 semaphore_release(&dw_ep->complete);
628 }
629 }
630
631#if CONFIG_CPU == S5L8701
632 /*
633 * Workaround for spurious -EPROTO when receiving bulk data on Nano2G.
634 *
635 * The Rx FIFO and Rx queue are currupted by the received (corrupted)
636 * data, must be flushed, otherwise the core can not set GONAK effective.
637 */
638 usb_dw_flush_fifo(RXFFLSH, 0);
639#endif
640
641 /* Flush and initialize EPs, includes disabling USBAEP on all EPs
642 * except EP0 (USB HW core keeps EP0 active on all configurations).
643 */
644 for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++)
645 {
646 if (usb_endpoints & (1 << (ep + 16)))
647 usb_dw_unconfigure_ep(ep, USB_DW_EPDIR_OUT);
648 if (usb_endpoints & (1 << ep))
649 usb_dw_unconfigure_ep(ep, USB_DW_EPDIR_IN);
650 }
651
652 ptxfifo_usage = 0;
653#ifdef USB_DW_SHARED_FIFO
654 ep_periodic_msk = 0;
655#endif
656}
657
658static void usb_dw_start_xfer(int epnum,
659 enum usb_dw_epdir epdir, const void* buf, int size)
660{
661 if ((uint32_t)buf & ((epdir == USB_DW_EPDIR_IN) ? 3 : CACHEALIGN_SIZE-1))
662 logf("%s: %s%d %p unaligned", __func__, dw_dir_str[epdir], epnum, buf);
663
664 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir);
665
666 dw_ep->busy = true;
667 dw_ep->status = -1;
668 dw_ep->sizeleft = size;
669 size = MIN(size, usb_dw_maxxfersize(epnum, epdir));
670 dw_ep->size = size;
671
672 int packets = usb_dw_calc_packets(size, usb_dw_maxpktsize(epnum, epdir));
673 uint32_t eptsiz = PKTCNT(packets) | size;
674 uint32_t nak;
675
676 /* Set up data source */
677 dw_ep->addr = (uint32_t*)buf;
678#ifndef USB_DW_ARCH_SLAVE
679 DWC_EPDMA(epnum, epdir) = (uint32_t)buf;
680#endif
681
682 if (epdir == USB_DW_EPDIR_IN)
683 {
684#ifndef USB_DW_ARCH_SLAVE
685 COMMIT_DCACHE_RANGE(buf, size);
686#endif
687#ifdef USB_DW_SHARED_FIFO
688 eptsiz |= MCCNT((ep_periodic_msk >> epnum) & 1);
689#endif
690 nak = CNAK;
691 }
692 else
693 {
694#ifndef USB_DW_ARCH_SLAVE
695 DISCARD_DCACHE_RANGE(buf, size);
696#endif
697 eptsiz |= STUPCNT(!epnum);
698 nak = epnum ? CNAK : SNAK;
699 }
700
701 DWC_EPTSIZ(epnum, epdir) = eptsiz;
702
703 /* Enable the endpoint */
704 DWC_EPCTL(epnum, epdir) |= EPENA | nak;
705
706#ifdef USB_DW_ARCH_SLAVE
707 /* Enable interrupts to start pushing data into the FIFO */
708 if ((epdir == USB_DW_EPDIR_IN) && size)
709#ifdef USB_DW_SHARED_FIFO
710 DWC_GINTMSK |= ((ep_periodic_msk & (1 << epnum)) ? PTXFE : NPTXFE);
711#else
712 DWC_DIEPEMPMSK |= (1 << GET_DTXFNUM(epnum));
713#endif
714#endif
715}
716
717static void usb_dw_ep0_wait_setup(void)
718{
719 usb_dw_start_xfer(0, USB_DW_EPDIR_OUT, ep0_buffer.raw, 64);
720}
721
722static void usb_dw_handle_setup_received(void)
723{
724 static struct usb_ctrlrequest usb_ctrlsetup;
725
726 usb_dw_flush_endpoint(0, USB_DW_EPDIR_IN);
727
728 memcpy(&usb_ctrlsetup, ep0_buffer.raw, sizeof(usb_ctrlsetup));
729
730 if (((usb_ctrlsetup.bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE)
731 && ((usb_ctrlsetup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
732 && (usb_ctrlsetup.bRequest == USB_REQ_SET_ADDRESS))
733 usb_dw_set_address(usb_ctrlsetup.wValue);
734
735 usb_core_control_request(&usb_ctrlsetup);
736}
737
738static void usb_dw_abort_endpoint(int epnum, enum usb_dw_epdir epdir)
739{
740 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir);
741 if (dw_ep->busy)
742 {
743 usb_dw_flush_endpoint(epnum, epdir);
744 usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ?
745 USB_DIR_OUT : USB_DIR_IN, -1, 0);
746 }
747}
748
749static void usb_dw_handle_xfer_complete(int epnum, enum usb_dw_epdir epdir)
750{
751 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir);
752
753 if (!dw_ep->busy)
754 return;
755
756 uint32_t bytesleft = DWC_EPTSIZ(epnum, epdir) & 0x7ffff;
757
758 if (!epnum && (epdir == USB_DW_EPDIR_OUT)) /* OUT0 */
759 {
760 int recvbytes = 64 - bytesleft;
761 dw_ep->sizeleft = dw_ep->req_size - recvbytes;
762 if (dw_ep->req_addr)
763 memcpy(dw_ep->req_addr, ep0_buffer.raw, dw_ep->req_size);
764 }
765 else
766 {
767 dw_ep->sizeleft -= (dw_ep->size - bytesleft);
768 if (!bytesleft && dw_ep->sizeleft)
769 {
770#ifndef USB_DW_ARCH_SLAVE
771 dw_ep->addr += (dw_ep->size >> 2); /* words */
772#endif
773 usb_dw_start_xfer(epnum, epdir, dw_ep->addr, dw_ep->sizeleft);
774 return;
775 }
776
777 if (epdir == USB_DW_EPDIR_IN)
778 {
779 /* SNAK the disabled EP, otherwise IN tokens for this
780 EP could raise unwanted EPMIS interrupts. Useful for
781 usbserial when there is no data to send. */
782 DWC_DIEPCTL(epnum) |= SNAK;
783
784#ifdef USB_DW_SHARED_FIFO
785 /* See usb-s5l8701.c */
786 if (usb_dw_config.use_ptxfifo_as_plain_buffer)
787 {
788 int dtxfnum = GET_DTXFNUM(epnum);
789 if (dtxfnum)
790 usb_dw_flush_fifo(TXFFLSH, dtxfnum);
791 }
792#endif
793 }
794 }
795
796 dw_ep->busy = false;
797 dw_ep->status = 0;
798 semaphore_release(&dw_ep->complete);
799
800 int transfered = dw_ep->req_size - dw_ep->sizeleft;
801 usb_core_transfer_complete(epnum, (epdir == USB_DW_EPDIR_OUT) ?
802 USB_DIR_OUT : USB_DIR_IN, dw_ep->status, transfered);
803}
804
805#ifdef USB_DW_SHARED_FIFO
806static int usb_dw_get_epmis(void)
807{
808 unsigned epmis;
809 uint32_t gnptxsts = DWC_GNPTXSTS;
810
811 if (((gnptxsts >> 16) & 0xff) >= hw_nptxqdepth)
812 return -1; /* empty queue */
813
814 /* Get the EP on the top of the queue, 0 < idx < number of available
815 IN endpoints */
816 int idx = (gnptxsts >> 27) & 0xf;
817 for (epmis = 0; epmis < USB_NUM_ENDPOINTS; epmis++)
818 if ((usb_endpoints & (1 << epmis)) && !idx--)
819 break;
820
821 /* The maximum EP mismatch counter is configured, so we verify all NPTX
822 queue entries, 4 bits per entry, first entry at DTKQNR1[11:8] */
823 uint32_t volatile *dtknqr = &DWC_DTKNQR1;
824 for (int i = 2; i < hw_nptxqdepth + 2; i++)
825 if (((*(dtknqr+(i>>3)) >> ((i & 0x7)*4)) & 0xf) == epmis)
826 return -1;
827
828 return epmis;
829}
830
831static void usb_dw_handle_token_mismatch(void)
832{
833 usb_dw_ginak_effective(true);
834 int epmis = usb_dw_get_epmis();
835 if (epmis >= 0)
836 {
837 /* The EP is disabled, unqueued, and reconfigured to re-reenable it
838 later when a token is received, (or it will be cancelled by
839 timeout if it was a blocking request). */
840 usb_dw_nptx_unqueue(epmis);
841
842 epmis_msk |= (1 << epmis);
843 if (epmis_msk)
844 DWC_DIEPMSK |= ITTXFE;
845
846 /* Be sure the status is clear */
847 DWC_DIEPINT(epmis) = ITTXFE;
848
849 /* Must disable NAK to allow to get ITTXFE interrupts for this EP */
850 DWC_DIEPCTL(epmis) |= CNAK;
851 }
852 usb_dw_ginak_effective(false);
853}
854#endif /* USB_DW_SHARED_FIFO */
855
856static void usb_dw_irq(void)
857{
858 int ep;
859 uint32_t daint;
860
861#ifdef USB_DW_ARCH_SLAVE
862 /* Handle one packet at a time, the IRQ will re-trigger if there's
863 something left. */
864 if (DWC_GINTSTS & RXFLVL)
865 {
866 usb_dw_handle_rxfifo();
867 }
868#endif
869
870#ifdef USB_DW_SHARED_FIFO
871 if (DWC_GINTSTS & EPMIS)
872 {
873 usb_dw_handle_token_mismatch();
874 DWC_GINTSTS = EPMIS;
875 }
876
877#ifdef USB_DW_ARCH_SLAVE
878 uint32_t gintsts = DWC_GINTSTS & DWC_GINTMSK;
879 if (gintsts & PTXFE)
880 {
881 /* First disable the IRQ, it will be re-enabled later if there
882 is anything left to be done. */
883 DWC_GINTMSK &= ~PTXFE;
884 /* Check all periodic endpoints for anything to be transmitted */
885 for (ep = 1; ep < USB_NUM_ENDPOINTS; ep++)
886 if (usb_endpoints & ep_periodic_msk & (1 << ep))
887 usb_dw_try_push(ep);
888 }
889
890 if (gintsts & NPTXFE)
891 {
892 /* First disable the IRQ, it will be re-enabled later if there
893 is anything left to be done. */
894 DWC_GINTMSK &= ~NPTXFE;
895 /* Check all non-periodic endpoints for anything to be transmitted */
896 for (ep = 0; ep < USB_NUM_ENDPOINTS; ep++)
897 if (usb_endpoints & ~ep_periodic_msk & (1 << ep))
898 usb_dw_try_push(ep);
899 }
900#endif /* USB_DW_ARCH_SLAVE */
901#endif /* USB_DW_SHARED_FIFO */
902
903 daint = DWC_DAINT;
904
905 /* IN */
906 for (ep = 0; ep < USB_NUM_ENDPOINTS; ep++)
907 {
908 if (daint & (1 << ep))
909 {
910 uint32_t epints = DWC_DIEPINT(ep);
911
912 if (epints & TOC)
913 {
914 usb_dw_abort_endpoint(ep, USB_DW_EPDIR_IN);
915 }
916
917#ifdef USB_DW_SHARED_FIFO
918 if (epints & ITTXFE)
919 {
920 if (epmis_msk & (1 << ep))
921 {
922 DWC_DIEPCTL(ep) |= EPENA;
923 epmis_msk &= ~(1 << ep);
924 if (!epmis_msk)
925 DWC_DIEPMSK &= ~ITTXFE;
926 }
927 }
928
929#elif defined(USB_DW_ARCH_SLAVE)
930 if (epints & TXFE)
931 {
932 usb_dw_handle_dtxfifo(ep);
933 }
934#endif
935
936 /* Clear XFRC here, if this is a 'multi-transfer' request then
937 a new transfer is going to be launched, this ensures it will
938 not miss a single interrupt. */
939 DWC_DIEPINT(ep) = epints;
940
941 if (epints & XFRC)
942 {
943 usb_dw_handle_xfer_complete(ep, USB_DW_EPDIR_IN);
944 }
945 }
946 }
947
948 /* OUT */
949 for (ep = 0; ep < USB_NUM_ENDPOINTS; ep++)
950 {
951 if (daint & (1 << (ep + 16)))
952 {
953 uint32_t epints = DWC_DOEPINT(ep);
954
955 if (!ep)
956 {
957 if (epints & STUP)
958 {
959 usb_dw_handle_setup_received();
960 }
961 else if (epints & XFRC)
962 {
963 usb_dw_handle_xfer_complete(0, USB_DW_EPDIR_OUT);
964 }
965 usb_dw_ep0_wait_setup();
966 /* Clear interrupt after the current EP0 packet is handled */
967 DWC_DOEPINT(0) = epints;
968 }
969 else
970 {
971 DWC_DOEPINT(ep) = epints;
972 if (epints & XFRC)
973 {
974 usb_dw_handle_xfer_complete(ep, USB_DW_EPDIR_OUT);
975 }
976 }
977 }
978 }
979
980 if (DWC_GINTSTS & USBRST)
981 {
982 DWC_GINTSTS = USBRST;
983 usb_dw_set_address(0);
984 usb_dw_reset_endpoints();
985 usb_dw_ep0_wait_setup();
986 usb_core_bus_reset();
987 }
988
989 if (DWC_GINTSTS & ENUMDNE)
990 {
991 DWC_GINTSTS = ENUMDNE;
992 /* Nothing to do? */
993 }
994}
995
996static void usb_dw_check_hw(void)
997{
998 uint32_t ghwcfg2 = DWC_GHWCFG2;
999 uint32_t ghwcfg3 = DWC_GHWCFG3;
1000 uint32_t ghwcfg4 = DWC_GHWCFG4;
1001 const struct usb_dw_config *c = &usb_dw_config;
1002 int hw_numeps;
1003 int hw_maxtxfifos; /* periodic or dedicated */
1004 char *err;
1005
1006 hw_numeps = ((ghwcfg2 >> 10) & 0xf) + 1;
1007
1008 if (hw_numeps < USB_NUM_ENDPOINTS)
1009 {
1010 err = "USB_NUM_ENDPOINTS too big";
1011 goto panic;
1012 }
1013 /* HWCFG registers are not checked to detect the PHY, if an option
1014 is not supported then the related bits should be Read-Only. */
1015 DWC_GUSBCFG = c->phytype;
1016 if (DWC_GUSBCFG != c->phytype)
1017 {
1018 err = "PHY type not supported";
1019 goto panic;
1020 }
1021#ifndef USB_DW_ARCH_SLAVE
1022 if (((ghwcfg2 >> 3) & 3) != 2)
1023 {
1024 err = "internal DMA not supported";
1025 goto panic;
1026 }
1027#endif
1028#ifdef USB_DW_SHARED_FIFO
1029 if ((ghwcfg4 >> 25) & 1)
1030 {
1031 err = "shared TxFIFO not supported";
1032 goto panic;
1033 }
1034 hw_maxtxfifos = ghwcfg4 & 0xf;
1035 hw_nptxqdepth = (1 << (((ghwcfg2 >> 22) & 3) + 1));
1036#else
1037 if (!((ghwcfg4 >> 25) & 1))
1038 {
1039 err = "dedicated TxFIFO not supported";
1040 goto panic;
1041 }
1042 hw_maxtxfifos = (ghwcfg4 >> 26) & 0xf;
1043#endif
1044 hw_maxbytes = (1 << (((ghwcfg3 >> 0) & 0xf) + 11)) - 1;
1045 hw_maxpackets = (1 << (((ghwcfg3 >> 4) & 0x7) + 4)) - 1;
1046 uint16_t hw_fifomem = ghwcfg3 >> 16;
1047
1048 /* Configure FIFOs, sizes are 32-bit words, we will need at least
1049 one periodic or dedicated Tx FIFO (really the periodic Tx FIFO
1050 is not needed if !USB_ENABLE_HID). */
1051 if (c->rx_fifosz + c->nptx_fifosz + c->ptx_fifosz > hw_fifomem)
1052 {
1053 err = "insufficient FIFO memory";
1054 goto panic;
1055 }
1056 n_ptxfifos = (hw_fifomem - c->rx_fifosz - c->nptx_fifosz) / c->ptx_fifosz;
1057 if (n_ptxfifos > hw_maxtxfifos) n_ptxfifos = hw_maxtxfifos;
1058
1059 logf("%s():", __func__);
1060 logf(" HW version: %4lx, num EPs: %d", DWC_GSNPSID & 0xffff, hw_numeps);
1061 logf(" FIFO mem=%d rx=%d nptx=%d ptx=%dx%d", hw_fifomem,
1062 c->rx_fifosz, c->nptx_fifosz, n_ptxfifos, c->ptx_fifosz);
1063
1064 return;
1065
1066panic:
1067 panicf("%s: %s", __func__, err);
1068}
1069
1070static void usb_dw_init(void)
1071{
1072 static bool initialized = false;
1073 const struct usb_dw_config *c = &usb_dw_config;
1074
1075 if (!initialized)
1076 {
1077 for (int ep = 0; ep < USB_NUM_ENDPOINTS; ep++)
1078 for (int dir = 0; dir < USB_DW_NUM_DIRS; dir++)
1079 semaphore_init(&usb_dw_get_ep(ep, dir)->complete, 1, 0);
1080 initialized = true;
1081 }
1082
1083 /* Disable IRQ during setup */
1084 usb_dw_target_disable_irq();
1085
1086 /* Enable OTG clocks */
1087 usb_dw_target_enable_clocks();
1088
1089 /* Enable PHY clocks */
1090 DWC_PCGCCTL = 0;
1091
1092 usb_dw_check_hw();
1093
1094 /* Configure PHY type (must be done before reset) */
1095#ifndef USB_DW_TURNAROUND
1096 /*
1097 * Turnaround time (in PHY clocks) = 4*AHB clocks + 1*PHY clock,
1098 * worst cases are:
1099 * 16-bit UTMI+: PHY=30MHz, AHB=30Mhz -> 5
1100 * 8-bit UTMI+: PHY=60MHz, AHB=30MHz -> 9
1101 */
1102 int USB_DW_TURNAROUND = (c->phytype == DWC_PHYTYPE_UTMI_16) ? 5 : 9;
1103#endif
1104 uint32_t gusbcfg = c->phytype|TRDT(USB_DW_TURNAROUND)|USB_DW_TOUTCAL;
1105 DWC_GUSBCFG = gusbcfg;
1106
1107 /* Reset the whole USB core */
1108 udelay(100);
1109 usb_dw_wait_for_ahb_idle();
1110 DWC_GRSTCTL = CSRST;
1111 while (DWC_GRSTCTL & CSRST);
1112 usb_dw_wait_for_ahb_idle();
1113
1114 /* Configure FIFOs */
1115 DWC_GRXFSIZ = c->rx_fifosz;
1116#ifdef USB_DW_SHARED_FIFO
1117 DWC_GNPTXFSIZ = (c->nptx_fifosz << 16) | c->rx_fifosz;
1118#else
1119 DWC_TX0FSIZ = (c->nptx_fifosz << 16) | c->rx_fifosz;
1120#endif
1121 for (int i = 0; i < n_ptxfifos; i++)
1122 DWC_DIEPTXF(i) = (c->ptx_fifosz << 16) |
1123 (c->nptx_fifosz + c->rx_fifosz + c->ptx_fifosz*i);
1124 /*
1125 * According to p428 of the design guide, we need to ensure that
1126 * fifos are flushed before continuing.
1127 */
1128 usb_dw_flush_fifo(TXFFLSH|RXFFLSH, 0x10);
1129
1130 /* Configure the core */
1131 DWC_GUSBCFG = gusbcfg;
1132
1133 uint32_t gahbcfg = GINT;
1134#ifdef USB_DW_ARCH_SLAVE
1135#ifdef USB_DW_SHARED_FIFO
1136 if (c->use_ptxfifo_as_plain_buffer)
1137 gahbcfg |= PTXFELVL;
1138#endif
1139 if (c->disable_double_buffering)
1140 gahbcfg |= TXFELVL;
1141#else
1142 gahbcfg |= HBSTLEN(c->ahb_burst_len)|DMAEN;
1143#endif
1144 DWC_GAHBCFG = gahbcfg;
1145
1146 DWC_DCFG = NZLSOHSK;
1147#ifdef USB_DW_SHARED_FIFO
1148 /* Set EP mismatch counter to the maximum */
1149 DWC_DCFG |= EPMISCNT(0x1f);
1150#endif
1151
1152#if !defined(USB_DW_ARCH_SLAVE) && !defined(USB_DW_SHARED_FIFO)
1153 if (c->ahb_threshold)
1154 DWC_DTHRCTL = ARPEN|RXTHRLEN(c->ahb_threshold)|RXTHREN;
1155#endif
1156
1157 /* Set up interrupts */
1158 DWC_DOEPMSK = STUP|XFRC;
1159 DWC_DIEPMSK = TOC|XFRC;
1160
1161 /* Unmask all available endpoints */
1162 DWC_DAINTMSK = 0xffffffff;
1163 usb_endpoints = DWC_DAINTMSK;
1164
1165 uint32_t gintmsk = USBRST|ENUMDNE|IEPINT|OEPINT;
1166#ifdef USB_DW_ARCH_SLAVE
1167 gintmsk |= RXFLVL;
1168#endif
1169#ifdef USB_DW_SHARED_FIFO
1170 gintmsk |= EPMIS;
1171#endif
1172 DWC_GINTMSK = gintmsk;
1173
1174 usb_dw_reset_endpoints();
1175
1176 /* Soft disconnect */
1177 DWC_DCTL = SDIS;
1178
1179 usb_dw_target_clear_irq();
1180 usb_dw_target_enable_irq();
1181
1182 /* Soft reconnect */
1183 udelay(3000);
1184 DWC_DCTL &= ~SDIS;
1185}
1186
1187static void usb_dw_exit(void)
1188{
1189 /* Soft disconnect */
1190 DWC_DCTL = SDIS;
1191 udelay(10);
1192
1193 DWC_PCGCCTL = 1; /* Stop Phy clock */
1194
1195 /* Disable IRQs */
1196 usb_dw_target_disable_irq();
1197
1198 /* Disable clocks */
1199 usb_dw_target_disable_clocks();
1200}
1201
1202
1203/*
1204 * API functions
1205 */
1206
1207/* Cancel transfers on configured EPs */
1208void usb_drv_cancel_all_transfers()
1209{
1210 usb_dw_target_disable_irq();
1211 for (int ep = 1; ep < USB_NUM_ENDPOINTS; ep++)
1212 for (int dir = 0; dir < USB_DW_NUM_DIRS; dir++)
1213 if (usb_endpoints & (1 << (ep + USB_DW_DIR_OFF(dir))))
1214 if (usb_dw_get_ep(ep, dir)->active)
1215 {
1216 //usb_dw_flush_endpoint(ep, dir);
1217 usb_dw_abort_endpoint(ep, dir);
1218 DWC_EPCTL(ep, dir) |= SD0PID;
1219 }
1220 usb_dw_target_enable_irq();
1221}
1222
1223bool usb_drv_stalled(int endpoint, bool in)
1224{
1225 return usb_dw_get_stall(EP_NUM(endpoint),
1226 in ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT);
1227}
1228
1229void usb_drv_stall(int endpoint, bool stall, bool in)
1230{
1231 usb_dw_target_disable_irq();
1232 usb_dw_set_stall(EP_NUM(endpoint),
1233 in ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT, stall);
1234 usb_dw_target_enable_irq();
1235}
1236
1237void usb_drv_set_address(int address)
1238{
1239#if 1
1240 /* Ignored intentionally, because the controller requires us to set the
1241 new address before sending the response for some reason. So we'll
1242 already set it when the control request arrives, before passing that
1243 into the USB core, which will then call this dummy function. */
1244 (void)address;
1245#else
1246 usb_dw_target_disable_irq();
1247 usb_dw_set_address(address);
1248 usb_dw_target_enable_irq();
1249#endif
1250}
1251
1252int usb_drv_port_speed(void)
1253{
1254 return ((DWC_DSTS & 0x6) == 0);
1255}
1256
1257void usb_drv_set_test_mode(int mode)
1258{
1259 (void)mode;
1260 /* Ignore this for now */
1261}
1262
1263void usb_attach(void)
1264{
1265}
1266
1267void usb_drv_init(void)
1268{
1269 usb_dw_init();
1270}
1271
1272void usb_drv_exit(void)
1273{
1274 usb_dw_exit();
1275}
1276
1277void INT_USB_FUNC(void)
1278{
1279 usb_dw_irq();
1280}
1281
1282int usb_drv_request_endpoint(int type, int dir)
1283{
1284 int request_ep = -1;
1285 enum usb_dw_epdir epdir = (EP_DIR(dir) == DIR_IN) ?
1286 USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
1287
1288 usb_dw_target_disable_irq();
1289 for (int ep = 1; ep < USB_NUM_ENDPOINTS; ep++)
1290 {
1291 if (usb_endpoints & (1 << (ep + USB_DW_DIR_OFF(epdir))))
1292 {
1293 struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, epdir);
1294 if (!dw_ep->active)
1295 {
1296 if (usb_dw_configure_ep(ep, epdir, type,
1297 usb_drv_port_speed() ? 512 : 64) >= 0)
1298 {
1299 dw_ep->active = true;
1300 request_ep = ep | dir;
1301 }
1302 break;
1303 }
1304 }
1305 }
1306 usb_dw_target_enable_irq();
1307 return request_ep;
1308}
1309
1310void usb_drv_release_endpoint(int endpoint)
1311{
1312 int epnum = EP_NUM(endpoint);
1313 if (!epnum) return;
1314 enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ?
1315 USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
1316 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, epdir);
1317
1318 usb_dw_target_disable_irq();
1319 if (dw_ep->active)
1320 {
1321 usb_dw_unconfigure_ep(epnum, epdir);
1322 dw_ep->active = false;
1323 }
1324 usb_dw_target_enable_irq();
1325}
1326
1327int usb_drv_recv(int endpoint, void* ptr, int length)
1328{
1329 int epnum = EP_NUM(endpoint);
1330 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_OUT);
1331
1332 usb_dw_target_disable_irq();
1333 if (dw_ep->active)
1334 {
1335 dw_ep->req_addr = ptr;
1336 dw_ep->req_size = length;
1337 /* OUT0 is always launched waiting for SETUP packet,
1338 it is CNAKed to receive app data */
1339 if (epnum == 0)
1340 DWC_DOEPCTL(0) |= CNAK;
1341 else
1342 usb_dw_start_xfer(epnum, USB_DW_EPDIR_OUT, ptr, length);
1343 }
1344 usb_dw_target_enable_irq();
1345 return 0;
1346}
1347
1348int usb_drv_send_nonblocking(int endpoint, void *ptr, int length)
1349{
1350 int epnum = EP_NUM(endpoint);
1351 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN);
1352
1353 usb_dw_target_disable_irq();
1354 if (dw_ep->active)
1355 {
1356 dw_ep->req_addr = ptr;
1357 dw_ep->req_size = length;
1358 usb_dw_start_xfer(epnum, USB_DW_EPDIR_IN, ptr, length);
1359 }
1360 usb_dw_target_enable_irq();
1361 return 0;
1362}
1363
1364int usb_drv_send(int endpoint, void *ptr, int length)
1365{
1366 int epnum = EP_NUM(endpoint);
1367 struct usb_dw_ep* dw_ep = usb_dw_get_ep(epnum, USB_DW_EPDIR_IN);
1368
1369 semaphore_wait(&dw_ep->complete, 0);
1370
1371 usb_drv_send_nonblocking(endpoint, ptr, length);
1372
1373 if (semaphore_wait(&dw_ep->complete, HZ) == OBJ_WAIT_TIMEDOUT)
1374 {
1375 usb_dw_target_disable_irq();
1376 usb_dw_abort_endpoint(epnum, USB_DW_EPDIR_IN);
1377 usb_dw_target_enable_irq();
1378 }
1379
1380 return dw_ep->status;
1381}
diff --git a/firmware/usbstack/usb_serial.c b/firmware/usbstack/usb_serial.c
index 4a80433435..e3ef4f5814 100644
--- a/firmware/usbstack/usb_serial.c
+++ b/firmware/usbstack/usb_serial.c
@@ -19,6 +19,7 @@
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#include "string.h" 21#include "string.h"
22#include "config.h"
22#include "system.h" 23#include "system.h"
23#include "usb_core.h" 24#include "usb_core.h"
24#include "usb_drv.h" 25#include "usb_drv.h"
@@ -60,6 +61,11 @@ static unsigned char send_buffer[BUFFER_SIZE]
60 USB_DEVBSS_ATTR __attribute__((aligned(32))); 61 USB_DEVBSS_ATTR __attribute__((aligned(32)));
61static unsigned char receive_buffer[32] 62static unsigned char receive_buffer[32]
62 USB_DEVBSS_ATTR __attribute__((aligned(32))); 63 USB_DEVBSS_ATTR __attribute__((aligned(32)));
64#if CONFIG_USBOTG == USBOTG_DESIGNWARE
65/* Aligned transit buffer */
66static unsigned char transit_buffer[32]
67 USB_DEVBSS_ATTR __attribute__((aligned(4)));
68#endif
63 69
64static void sendout(void); 70static void sendout(void);
65 71
@@ -163,8 +169,13 @@ static void sendout(void)
163 if(buffer_transitlength > 0) 169 if(buffer_transitlength > 0)
164 { 170 {
165 buffer_length -= buffer_transitlength; 171 buffer_length -= buffer_transitlength;
172#if CONFIG_USBOTG == USBOTG_DESIGNWARE
173 memcpy(transit_buffer,&send_buffer[buffer_start],buffer_transitlength);
174 usb_drv_send_nonblocking(ep_in,transit_buffer,buffer_transitlength);
175#else
166 usb_drv_send_nonblocking(ep_in, &send_buffer[buffer_start], 176 usb_drv_send_nonblocking(ep_in, &send_buffer[buffer_start],
167 buffer_transitlength); 177 buffer_transitlength);
178#endif
168 } 179 }
169} 180}
170 181