#include #include #include #include #include #include "serial_dma.h" /* Defines */ #define SDMA_NUM 2 #define SDMA_LEN 1024 /* Port structure */ struct sdma_t { /* DMA channel */ struct { uint32_t sar; // offset 0x00, Source Address Register uint32_t dar; // offset 0x04, Destination Address Register uint32_t dsr; // offset 0x08, DMA Status Register / Byte Count Register uint32_t dcr; // offset 0x0C, DMA Control Register } *dma; /* DMA mux */ struct { uint8_t cfg; // offset 0x00, Channel Configuration register } *mux; /* Data buffering */ int index; int length[SDMA_NUM]; uint8_t queue[SDMA_NUM][SDMA_LEN]; /* Error logging */ int stuck; int full; }; /* DMA Request Sources */ static int sdma_req_rx[] = { [SDMA_UART0] 2, [SDMA_UART1] 4, [SDMA_UART2] 6, }; static int sdma_req_tx[] = { [SDMA_UART0] 3, [SDMA_UART1] 5, [SDMA_UART2] 7, }; /* Port data */ static sdma_t sdma_ports[SDMA_NUM_UART]; /* DMA Functions */ sdma_t *sdma_open(sdma_uart_t uart, sdma_dma_t dma) { int rxreq = sdma_req_rx[uart]; (void)rxreq; int txreq = sdma_req_tx[uart]; (void)txreq; // Setup port sdma_t *port = &sdma_ports[uart]; port->dma = (void*)&DMA0->DMA[dma]; port->mux = (void*)&DMAMUX0->CHCFG[dma]; // Enable DMA Cock SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK; SIM->SCGC7 |= SIM_SCGC7_DMA_MASK; // Reset channel port->dma->dsr = DMA_DSR_BCR_DONE_MASK; // Configure DMA transfer port->dma->dar = (uint32_t)&UART1->D; port->dma->dcr = DMA_DCR_CS_MASK | DMA_DCR_SINC_MASK | DMA_DCR_SSIZE(1) | DMA_DCR_DSIZE(1) | DMA_DCR_D_REQ_MASK; // Configure DMA Mux port->mux->cfg = DMAMUX_CHCFG_SOURCE(txreq) | DMAMUX_CHCFG_ENBL_MASK; // Configure UART for DMA Channel 0 switch (uart) { case SDMA_UART0: UART0->C5 |= UART0_C5_TDMAE_MASK; break; case SDMA_UART1: UART1->C2 = UART_C2_TIE_MASK | UART_C2_TE_MASK | UART_C2_RE_MASK; UART1->C4 = UART_C4_TDMAS_MASK; break; case SDMA_UART2: UART2->C2 = UART_C2_TIE_MASK | UART_C2_TE_MASK | UART_C2_RE_MASK; UART2->C4 = UART_C4_TDMAS_MASK; break; } return port; } /* Write binary data out the DMA output queue */ void sdma_write(sdma_t *port, void *data, int len) { if (port->length[port->index] + len > SDMA_LEN) { port->full++; } else { int pos = port->length[port->index]; void *dst = &port->queue[port->index][pos]; memcpy(dst, data, len); port->length[port->index] += len; } } /* Write ASCII data to the output queue */ void sdma_vprintf(sdma_t *port, const char *fmt, va_list ap) { int pos = port->length[port->index]; void *dst = &port->queue[port->index][pos]; port->length[port->index] += vsnprintf((char*)dst, SDMA_LEN-pos, fmt, ap); } void sdma_printf(sdma_t *port, const char *fmt, ...) { va_list ap; va_start(ap, fmt); sdma_vprintf(port, fmt, ap); va_end(ap); } /* Trigger DMA transmit of the current output queue * and swap buffers so we can write into unused space */ void sdma_flush(sdma_t *port) { if (port->length[port->index] == 0) return; // Wait for transmit complete while (port->dma->dsr & DMA_DSR_BCR_BCR_MASK) port->stuck++; // Reset channel port->dma->dsr = DMA_DSR_BCR_DONE_MASK; // Set source address and length port->dma->sar = (uint32_t)&port->queue[port->index]; port->dma->dsr = DMA_DSR_BCR_BCR(port->length[port->index]); // Enable DMA transmit port->dma->dcr |= DMA_DCR_ERQ_MASK; // Swap buffers port->length[port->index] = 0; port->index = (port->index + 1) % SDMA_NUM; }