Skip to content

Commit 441fb06

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 9dd059a commit 441fb06

File tree

1 file changed

+294
-3
lines changed

1 file changed

+294
-3
lines changed

lib/pbio/drv/usb/usb_ev3.c

Lines changed: 294 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#if PBDRV_CONFIG_USB_EV3
1010

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

1314
#include <pbdrv/usb.h>
@@ -19,6 +20,7 @@
1920
#include "pbdrvconfig.h"
2021

2122
#include <tiam1808/armv5/am1808/interrupt.h>
23+
#include <tiam1808/cppi41dma.h>
2224
#include <tiam1808/hw/hw_types.h>
2325
#include <tiam1808/hw/hw_usbOtg_AM1808.h>
2426
#include <tiam1808/hw/hw_syscfg0_AM1808.h>
@@ -225,6 +227,115 @@ static uint32_t setup_misc_tx_byte;
225227
// Whether the device is using USB high-speed mode or not
226228
static bool is_usb_hs;
227229

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

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

272440
if (intr_src & INTR_BIT_EP0) {
@@ -313,11 +481,15 @@ static void usb_device_intr(void) {
313481
if (usb_config == 1) {
314482
// configuring
315483

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

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

427631
if (!handled) {
@@ -460,6 +664,61 @@ static void usb_device_intr(void) {
460664
}
461665
}
462666

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

731+
for (;;) {
732+
PBIO_OS_AWAIT_UNTIL(state, usb_rx_is_ready);
733+
734+
if (usb_rx_is_ready) {
735+
__asm__ volatile ("" ::: "memory");
736+
737+
uint32_t usb_rx_sz = cppi_descriptors[CPPI_DESC_RX].word0.pktLength;
738+
739+
// TODO: Remove this echo test
740+
unsigned int i;
741+
for (i = 0; i < usb_rx_sz; i++) {
742+
ep1_tx_response_buf[i] = ep1_rx_buf[i] + 1;
743+
}
744+
for (; i < 512; i++) {
745+
ep1_tx_response_buf[i] = 0xaa;
746+
}
747+
748+
(void)ep1_tx_status_buf;
749+
(void)ep1_tx_stdout_buf;
750+
751+
unsigned int tx_sz = is_usb_hs ? PYBRICKS_EP_PKT_SZ_HS : PYBRICKS_EP_PKT_SZ_FS;
752+
if (!usb_tx_response_is_not_ready) {
753+
usb_tx_response_is_not_ready = true;
754+
usb_setup_tx_dma_desc(CPPI_DESC_TX_RESPONSE, ep1_tx_response_buf, tx_sz);
755+
}
756+
757+
// Re-queue RX buffer after processing is complete
758+
usb_rx_is_ready = false;
759+
usb_setup_rx_dma_desc();
760+
}
761+
}
762+
472763
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
473764
}
474765

0 commit comments

Comments
 (0)