Skip to content

Commit bbc7de3

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 5191f8e commit bbc7de3

File tree

2 files changed

+333
-3
lines changed

2 files changed

+333
-3
lines changed

lib/pbio/drv/usb/usb_ev3.c

Lines changed: 316 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>
@@ -217,6 +220,176 @@ static uint32_t pbdrv_usb_setup_misc_tx_byte;
217220
// Whether the device is using USB high-speed mode or not
218221
static bool pbdrv_usb_is_usb_hs;
219222

223+
// Buffers, used for different logical flows on the data endpoint
224+
static uint8_t ep1_rx_buf[PYBRICKS_EP_PKT_SZ_HS];
225+
static uint8_t ep1_tx_response_buf[PYBRICKS_EP_PKT_SZ_HS];
226+
static uint8_t ep1_tx_status_buf[PYBRICKS_EP_PKT_SZ_HS];
227+
static uint8_t ep1_tx_stdout_buf[PYBRICKS_EP_PKT_SZ_HS];
228+
229+
// Buffer status flags
230+
static volatile bool usb_rx_is_ready;
231+
static volatile bool usb_tx_response_is_not_ready;
232+
static volatile bool usb_tx_status_is_not_ready;
233+
static volatile bool usb_tx_stdout_is_not_ready;
234+
235+
// CPPI DMA support code
236+
237+
// Descriptors must be aligned to a power of 2 greater than or equal to their size.
238+
// We are using a descriptor of 32 bytes which is also the required alignment.
239+
#define CPPI_DESCRIPTOR_ALIGN 32
240+
241+
// Host Packet Descriptor
242+
// The TI support library has hardcoded assumptions about the layout of these structures,
243+
// so we declare it ourselves here in order to control it as we wish.
244+
typedef struct {
245+
hPDWord0 word0;
246+
hPDWord1 word1;
247+
hPDWord2 word2;
248+
uint32_t buf_len;
249+
void *buf_ptr;
250+
void *next_desc_ptr;
251+
uint32_t orig_buf_len;
252+
void *orig_buf_ptr;
253+
} __attribute__((aligned(CPPI_DESCRIPTOR_ALIGN))) usb_cppi_hpd_t;
254+
_Static_assert(sizeof(usb_cppi_hpd_t) <= CPPI_DESCRIPTOR_ALIGN);
255+
256+
// This goes into the lower bits of the queue CTRLD register
257+
#define CPPI_DESCRIPTOR_SIZE_BITS CPDMA_QUEUE_REGISTER_DESC_SIZE(usb_cppi_hpd_t)
258+
259+
// We only use a hardcoded descriptor for each logical flow,
260+
// rather than dynamically allocating them as needed
261+
enum {
262+
CPPI_DESC_RX,
263+
CPPI_DESC_TX_RESPONSE,
264+
CPPI_DESC_TX_STATUS,
265+
CPPI_DESC_TX_STDOUT,
266+
// the minimum number of descriptors we can allocate is 32,
267+
// even though we do not use nearly all of them
268+
CPPI_DESC_COUNT = 32,
269+
};
270+
271+
enum {
272+
// Documenting explicitly that we only use RX queue 0
273+
// (out of 16 total which are supported by the hardware)
274+
CPPI_RX_SUBMIT_QUEUE = 0,
275+
};
276+
277+
// CPPI memory
278+
static usb_cppi_hpd_t cppi_descriptors[CPPI_DESC_COUNT];
279+
static uint32_t cppi_linking_ram[CPPI_DESC_COUNT];
280+
281+
// Fill in the CPPI DMA descriptor to receive a packet
282+
static void usb_setup_rx_dma_desc(void) {
283+
cppi_descriptors[CPPI_DESC_RX] = (usb_cppi_hpd_t) {
284+
.word0 = {
285+
.hostPktType = 0x10,
286+
},
287+
.word1 = {},
288+
.word2 = {
289+
.pktRetQueue = RX_COMPQ1,
290+
},
291+
.buf_len = PYBRICKS_EP_PKT_SZ_HS,
292+
.buf_ptr = ep1_rx_buf,
293+
.next_desc_ptr = 0,
294+
.orig_buf_len = PYBRICKS_EP_PKT_SZ_HS,
295+
.orig_buf_ptr = ep1_rx_buf,
296+
};
297+
298+
pbdrv_compiler_memory_barrier();
299+
300+
HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + CPPI_RX_SUBMIT_QUEUE * 16) =
301+
(uint32_t)(&cppi_descriptors[CPPI_DESC_RX]) | CPPI_DESCRIPTOR_SIZE_BITS;
302+
}
303+
304+
305+
// Fill in the CPPI DMA descriptor to send a packet
306+
static void usb_setup_tx_dma_desc(int tx_type, void *buf, uint32_t buf_len) {
307+
cppi_descriptors[tx_type] = (usb_cppi_hpd_t) {
308+
.word0 = {
309+
.hostPktType = 0x10,
310+
.pktLength = buf_len,
311+
},
312+
.word1 = {
313+
.srcPrtNum = 1, // port is EP1
314+
},
315+
.word2 = {
316+
.pktType = 5, // USB packet type
317+
.pktRetQueue = TX_COMPQ1,
318+
},
319+
.buf_len = buf_len,
320+
.buf_ptr = buf,
321+
.next_desc_ptr = 0,
322+
.orig_buf_len = buf_len,
323+
.orig_buf_ptr = buf,
324+
};
325+
326+
pbdrv_compiler_memory_barrier();
327+
328+
HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + TX_SUBMITQ1 * 16) =
329+
(uint32_t)(&cppi_descriptors[tx_type]) | CPPI_DESCRIPTOR_SIZE_BITS;
330+
}
331+
332+
// Helper function to set up CPPI DMA upon USB reset
333+
static void usb_reset_cppi_dma(void) {
334+
// Set up the FIFOs
335+
// We use a hardcoded address allocation as follows
336+
// @ 0 ==> EP0
337+
// @ 64 ==> EP1 IN (device to host, tx)
338+
// @ 64+512 ==> EP1 OUT (host to device, rx)
339+
HWREGB(USB0_BASE + USB_O_EPIDX) = 1;
340+
if (pbdrv_usb_is_usb_hs) {
341+
HWREGB(USB0_BASE + USB_O_TXFIFOSZ) = USB_TXFIFOSZ_SIZE_512;
342+
HWREGB(USB0_BASE + USB_O_RXFIFOSZ) = USB_RXFIFOSZ_SIZE_512;
343+
HWREGH(USB0_BASE + USB_O_TXMAXP1) = PYBRICKS_EP_PKT_SZ_HS;
344+
HWREGH(USB0_BASE + USB_O_RXMAXP1) = PYBRICKS_EP_PKT_SZ_HS;
345+
} else {
346+
HWREGB(USB0_BASE + USB_O_TXFIFOSZ) = USB_TXFIFOSZ_SIZE_64;
347+
HWREGB(USB0_BASE + USB_O_RXFIFOSZ) = USB_RXFIFOSZ_SIZE_64;
348+
HWREGH(USB0_BASE + USB_O_TXMAXP1) = PYBRICKS_EP_PKT_SZ_FS;
349+
HWREGH(USB0_BASE + USB_O_RXMAXP1) = PYBRICKS_EP_PKT_SZ_FS;
350+
}
351+
HWREGH(USB0_BASE + USB_O_TXFIFOADD) = EP0_BUF_SZ / 8;
352+
HWREGH(USB0_BASE + USB_O_RXFIFOADD) = (EP0_BUF_SZ + PYBRICKS_EP_PKT_SZ_HS) / 8;
353+
354+
// Set up the TX fifo for DMA and a stall condition
355+
HWREGH(USB0_BASE + USB_O_TXCSRL1) = ((USB_TXCSRH1_AUTOSET | USB_TXCSRH1_MODE | USB_TXCSRH1_DMAEN | USB_TXCSRH1_DMAMOD) << 8) | USB_TXCSRL1_STALL;
356+
// Set up the RX fifo for DMA and a stall condition
357+
HWREGH(USB0_BASE + USB_O_RXCSRL1) = ((USB_RXCSRH1_AUTOCL | USB_RXCSRH1_DMAEN) << 8) | USB_RXCSRL1_STALL;
358+
359+
// Set up CPPI DMA
360+
HWREG(USB_0_OTGBASE + CPDMA_LRAM_0_BASE) = (uint32_t)cppi_linking_ram;
361+
HWREG(USB_0_OTGBASE + CPDMA_LRAM_0_SIZE) = CPPI_DESC_COUNT;
362+
HWREG(USB_0_OTGBASE + CPDMA_LRAM_1_BASE) = 0;
363+
364+
HWREG(USB_0_OTGBASE + CPDMA_QUEUEMGR_REGION_0) = (uint32_t)cppi_descriptors;
365+
// 32 descriptors of 32 bytes each
366+
HWREG(USB_0_OTGBASE + CPDMA_QUEUEMGR_REGION_0_CONTROL) = 0;
367+
368+
// scheduler table: RX on 0, TX on 0
369+
HWREG(USB_0_OTGBASE + CPDMA_SCHED_TABLE_0) =
370+
((CPDMA_SCHED_TX | 0) << CPDMA_SCHED_ENTRY_SHIFT) |
371+
((CPDMA_SCHED_RX | 0) << 0);
372+
HWREG(USB_0_OTGBASE + CPDMA_SCHED_CONTROL_REG) = (1 << SCHEDULER_ENABLE_SHFT) | (2 - 1);
373+
374+
// CPPI RX
375+
HWREG(USB_0_OTGBASE + CPDMA_RX_CHANNEL_REG_A) =
376+
(CPPI_RX_SUBMIT_QUEUE << 0) |
377+
(CPPI_RX_SUBMIT_QUEUE << 16);
378+
HWREG(USB_0_OTGBASE + CPDMA_RX_CHANNEL_REG_B) =
379+
(CPPI_RX_SUBMIT_QUEUE << 0) |
380+
(CPPI_RX_SUBMIT_QUEUE << 16);
381+
HWREG(USB_0_OTGBASE + CPDMA_RX_CHANNEL_CONFIG_REG) =
382+
CPDMA_RX_GLOBAL_CHAN_CFG_ENABLE |
383+
CPDMA_RX_GLOBAL_CHAN_CFG_ERR_RETRY | // starvation = retry
384+
CPDMA_RX_GLOBAL_CHAN_CFG_DESC_TY | // "host" descriptors (the only valid type)
385+
RX_COMPQ1;
386+
387+
// CPPI TX
388+
HWREG(USB_0_OTGBASE + CPDMA_TX_CHANNEL_CONFIG_REG) =
389+
CPDMA_TX_GLOBAL_CHAN_CFG_ENABLE |
390+
TX_COMPQ1;
391+
}
392+
220393
// Helper function for dividing up buffers for EP0 and feeding the FIFO
221394
static void usb_setup_send_chunk(void) {
222395
unsigned int this_chunk_sz = pbdrv_usb_setup_data_to_send_sz;
@@ -357,7 +530,11 @@ static void usb_device_intr(void) {
357530
pbdrv_usb_is_usb_hs = false;
358531
}
359532

360-
// TODO: More tasks in the future
533+
// Set up all the CPPI DMA registers
534+
usb_reset_cppi_dma();
535+
536+
// queue RX descriptor
537+
usb_setup_rx_dma_desc();
361538
}
362539

