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>
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
226229static 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
229341static 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;
469729static 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