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