363540
if (intr_src & USBOTG_INTR_EP0) {
@@ -407,11 +584,15 @@ static void usb_device_intr(void) {
407584
if (pbdrv_usb_config == 1) {
408585
// configuring
409586

410-
// TODO: Handle configuring
587+
// Reset data toggle, clear stall, flush fifo
588+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
589+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
411590
} else {
412591
// deconfiguring
413592

414-
// TODO: Handle deconfiguring
593+
// Set stall condition
594+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_STALL;
595+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_STALL;
415596
}
416597
handled = true;
417598
}
@@ -458,6 +639,48 @@ static void usb_device_intr(void) {
458639
}
459640
}
460641
break;
642+
643+
case BM_REQ_RECIP_EP:
644+
switch (setup_pkt.s.bRequest) {
645+
case GET_STATUS:
646+
if (setup_pkt.s.wIndex == 1) {
647+
pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_RXCSRL1) & USB_RXCSRL1_STALL);
648+
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
649+
pbdrv_usb_setup_data_to_send_sz = 2;
650+
handled = true;
651+
} else if (setup_pkt.s.wIndex == 0x81) {
652+
pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_TXCSRL1) & USB_TXCSRL1_STALL);
653+
pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte;
654+
pbdrv_usb_setup_data_to_send_sz = 2;
655+
handled = true;
656+
}
657+
break;
658+
659+
case CLEAR_FEATURE:
660+
if (setup_pkt.s.wValue == 0) {
661+
if (setup_pkt.s.wIndex == 1) {
662+
HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL;
663+
handled = true;
664+
} else if (setup_pkt.s.wIndex == 0x81) {
665+
HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL;
666+
handled = true;
667+
}
668+
}
669+
break;
670+
671+
case SET_FEATURE:
672+
if (setup_pkt.s.wValue == 0) {
673+
if (setup_pkt.s.wIndex == 1) {
674+
HWREGB(USB0_BASE + USB_O_RXCSRL1) |= USB_RXCSRL1_STALL;
675+
handled = true;
676+
} else if (setup_pkt.s.wIndex == 0x81) {
677+
HWREGB(USB0_BASE + USB_O_TXCSRL1) |= USB_TXCSRL1_STALL;
678+
handled = true;
679+
}
680+
}
681+
break;
682+
}
683+
break;
461684
}
462685
}
463686

