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>
@@ -218,6 +221,180 @@ static uint32_t pbdrv_usb_setup_misc_tx_byte;
218221// Whether the device is using USB high-speed mode or not
219222static bool pbdrv_usb_is_usb_hs ;
220223
224+ // Buffers, used for different logical flows on the data endpoint
225+ static uint8_t ep1_rx_buf [PYBRICKS_EP_PKT_SZ_HS ];
226+ static uint8_t ep1_tx_response_buf [PYBRICKS_EP_PKT_SZ_HS ];
227+ static uint8_t ep1_tx_status_buf [PYBRICKS_EP_PKT_SZ_HS ];
228+ static uint8_t ep1_tx_stdout_buf [PYBRICKS_EP_PKT_SZ_HS ];
229+
230+ // Buffer status flags
231+ static volatile bool usb_rx_is_ready ;
232+ static volatile bool usb_tx_response_is_not_ready ;
233+ static volatile bool usb_tx_status_is_not_ready ;
234+ static volatile bool usb_tx_stdout_is_not_ready ;
235+
236+ // CPPI DMA support code
237+
238+ // Descriptors must be aligned to a power of 2 greater than or equal to their size.
239+ // We are using a descriptor of 32 bytes which is also the required alignment.
240+ #define CPPI_DESCRIPTOR_ALIGN 32
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 (CPPI_DESCRIPTOR_ALIGN ))) usb_cppi_hpd_t ;
255+ _Static_assert (sizeof (usb_cppi_hpd_t ) <= CPPI_DESCRIPTOR_ALIGN );
256+
257+ // This goes into the lower bits of the queue CTRLD register
258+ #define CPPI_DESCRIPTOR_SIZE_BITS CPDMA_QUEUE_REGISTER_DESC_SIZE(usb_cppi_hpd_t)
259+
260+ // We only use a hardcoded descriptor for each logical flow,
261+ // rather than dynamically allocating them as needed
262+ enum {
263+ CPPI_DESC_RX ,
264+ CPPI_DESC_TX_RESPONSE ,
265+ CPPI_DESC_TX_STATUS ,
266+ CPPI_DESC_TX_STDOUT ,
267+ // the minimum number of descriptors we can allocate is 32,
268+ // even though we do not use nearly all of them
269+ CPPI_DESC_COUNT = 32 ,
270+ };
271+
272+ enum {
273+ // Documenting explicitly that we only use RX queue 0
274+ // (out of 16 total which are supported by the hardware)
275+ CPPI_RX_SUBMIT_QUEUE = 0 ,
276+ };
277+
278+ // CPPI memory
279+ static usb_cppi_hpd_t cppi_descriptors [CPPI_DESC_COUNT ];
280+ static uint32_t cppi_linking_ram [CPPI_DESC_COUNT ];
281+ // Tags a Host Packet Descriptor (i.e. the first descriptor
282+ // which contains full information about a packet, rather than
283+ // a Host Buffer Descriptor containing only an additional buffer).
284+ #define CPPI_HOST_PACKET_DESCRIPTOR_TYPE 0x10
285+
286+ // Fill in the CPPI DMA descriptor to receive a packet
287+ static void usb_setup_rx_dma_desc (void ) {
288+ cppi_descriptors [CPPI_DESC_RX ] = (usb_cppi_hpd_t ) {
289+ .word0 = {
290+ .hostPktType = CPPI_HOST_PACKET_DESCRIPTOR_TYPE ,
291+ },
292+ .word1 = {},
293+ .word2 = {
294+ .pktRetQueue = RX_COMPQ1 ,
295+ },
296+ .buf_len = PYBRICKS_EP_PKT_SZ_HS ,
297+ .buf_ptr = ep1_rx_buf ,
298+ .next_desc_ptr = 0 ,
299+ .orig_buf_len = PYBRICKS_EP_PKT_SZ_HS ,
300+ .orig_buf_ptr = ep1_rx_buf ,
301+ };
302+
303+ pbdrv_compiler_memory_barrier ();
304+
305+ HWREG (USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + CPPI_RX_SUBMIT_QUEUE * 16 ) =
306+ (uint32_t )(& cppi_descriptors [CPPI_DESC_RX ]) | CPPI_DESCRIPTOR_SIZE_BITS ;
307+ }
308+
309+
310+ // Fill in the CPPI DMA descriptor to send a packet
311+ static void usb_setup_tx_dma_desc (int tx_type , void * buf , uint32_t buf_len ) {
312+ cppi_descriptors [tx_type ] = (usb_cppi_hpd_t ) {
313+ .word0 = {
314+ .hostPktType = CPPI_HOST_PACKET_DESCRIPTOR_TYPE ,
315+ .pktLength = buf_len ,
316+ },
317+ .word1 = {
318+ .srcPrtNum = 1 , // port is EP1
319+ },
320+ .word2 = {
321+ .pktType = 5 , // USB packet type
322+ .pktRetQueue = TX_COMPQ1 ,
323+ },
324+ .buf_len = buf_len ,
325+ .buf_ptr = buf ,
326+ .next_desc_ptr = 0 ,
327+ .orig_buf_len = buf_len ,
328+ .orig_buf_ptr = buf ,
329+ };
330+
331+ pbdrv_compiler_memory_barrier ();
332+
333+ HWREG (USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + TX_SUBMITQ1 * 16 ) =
334+ (uint32_t )(& cppi_descriptors [tx_type ]) | CPPI_DESCRIPTOR_SIZE_BITS ;
335+ }
336+
337+ // Helper function to set up CPPI DMA upon USB reset
338+ static void usb_reset_cppi_dma (void ) {
339+ // Set up the FIFOs
340+ // We use a hardcoded address allocation as follows
341+ // @ 0 ==> EP0
342+ // @ 64 ==> EP1 IN (device to host, tx)
343+ // @ 64+512 ==> EP1 OUT (host to device, rx)
344+ HWREGB (USB0_BASE + USB_O_EPIDX ) = 1 ;
345+ if (pbdrv_usb_is_usb_hs ) {
346+ HWREGB (USB0_BASE + USB_O_TXFIFOSZ ) = USB_TXFIFOSZ_SIZE_512 ;
347+ HWREGB (USB0_BASE + USB_O_RXFIFOSZ ) = USB_RXFIFOSZ_SIZE_512 ;
348+ HWREGH (USB0_BASE + USB_O_TXMAXP1 ) = PYBRICKS_EP_PKT_SZ_HS ;
349+ HWREGH (USB0_BASE + USB_O_RXMAXP1 ) = PYBRICKS_EP_PKT_SZ_HS ;
350+ } else {
351+ HWREGB (USB0_BASE + USB_O_TXFIFOSZ ) = USB_TXFIFOSZ_SIZE_64 ;
352+ HWREGB (USB0_BASE + USB_O_RXFIFOSZ ) = USB_RXFIFOSZ_SIZE_64 ;
353+ HWREGH (USB0_BASE + USB_O_TXMAXP1 ) = PYBRICKS_EP_PKT_SZ_FS ;
354+ HWREGH (USB0_BASE + USB_O_RXMAXP1 ) = PYBRICKS_EP_PKT_SZ_FS ;
355+ }
356+ HWREGH (USB0_BASE + USB_O_TXFIFOADD ) = EP0_BUF_SZ / 8 ;
357+ HWREGH (USB0_BASE + USB_O_RXFIFOADD ) = (EP0_BUF_SZ + PYBRICKS_EP_PKT_SZ_HS ) / 8 ;
358+
359+ // Set up the TX fifo for DMA and a stall condition
360+ HWREGH (USB0_BASE + USB_O_TXCSRL1 ) = ((USB_TXCSRH1_AUTOSET | USB_TXCSRH1_MODE | USB_TXCSRH1_DMAEN | USB_TXCSRH1_DMAMOD ) << 8 ) | USB_TXCSRL1_STALL ;
361+ // Set up the RX fifo for DMA and a stall condition
362+ HWREGH (USB0_BASE + USB_O_RXCSRL1 ) = ((USB_RXCSRH1_AUTOCL | USB_RXCSRH1_DMAEN ) << 8 ) | USB_RXCSRL1_STALL ;
363+
364+ // Set up CPPI DMA
365+ HWREG (USB_0_OTGBASE + CPDMA_LRAM_0_BASE ) = (uint32_t )cppi_linking_ram ;
366+ HWREG (USB_0_OTGBASE + CPDMA_LRAM_0_SIZE ) = CPPI_DESC_COUNT ;
367+ HWREG (USB_0_OTGBASE + CPDMA_LRAM_1_BASE ) = 0 ;
368+
369+ HWREG (USB_0_OTGBASE + CPDMA_QUEUEMGR_REGION_0 ) = (uint32_t )cppi_descriptors ;
370+ // 32 descriptors of 32 bytes each
371+ HWREG (USB_0_OTGBASE + CPDMA_QUEUEMGR_REGION_0_CONTROL ) = 0 ;
372+
373+ // scheduler table: RX on 0, TX on 0
374+ HWREG (USB_0_OTGBASE + CPDMA_SCHED_TABLE_0 ) =
375+ ((CPDMA_SCHED_TX | 0 ) << CPDMA_SCHED_ENTRY_SHIFT ) |
376+ ((CPDMA_SCHED_RX | 0 ) << 0 );
377+ HWREG (USB_0_OTGBASE + CPDMA_SCHED_CONTROL_REG ) = (1 << SCHEDULER_ENABLE_SHFT ) | (2 - 1 );
378+
379+ // CPPI RX
380+ HWREG (USB_0_OTGBASE + CPDMA_RX_CHANNEL_REG_A ) =
381+ (CPPI_RX_SUBMIT_QUEUE << 0 ) |
382+ (CPPI_RX_SUBMIT_QUEUE << 16 );
383+ HWREG (USB_0_OTGBASE + CPDMA_RX_CHANNEL_REG_B ) =
384+ (CPPI_RX_SUBMIT_QUEUE << 0 ) |
385+ (CPPI_RX_SUBMIT_QUEUE << 16 );
386+ HWREG (USB_0_OTGBASE + CPDMA_RX_CHANNEL_CONFIG_REG ) =
387+ CPDMA_RX_GLOBAL_CHAN_CFG_ENABLE |
388+ CPDMA_RX_GLOBAL_CHAN_CFG_ERR_RETRY | // starvation = retry
389+ CPDMA_RX_GLOBAL_CHAN_CFG_DESC_TY | // "host" descriptors (the only valid type)
390+ RX_COMPQ1 ;
391+
392+ // CPPI TX
393+ HWREG (USB_0_OTGBASE + CPDMA_TX_CHANNEL_CONFIG_REG ) =
394+ CPDMA_TX_GLOBAL_CHAN_CFG_ENABLE |
395+ TX_COMPQ1 ;
396+ }
397+
221398// Helper function for dividing up buffers for EP0 and feeding the FIFO
222399static void usb_setup_send_chunk (void ) {
223400 unsigned int this_chunk_sz = pbdrv_usb_setup_data_to_send_sz ;
@@ -349,7 +526,11 @@ static void usb_device_intr(void) {
349526 pbdrv_usb_is_usb_hs = false;
350527 }
351528
352- // TODO: More tasks in the future
529+ // Set up all the CPPI DMA registers
530+ usb_reset_cppi_dma ();
531+
532+ // queue RX descriptor
533+ usb_setup_rx_dma_desc ();
353534 }
354535
355536 if (intr_src & USBOTG_INTR_EP0 ) {
@@ -399,11 +580,15 @@ static void usb_device_intr(void) {
399580 if (pbdrv_usb_config == 1 ) {
400581 // configuring
401582
402- // TODO: Handle configuring
583+ // Reset data toggle, clear stall, flush fifo
584+ HWREGB (USB0_BASE + USB_O_TXCSRL1 ) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH ;
585+ HWREGB (USB0_BASE + USB_O_RXCSRL1 ) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH ;
403586 } else {
404587 // deconfiguring
405588
406- // TODO: Handle deconfiguring
589+ // Set stall condition
590+ HWREGB (USB0_BASE + USB_O_TXCSRL1 ) = USB_TXCSRL1_STALL ;
591+ HWREGB (USB0_BASE + USB_O_RXCSRL1 ) = USB_RXCSRL1_STALL ;
407592 }
408593 handled = true;
409594 }
@@ -450,6 +635,48 @@ static void usb_device_intr(void) {
450635 }
451636 }
452637 break ;
638+
639+ case BM_REQ_RECIP_EP :
640+ switch (setup_pkt .s .bRequest ) {
641+ case GET_STATUS :
642+ if (setup_pkt .s .wIndex == 1 ) {
643+ pbdrv_usb_setup_misc_tx_byte = !!(HWREGB (USB0_BASE + USB_O_RXCSRL1 ) & USB_RXCSRL1_STALL );
644+ pbdrv_usb_setup_data_to_send = & pbdrv_usb_setup_misc_tx_byte ;
645+ pbdrv_usb_setup_data_to_send_sz = 2 ;
646+ handled = true;
647+ } else if (setup_pkt .s .wIndex == 0x81 ) {
648+ pbdrv_usb_setup_misc_tx_byte = !!(HWREGB (USB0_BASE + USB_O_TXCSRL1 ) & USB_TXCSRL1_STALL );
649+ pbdrv_usb_setup_data_to_send = & pbdrv_usb_setup_misc_tx_byte ;
650+ pbdrv_usb_setup_data_to_send_sz = 2 ;
651+ handled = true;
652+ }
653+ break ;
654+
655+ case CLEAR_FEATURE :
656+ if (setup_pkt .s .wValue == 0 ) {
657+ if (setup_pkt .s .wIndex == 1 ) {
658+ HWREGB (USB0_BASE + USB_O_RXCSRL1 ) &= ~USB_RXCSRL1_STALL ;
659+ handled = true;
660+ } else if (setup_pkt .s .wIndex == 0x81 ) {
661+ HWREGB (USB0_BASE + USB_O_TXCSRL1 ) &= ~USB_TXCSRL1_STALL ;
662+ handled = true;
663+ }
664+ }
665+ break ;
666+
667+ case SET_FEATURE :
668+ if (setup_pkt .s .wValue == 0 ) {
669+ if (setup_pkt .s .wIndex == 1 ) {
670+ HWREGB (USB0_BASE + USB_O_RXCSRL1 ) |= USB_RXCSRL1_STALL ;
671+ handled = true;
672+ } else if (setup_pkt .s .wIndex == 0x81 ) {
673+ HWREGB (USB0_BASE + USB_O_TXCSRL1 ) |= USB_TXCSRL1_STALL ;
674+ handled = true;
675+ }
676+ }
677+ break ;
678+ }
679+ break ;
453680 }
454681 }
455682
@@ -489,6 +716,61 @@ static void usb_device_intr(void) {
489716 }
490717 }
491718
719+ // EP1 interrupts, which only trigger on error conditions since we use DMA
720+
721+ if (intr_src & USBOTG_INTR_EP1_OUT ) {
722+ // EP 1 OUT, host to device, rx
723+ uint8_t rxcsr = HWREGB (USB0_BASE + USB_O_RXCSRL1 );
724+
725+ // Clear error bits
726+ rxcsr &= ~USB_RXCSRL1_STALLED ;
727+
728+ HWREGB (USB0_BASE + USB_O_RXCSRL1 ) = rxcsr ;
729+ }
730+
731+ if (intr_src & USBOTG_INTR_EP1_IN ) {
732+ // EP 1 IN, device to host, tx
733+ uint8_t txcsr = HWREGB (USB0_BASE + USB_O_TXCSRL1 );
734+
735+ // Clear error bits
736+ txcsr &= ~(USB_TXCSRL1_STALLED | USB_TXCSRL1_UNDRN | USB_TXCSRL1_FIFONE );
737+
738+ HWREGB (USB0_BASE + USB_O_TXCSRL1 ) = txcsr ;
739+ }
740+
741+ // Check for DMA completions
742+ uint32_t dma_q_pend_0 = HWREG (USB_0_OTGBASE + CPDMA_PEND_0_REGISTER );
743+
744+ if (dma_q_pend_0 & (1 << RX_COMPQ1 )) {
745+ // DMA for EP 1 OUT is done
746+
747+ // Pop the descriptor from the queue
748+ uint32_t qctrld = HWREG (USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + RX_COMPQ1 * 16 );
749+ (void )qctrld ;
750+
751+ // Signal the main loop that we have something
752+ usb_rx_is_ready = true;
753+ pbio_os_request_poll ();
754+ }
755+
756+ if (dma_q_pend_0 & (1 << TX_COMPQ1 )) {
757+ // DMA for EP 1 IN is done
758+
759+ // Pop the descriptor from the queue
760+ uint32_t qctrld = HWREG (USB_0_OTGBASE + CPDMA_QUEUE_REGISTER_D + TX_COMPQ1 * 16 ) & ~CPDMA_QUEUE_REGISTER_DESC_SIZE_MASK ;
761+
762+ if (qctrld == (uint32_t )(& cppi_descriptors [CPPI_DESC_TX_RESPONSE ])) {
763+ usb_tx_response_is_not_ready = false;
764+ pbio_os_request_poll ();
765+ } else if (qctrld == (uint32_t )(& cppi_descriptors [CPPI_DESC_TX_STATUS ])) {
766+ usb_tx_status_is_not_ready = false;
767+ pbio_os_request_poll ();
768+ } else if (qctrld == (uint32_t )(& cppi_descriptors [CPPI_DESC_TX_STDOUT ])) {
769+ usb_tx_stdout_is_not_ready = false;
770+ pbio_os_request_poll ();
771+ }
772+ }
773+
492774 HWREG (USB_0_OTGBASE + USB_0_INTR_SRC_CLEAR ) = intr_src ;
493775 HWREG (USB_0_OTGBASE + USB_0_END_OF_INTR ) = 0 ;
494776}
@@ -498,6 +780,41 @@ static pbio_os_process_t pbdrv_usb_ev3_process;
498780static pbio_error_t pbdrv_usb_ev3_process_thread (pbio_os_state_t * state , void * context ) {
499781 PBIO_OS_ASYNC_BEGIN (state );
500782
783+ for (;;) {
784+ PBIO_OS_AWAIT_UNTIL (state , usb_rx_is_ready );
785+
786+ if (usb_rx_is_ready ) {
787+ // This barrier prevents *subsequent* memory reads from being
788+ // speculatively moved *earlier*, outside the if statement
789+ // (which is technically allowed by the as-if rule).
790+ pbdrv_compiler_memory_barrier ();
791+
792+ uint32_t usb_rx_sz = cppi_descriptors [CPPI_DESC_RX ].word0 .pktLength ;
793+
794+ // TODO: Remove this echo test
795+ unsigned int i ;
796+ for (i = 0 ; i < usb_rx_sz ; i ++ ) {
797+ ep1_tx_response_buf [i ] = ep1_rx_buf [i ] + 1 ;
798+ }
799+ for (; i < 512 ; i ++ ) {
800+ ep1_tx_response_buf [i ] = 0xaa ;
801+ }
802+
803+ (void )ep1_tx_status_buf ;
804+ (void )ep1_tx_stdout_buf ;
805+
806+ unsigned int tx_sz = pbdrv_usb_is_usb_hs ? PYBRICKS_EP_PKT_SZ_HS : PYBRICKS_EP_PKT_SZ_FS ;
807+ if (!usb_tx_response_is_not_ready ) {
808+ usb_tx_response_is_not_ready = true;
809+ usb_setup_tx_dma_desc (CPPI_DESC_TX_RESPONSE , ep1_tx_response_buf , tx_sz );
810+ }
811+
812+ // Re-queue RX buffer after processing is complete
813+ usb_rx_is_ready = false;
814+ usb_setup_rx_dma_desc ();
815+ }
816+ }
817+
501818 PBIO_OS_ASYNC_END (PBIO_SUCCESS );
502819}
503820
0 commit comments