Skip to content

Commit e245b1f

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 7ba302f commit e245b1f

File tree

1 file changed

+283
-3
lines changed

1 file changed

+283
-3
lines changed

lib/pbio/drv/usb/usb_ev3.c

Lines changed: 283 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "pbdrvconfig.h"
2020

2121
#include <tiam1808/armv5/am1808/interrupt.h>
22+
#include <tiam1808/cppi41dma.h>
2223
#include <tiam1808/hw/hw_types.h>
2324
#include <tiam1808/hw/hw_usbOtg_AM1808.h>
2425
#include <tiam1808/hw/hw_syscfg0_AM1808.h>
@@ -224,6 +225,105 @@ static uint32_t setup_misc_tx_byte;
224225
// Whether the device is using USB high-speed mode or not
225226
static int is_usb_hs;
226227

228+
// Buffers, used for different logical flows on the data endpoint
229+
static uint8_t ep1_rx_buf[PYBRICKS_EP_PKT_SZ_HS];
230+
static uint8_t ep1_tx_response_buf[PYBRICKS_EP_PKT_SZ_HS];
231+
static uint8_t ep1_tx_status_buf[PYBRICKS_EP_PKT_SZ_HS];
232+
static uint8_t ep1_tx_stdout_buf[PYBRICKS_EP_PKT_SZ_HS];
233+
234+
// Buffer status flags
235+
static volatile int usb_rx_is_ready;
236+
static volatile int usb_tx_response_is_not_ready;
237+
static volatile int usb_tx_status_is_not_ready;
238+
static volatile int usb_tx_stdout_is_not_ready;
239+
240+
// CPPI DMA support code
241+
242+
// Host Packet Descriptor
243+
// The TI support library has hardcoded assumptions about the layout of these structures,
244+
// so we declare it ourselves here in order to control it as we wish.
245+
typedef struct {
246+
hPDWord0 word0;
247+
hPDWord1 word1;
248+
hPDWord2 word2;
249+
uint32_t buf_len;
250+
void *buf_ptr;
251+
void *next_desc_ptr;
252+
uint32_t orig_buf_len;
253+
void *orig_buf_ptr;
254+
} __attribute__((aligned(32))) usb_cppi_hpd;
255+
256+
// We only use a hardcoded descriptor for each logical flow,
257+
// rather than dynamically allocating them as needed
258+
enum {
259+
CPPI_DESC_RX,
260+
CPPI_DESC_TX_RESPONSE,
261+
CPPI_DESC_TX_STATUS,
262+
CPPI_DESC_TX_STDOUT,
263+
// the minimum number of descriptors we can allocate is 32,
264+
// even though we do not use nearly all of them
265+
CPPI_DESC_COUNT = 32,
266+
};
267+
268+
enum {
269+
CPPI_RX_SUBMIT_QUEUE = 0,
270+
};
271+
272+
// CPPI memory
273+
usb_cppi_hpd cppi_descriptors[CPPI_DESC_COUNT];
274+
uint32_t cppi_linking_ram[CPPI_DESC_COUNT];
275+
276+
// Fill in the CPPI DMA descriptor to receive a packet
277+
static void usb_setup_rx_dma_desc() {
278+
cppi_descriptors[CPPI_DESC_RX] = (usb_cppi_hpd) {
279+
.word0 = {
280+
.hostPktType = 0x10,
281+
},
282+
.word1 = {},
283+
.word2 = {
284+
.pktRetQueue = RX_COMPQ1,
285+
},
286+
.buf_len = PYBRICKS_EP_PKT_SZ_HS,
287+
.buf_ptr = ep1_rx_buf,
288+
.next_desc_ptr = 0,
289+
.orig_buf_len = PYBRICKS_EP_PKT_SZ_HS,
290+
.orig_buf_ptr = ep1_rx_buf,
291+
};
292+
293+
__asm__ volatile ("" ::: "memory");
294+
295+
HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + CPPI_RX_SUBMIT_QUEUE * 16) =
296+
(uint32_t)(&cppi_descriptors[CPPI_DESC_RX]) | 2;
297+
}
298+
299+
300+
// Fill in the CPPI DMA descriptor to send a packet
301+
static void usb_setup_tx_dma_desc(int tx_type, void *buf, uint32_t buf_len) {
302+
cppi_descriptors[tx_type] = (usb_cppi_hpd) {
303+
.word0 = {
304+
.hostPktType = 0x10,
305+
.pktLength = buf_len,
306+
},
307+
.word1 = {
308+
.srcPrtNum = 1, // port is EP1
309+
},
310+
.word2 = {
311+
.pktType = 5, // USB packet type
312+
.pktRetQueue = TX_COMPQ1,
313+
},
314+
.buf_len = buf_len,
315+
.buf_ptr = buf,
316+
.next_desc_ptr = 0,
317+
.orig_buf_len = buf_len,
318+
.orig_buf_ptr = buf,
319+
};
320+
321+
__asm__ volatile ("" ::: "memory");
322+
323+
HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + TX_SUBMITQ1 * 16) =
324+
(uint32_t)(&cppi_descriptors[tx_type]) | 2;
325+
}
326+
227327
// Helper function for dividing up buffers for EP0 and feeding the FIFO
228328
static void usb_setup_send_chunk() {
229329
unsigned int this_chunk_sz = setup_data_to_send_sz;
@@ -265,7 +365,64 @@ static void usb_device_intr() {
265365
is_usb_hs = 0;
266366
}
267367

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

271428
if (intr_src & (1 << 0)) {
@@ -312,11 +469,15 @@ static void usb_device_intr() {
312469
if (usb_config == 1) {
313470
// configuring
314471

315-
// TODO: Handle configuring
472+
// Reset data toggle, clear stall, flush fifo
473+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH;
474+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH;
316475
} else {
317476
// deconfiguring
318477

319-
// TODO: Handle deconfiguring
478+
// Set stall condition
479+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_STALL;
480+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_STALL;
320481
}
321482
handled = 1;
322483
}
@@ -421,6 +582,38 @@ static void usb_device_intr() {
421582
handled = 1;
422583
}
423584
}
585+
} else if (((setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) == BM_REQ_TYPE_STANDARD) &&
586+
((setup_pkt.s.bmRequestType & BM_REQ_RECIP_MASK) == BM_REQ_RECIP_EP)) {
587+
588+
if (setup_pkt.s.bRequest == GET_STATUS) {
589+
if (setup_pkt.s.wIndex == 1) {
590+
setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_RXCSRL1) & USB_RXCSRL1_STALL);
591+
setup_data_to_send = &setup_misc_tx_byte;
592+
setup_data_to_send_sz = 2;
593+
handled = 1;
594+
} else if (setup_pkt.s.wIndex == 0x81) {
595+
setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_TXCSRL1) & USB_TXCSRL1_STALL);
596+
setup_data_to_send = &setup_misc_tx_byte;
597+
setup_data_to_send_sz = 2;
598+
handled = 1;
599+
}
600+
} else if (setup_pkt.s.bRequest == CLEAR_FEATURE && setup_pkt.s.wValue == 0) {
601+
if (setup_pkt.s.wIndex == 1) {
602+
HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL;
603+
handled = 1;
604+
} else if (setup_pkt.s.wIndex == 0x81) {
605+
HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL;
606+
handled = 1;
607+
}
608+
} else if (setup_pkt.s.bRequest == SET_FEATURE && setup_pkt.s.wValue == 0) {
609+
if (setup_pkt.s.wIndex == 1) {
610+
HWREGB(USB0_BASE + USB_O_RXCSRL1) |= USB_RXCSRL1_STALL;
611+
handled = 1;
612+
} else if (setup_pkt.s.wIndex == 0x81) {
613+
HWREGB(USB0_BASE + USB_O_TXCSRL1) |= USB_TXCSRL1_STALL;
614+
handled = 1;
615+
}
616+
}
424617
}
425618

