Skip to content

Commit 83fe2f8

Browse files
committed
pbio/drv/usb/usb_ev3: Implement DMA-based endpoint handling
This currently implements an echo test rather than hooking up command handlers
1 parent 5b6e6cd commit 83fe2f8

File tree

1 file changed

+298
-3
lines changed

1 file changed

+298
-3
lines changed

lib/pbio/drv/usb/usb_ev3.c

Lines changed: 298 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88

99
#if PBDRV_CONFIG_USB_EV3
1010

11+
#include <assert.h>
1112
#include <stdint.h>
1213

14+
#include <pbdrv/compiler.h>
1315
#include <pbdrv/usb.h>
1416
#include <pbio/os.h>
1517
#include <pbio/protocol.h>
@@ -19,6 +21,7 @@
1921
#include "pbdrvconfig.h"
2022

2123
#include <tiam1808/armv5/am1808/interrupt.h>
24+
#include <tiam1808/cppi41dma.h>
2225
#include <tiam1808/hw/hw_types.h>
2326
#include <tiam1808/hw/hw_usbOtg_AM1808.h>
2427
#include <tiam1808/hw/hw_syscfg0_AM1808.h>
@@ -225,6 +228,115 @@ static uint32_t setup_misc_tx_byte;
225228
// Whether the device is using USB high-speed mode or not
226229
static bool is_usb_hs;
227230

