]> Pileus Git - ~andy/csm213a-hw/blob - hw2/serial_dma.c
Add dma and more tests
[~andy/csm213a-hw] / hw2 / serial_dma.c
1 #include <MKL46Z4.h>\r
2 \r
3 #include <stdint.h>\r
4 #include <stdarg.h>\r
5 #include <stdio.h>\r
6 #include <string.h>\r
7 \r
8 #include "serial_dma.h"\r
9 \r
10 /* Defines */\r
11 #define SDMA_NUM  2\r
12 #define SDMA_LEN  1024\r
13 \r
14 /* Port structure */\r
15 struct sdma_t {\r
16         /* DMA channel */\r
17         struct {\r
18                 uint32_t sar; // offset 0x00, Source Address Register\r
19                 uint32_t dar; // offset 0x04, Destination Address Register\r
20                 uint32_t dsr; // offset 0x08, DMA Status Register / Byte Count Register\r
21                 uint32_t dcr; // offset 0x0C, DMA Control Register\r
22         } *dma;\r
23 \r
24         /* DMA mux */\r
25         struct {\r
26                 uint8_t  cfg; // offset 0x00, Channel Configuration register\r
27         } *mux;\r
28 \r
29         /* Data buffering */\r
30         int     index;\r
31         int     length[SDMA_NUM];\r
32         uint8_t queue[SDMA_NUM][SDMA_LEN];\r
33 \r
34         /* Error logging */\r
35         int     stuck;\r
36         int     full;\r
37 };\r
38 \r
39 /* DMA Request Sources */\r
40 static int sdma_req_rx[] = {\r
41         [SDMA_UART0] 2,\r
42         [SDMA_UART1] 4,\r
43         [SDMA_UART2] 6,\r
44 };\r
45 static int sdma_req_tx[] = {\r
46         [SDMA_UART0] 3,\r
47         [SDMA_UART1] 5,\r
48         [SDMA_UART2] 7,\r
49 };\r
50 \r
51 /* Port data */\r
52 static sdma_t sdma_ports[SDMA_NUM_UART];\r
53 \r
54 /* DMA Functions */\r
55 sdma_t *sdma_open(sdma_uart_t uart, sdma_dma_t dma)\r
56 {\r
57         int rxreq = sdma_req_rx[uart]; (void)rxreq;\r
58         int txreq = sdma_req_tx[uart]; (void)txreq;\r
59 \r
60         // Setup port\r
61         sdma_t *port = &sdma_ports[uart];\r
62 \r
63         port->dma = (void*)&DMA0->DMA[dma];\r
64         port->mux = (void*)&DMAMUX0->CHCFG[dma];\r
65 \r
66         // Enable DMA Cock\r
67         SIM->SCGC6         |= SIM_SCGC6_DMAMUX_MASK;\r
68         SIM->SCGC7         |= SIM_SCGC7_DMA_MASK;\r
69 \r
70         // Reset channel\r
71         port->dma->dsr      = DMA_DSR_BCR_DONE_MASK;\r
72 \r
73         // Configure DMA transfer\r
74         port->dma->dar      = (uint32_t)&UART1->D;\r
75         port->dma->dcr      = DMA_DCR_CS_MASK   |\r
76                               DMA_DCR_SINC_MASK |\r
77                               DMA_DCR_SSIZE(1)  |\r
78                               DMA_DCR_DSIZE(1)  |\r
79                               DMA_DCR_D_REQ_MASK;\r
80 \r
81         // Configure DMA Mux\r
82         port->mux->cfg      = DMAMUX_CHCFG_SOURCE(txreq) |\r
83                               DMAMUX_CHCFG_ENBL_MASK;\r
84 \r
85         // Configure UART for DMA Channel 0\r
86         switch (uart) {\r
87                 case SDMA_UART0:\r
88                         UART0->C5 |= UART0_C5_TDMAE_MASK;\r
89                         break;\r
90 \r
91                 case SDMA_UART1:\r
92                         UART1->C2 = UART_C2_TIE_MASK\r
93                                   | UART_C2_TE_MASK\r
94                                   | UART_C2_RE_MASK;\r
95                         UART1->C4 = UART_C4_TDMAS_MASK;\r
96                         break;\r
97 \r
98                 case SDMA_UART2:\r
99                         UART2->C2 = UART_C2_TIE_MASK\r
100                                   | UART_C2_TE_MASK\r
101                                   | UART_C2_RE_MASK;\r
102                         UART2->C4 = UART_C4_TDMAS_MASK;\r
103                         break;\r
104         }\r
105         return port;\r
106 }\r
107 \r
108 /* Write binary data out the DMA output queue */\r
109 void sdma_write(sdma_t *port, void *data, int len)\r
110 {\r
111         if (port->length[port->index] + len > SDMA_LEN) {\r
112                 port->full++;\r
113         } else {\r
114                 int   pos = port->length[port->index];\r
115                 void *dst = &port->queue[port->index][pos];\r
116                 memcpy(dst, data, len);\r
117                 port->length[port->index] += len;\r
118         }\r
119 }\r
120 \r
121 /* Write ASCII data to the output queue */\r
122 void sdma_vprintf(sdma_t *port, const char *fmt, va_list ap)\r
123 {\r
124         int   pos = port->length[port->index];\r
125         void *dst = &port->queue[port->index][pos];\r
126         port->length[port->index] +=\r
127                 vsnprintf((char*)dst, SDMA_LEN-pos, fmt, ap);\r
128 }\r
129 \r
130 void sdma_printf(sdma_t *port, const char *fmt, ...)\r
131 {\r
132         va_list ap;\r
133         va_start(ap, fmt);\r
134         sdma_vprintf(port, fmt, ap);\r
135         va_end(ap);\r
136 }\r
137 \r
138 /* Trigger DMA transmit of the current output queue\r
139  * and swap buffers so we can write into unused space */\r
140 void sdma_flush(sdma_t *port)\r
141 {\r
142         if (port->length[port->index] == 0)\r
143                 return;\r
144 \r
145         // Wait for transmit complete\r
146         while (port->dma->dsr & DMA_DSR_BCR_BCR_MASK)\r
147                 port->stuck++;\r
148 \r
149         // Reset channel\r
150         port->dma->dsr  = DMA_DSR_BCR_DONE_MASK;\r
151 \r
152         // Set source address and length\r
153         port->dma->sar  = (uint32_t)&port->queue[port->index];\r
154         port->dma->dsr  = DMA_DSR_BCR_BCR(port->length[port->index]);\r
155 \r
156         // Enable DMA transmit\r
157         port->dma->dcr |= DMA_DCR_ERQ_MASK;\r
158 \r
159         // Swap buffers\r
160         port->length[port->index] = 0;\r
161         port->index = (port->index + 1) % SDMA_NUM;\r
162 }\r