426619
if (!handled) {
@@ -459,6 +652,61 @@ static void usb_device_intr() {
459652
}
460653
}
461654

655+
// EP1 interrupts, which only trigger on error conditions since we use DMA
656+
657+
if (intr_src & (1 << 9)) {
658+
// EP 1 OUT, host to device, rx
659+
uint8_t rxcsr = HWREGB(USB0_BASE + USB_O_RXCSRL1);
660+
661+
// Clear error bits
662+
rxcsr &= ~USB_RXCSRL1_STALLED;
663+
664+
HWREGB(USB0_BASE + USB_O_RXCSRL1) = rxcsr;
665+
}
666+
667+
if (intr_src & (1 << 1)) {
668+
// EP 1 IN, device to host, tx
669+
uint8_t txcsr = HWREGB(USB0_BASE + USB_O_TXCSRL1);
670+
671+
// Clear error bits
672+
txcsr &= ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_UNDRN | USB_TXCSRL1_FIFONE);
673+
674+
HWREGB(USB0_BASE + USB_O_TXCSRL1) = txcsr;
675+
}
676+
677+
// Check for DMA completions
678+
uint32_t dma_q_pend_0 = HWREG(USB_0_OTGBASE + CPDMA_PEND_0_REGISTER);
679+
680+
if (dma_q_pend_0 & (1 << RX_COMPQ1)) {
681+
// DMA for EP 1 OUT is done
682+
683+
// Pop the descriptor from the queue
684+
uint32_t qctrld = HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + RX_COMPQ1 * 16);
685+
(void)qctrld;
686+
687+
// Signal the main loop that we have something
688+
usb_rx_is_ready = 1;
689+
pbio_os_request_poll();
690+
}
691+
692+
if (dma_q_pend_0 & (1 << TX_COMPQ1)) {
693+
// DMA for EP 1 IN is done
694+
695+
// Pop the descriptor from the queue
696+
uint32_t qctrld = HWREG(USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + TX_COMPQ1 * 16) & ~0x1f;
697+
698+
if (qctrld == (uint32_t)(&cppi_descriptors[CPPI_DESC_TX_RESPONSE])) {
699+
usb_tx_response_is_not_ready = 0;
700+
pbio_os_request_poll();
701+
} else if (qctrld == (uint32_t)(&cppi_descriptors[CPPI_DESC_TX_STATUS])) {
702+
usb_tx_status_is_not_ready = 0;
703+
pbio_os_request_poll();
704+
} else if (qctrld == (uint32_t)(&cppi_descriptors[CPPI_DESC_TX_STDOUT])) {
705+
usb_tx_stdout_is_not_ready = 0;
706+
pbio_os_request_poll();
707+
}
708+
}
709+
462710
HWREG(USB_0_OTGBASE + USB_0_INTR_SRC_CLEAR) = intr_src;
463711
HWREG(USB_0_OTGBASE + USB_0_END_OF_INTR) = 0;
464712
}
@@ -468,6 +716,38 @@ static pbio_os_process_t pbdrv_usb_ev3_process;
468716
pbio_error_t pbdrv_usb_ev3_process_thread(pbio_os_state_t *state, void *context) {
469717
PBIO_OS_ASYNC_BEGIN(state);
470718

719+
for (;;) {
720+
PBIO_OS_AWAIT_UNTIL(state, usb_rx_is_ready);
721+
722+
if (usb_rx_is_ready) {
723+
__asm__ volatile ("" ::: "memory");
724+
725+
uint32_t usb_rx_sz = cppi_descriptors[CPPI_DESC_RX].word0.pktLength;
726+
727+
// TODO: Remove this echo test
728+
unsigned int i;
729+
for (i = 0; i < usb_rx_sz; i++) {
730+
ep1_tx_response_buf[i] = ep1_rx_buf[i] + 1;
731+
}
732+
for (; i < 512; i++) {
733+
ep1_tx_response_buf[i] = 0xaa;
734+
}
735+
736+
(void)ep1_tx_status_buf;
737+
(void)ep1_tx_stdout_buf;
738+
739+
unsigned int tx_sz = is_usb_hs ? PYBRICKS_EP_PKT_SZ_HS : PYBRICKS_EP_PKT_SZ_FS;
740+
if (!usb_tx_response_is_not_ready) {
741+
usb_tx_response_is_not_ready = 1;
742+
usb_setup_tx_dma_desc(CPPI_DESC_TX_RESPONSE, ep1_tx_response_buf, tx_sz);
743+
}
744+
745+
// Re-queue RX buffer after processing is complete
746+
usb_rx_is_ready = 0;
747+
usb_setup_rx_dma_desc();
748+
}
749+
}
750+
471751
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
472752
}
473753

0 commit comments

Comments
 (0)