231+
// Buffers, used for different logical flows on the data endpoint
232+
static uint8_t ep1_rx_buf[PYBRICKS_EP_PKT_SZ_HS];
233+
static uint8_t ep1_tx_response_buf[PYBRICKS_EP_PKT_SZ_HS];
234+
static uint8_t ep1_tx_status_buf[PYBRICKS_EP_PKT_SZ_HS];
235+
static uint8_t ep1_tx_stdout_buf[PYBRICKS_EP_PKT_SZ_HS];
236+
237+
// Buffer status flags
238+
static volatile bool usb_rx_is_ready;
239+
static volatile bool usb_tx_response_is_not_ready;
240+
static volatile bool usb_tx_status_is_not_ready;
241+
static volatile bool usb_tx_stdout_is_not_ready;
242+
243+
// CPPI DMA support code
244+
245+
// Descriptors must be aligned to a power of 2 greater than or equal to their size.
246+
// We are using a descriptor of 32 bytes which is also the required alignment.
247+
#define CPPI_DESCRIPTOR_ALIGN 32
248+
249+
// Host Packet Descriptor
250+
// The TI support library has hardcoded assumptions about the layout of these structures,
251+
// so we declare it ourselves here in order to control it as we wish.
252+
typedef struct {
253+
hPDWord0 word0;
254+
hPDWord1 word1;
255+
hPDWord2 word2;
256+
uint32_t buf_len;
257+
void *buf_ptr;
258+
void *next_desc_ptr;
259+
uint32_t orig_buf_len;
260+
void *orig_buf_ptr;
261+
} __attribute__((aligned(CPPI_DESCRIPTOR_ALIGN))) usb_cppi_hpd_t;
262+
_Static_assert(sizeof(usb_cppi_hpd_t) <= CPPI_DESCRIPTOR_ALIGN);
263+
264+
// This goes into the lower bits of the queue CTRLD register
265+
#define CPPI_DESCRIPTOR_SIZE_BITS ((sizeof(usb_cppi_hpd_t) - 24) / 4)
266+
267+
// We only use a hardcoded descriptor for each logical flow,
268+
// rather than dynamically allocating them as needed
269+
enum {
270+
CPPI_DESC_RX,
271+
CPPI_DESC_TX_RESPONSE,
272+
CPPI_DESC_TX_STATUS,
273+
CPPI_DESC_TX_STDOUT,
274+
// the minimum number of descriptors we can allocate is 32,
275+
// even though we do not use nearly all of them
276+
CPPI_DESC_COUNT = 32,
277+
};
278+
279+
enum {
280+
// Documenting explicitly that we only use RX queue 0
281+
// (out of 16 total which are supported by the hardware)
282+
CPPI_RX_SUBMIT_QUEUE = 0,
283+
};
284+
285+
// CPPI memory
286+
static usb_cppi_hpd_t cppi_descriptors[CPPI_DESC_COUNT];
287+
static uint32_t cppi_linking_ram[CPPI_DESC_COUNT];
288+
289+
// Fill in the CPPI DMA descriptor to receive a packet
290+
static void usb_setup_rx_dma_desc(void) {
291+
cppi_descriptors[CPPI_DESC_RX] = (usb_cppi_hpd_t) {
292+
.word0 = {
293+
.hostPktType = 0x10,
294+
},
295+
.word1 = {},
296+
.word2 = {
297+
.pktRetQueue = RX_COMPQ1,
298+
},
299+
.buf_len = PYBRICKS_EP_PKT_SZ_HS,
300+
.buf_ptr = ep1_rx_buf,
301+
.next_desc_ptr = 0,
302+
.orig_buf_len = PYBRICKS_EP_PKT_SZ_HS,
303+
.orig_buf_ptr = ep1_rx_buf,
304+
};
305+
306+
pbdrv_compiler_memory_barrier();
307+
308+
HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + CPPI_RX_SUBMIT_QUEUE * 16) =
309+
(uint32_t)(&cppi_descriptors[CPPI_DESC_RX]) | CPPI_DESCRIPTOR_SIZE_BITS;
310+
}
311+
312+
313+
// Fill in the CPPI DMA descriptor to send a packet
314+
static void usb_setup_tx_dma_desc(int tx_type, void *buf, uint32_t buf_len) {
315+
cppi_descriptors[tx_type] = (usb_cppi_hpd_t) {
316+
.word0 = {
317+
.hostPktType = 0x10,
318+
.pktLength = buf_len,
319+
},
320+
.word1 = {
321+
.srcPrtNum = 1, // port is EP1
322+
},
323+
.word2 = {
324+
.pktType = 5, // USB packet type
325+
.pktRetQueue = TX_COMPQ1,
326+
},
327+
.buf_len = buf_len,
328+
.buf_ptr = buf,
329+
.next_desc_ptr = 0,
330+
.orig_buf_len = buf_len,
331+
.orig_buf_ptr = buf,
332+
};
333+
334+
pbdrv_compiler_memory_barrier();
335+
336+
HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + TX_SUBMITQ1 * 16) =
337+
(uint32_t)(&cppi_descriptors[tx_type]) | CPPI_DESCRIPTOR_SIZE_BITS;
338+
}
339+
228340
// Helper function for dividing up buffers for EP0 and feeding the FIFO
229341
static void usb_setup_send_chunk(void) {
230342
unsigned int this_chunk_sz = setup_data_to_send_sz;
@@ -266,7 +378,64 @@ static void usb_device_intr(void) {
266378
is_usb_hs = false;
267379
}
268380

269-
// TODO: More tasks in the future
381+
// Set up the FIFOs
382+
// We use a hardcoded address allocation as follows
383+
// @ 0 ==> EP0
384+
// @ 64 ==> EP1 IN (device to host, tx)
385+
// @ 64+512 ==> EP1 OUT (host to device, rx)
386+
HWREGB(USB0_BASE + USB_O_EPIDX) = 1;
387+
if (is_usb_hs) {
388+
HWREGB(USB0_BASE + USB_O_TXFIFOSZ) = USB_TXFIFOSZ_SIZE_512;
389+
HWREGB(USB0_BASE + USB_O_RXFIFOSZ) = USB_RXFIFOSZ_SIZE_512;
390+
HWREGH(USB0_BASE + USB_O_TXMAXP1) = PYBRICKS_EP_PKT_SZ_HS;
391+
HWREGH(USB0_BASE + USB_O_RXMAXP1) = PYBRICKS_EP_PKT_SZ_HS;
392+
} else {
393+
HWREGB(USB0_BASE + USB_O_TXFIFOSZ) = USB_TXFIFOSZ_SIZE_64;
394+
HWREGB(USB0_BASE + USB_O_RXFIFOSZ) = USB_RXFIFOSZ_SIZE_64;
395+
HWREGH(USB0_BASE + USB_O_TXMAXP1) = PYBRICKS_EP_PKT_SZ_FS;
396+
HWREGH(USB0_BASE + USB_O_RXMAXP1) = PYBRICKS_EP_PKT_SZ_FS;
397+
}
398+
HWREGH(USB0_BASE + USB_O_TXFIFOADD) = EP0_BUF_SZ / 8;
399+
HWREGH(USB0_BASE + USB_O_RXFIFOADD) = (EP0_BUF_SZ + PYBRICKS_EP_PKT_SZ_HS) / 8;
400+
401+
// Set up the TX fifo for DMA and a stall condition
402+
HWREGH(USB0_BASE + USB_O_TXCSRL1) = ((USB_TXCSRH1_AUTOSET | USB_TXCSRH1_MODE | USB_TXCSRH1_DMAEN | USB_TXCSRH1_DMAMOD) << 8) | USB_TXCSRL1_STALL;
403+
// Set up the RX fifo for DMA and a stall condition
404+
HWREGH(USB0_BASE + USB_O_RXCSRL1) = ((USB_RXCSRH1_AUTOCL | USB_RXCSRH1_DMAEN) << 8) | USB_RXCSRL1_STALL;
405+
406+
// Set up CPPI DMA
407+
HWREG(USB_0_OTGBASE + CPDMA_LRAM_0_BASE) = (uint32_t)cppi_linking_ram;
408+
HWREG(USB_0_OTGBASE + CPDMA_LRAM_0_SIZE) = CPPI_DESC_COUNT;
409+
HWREG(USB_0_OTGBASE + CPDMA_LRAM_1_BASE) = 0;
410+
411+
HWREG(USB_0_OTGBASE + CPDMA_QUEUEMGR_REGION_0) = (uint32_t)cppi_descriptors;
412+
// 32 descriptors of 32 bytes each
413+
HWREG(USB_0_OTGBASE + CPDMA_QUEUEMGR_REGION_0_CONTROL) = 0;
414+
415+
// scheduler table: RX on 0, TX on 0
416+
HWREG(USB_0_OTGBASE + CPDMA_SCHED_TABLE_0) = 0x0080;
417+
HWREG(USB_0_OTGBASE + CPDMA_SCHED_CONTROL_REG) = (1 << SCHEDULER_ENABLE_SHFT) | (2 - 1);
418+
419+
// CPPI RX
420+
HWREG(USB_0_OTGBASE + CPDMA_RX_CHANNEL_REG_A) =
421+
(CPPI_RX_SUBMIT_QUEUE << 0) |
422+
(CPPI_RX_SUBMIT_QUEUE << 16);
423+
HWREG(USB_0_OTGBASE + CPDMA_RX_CHANNEL_REG_B) =
424+
(CPPI_RX_SUBMIT_QUEUE << 0) |
425+
(CPPI_RX_SUBMIT_QUEUE << 16);
426+
HWREG(USB_0_OTGBASE + CPDMA_RX_CHANNEL_CONFIG_REG) =
427+
(1 << 31) | // enable
428+
(1 << 24) | // starvation = retry
429+
(1 << 14) | // "host" descriptors (the only valid type)
430+
RX_COMPQ1;
431+
432+
// CPPI TX
433+
HWREG(USB_0_OTGBASE + CPDMA_TX_CHANNEL_CONFIG_REG) =
434+
(1 << 31) | // enable
435+
TX_COMPQ1;
436+
437+
// queue RX descriptor
438+
usb_setup_rx_dma_desc();
270439
}
271440