@@ -497,6 +720,61 @@ static void usb_device_intr(void) {
497720
}
498721
}
499722

723+
// EP1 interrupts, which only trigger on error conditions since we use DMA
724+
725+
if (intr_src & USBOTG_INTR_EP1_OUT) {
726+
// EP 1 OUT, host to device, rx
727+
uint8_t rxcsr = HWREGB(USB0_BASE + USB_O_RXCSRL1);
728+
729+
// Clear error bits
730+
rxcsr &= ~USB_RXCSRL1_STALLED;
731+
732+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = rxcsr;
733+
}
734+
735+
if (intr_src & USBOTG_INTR_EP1_IN) {
736+
// EP 1 IN, device to host, tx
737+
uint8_t txcsr = HWREGB(USB0_BASE + USB_O_TXCSRL1);
738+
739+
// Clear error bits
740+
txcsr &= ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_UNDRN | USB_TXCSRL1_FIFONE);
741+
742+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = txcsr;
743+
}
744+
745+
// Check for DMA completions
746+
uint32_t dma_q_pend_0 = HWREG(USB_0_OTGBASE + CPDMA_PEND_0_REGISTER);
747+
748+
if (dma_q_pend_0 & (1 << RX_COMPQ1)) {
749+
// DMA for EP 1 OUT is done
750+
751+
// Pop the descriptor from the queue
752+
uint32_t qctrld = HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + RX_COMPQ1 * 16);
753+
(void)qctrld;
754+
755+
// Signal the main loop that we have something
756+
usb_rx_is_ready = true;
757+
pbio_os_request_poll();
758+
}
759+
760+
if (dma_q_pend_0 & (1 << TX_COMPQ1)) {
761+
// DMA for EP 1 IN is done
762+
763+
// Pop the descriptor from the queue
764+
uint32_t qctrld = HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + TX_COMPQ1 * 16) & ~CPDMA_QUEUE_REGISTER_DESC_SIZE_MASK;
765+
766+
if (qctrld == (uint32_t)(&cppi_descriptors[CPPI_DESC_TX_RESPONSE])) {
767+
usb_tx_response_is_not_ready = false;
768+
pbio_os_request_poll();
769+
} else if (qctrld == (uint32_t)(&cppi_descriptors[CPPI_DESC_TX_STATUS])) {
770+
usb_tx_status_is_not_ready = false;
771+
pbio_os_request_poll();
772+
} else if (qctrld == (uint32_t)(&cppi_descriptors[CPPI_DESC_TX_STDOUT])) {
773+
usb_tx_stdout_is_not_ready = false;
774+
pbio_os_request_poll();
775+
}
776+
}
777+
500778
HWREG(USB_0_OTGBASE + USB_0_INTR_SRC_CLEAR) = intr_src;
501779
HWREG(USB_0_OTGBASE + USB_0_END_OF_INTR) = 0;
502780
}
@@ -506,6 +784,41 @@ static pbio_os_process_t pbdrv_usb_ev3_process;
506784
static pbio_error_t pbdrv_usb_ev3_process_thread(pbio_os_state_t *state, void *context) {
507785
PBIO_OS_ASYNC_BEGIN(state);
508786

787+
for (;;) {
788+
PBIO_OS_AWAIT_UNTIL(state, usb_rx_is_ready);
789+
790+
if (usb_rx_is_ready) {
791+
// This barrier prevents *subsequent* memory reads from being
792+
// speculatively moved *earlier*, outside the if statement
793+
// (which is technically allowed by the as-if rule).
794+
pbdrv_compiler_memory_barrier();
795+
796+
uint32_t usb_rx_sz = cppi_descriptors[CPPI_DESC_RX].word0.pktLength;
797+
798+
// TODO: Remove this echo test
799+
unsigned int i;
800+
for (i = 0; i < usb_rx_sz; i++) {
801+
ep1_tx_response_buf[i] = ep1_rx_buf[i] + 1;
802+
}
803+
for (; i < 512; i++) {
804+
ep1_tx_response_buf[i] = 0xaa;
805+
}
806+
807+
(void)ep1_tx_status_buf;
808+
(void)ep1_tx_stdout_buf;
809+
810+
unsigned int tx_sz = pbdrv_usb_is_usb_hs ? PYBRICKS_EP_PKT_SZ_HS : PYBRICKS_EP_PKT_SZ_FS;
811+
if (!usb_tx_response_is_not_ready) {
812+
usb_tx_response_is_not_ready = true;
813+
usb_setup_tx_dma_desc(CPPI_DESC_TX_RESPONSE, ep1_tx_response_buf, tx_sz);
814+
}
815+
816+
// Re-queue RX buffer after processing is complete
817+
usb_rx_is_ready = false;
818+
usb_setup_rx_dma_desc();
819+
}
820+
}
821+
509822
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
510823
}
511824

0 commit comments

Comments
 (0)