libopencm3
A free/libre/open-source firmware library for various ARM Cortex-M3 microcontrollers.
usb_dwc_common.c
Go to the documentation of this file.
1/*
2 * This file is part of the libopencm3 project.
3 *
4 * Copyright (C) 2011 Gareth McMullin <gareth@blacksphere.co.nz>
5 *
6 * This library is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this library. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <string.h>
22#include <libopencm3/usb/usbd.h>
24#include "usb_private.h"
25#include "usb_dwc_common.h"
26
27/* The FS core and the HS core have the same register layout.
28 * As the code can be used on both cores, the registers offset is modified
29 * according to the selected cores base address. */
30#define dev_base_address (usbd_dev->driver->base_address)
31#define REBASE(x) MMIO32((x) + (dev_base_address))
32
34{
35 REBASE(OTG_DCFG) = (REBASE(OTG_DCFG) & ~OTG_DCFG_DAD) | (addr << 4);
36}
37
38void dwc_ep_setup(usbd_device *usbd_dev, uint8_t addr, uint8_t type,
39 uint16_t max_size,
40 void (*callback) (usbd_device *usbd_dev, uint8_t ep))
41{
42 /*
43 * Configure endpoint address and type. Allocate FIFO memory for
44 * endpoint. Install callback function.
45 */
46 uint8_t dir = addr & 0x80;
47 addr &= 0x7f;
48
49 if (addr == 0) { /* For the default control endpoint */
50 /* Configure IN part. */
51 if (max_size >= 64) {
53 } else if (max_size >= 32) {
55 } else if (max_size >= 16) {
57 } else {
59 }
60
62 (max_size & OTG_DIEPSIZ0_XFRSIZ_MASK);
65
66 /* Configure OUT part. */
67 usbd_dev->doeptsiz[0] = OTG_DIEPSIZ0_STUPCNT_1 |
69 (max_size & OTG_DIEPSIZ0_XFRSIZ_MASK);
70 REBASE(OTG_DOEPTSIZ(0)) = usbd_dev->doeptsiz[0];
71 REBASE(OTG_DOEPCTL(0)) |=
73
74 REBASE(OTG_GNPTXFSIZ) = ((max_size / 4) << 16) |
75 usbd_dev->driver->rx_fifo_size;
76 usbd_dev->fifo_mem_top += max_size / 4;
77 usbd_dev->fifo_mem_top_ep0 = usbd_dev->fifo_mem_top;
78
79 return;
80 }
81
82 if (dir) {
83 REBASE(OTG_DIEPTXF(addr)) = ((max_size / 4) << 16) |
84 usbd_dev->fifo_mem_top;
85 usbd_dev->fifo_mem_top += max_size / 4;
86
87 REBASE(OTG_DIEPTSIZ(addr)) =
88 (max_size & OTG_DIEPSIZ0_XFRSIZ_MASK);
89 REBASE(OTG_DIEPCTL(addr)) |=
92 | (addr << 22) | max_size;
93
94 if (callback) {
95 usbd_dev->user_callback_ctr[addr][USB_TRANSACTION_IN] =
96 (void *)callback;
97 }
98 }
99
100 if (!dir) {
101 usbd_dev->doeptsiz[addr] = OTG_DIEPSIZ0_PKTCNT |
102 (max_size & OTG_DIEPSIZ0_XFRSIZ_MASK);
103 REBASE(OTG_DOEPTSIZ(addr)) = usbd_dev->doeptsiz[addr];
106 OTG_DOEPCTLX_SD0PID | (type << 18) | max_size;
107
108 if (callback) {
109 usbd_dev->user_callback_ctr[addr][USB_TRANSACTION_OUT] =
110 (void *)callback;
111 }
112 }
113}
114
116{
117 int i;
118 /* The core resets the endpoints automatically on reset. */
119 usbd_dev->fifo_mem_top = usbd_dev->fifo_mem_top_ep0;
120
121 /* Disable any currently active endpoints */
122 for (i = 1; i < 4; i++) {
125 }
128 }
129 }
130
131 /* Flush all tx/rx fifos */
134}
135
136void dwc_ep_stall_set(usbd_device *usbd_dev, uint8_t addr, uint8_t stall)
137{
138 if (addr == 0) {
139 if (stall) {
141 } else {
142 REBASE(OTG_DIEPCTL(addr)) &= ~OTG_DIEPCTL0_STALL;
143 }
144 }
145
146 if (addr & 0x80) {
147 addr &= 0x7F;
148
149 if (stall) {
151 } else {
152 REBASE(OTG_DIEPCTL(addr)) &= ~OTG_DIEPCTL0_STALL;
154 }
155 } else {
156 if (stall) {
158 } else {
159 REBASE(OTG_DOEPCTL(addr)) &= ~OTG_DOEPCTL0_STALL;
161 }
162 }
163}
164
165uint8_t dwc_ep_stall_get(usbd_device *usbd_dev, uint8_t addr)
166{
167 /* Return non-zero if STALL set. */
168 if (addr & 0x80) {
169 return (REBASE(OTG_DIEPCTL(addr & 0x7f)) &
170 OTG_DIEPCTL0_STALL) ? 1 : 0;
171 } else {
172 return (REBASE(OTG_DOEPCTL(addr)) &
173 OTG_DOEPCTL0_STALL) ? 1 : 0;
174 }
175}
176
177void dwc_ep_nak_set(usbd_device *usbd_dev, uint8_t addr, uint8_t nak)
178{
179 /* It does not make sense to force NAK on IN endpoints. */
180 if (addr & 0x80) {
181 return;
182 }
183
184 usbd_dev->force_nak[addr] = nak;
185
186 if (nak) {
188 } else {
190 }
191}
192
194 const void *buf, uint16_t len)
195{
196 const uint32_t *buf32 = buf;
197#if defined(__ARM_ARCH_6M__)
198 const uint8_t *buf8 = buf;
199 uint32_t word32;
200#endif /* defined(__ARM_ARCH_6M__) */
201 int i;
202
203 addr &= 0x7F;
204
205 /* Return if endpoint is already enabled. */
207 return 0;
208 }
209
210 /* Enable endpoint for transmission. */
214
215 /* Copy buffer to endpoint FIFO, note - memcpy does not work.
216 * ARMv7M supports non-word-aligned accesses, ARMv6M does not. */
217#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
218 for (i = len; i > 0; i -= 4) {
219 REBASE(OTG_FIFO(addr)) = *buf32++;
220 }
221#endif /* defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) */
222
223#if defined(__ARM_ARCH_6M__)
224 /* Take care of word-aligned and non-word-aligned buffers */
225 if (((uint32_t)buf8 & 0x3) == 0) {
226 for (i = len; i > 0; i -= 4) {
227 REBASE(OTG_FIFO(addr)) = *buf32++;
228 }
229 } else {
230 for (i = len; i > 0; i -= 4) {
231 memcpy(&word32, buf8, 4);
232 REBASE(OTG_FIFO(addr)) = word32;
233 buf8 += 4;
234 }
235 }
236#endif /* defined(__ARM_ARCH_6M__) */
237
238 return len;
239}
240
242 void *buf, uint16_t len)
243{
244 int i;
245 uint32_t *buf32 = buf;
246#if defined(__ARM_ARCH_6M__)
247 uint8_t *buf8 = buf;
248 uint32_t word32;
249#endif /* defined(__ARM_ARCH_6M__) */
250 uint32_t extra;
251
252 /* We do not need to know the endpoint address since there is only one
253 * receive FIFO for all endpoints.
254 */
255 (void) addr;
256 len = MIN(len, usbd_dev->rxbcnt);
257
258 /* ARMv7M supports non-word-aligned accesses, ARMv6M does not. */
259#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
260 for (i = len; i >= 4; i -= 4) {
261 *buf32++ = REBASE(OTG_FIFO(0));
262 usbd_dev->rxbcnt -= 4;
263 }
264#endif /* defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__) */
265
266#if defined(__ARM_ARCH_6M__)
267 /* Take care of word-aligned and non-word-aligned buffers */
268 if (((uint32_t)buf8 & 0x3) == 0) {
269 for (i = len; i >= 4; i -= 4) {
270 *buf32++ = REBASE(OTG_FIFO(0));
271 usbd_dev->rxbcnt -= 4;
272 }
273 } else {
274 for (i = len; i >= 4; i -= 4) {
275 word32 = REBASE(OTG_FIFO(0));
276 memcpy(buf8, &word32, 4);
277 usbd_dev->rxbcnt -= 4;
278 buf8 += 4;
279 }
280 /* buf32 needs to be updated as it is used for extra */
281 buf32 = (uint32_t *)buf8;
282 }
283#endif /* defined(__ARM_ARCH_6M__) */
284
285 if (i) {
286 extra = REBASE(OTG_FIFO(0));
287 /* we read 4 bytes from the fifo, so update rxbcnt */
288 if (usbd_dev->rxbcnt < 4) {
289 /* Be careful not to underflow (rxbcnt is unsigned) */
290 usbd_dev->rxbcnt = 0;
291 } else {
292 usbd_dev->rxbcnt -= 4;
293 }
294 memcpy(buf32, &extra, i);
295 }
296
297 return len;
298}
299
301{
302 uint32_t fifo;
303 /* set IN endpoint NAK */
305 /* wait for core to respond */
306 while (!(REBASE(OTG_DIEPINT(ep)) & OTG_DIEPINTX_INEPNE)) {
307 /* idle */
308 }
309 /* get fifo for this endpoint */
310 fifo = (REBASE(OTG_DIEPCTL(ep)) & OTG_DIEPCTL0_TXFNUM_MASK) >> 22;
311 /* wait for core to idle */
313 /* idle */
314 }
315 /* flush tx fifo */
316 REBASE(OTG_GRSTCTL) = (fifo << 6) | OTG_GRSTCTL_TXFFLSH;
317 /* reset packet counter */
318 REBASE(OTG_DIEPTSIZ(ep)) = 0;
320 /* idle */
321 }
322}
323
325{
326 /* Read interrupt status register. */
327 uint32_t intsts = REBASE(OTG_GINTSTS);
328 int i;
329
330 if (intsts & OTG_GINTSTS_ENUMDNE) {
331 /* Handle USB RESET condition. */
333 usbd_dev->fifo_mem_top = usbd_dev->driver->rx_fifo_size;
335 return;
336 }
337
338 /*
339 * There is no global interrupt flag for transmit complete.
340 * The XFRC bit must be checked in each OTG_DIEPINT(x).
341 */
342 for (i = 0; i < 4; i++) { /* Iterate over endpoints. */
344 /* Transfer complete. */
345 if (usbd_dev->user_callback_ctr[i]
346 [USB_TRANSACTION_IN]) {
347 usbd_dev->user_callback_ctr[i]
348 [USB_TRANSACTION_IN](usbd_dev, i);
349 }
350
352 }
353 }
354
355 /* Note: RX and TX handled differently in this device. */
356 if (intsts & OTG_GINTSTS_RXFLVL) {
357 /* Receive FIFO non-empty. */
358 uint32_t rxstsp = REBASE(OTG_GRXSTSP);
359 uint32_t pktsts = rxstsp & OTG_GRXSTSP_PKTSTS_MASK;
360 uint8_t ep = rxstsp & OTG_GRXSTSP_EPNUM_MASK;
361
362 if (pktsts == OTG_GRXSTSP_PKTSTS_SETUP_COMP) {
363 usbd_dev->user_callback_ctr[ep][USB_TRANSACTION_SETUP] (usbd_dev, ep);
364 }
365
366 if (pktsts == OTG_GRXSTSP_PKTSTS_OUT_COMP
367 || pktsts == OTG_GRXSTSP_PKTSTS_SETUP_COMP) {
368 REBASE(OTG_DOEPTSIZ(ep)) = usbd_dev->doeptsiz[ep];
370 (usbd_dev->force_nak[ep] ?
372 return;
373 }
374
375 if ((pktsts != OTG_GRXSTSP_PKTSTS_OUT) &&
376 (pktsts != OTG_GRXSTSP_PKTSTS_SETUP)) {
377 return;
378 }
379
380 uint8_t type;
381 if (pktsts == OTG_GRXSTSP_PKTSTS_SETUP) {
382 type = USB_TRANSACTION_SETUP;
383 } else {
384 type = USB_TRANSACTION_OUT;
385 }
386
387 if (type == USB_TRANSACTION_SETUP
389 /* SETUP received but there is still something stuck
390 * in the transmit fifo. Flush it.
391 */
393 }
394
395 /* Save packet size for dwc_ep_read_packet(). */
396 usbd_dev->rxbcnt = (rxstsp & OTG_GRXSTSP_BCNT_MASK) >> 4;
397
398 if (type == USB_TRANSACTION_SETUP) {
399 dwc_ep_read_packet(usbd_dev, ep, &usbd_dev->control_state.req, 8);
400 } else if (usbd_dev->user_callback_ctr[ep][type]) {
401 usbd_dev->user_callback_ctr[ep][type] (usbd_dev, ep);
402 }
403
404 /* Discard unread packet data. */
405 for (i = 0; i < usbd_dev->rxbcnt; i += 4) {
406 /* There is only one receive FIFO, so use OTG_FIFO(0) */
407 (void)REBASE(OTG_FIFO(0));
408 }
409
410 usbd_dev->rxbcnt = 0;
411 }
412
413 if (intsts & OTG_GINTSTS_USBSUSP) {
414 if (usbd_dev->user_callback_suspend) {
415 usbd_dev->user_callback_suspend();
416 }
418 }
419
420 if (intsts & OTG_GINTSTS_WKUPINT) {
421 if (usbd_dev->user_callback_resume) {
422 usbd_dev->user_callback_resume();
423 }
425 }
426
427 if (intsts & OTG_GINTSTS_SOF) {
428 if (usbd_dev->user_callback_sof) {
429 usbd_dev->user_callback_sof();
430 }
432 }
433
434 if (usbd_dev->user_callback_sof) {
436 } else {
437 REBASE(OTG_GINTMSK) &= ~OTG_GINTMSK_SOFM;
438 }
439}
440
441void dwc_disconnect(usbd_device *usbd_dev, bool disconnected)
442{
443 if (disconnected) {
445 } else {
446 REBASE(OTG_DCTL) &= ~OTG_DCTL_SDIS;
447 }
448}
struct _usbd_device usbd_device
Definition: usbd.h:53
void _usbd_reset(usbd_device *usbd_dev)
Definition: usb.c:113
#define OTG_DIEPCTL0_MPSIZ_32
Definition: otg_common.h:302
#define OTG_DIEPCTL0_MPSIZ_8
Definition: otg_common.h:304
#define OTG_GINTSTS
Definition: otg_common.h:35
#define OTG_GINTMSK_SOFM
Definition: otg_common.h:176
#define OTG_FIFO(x)
Definition: otg_common.h:89
#define OTG_DIEPCTL0_TXFNUM_MASK
Definition: otg_common.h:292
#define OTG_GRSTCTL_AHBIDL
Definition: otg_common.h:130
#define OTG_DIEPCTL0_USBAEP
Definition: otg_common.h:298
#define OTG_GRXSTSP_PKTSTS_MASK
Definition: otg_common.h:204
#define OTG_GINTSTS_RXFLVL
Definition: otg_common.h:167
#define OTG_DIEPSIZ0_XFRSIZ_MASK
Definition: otg_common.h:357
#define OTG_DIEPCTL0_MPSIZ_64
Definition: otg_common.h:301
#define OTG_GINTSTS_SOF
Definition: otg_common.h:168
#define OTG_DIEPTSIZ0
Definition: otg_common.h:79
#define OTG_DIEPSIZ0_STUPCNT_1
Definition: otg_common.h:350
#define OTG_DIEPCTLX_SD0PID
Definition: otg_common.h:289
#define OTG_DIEPINT(x)
Definition: otg_common.h:77
#define OTG_GRXSTSP_PKTSTS_SETUP_COMP
Definition: otg_common.h:210
#define OTG_DOEPCTL(x)
Definition: otg_common.h:76
#define OTG_GRXSTSP_EPNUM_MASK
Definition: otg_common.h:220
#define OTG_DIEPCTL0_SNAK
Definition: otg_common.h:290
#define OTG_DIEPCTL(x)
Definition: otg_common.h:74
#define OTG_GRSTCTL
Definition: otg_common.h:34
#define OTG_DIEPINTX_XFRC
Definition: otg_common.h:336
#define OTG_DOEPCTL0_EPENA
Definition: otg_common.h:307
#define OTG_DIEPCTL0_STALL
Definition: otg_common.h:293
#define OTG_GRXSTSP_PKTSTS_OUT_COMP
Definition: otg_common.h:208
#define OTG_GRXSTSP_BCNT_MASK
Definition: otg_common.h:219
#define OTG_DOEPCTL0_EPDIS
Definition: otg_common.h:308
#define OTG_DOEPCTL0_CNAK
Definition: otg_common.h:312
#define OTG_DOEPCTL0_USBAEP
Definition: otg_common.h:319
#define OTG_GRXSTSP_PKTSTS_SETUP
Definition: otg_common.h:212
#define OTG_DOEPTSIZ(x)
Definition: otg_common.h:82
#define OTG_GRXSTSP
Definition: otg_common.h:38
#define OTG_GRSTCTL_TXFFLSH
Definition: otg_common.h:134
#define OTG_DIEPCTL0_MPSIZ_16
Definition: otg_common.h:303
#define OTG_GRXSTSP_PKTSTS_OUT
Definition: otg_common.h:206
#define OTG_DIEPCTL0_EPDIS
Definition: otg_common.h:287
#define OTG_GNPTXFSIZ
Definition: otg_common.h:40
#define OTG_GINTSTS_WKUPINT
Definition: otg_common.h:142
#define OTG_DOEPCTL0_STALL
Definition: otg_common.h:314
#define OTG_DOEPCTL0_SNAK
Definition: otg_common.h:311
#define OTG_DIEPTXF(x)
Definition: otg_common.h:45
#define OTG_DIEPINTX_INEPNE
Definition: otg_common.h:330
#define OTG_GRSTCTL_TXFNUM_ALL
Definition: otg_common.h:133
#define OTG_DIEPCTL0_CNAK
Definition: otg_common.h:291
#define OTG_GINTSTS_ENUMDNE
Definition: otg_common.h:159
#define OTG_GRSTCTL_RXFFLSH
Definition: otg_common.h:135
#define OTG_DCTL
Definition: otg_common.h:63
#define OTG_DCTL_SDIS
Definition: otg_common.h:247
#define OTG_DIEPSIZ0_PKTCNT
Definition: otg_common.h:355
#define OTG_DIEPTSIZ(x)
Definition: otg_common.h:80
#define OTG_GINTMSK
Definition: otg_common.h:36
#define OTG_GINTSTS_USBSUSP
Definition: otg_common.h:161
#define OTG_DIEPCTL0_EPENA
Definition: otg_common.h:286
#define OTG_DCFG
Definition: otg_common.h:62
#define OTG_DOEPCTLX_SD0PID
Definition: otg_common.h:310
#define OTG_DIEPCTL0
Definition: otg_common.h:73
uint8_t dwc_ep_stall_get(usbd_device *usbd_dev, uint8_t addr)
void dwc_disconnect(usbd_device *usbd_dev, bool disconnected)
void dwc_endpoints_reset(usbd_device *usbd_dev)
static void dwc_flush_txfifo(usbd_device *usbd_dev, int ep)
#define REBASE(x)
uint16_t dwc_ep_write_packet(usbd_device *usbd_dev, uint8_t addr, const void *buf, uint16_t len)
uint16_t dwc_ep_read_packet(usbd_device *usbd_dev, uint8_t addr, void *buf, uint16_t len)
void dwc_set_address(usbd_device *usbd_dev, uint8_t addr)
void dwc_ep_nak_set(usbd_device *usbd_dev, uint8_t addr, uint8_t nak)
void dwc_ep_stall_set(usbd_device *usbd_dev, uint8_t addr, uint8_t stall)
void dwc_ep_setup(usbd_device *usbd_dev, uint8_t addr, uint8_t type, uint16_t max_size, void(*callback)(usbd_device *usbd_dev, uint8_t ep))
void dwc_poll(usbd_device *usbd_dev)
static struct _usbd_device usbd_dev
Definition: usb_f107.c:34