272441
if (intr_src & INTR_BIT_EP0) {
@@ -313,11 +482,15 @@ static void usb_device_intr(void) {
313482
if (usb_config == 1) {
314483
// configuring
315484

316-
// TODO: Handle configuring
485+
// Reset data toggle, clear stall, flush fifo
486+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
487+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
317488
} else {
318489
// deconfiguring
319490

320-
// TODO: Handle deconfiguring
491+
// Set stall condition
492+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_STALL;
493+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_STALL;
321494
}
322495
handled = 1;
323496
}
@@ -422,6 +595,38 @@ static void usb_device_intr(void) {
422595
handled = 1;
423596
}
424597
}
598+
} else if (((setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) == BM_REQ_TYPE_STANDARD) &&
599+
((setup_pkt.s.bmRequestType & BM_REQ_RECIP_MASK) == BM_REQ_RECIP_EP)) {
600+
601+
if (setup_pkt.s.bRequest == GET_STATUS) {
602+
if (setup_pkt.s.wIndex == 1) {
603+
setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_RXCSRL1) & USB_RXCSRL1_STALL);
604+
setup_data_to_send = &setup_misc_tx_byte;
605+
setup_data_to_send_sz = 2;
606+
handled = 1;
607+
} else if (setup_pkt.s.wIndex == 0x81) {
608+
setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_TXCSRL1) & USB_TXCSRL1_STALL);
609+
setup_data_to_send = &setup_misc_tx_byte;
610+
setup_data_to_send_sz = 2;
611+
handled = 1;
612+
}
613+
} else if (setup_pkt.s.bRequest == CLEAR_FEATURE && setup_pkt.s.wValue == 0) {
614+
if (setup_pkt.s.wIndex == 1) {
615+
HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL;
616+
handled = 1;
617+
} else if (setup_pkt.s.wIndex == 0x81) {
618+
HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL;
619+
handled = 1;
620+
}
621+
} else if (setup_pkt.s.bRequest == SET_FEATURE && setup_pkt.s.wValue == 0) {
622+
if (setup_pkt.s.wIndex == 1) {
623+
HWREGB(USB0_BASE + USB_O_RXCSRL1) |= USB_RXCSRL1_STALL;
624+
handled = 1;
625+
} else if (setup_pkt.s.wIndex == 0x81) {
626+
HWREGB(USB0_BASE + USB_O_TXCSRL1) |= USB_TXCSRL1_STALL;
627+
handled = 1;
628+
}
629+
}
425630
}
426631

