]> Pileus Git - ~andy/csm213a-hw/blob - hw2/serial_dma.c
26987769119b68a7419a097e839112b6426f41c8
[~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 enum {\r
15         SDMA_REQ_U0RX = 2,\r
16         SDMA_REQ_U0TX = 3,\r
17         SDMA_REQ_U1RX = 4,\r
18         SDMA_REQ_U1TX = 5,\r
19         SDMA_REQ_U2RX = 6,\r
20         SDMA_REQ_U2TX = 7,\r
21         SDMA_REQ_PTA  = 49,\r
22         SDMA_REQ_PTC  = 51,\r
23         SDMA_REQ_PTD  = 52,\r
24         SDMA_REQ_ON0  = 60,\r
25         SDMA_REQ_ON1  = 61,\r
26         SDMA_REQ_ON2  = 62,\r
27         SDMA_REQ_ON3  = 63,\r
28 };\r
29 \r
30 /* Port structure */\r
31 struct sdma_t {\r
32         /* DMA channel */\r
33         struct {\r
34                 uint32_t sar; // offset 0x00, Source Address Register\r
35                 uint32_t dar; // offset 0x04, Destination Address Register\r
36                 uint32_t dsr; // offset 0x08, DMA Status Register / Byte Count Register\r
37                 uint32_t dcr; // offset 0x0C, DMA Control Register\r
38         } *dma_xmt, *dma_rcv;\r
39 \r
40         /* DMA mux */\r
41         struct {\r
42                 uint8_t  cfg; // offset 0x00, Channel Configuration register\r
43         } *mux_xmt, *mux_rcv;\r
44 \r
45         /* Pin names */\r
46         struct {\r
47                 uint32_t pcr; // offset 0x00, Pin Control register\r
48         } *pin_xmt, *pin_rcv;\r
49 \r
50         /* UART */\r
51         struct {\r
52                 uint8_t  bdh;    // offset 0x00, Baud Rate Register High\r
53                 uint8_t  bdl;    // offset 0x01, Baud Rate Register Low\r
54                 uint8_t  c1;     // offset 0x02, Control Register 1\r
55                 uint8_t  c2;     // offset 0x03, Control Register 2\r
56                 uint8_t  s1;     // offset 0x04, Status Register 1\r
57                 uint8_t  s2;     // offset 0x05, Status Register 2\r
58                 uint8_t  c3;     // offset 0x06, Control Register 3\r
59                 uint8_t  d;      // offset 0x07, Data Register\r
60         } *uart;\r
61 \r
62         /* Data buffering */\r
63         int      index;\r
64         int      length[SDMA_NUM];\r
65         uint8_t  queue[SDMA_NUM][SDMA_LEN];\r
66 \r
67         /* Timestamping */\r
68         uint32_t time_xmt[2];\r
69         uint32_t time_rcv[2];\r
70 \r
71         /* Error logging */\r
72         int      stuck;\r
73         int      full;\r
74 };\r
75 \r
76 /* DMA Request Sources */\r
77 static int sdma_req_rx[] = {\r
78         [SDMA_UART0] SDMA_REQ_U0RX,\r
79         [SDMA_UART1] SDMA_REQ_U1RX,\r
80         [SDMA_UART2] SDMA_REQ_U2RX,\r
81 };\r
82 static int sdma_req_tx[] = {\r
83         [SDMA_UART0] SDMA_REQ_U0TX,\r
84         [SDMA_UART1] SDMA_REQ_U1TX,\r
85         [SDMA_UART2] SDMA_REQ_U2TX,\r
86 };\r
87 static int sdma_uart[] = {\r
88         [SDMA_UART0] UART0_BASE,\r
89         [SDMA_UART1] UART1_BASE,\r
90         [SDMA_UART2] UART2_BASE,\r
91 };\r
92 \r
93 /* Port data */\r
94 static sdma_t sdma_ports[SDMA_NUM_UART];\r
95 \r
96 /* DMA Functions */\r
97 sdma_t *sdma_open(sdma_uart_t uart, sdma_dma_t tx_chan, sdma_dma_t rx_chan)\r
98 {\r
99         int rxreq = sdma_req_rx[uart]; (void)rxreq;\r
100         int txreq = sdma_req_tx[uart]; (void)txreq;\r
101 \r
102         // Setup port\r
103         sdma_t *port = &sdma_ports[uart];\r
104 \r
105         port->uart      = (void*)sdma_uart[uart];\r
106         port->dma_xmt   = (void*)&DMA0->DMA[tx_chan];\r
107         port->dma_rcv   = (void*)&DMA0->DMA[rx_chan];\r
108         port->mux_xmt   = (void*)&DMAMUX0->CHCFG[tx_chan];\r
109         port->mux_rcv   = (void*)&DMAMUX0->CHCFG[rx_chan];\r
110 \r
111         // Enable DMA Cock\r
112         SIM->SCGC5     |= SIM_SCGC5_PORTA_MASK;\r
113         SIM->SCGC5     |= SIM_SCGC5_PORTC_MASK;\r
114         SIM->SCGC5     |= SIM_SCGC5_PORTD_MASK;\r
115         SIM->SCGC6     |= SIM_SCGC6_DMAMUX_MASK;\r
116         SIM->SCGC7     |= SIM_SCGC7_DMA_MASK;\r
117 \r
118         // Reset channel\r
119         port->dma_xmt->dsr  = DMA_DSR_BCR_DONE_MASK;\r
120 \r
121         // Configure DMA transfer\r
122         port->dma_xmt->dar  = (uint32_t)&port->uart->d;\r
123         port->dma_xmt->dcr  = DMA_DCR_CS_MASK\r
124                             | DMA_DCR_SINC_MASK\r
125                             | DMA_DCR_SSIZE(1)\r
126                             | DMA_DCR_DSIZE(1)\r
127                             | DMA_DCR_D_REQ_MASK;\r
128 \r
129         // Configure DMA Mux\r
130         port->mux_xmt->cfg  = DMAMUX_CHCFG_SOURCE(txreq)\r
131                             | DMAMUX_CHCFG_ENBL_MASK;\r
132 \r
133         // Configure UART for DMA Channel 0\r
134         switch (uart) {\r
135                 case SDMA_UART0:\r
136                         UART0->C5 |= UART0_C5_TDMAE_MASK;\r
137                         break;\r
138 \r
139                 case SDMA_UART1:\r
140                         UART1->C2 = UART_C2_TIE_MASK\r
141                                   | UART_C2_TE_MASK\r
142                                   | UART_C2_RE_MASK;\r
143                         UART1->C4 = UART_C4_TDMAS_MASK;\r
144                         break;\r
145 \r
146                 case SDMA_UART2:\r
147                         UART2->C2 = UART_C2_TIE_MASK\r
148                                   | UART_C2_TE_MASK\r
149                                   | UART_C2_RE_MASK;\r
150                         UART2->C4 = UART_C4_TDMAS_MASK;\r
151                         break;\r
152         }\r
153         return port;\r
154 }\r
155 \r
156 /* Map port to pins - for now, just save for timing */\r
157 void sdma_pinmap(sdma_t *port, PinName tx, PinName rx)\r
158 {\r
159         port->pin_xmt = (void*)(PORTA_BASE+tx);\r
160         port->pin_rcv = (void*)(PORTA_BASE+rx);\r
161 }\r
162 \r
163 /* Write binary data out the DMA output queue */\r
164 void sdma_write(sdma_t *port, void *data, int len)\r
165 {\r
166         if (port->length[port->index] + len > SDMA_LEN) {\r
167                 port->full++;\r
168         } else {\r
169                 int   pos = port->length[port->index];\r
170                 void *dst = &port->queue[port->index][pos];\r
171                 memcpy(dst, data, len);\r
172                 port->length[port->index] += len;\r
173         }\r
174 }\r
175 \r
176 /* Read binary data from the channel */\r
177 void sdma_read(sdma_t *port, void *data, int len)\r
178 {\r
179         for (int i = 0; i < len; i++) {\r
180                 // wait for byte\r
181                 while (!(port->uart->s1 & UART_S1_RDRF_MASK))\r
182                         if (port->uart->s1 & UART_S1_OR_MASK)\r
183                                 port->uart->s1 |= UART_S1_OR_MASK;\r
184 \r
185                 // read the byte\r
186                 ((uint8_t*)data)[i] = port->uart->d;\r
187         }\r
188 }\r
189 \r
190 /* Trigger DMA transmit of the current output queue\r
191  * and swap buffers so we can write into unused space */\r
192 void sdma_flush(sdma_t *port, uint64_t *time)\r
193 {\r
194         if (port->length[port->index] == 0)\r
195                 return;\r
196 \r
197         // Wait for transmit complete\r
198         while (port->dma_xmt->dsr & DMA_DSR_BCR_BCR_MASK)\r
199                 port->stuck++;\r
200 \r
201         // Reset channel\r
202         port->dma_xmt->dsr  = DMA_DSR_BCR_DONE_MASK;\r
203 \r
204         // Set source address and length\r
205         port->dma_xmt->sar  = (uint32_t)&port->queue[port->index];\r
206         port->dma_xmt->dsr  = DMA_DSR_BCR_BCR(port->length[port->index]);\r
207 \r
208         // Enable DMA transmit\r
209         port->dma_xmt->dcr |= DMA_DCR_ERQ_MASK;\r
210 \r
211         // Swap buffers\r
212         port->length[port->index] = 0;\r
213         port->index = (port->index + 1) % SDMA_NUM;\r
214 }\r
215 \r
216 /* Wait for DMA receive complete */\r
217 void sdma_wait(sdma_t *port, uint64_t *time)\r
218 {\r
219         int req = (void*)port->pin_rcv > (void*)PORTD ? SDMA_REQ_PTD :\r
220                   (void*)port->pin_rcv > (void*)PORTC ? SDMA_REQ_PTC :\r
221                   (void*)port->pin_rcv > (void*)PORTA ? SDMA_REQ_PTA : 0;\r
222 \r
223         // Reset channel\r
224         port->dma_rcv->dsr = DMA_DSR_BCR_DONE_MASK;\r
225 \r
226         // Configure channel\r
227         port->dma_rcv->dcr  = DMA_DCR_SINC_MASK\r
228                             | DMA_DCR_DINC_MASK\r
229                             | DMA_DCR_SSIZE(0)\r
230                             | DMA_DCR_DSIZE(0)\r
231                             | DMA_DCR_D_REQ_MASK;\r
232 \r
233         // Setup muxing\r
234         port->mux_rcv->cfg  = DMAMUX_CHCFG_SOURCE(req)\r
235                             | DMAMUX_CHCFG_ENBL_MASK;\r
236 \r
237         // Set address and size\r
238         port->dma_rcv->sar  = (uint32_t)&PIT->LTMR64H;\r
239         port->dma_rcv->dar  = (uint32_t)&port->time_rcv;\r
240         port->dma_rcv->dsr  = DMA_DSR_BCR_BCR(sizeof(uint64_t));\r
241 \r
242         // Enable DMA transmit\r
243         port->dma_rcv->dcr |= DMA_DCR_ERQ_MASK;\r
244 \r
245         // set pin to generate DMA req\r
246         port->pin_rcv->pcr  = PORT_PCR_ISF_MASK\r
247                             | PORT_PCR_IRQC(1)\r
248                             | PORT_PCR_MUX(3)\r
249                             | PORT_PCR_PE_MASK;\r
250 \r
251         // Wait for transmit complete\r
252         while ((port->dma_rcv->dsr & DMA_DSR_BCR_BCR_MASK))\r
253                 port->stuck++;\r
254 \r
255         // Save recv time\r
256         *time = ((uint64_t)~port->time_rcv[0]) << 32\r
257               | ((uint64_t)~port->time_rcv[1]) << 0;\r
258 \r
259         // pcr:00030302 dsr:41000008\r
260         printf(" - pcr:%08lx dsr:%08lx time:%08lx:%08lx",\r
261                 port->pin_rcv->pcr, port->dma_rcv->dsr,\r
262                 (uint32_t)(*time >> 32), (uint32_t)*time);\r
263 }\r
264 \r
265 /* Write ASCII data to the output queue */\r
266 void sdma_vprintf(sdma_t *port, const char *fmt, va_list ap)\r
267 {\r
268         int   pos = port->length[port->index];\r
269         void *dst = &port->queue[port->index][pos];\r
270         port->length[port->index] +=\r
271                 vsnprintf((char*)dst, SDMA_LEN-pos, fmt, ap);\r
272 }\r
273 \r
274 void sdma_printf(sdma_t *port, const char *fmt, ...)\r
275 {\r
276         va_list ap;\r
277         va_start(ap, fmt);\r
278         sdma_vprintf(port, fmt, ap);\r
279         va_end(ap);\r
280 }\r