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>
@@ -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
218221static 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
221394static 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;
506784static 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