427632
if (!handled) {
@@ -460,6 +665,61 @@ static void usb_device_intr(void) {
460665
}
461666
}
462667

668+
// EP1 interrupts, which only trigger on error conditions since we use DMA
669+
670+
if (intr_src & INTR_BIT_EP1_OUT) {
671+
// EP 1 OUT, host to device, rx
672+
uint8_t rxcsr = HWREGB(USB0_BASE + USB_O_RXCSRL1);
673+
674+
// Clear error bits
675+
rxcsr &= ~USB_RXCSRL1_STALLED;
676+
677+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = rxcsr;
678+
}
679+
680+
if (intr_src & INTR_BIT_EP1_IN) {
681+
// EP 1 IN, device to host, tx
682+
uint8_t txcsr = HWREGB(USB0_BASE + USB_O_TXCSRL1);
683+
684+
// Clear error bits
685+
txcsr &= ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_UNDRN | USB_TXCSRL1_FIFONE);
686+
687+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = txcsr;
688+
}
689+
690+
// Check for DMA completions
691+
uint32_t dma_q_pend_0 = HWREG(USB_0_OTGBASE + CPDMA_PEND_0_REGISTER);
692+
693+
if (dma_q_pend_0 & (1 << RX_COMPQ1)) {
694+
// DMA for EP 1 OUT is done
695+
696+
// Pop the descriptor from the queue
697+
uint32_t qctrld = HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + RX_COMPQ1 * 16);
698+
(void)qctrld;
699+
700+
// Signal the main loop that we have something
701+
usb_rx_is_ready = true;
702+
pbio_os_request_poll();
703+
}
704+
705+
if (dma_q_pend_0 & (1 << TX_COMPQ1)) {
706+
// DMA for EP 1 IN is done
707+
708+
// Pop the descriptor from the queue
709+
uint32_t qctrld = HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + TX_COMPQ1 * 16) & ~0x1f;
710+
711+
if (qctrld == (uint32_t)(&cppi_descriptors[CPPI_DESC_TX_RESPONSE])) {
712+
usb_tx_response_is_not_ready = false;
713+
pbio_os_request_poll();
714+
} else if (qctrld == (uint32_t)(&cppi_descriptors[CPPI_DESC_TX_STATUS])) {
715+
usb_tx_status_is_not_ready = false;
716+
pbio_os_request_poll();
717+
} else if (qctrld == (uint32_t)(&cppi_descriptors[CPPI_DESC_TX_STDOUT])) {
718+
usb_tx_stdout_is_not_ready = false;
719+
pbio_os_request_poll();
720+
}
721+
}
722+
463723
HWREG(USB_0_OTGBASE + USB_0_INTR_SRC_CLEAR) = intr_src;
464724
HWREG(USB_0_OTGBASE + USB_0_END_OF_INTR) = 0;
465725
}
@@ -469,6 +729,41 @@ static pbio_os_process_t pbdrv_usb_ev3_process;
469729
static pbio_error_t pbdrv_usb_ev3_process_thread(pbio_os_state_t *state, void *context) {
470730
PBIO_OS_ASYNC_BEGIN(state);
471731

732+
for (;;) {
733+
PBIO_OS_AWAIT_UNTIL(state, usb_rx_is_ready);
734+
735+
if (usb_rx_is_ready) {
736+
// This barrier prevents *subsequent* memory reads from being
737+
// speculatively moved *earlier*, outside the if statement
738+
// (which is technically allowed by the as-if rule).
739+
pbdrv_compiler_memory_barrier();
740+
741+
uint32_t usb_rx_sz = cppi_descriptors[CPPI_DESC_RX].word0.pktLength;
742+
743+
// TODO: Remove this echo test
744+
unsigned int i;
745+
for (i = 0; i < usb_rx_sz; i++) {
746+
ep1_tx_response_buf[i] = ep1_rx_buf[i] + 1;
747+
}
748+
for (; i < 512; i++) {
749+
ep1_tx_response_buf[i] = 0xaa;
750+
}
751+
752+
(void)ep1_tx_status_buf;
753+
(void)ep1_tx_stdout_buf;
754+
755+
unsigned int tx_sz = is_usb_hs ? PYBRICKS_EP_PKT_SZ_HS : PYBRICKS_EP_PKT_SZ_FS;
756+
if (!usb_tx_response_is_not_ready) {
757+
usb_tx_response_is_not_ready = true;
758+
usb_setup_tx_dma_desc(CPPI_DESC_TX_RESPONSE, ep1_tx_response_buf, tx_sz);
759+
}
760+
761+
// Re-queue RX buffer after processing is complete
762+
usb_rx_is_ready = false;
763+
usb_setup_rx_dma_desc();
764+
}
765+
}
766+
472767
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
473768
}
474769

0 commit comments

Comments
 (0)