Intel® Quark™ Microcontroller Software Interface 1.4.0 Intel® Quark™ Microcontroller BSP

- Main Page
- Modules
- Data Structures
- Files

- File List

qm_uart.c
1 /*
2 * {% copyright %}
3 */
4
5 #include <errno.h>
6 #include "qm_uart.h"
7
8 #ifndef UNIT_TEST
9 qm_uart_reg_t *qm_uart[QM_UART_NUM] = {(qm_uart_reg_t *)QM_UART_0_BASE,
10 (qm_uart_reg_t *)QM_UART_1_BASE};
11 #endif
12
13 typedef void (*uart_client_callback_t)(void *data, int error,
14 qm_uart_status_t status, uint32_t len);
15
16 /**
17 * DMA transfer information, relevant on callback invocations from the DMA
18 * driver.
19 */
20 typedef struct {
21 qm_dma_channel_id_t dma_channel_id; /**< DMA channel. */
22 const qm_uart_transfer_t *xfer; /**< User transfer structure. */
23 } dma_context_t;
24
25 /**
26 * Parameters returned by DMA driver on DMA transfer complete callback.
27 */
28 typedef volatile struct {
29 void *context; /**< Pointer to dma_context_t struct. */
30 uint32_t len; /**< Amount of data successfully transferred. */
31 int error_code; /**< Error code of failed transfer. */
32 } dma_callback_par_t;
33
34 /* Buffer pointers to store transmit / receive data for UART */
35 static uint32_t write_pos[QM_UART_NUM];
36 static uint32_t read_pos[QM_UART_NUM];
37 static const qm_uart_transfer_t *uart_read_transfer[QM_UART_NUM];
38 static const qm_uart_transfer_t *uart_write_transfer[QM_UART_NUM];
39
40 /* DMA (memory to UART) callback information. */
41 static dma_context_t dma_context_tx[QM_UART_NUM];
42 /* DMA (UART to memory) callback information. */
43 static dma_context_t dma_context_rx[QM_UART_NUM];
44 /* DMA core being used by each UART. */
45 static qm_dma_t dma_core[QM_UART_NUM];
46 /* DMA callback parameters returned by DMA driver (TX transfers). */
47 static dma_callback_par_t dma_delayed_callback_par[QM_UART_NUM];
48
49 static bool is_read_xfer_complete(const qm_uart_t uart)
50 {
51 const qm_uart_transfer_t *const transfer = uart_read_transfer[uart];
52
53 return read_pos[uart] >= transfer->data_len;
54 }
55
56 static bool is_write_xfer_complete(const qm_uart_t uart)
57 {
58 const qm_uart_transfer_t *const transfer = uart_write_transfer[uart];
59
60 return write_pos[uart] >= transfer->data_len;
61 }
62
63 static void uart_client_callback(qm_uart_t uart, void *data, int error,
64 uint32_t len);
65
66 static void qm_uart_isr_handler(const qm_uart_t uart)
67 {
68 qm_uart_reg_t *const regs = QM_UART[uart];
69 uint8_t interrupt_id = regs->iir_fcr & QM_UART_IIR_IID_MASK;
70 const qm_uart_transfer_t *const read_transfer =
71 uart_read_transfer[uart];
72 const qm_uart_transfer_t *const write_transfer =
73 uart_write_transfer[uart];
74
75 /*
76 * Interrupt ID priority levels (from highest to lowest):
77 * 1: QM_UART_IIR_RECV_LINE_STATUS
78 * 2: QM_UART_IIR_RECV_DATA_AVAIL and QM_UART_IIR_CHAR_TIMEOUT
79 * 3: QM_UART_IIR_THR_EMPTY
80 */
81 switch (interrupt_id) {
82 case QM_UART_IIR_THR_EMPTY:
83
84 if (dma_delayed_callback_par[uart].context) {
85 /*
86 * A DMA TX transfer just completed, disable interrupt
87 * and invoke client callback.
88 */
89 regs->ier_dlh &= ~QM_UART_IER_ETBEI;
90
91 uart_client_callback(
92 uart, dma_delayed_callback_par[uart].context,
93 dma_delayed_callback_par[uart].error_code,
94 dma_delayed_callback_par[uart].len);
95 dma_delayed_callback_par[uart].context = NULL;
96 return;
97 }
98
99 if (is_write_xfer_complete(uart)) {
100 regs->ier_dlh &= ~QM_UART_IER_ETBEI;
101 /*
102 * At this point the FIFOs are empty, but the shift
103 * register still is transmitting the last 8 bits. So if
104 * we were to read LSR, it would say the device is still
105 * busy. Use the SCR Bit 0 to indicate an irq tx is
106 * complete.
107 */
108 regs->scr |= BIT(0);
109 if (write_transfer->callback) {
110 write_transfer->callback(
111 write_transfer->callback_data, 0,
112 QM_UART_IDLE, write_pos[uart]);
113 }
114 return;
115 }
116
117 /*
118 * If we are starting the transfer then the TX FIFO is empty.
119 * In that case we set 'count' variable to QM_UART_FIFO_DEPTH
120 * in order to take advantage of the whole FIFO capacity.
121 */
122 int count = (write_pos[uart] == 0) ? QM_UART_FIFO_DEPTH
123 : QM_UART_FIFO_HALF_DEPTH;
124 while (count-- && !is_write_xfer_complete(uart)) {
125 regs->rbr_thr_dll =
126 write_transfer->data[write_pos[uart]++];
127 }
128
129 /*
130 * Change the threshold level to trigger an interrupt when the
131 * TX buffer is empty.
132 */
133 if (is_write_xfer_complete(uart)) {
134 regs->iir_fcr = QM_UART_FCR_TX_0_RX_1_2_THRESHOLD |
135 QM_UART_FCR_FIFOE;
136 }
137
138 break;
139
140 case QM_UART_IIR_CHAR_TIMEOUT:
141 case QM_UART_IIR_RECV_DATA_AVAIL:
142 /*
143 * Copy data from RX FIFO to xfer buffer as long as the xfer
144 * has not completed and we have data in the RX FIFO.
145 */
146 while (!is_read_xfer_complete(uart)) {
147 uint32_t lsr = regs->lsr;
148 /*
149 * A break condition may cause a line status interrupt
150 * to follow very closely after a char timeout
151 * interrupt, but reading the lsr effectively clears the
152 * pending interrupts so we issue here the callback
153 * instead, otherwise we would miss it.
154 * NOTE: Returned len is 0 for now, this might change
155 * in the future.
156 */
157 if (lsr & QM_UART_LSR_ERROR_BITS) {
158 if (read_transfer->callback) {
159 read_transfer->callback(
160 read_transfer->callback_data, -EIO,
161 lsr & QM_UART_LSR_ERROR_BITS, 0);
162 }
163 }
164 if (lsr & QM_UART_LSR_DR) {
165 read_transfer->data[read_pos[uart]++] =
166 regs->rbr_thr_dll;
167 } else {
168 /* No more data in the RX FIFO */
169 break;
170 }
171 }
172
173 if (is_read_xfer_complete(uart)) {
174 /*
175 * Disable both 'Receiver Data Available' and
176 * 'Receiver Line Status' interrupts.
177 */
178 regs->ier_dlh &=
179 ~(QM_UART_IER_ERBFI | QM_UART_IER_ELSI);
180 if (read_transfer->callback) {
181 read_transfer->callback(
182 read_transfer->callback_data, 0,
183 QM_UART_IDLE, read_pos[uart]);
184 }
185 }
186
187 break;
188
189 case QM_UART_IIR_RECV_LINE_STATUS:
190 if (read_transfer->callback) {
191 /*
192 * NOTE: Returned len is 0 for now, this might change
193 * in the future.
194 */
195 read_transfer->callback(
196 read_transfer->callback_data, -EIO,
197 regs->lsr & QM_UART_LSR_ERROR_BITS, 0);
198 }
199 break;
200 }
201 }
202
203 QM_ISR_DECLARE(qm_uart_0_isr)
204 {
205 qm_uart_isr_handler(QM_UART_0);
206 QM_ISR_EOI(QM_IRQ_UART_0_INT_VECTOR);
207 }
208
209 QM_ISR_DECLARE(qm_uart_1_isr)
210 {
211 qm_uart_isr_handler(QM_UART_1);
212 QM_ISR_EOI(QM_IRQ_UART_1_INT_VECTOR);
213 }
214
215 int qm_uart_set_config(const qm_uart_t uart, const qm_uart_config_t *cfg)
216 {
217 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
218 QM_CHECK(cfg != NULL, -EINVAL);
219
220 qm_uart_reg_t *const regs = QM_UART[uart];
221 volatile uint32_t unused_lsr __attribute__((unused));
222
223 /* Clear DLAB by unsetting line parameters */
224 regs->lcr = 0;
225
226 /* Set divisor latch registers (integer + fractional part) */
227 regs->lcr = QM_UART_LCR_DLAB;
228 regs->ier_dlh = QM_UART_CFG_BAUD_DLH_UNPACK(cfg->baud_divisor);
229 regs->rbr_thr_dll = QM_UART_CFG_BAUD_DLL_UNPACK(cfg->baud_divisor);
230 regs->dlf = QM_UART_CFG_BAUD_DLF_UNPACK(cfg->baud_divisor);
231
232 /* Set line parameters. This also unsets the DLAB */
233 regs->lcr = cfg->line_control;
234
235 /* Hardware automatic flow control */
236 regs->mcr = 0;
237 if (true == cfg->hw_fc) {
238 regs->mcr |= QM_UART_MCR_AFCE | QM_UART_MCR_RTS;
239 }
240
241 /* FIFO's enable and reset, set interrupt threshold */
242 regs->iir_fcr =
243 (QM_UART_FCR_FIFOE | QM_UART_FCR_RFIFOR | QM_UART_FCR_XFIFOR |
244 QM_UART_FCR_DEFAULT_TX_RX_THRESHOLD);
245 regs->ier_dlh |= QM_UART_IER_PTIME;
246
247 /* Clear LSR */
248 unused_lsr = regs->lsr;
249
250 return 0;
251 }
252
253 int qm_uart_get_status(const qm_uart_t uart, qm_uart_status_t *const status)
254 {
255 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
256 QM_CHECK(status != NULL, -EINVAL);
257 qm_uart_reg_t *const regs = QM_UART[uart];
258 uint32_t lsr = regs->lsr;
259
260 *status = (lsr & (QM_UART_LSR_OE | QM_UART_LSR_PE | QM_UART_LSR_FE |
261 QM_UART_LSR_BI));
262
263 /*
264 * Check as an IRQ TX completed, if so, the Shift register may still be
265 * busy.
266 */
267 if (regs->scr & BIT(0)) {
268 regs->scr &= ~BIT(0);
269 } else if (!(lsr & (QM_UART_LSR_TEMT))) {
270 *status |= QM_UART_TX_BUSY;
271 }
272
273 if (lsr & QM_UART_LSR_DR) {
274 *status |= QM_UART_RX_BUSY;
275 }
276
277 return 0;
278 }
279
280 int qm_uart_write(const qm_uart_t uart, const uint8_t data)
281 {
282 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
283
284 qm_uart_reg_t *const regs = QM_UART[uart];
285
286 while (regs->lsr & QM_UART_LSR_THRE) {
287 }
288 regs->rbr_thr_dll = data;
289 /* Wait for transaction to complete. */
290 while (!(regs->lsr & QM_UART_LSR_TEMT)) {
291 }
292
293 return 0;
294 }
295
296 int qm_uart_read(const qm_uart_t uart, uint8_t *const data,
297 qm_uart_status_t *status)
298 {
299 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
300 QM_CHECK(data != NULL, -EINVAL);
301
302 qm_uart_reg_t *const regs = QM_UART[uart];
303
304 uint32_t lsr = regs->lsr;
305 while (!(lsr & QM_UART_LSR_DR)) {
306 lsr = regs->lsr;
307 }
308 /* Check if there are any errors on the line. */
309 if (lsr & QM_UART_LSR_ERROR_BITS) {
310 if (status) {
311 *status = (lsr & QM_UART_LSR_ERROR_BITS);
312 }
313 return -EIO;
314 }
315 *data = regs->rbr_thr_dll;
316
317 return 0;
318 }
319
320 int qm_uart_write_non_block(const qm_uart_t uart, const uint8_t data)
321 {
322 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
323
324 qm_uart_reg_t *const regs = QM_UART[uart];
325
326 regs->rbr_thr_dll = data;
327
328 return 0;
329 }
330
331 int qm_uart_read_non_block(const qm_uart_t uart, uint8_t *const data)
332 {
333 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
334 QM_CHECK(data != NULL, -EINVAL);
335 qm_uart_reg_t *const regs = QM_UART[uart];
336
337 *data = regs->rbr_thr_dll;
338
339 return 0;
340 }
341
342 int qm_uart_write_buffer(const qm_uart_t uart, const uint8_t *const data,
343 uint32_t len)
344 {
345 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
346 QM_CHECK(data != NULL, -EINVAL);
347
348 qm_uart_reg_t *const regs = QM_UART[uart];
349
350 uint8_t *d = (uint8_t *)data;
351
352 while (len--) {
353 /*
354 * Because FCR_FIFOE and IER_PTIME are enabled, LSR_THRE
355 * behaves as a TX FIFO full indicator.
356 */
357 while (regs->lsr & QM_UART_LSR_THRE) {
358 }
359 regs->rbr_thr_dll = *d;
360 d++;
361 }
362 /* Wait for transaction to complete. */
363 while (!(regs->lsr & QM_UART_LSR_TEMT)) {
364 }
365 return 0;
366 }
367
368 int qm_uart_irq_write(const qm_uart_t uart,
369 const qm_uart_transfer_t *const xfer)
370 {
371 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
372 QM_CHECK(xfer != NULL, -EINVAL);
373
374 qm_uart_reg_t *const regs = QM_UART[uart];
375
376 write_pos[uart] = 0;
377 uart_write_transfer[uart] = xfer;
378
379 /* Set threshold */
380 regs->iir_fcr =
381 (QM_UART_FCR_FIFOE | QM_UART_FCR_DEFAULT_TX_RX_THRESHOLD);
382
383 /* Enable TX holding reg empty interrupt. */
384 regs->ier_dlh |= QM_UART_IER_ETBEI;
385
386 return 0;
387 }
388
389 int qm_uart_irq_read(const qm_uart_t uart, const qm_uart_transfer_t *const xfer)
390 {
391 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
392 QM_CHECK(xfer != NULL, -EINVAL);
393
394 qm_uart_reg_t *const regs = QM_UART[uart];
395
396 read_pos[uart] = 0;
397 uart_read_transfer[uart] = xfer;
398
399 /* Set threshold */
400 regs->iir_fcr =
401 (QM_UART_FCR_FIFOE | QM_UART_FCR_DEFAULT_TX_RX_THRESHOLD);
402
403 /*
404 * Enable both 'Receiver Data Available' and 'Receiver
405 * Line Status' interrupts.
406 */
407 regs->ier_dlh |= QM_UART_IER_ERBFI | QM_UART_IER_ELSI;
408
409 return 0;
410 }
411
412 int qm_uart_irq_write_terminate(const qm_uart_t uart)
413 {
414 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
415
416 qm_uart_reg_t *const regs = QM_UART[uart];
417 const qm_uart_transfer_t *const transfer = uart_write_transfer[uart];
418
419 /* Disable TX holding reg empty interrupt. */
420 regs->ier_dlh &= ~QM_UART_IER_ETBEI;
421 if (transfer->callback) {
422 transfer->callback(transfer->callback_data, -ECANCELED,
423 QM_UART_IDLE, write_pos[uart]);
424 }
425
426 return 0;
427 }
428
429 int qm_uart_irq_read_terminate(const qm_uart_t uart)
430 {
431 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
432
433 qm_uart_reg_t *const regs = QM_UART[uart];
434 const qm_uart_transfer_t *const transfer = uart_read_transfer[uart];
435
436 /*
437 * Disable both 'Receiver Data Available' and 'Receiver Line Status'
438 * interrupts.
439 */
440 regs->ier_dlh &= ~(QM_UART_IER_ERBFI | QM_UART_IER_ELSI);
441 if (transfer->callback) {
442 transfer->callback(transfer->callback_data, -ECANCELED,
443 QM_UART_IDLE, read_pos[uart]);
444 }
445
446 return 0;
447 }
448
449 /*
450 * Called by the DMA driver when the whole TX buffer has been written to the TX
451 * FIFO (write transfers) or the expected amount of data has been read from the
452 * RX FIFO (read transfers).
453 */
454 static void uart_dma_callback(void *callback_context, uint32_t len,
455 int error_code)
456 {
457 qm_uart_t uart;
458 QM_ASSERT(callback_context);
459 /*
460 * On TX transfers, the DMA driver invokes this function as soon as all
461 * data has been written to the TX FIFO, but we still need to wait until
462 * everything has been written to the shift register (TX FIFO empty
463 * interrupt) before the client callback is invoked.
464 */
465 if (callback_context >= (void *)&dma_context_tx[0] &&
466 callback_context <= (void *)&dma_context_tx[QM_UART_NUM - 1]) {
467 /*
468 * callback_context is within dma_context_tx array so this is a
469 * TX transfer, we extract the uart index from the position in
470 * the array.
471 */
472 uart = (dma_context_t *)callback_context - dma_context_tx;
473 QM_ASSERT(callback_context == (void *)&dma_context_tx[uart]);
474 qm_uart_reg_t *const regs = QM_UART[uart];
475
476 dma_delayed_callback_par[uart].context = callback_context;
477 dma_delayed_callback_par[uart].len = len;
478 dma_delayed_callback_par[uart].error_code = error_code;
479
480 /*
481 * Change the threshold level to trigger an interrupt when the
482 * TX FIFO is empty and enable the TX FIFO empty interrupt.
483 */
484 regs->iir_fcr =
485 (QM_UART_FCR_FIFOE | QM_UART_FCR_TX_0_RX_1_2_THRESHOLD);
486 regs->ier_dlh |= QM_UART_IER_ETBEI;
487 } else {
488 /* RX transfer. */
489 uart = (dma_context_t *)callback_context - dma_context_rx;
490 QM_ASSERT(callback_context == (void *)&dma_context_rx[uart]);
491 uart_client_callback(uart, callback_context, error_code, len);
492 }
493 }
494
495 /* Invoke the UART client callback. */
496 static void uart_client_callback(qm_uart_t uart, void *data, int error,
497 uint32_t len)
498 {
499 const qm_uart_transfer_t *const xfer = ((dma_context_t *)data)->xfer;
500 QM_ASSERT(xfer);
501 const uart_client_callback_t client_callback = xfer->callback;
502 void *const client_data = xfer->callback_data;
503 const uint32_t client_expected_len = xfer->data_len;
504 qm_uart_reg_t *const regs = QM_UART[uart];
505 qm_uart_status_t status;
506
507 if (!client_callback) {
508 return;
509 }
510
511 status = regs->lsr & QM_UART_LSR_ERROR_BITS;
512 status |= (regs->lsr & QM_UART_LSR_DR) ? QM_UART_RX_BUSY : 0;
513
514 if (error) {
515 /*
516 * Transfer failed, pass to client the error code returned by
517 * the DMA driver.
518 */
519 client_callback(client_data, error, status, 0);
520 } else if (len == client_expected_len) {
521 /* Transfer completed successfully. */
522 client_callback(client_data, 0, status, len);
523 } else {
524 QM_ASSERT(len < client_expected_len);
525 /* Transfer cancelled. */
526 client_callback(client_data, -ECANCELED, status, len);
527 }
528 }
529
530 int qm_uart_dma_channel_config(
531 const qm_uart_t uart, const qm_dma_t dma_ctrl_id,
532 const qm_dma_channel_id_t dma_channel_id,
533 const qm_dma_channel_direction_t dma_channel_direction)
534 {
535 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
536 QM_CHECK(dma_ctrl_id < QM_DMA_NUM, -EINVAL);
537 QM_CHECK(dma_channel_id < QM_DMA_CHANNEL_NUM, -EINVAL);
538
539 int ret = -EINVAL;
540 qm_dma_channel_config_t dma_chan_cfg = {0};
541 /* UART has inverted handshake polarity. */
542 dma_chan_cfg.handshake_polarity = QM_DMA_HANDSHAKE_POLARITY_LOW;
543 dma_chan_cfg.channel_direction = dma_channel_direction;
544 dma_chan_cfg.source_transfer_width = QM_DMA_TRANS_WIDTH_8;
545 dma_chan_cfg.destination_transfer_width = QM_DMA_TRANS_WIDTH_8;
546 /* Default FIFO threshold is 1/2 full (8 bytes). */
547 dma_chan_cfg.source_burst_length = QM_DMA_BURST_TRANS_LENGTH_8;
548 dma_chan_cfg.destination_burst_length = QM_DMA_BURST_TRANS_LENGTH_8;
549 dma_chan_cfg.client_callback = uart_dma_callback;
550 dma_chan_cfg.transfer_type = QM_DMA_TYPE_SINGLE;
551
552 switch (dma_channel_direction) {
553 case QM_DMA_MEMORY_TO_PERIPHERAL:
554 dma_chan_cfg.handshake_interface = (QM_UART_0 == uart)
555 ? DMA_HW_IF_UART_A_TX
556 : DMA_HW_IF_UART_B_TX;
557
558 /*
559 * The DMA driver needs a pointer to the DMA context structure
560 * used on DMA callback invocation.
561 */
562 dma_context_tx[uart].dma_channel_id = dma_channel_id;
563 dma_chan_cfg.callback_context = &dma_context_tx[uart];
564 break;
565
566 case QM_DMA_PERIPHERAL_TO_MEMORY:
567 dma_chan_cfg.handshake_interface = (QM_UART_0 == uart)
568 ? DMA_HW_IF_UART_A_RX
569 : DMA_HW_IF_UART_B_RX;
570
571 /*
572 * The DMA driver needs a pointer to the DMA context structure
573 * used on DMA callback invocation.
574 */
575 dma_context_rx[uart].dma_channel_id = dma_channel_id;
576 dma_chan_cfg.callback_context = &dma_context_rx[uart];
577 break;
578
579 default:
580 /* Direction not allowed on UART transfers. */
581 return -EINVAL;
582 }
583
584 dma_core[uart] = dma_ctrl_id;
585
586 ret = qm_dma_channel_set_config(dma_ctrl_id, dma_channel_id,
587 &dma_chan_cfg);
588 return ret;
589 }
590
591 int qm_uart_dma_write(const qm_uart_t uart,
592 const qm_uart_transfer_t *const xfer)
593 {
594 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
595 QM_CHECK(xfer, -EINVAL);
596 QM_CHECK(xfer->data, -EINVAL);
597 QM_CHECK(xfer->data_len, -EINVAL);
598
599 int ret = -EINVAL;
600 qm_uart_reg_t *regs = QM_UART[uart];
601 qm_dma_transfer_t dma_trans = {0};
602 dma_trans.block_size = xfer->data_len;
603 dma_trans.source_address = (uint32_t *)xfer->data;
604 dma_trans.destination_address = (uint32_t *)&regs->rbr_thr_dll;
605
606 ret = qm_dma_transfer_set_config(
607 dma_core[uart], dma_context_tx[uart].dma_channel_id, &dma_trans);
608 if (ret) {
609 return ret;
610 }
611
612 /* Store the user transfer pointer that we will need on DMA callback. */
613 dma_context_tx[uart].xfer = xfer;
614
615 /* Set the FCR register FIFO thresholds. */
616 regs->iir_fcr =
617 (QM_UART_FCR_FIFOE | QM_UART_FCR_DEFAULT_TX_RX_THRESHOLD);
618
619 ret = qm_dma_transfer_start(dma_core[uart],
620 dma_context_tx[uart].dma_channel_id);
621
622 return ret;
623 }
624
625 int qm_uart_dma_read(const qm_uart_t uart, const qm_uart_transfer_t *const xfer)
626 {
627 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
628 QM_CHECK(xfer, -EINVAL);
629 QM_CHECK(xfer->data, -EINVAL);
630 QM_CHECK(xfer->data_len, -EINVAL);
631
632 int ret = -EINVAL;
633 qm_uart_reg_t *regs = QM_UART[uart];
634 qm_dma_transfer_t dma_trans = {0};
635 dma_trans.block_size = xfer->data_len;
636 dma_trans.source_address = (uint32_t *)&regs->rbr_thr_dll;
637 dma_trans.destination_address = (uint32_t *)xfer->data;
638
639 ret = qm_dma_transfer_set_config(
640 dma_core[uart], dma_context_rx[uart].dma_channel_id, &dma_trans);
641 if (ret) {
642 return ret;
643 }
644
645 /* Store the user transfer pointer that we will need on DMA callback. */
646 dma_context_rx[uart].xfer = xfer;
647
648 /* Set the FCR register FIFO thresholds. */
649 regs->iir_fcr =
650 (QM_UART_FCR_FIFOE | QM_UART_FCR_DEFAULT_TX_RX_THRESHOLD);
651
652 ret = qm_dma_transfer_start(dma_core[uart],
653 dma_context_rx[uart].dma_channel_id);
654
655 return ret;
656 }
657
658 int qm_uart_dma_write_terminate(const qm_uart_t uart)
659 {
660 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
661
662 int ret = qm_dma_transfer_terminate(
663 dma_core[uart], dma_context_tx[uart].dma_channel_id);
664
665 return ret;
666 }
667
668 int qm_uart_dma_read_terminate(const qm_uart_t uart)
669 {
670 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
671
672 int ret = qm_dma_transfer_terminate(
673 dma_core[uart], dma_context_rx[uart].dma_channel_id);
674
675 return ret;
676 }
677
678 #if (ENABLE_RESTORE_CONTEXT)
679 int qm_uart_save_context(const qm_uart_t uart, qm_uart_context_t *const ctx)
680 {
681 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
682 QM_CHECK(ctx != NULL, -EINVAL);
683
684 qm_uart_reg_t *const regs = QM_UART[uart];
685
686 ctx->ier = regs->ier_dlh;
687 ctx->lcr = regs->lcr;
688 ctx->mcr = regs->mcr;
689 ctx->scr = regs->scr;
690 ctx->htx = regs->htx;
691 ctx->dlf = regs->dlf;
692
693 regs->lcr |= QM_UART_LCR_DLAB;
694 ctx->dlh = regs->ier_dlh;
695 ctx->dll = regs->rbr_thr_dll;
696 regs->lcr &= ~QM_UART_LCR_DLAB;
697
698 return 0;
699 }
700
701 int qm_uart_restore_context(const qm_uart_t uart,
702 const qm_uart_context_t *const ctx)
703 {
704 QM_CHECK(uart < QM_UART_NUM, -EINVAL);
705 QM_CHECK(ctx != NULL, -EINVAL);
706
707 qm_uart_reg_t *const regs = QM_UART[uart];
708
709 /* When DLAB is set, DLL and DLH registers can be accessed. */
710 regs->lcr |= QM_UART_LCR_DLAB;
711 regs->ier_dlh = ctx->dlh;
712 regs->rbr_thr_dll = ctx->dll;
713 regs->lcr &= ~QM_UART_LCR_DLAB;
714
715 regs->ier_dlh = ctx->ier;
716 regs->lcr = ctx->lcr;
717 regs->mcr = ctx->mcr;
718 regs->scr = ctx->scr;
719 regs->htx = ctx->htx;
720 regs->dlf = ctx->dlf;
721
722 /*
723 * FIFO control register cannot be read back,
724 * default config is applied for this register.
725 * Application will need to restore its own parameters.
726 */
727 regs->iir_fcr =
728 (QM_UART_FCR_FIFOE | QM_UART_FCR_RFIFOR | QM_UART_FCR_XFIFOR |
729 QM_UART_FCR_DEFAULT_TX_RX_THRESHOLD);
730
731 return 0;
732 }
733 #else
734 int qm_uart_save_context(const qm_uart_t uart, qm_uart_context_t *const ctx)
735 {
736 (void)uart;
737 (void)ctx;
738
739 return 0;
740 }
741
742 int qm_uart_restore_context(const qm_uart_t uart,
743 const qm_uart_context_t *const ctx)
744 {
745 (void)uart;
746 (void)ctx;
747
748 return 0;
749 }
750 #endif /* ENABLE_RESTORE_CONTEXT */
qm_uart_context_t::dlh
uint32_t dlh
Divisor Latch High.
Definition: qm_soc_regs.h:995
qm_uart_dma_channel_config
int qm_uart_dma_channel_config(const qm_uart_t uart, const qm_dma_t dma_ctrl_id, const qm_dma_channel_id_t dma_channel_id, const qm_dma_channel_direction_t dma_channel_direction)
Configure a DMA channel with a specific transfer direction.
Definition: qm_uart.c:530
qm_uart_irq_read
int qm_uart_irq_read(const qm_uart_t uart, const qm_uart_transfer_t *const xfer)
Interrupt based RX on UART.
Definition: qm_uart.c:389
qm_dma_channel_config_t
DMA channel configuration structure.
Definition: qm_dma.h:77
qm_dma_channel_config_t::transfer_type
qm_dma_transfer_type_t transfer_type
DMA transfer type.
Definition: qm_dma.h:100
qm_uart_dma_read
int qm_uart_dma_read(const qm_uart_t uart, const qm_uart_transfer_t *const xfer)
Perform a DMA-based RX transfer on the UART bus.
Definition: qm_uart.c:625
qm_uart_context_t::mcr
uint32_t mcr
Modem Control.
Definition: qm_soc_regs.h:998
QM_DMA_BURST_TRANS_LENGTH_8
Burst length 8 data items.
Definition: qm_dma.h:32
DMA_HW_IF_UART_B_RX
UART_B_RX.
Definition: qm_soc_regs.h:1497
qm_dma_transfer_t::destination_address
uint32_t * destination_address
DMA destination transfer address.
Definition: qm_dma.h:136
qm_dma_t
qm_dma_t
DMA instances.
Definition: qm_soc_regs.h:1480
DMA_HW_IF_UART_B_TX
UART_B_TX.
Definition: qm_soc_regs.h:1496
qm_uart_context_t
UART context to be saved between sleep/resume.
Definition: qm_soc_regs.h:993
qm_uart_reg_t::dlf
QM_RW uint32_t dlf
Divisor Latch Fraction.
Definition: qm_soc_regs.h:678
qm_uart_reg_t::lcr
QM_RW uint32_t lcr
Line Control.
Definition: qm_soc_regs.h:663
qm_uart_save_context
int qm_uart_save_context(const qm_uart_t uart, qm_uart_context_t *const ctx)
Save UART context.
Definition: qm_uart.c:679
QM_DMA_CHANNEL_NUM
Number of DMA channels.
Definition: qm_soc_regs.h:1489
qm_dma_transfer_t
DMA single block transfer configuration structure.
Definition: qm_dma.h:133
qm_uart_reg_t::mcr
QM_RW uint32_t mcr
MODEM Control.
Definition: qm_soc_regs.h:664
qm_dma_channel_config_t::source_burst_length
qm_dma_burst_length_t source_burst_length
DMA source burst length.
Definition: qm_dma.h:94
QM_UART_IDLE
IDLE.
Definition: qm_uart.h:53
qm_uart_transfer_t
UART asynchronous transfer structure.
Definition: qm_uart.h:76
qm_uart_get_status
int qm_uart_get_status(const qm_uart_t uart, qm_uart_status_t *const status)
Get UART bus status.
Definition: qm_uart.c:253
qm_uart_config_t::line_control
qm_uart_lc_t line_control
Line control (enum).
Definition: qm_uart.h:68
qm_uart_dma_write_terminate
int qm_uart_dma_write_terminate(const qm_uart_t uart)
Terminate the current DMA TX transfer on the UART bus.
Definition: qm_uart.c:658
qm_dma_transfer_t::block_size
uint32_t block_size
DMA block size, Min = 1, Max = 4095.
Definition: qm_dma.h:134
qm_dma_channel_config_t::callback_context
void * callback_context
DMA client context passed to the callbacks.
Definition: qm_dma.h:113
qm_uart_reg_t
UART register map.
Definition: qm_soc_regs.h:658
qm_uart_transfer_t::data
uint8_t * data
Pre-allocated write or read buffer.
Definition: qm_uart.h:77
qm_uart_t
qm_uart_t
Number of UART controllers.
Definition: qm_soc_regs.h:655
qm_uart_transfer_t::callback
void(* callback)(void *data, int error, qm_uart_status_t status, uint32_t len)
Transfer callback.
Definition: qm_uart.h:89
QM_DMA_TRANS_WIDTH_8
Transfer width of 8 bits.
Definition: qm_dma.h:45
qm_uart_context_t::htx
uint32_t htx
Halt Transmission.
Definition: qm_soc_regs.h:1000
qm_uart_reg_t::rbr_thr_dll
QM_RW uint32_t rbr_thr_dll
Rx Buffer/ Tx Holding/ Div Latch Low.
Definition: qm_soc_regs.h:660
QM_ISR_DECLARE
QM_ISR_DECLARE(qm_uart_0_isr)
ISR for UART 0 interrupt.
Definition: qm_uart.c:203
qm_uart_dma_read_terminate
int qm_uart_dma_read_terminate(const qm_uart_t uart)
Terminate the current DMA RX transfer on the UART bus.
Definition: qm_uart.c:668
qm_uart_irq_write
int qm_uart_irq_write(const qm_uart_t uart, const qm_uart_transfer_t *const xfer)
Interrupt based TX on UART.
Definition: qm_uart.c:368
qm_uart_write_non_block
int qm_uart_write_non_block(const qm_uart_t uart, const uint8_t data)
UART character data write.
Definition: qm_uart.c:320
qm_uart_read_non_block
int qm_uart_read_non_block(const qm_uart_t uart, uint8_t *const data)
UART character data read.
Definition: qm_uart.c:331
qm_dma_channel_config_t::handshake_interface
qm_dma_handshake_interface_t handshake_interface
DMA channel handshake interface ID.
Definition: qm_dma.h:79
qm_dma_transfer_set_config
int qm_dma_transfer_set_config(const qm_dma_t dma, const qm_dma_channel_id_t channel_id, qm_dma_transfer_t *const transfer_config)
Setup a DMA single block transfer.
Definition: qm_dma.c:345
DMA_HW_IF_UART_A_RX
UART_A_RX.
Definition: qm_soc_regs.h:1495
qm_uart_config_t::hw_fc
bool hw_fc
Hardware Automatic Flow Control.
Definition: qm_uart.h:70
qm_dma_channel_config_t::handshake_polarity
qm_dma_handshake_polarity_t handshake_polarity
DMA channel handshake polarity.
Definition: qm_dma.h:82
QM_DMA_HANDSHAKE_POLARITY_LOW
Set HS polarity low.
Definition: qm_dma.h:23
QM_DMA_TYPE_SINGLE
Single block mode.
Definition: qm_dma.h:67
qm_uart_reg_t::htx
QM_RW uint32_t htx
Halt Transmission.
Definition: qm_soc_regs.h:671
qm_uart_reg_t::lsr
QM_RW uint32_t lsr
Line Status.
Definition: qm_soc_regs.h:665
qm_uart_status_t
qm_uart_status_t
UART Status type.
Definition: qm_uart.h:52
QM_DMA_PERIPHERAL_TO_MEMORY
Peripheral to memory transfer.
Definition: qm_dma.h:60
qm_uart_context_t::scr
uint32_t scr
Scratchpad.
Definition: qm_soc_regs.h:999
qm_dma_channel_config_t::destination_burst_length
qm_dma_burst_length_t destination_burst_length
DMA destination burst length.
Definition: qm_dma.h:97
qm_uart_set_config
int qm_uart_set_config(const qm_uart_t uart, const qm_uart_config_t *cfg)
Set UART configuration.
Definition: qm_uart.c:215
qm_dma_channel_config_t::destination_transfer_width
qm_dma_transfer_width_t destination_transfer_width
DMA destination transfer width.
Definition: qm_dma.h:91
qm_dma_channel_set_config
int qm_dma_channel_set_config(const qm_dma_t dma, const qm_dma_channel_id_t channel_id, qm_dma_channel_config_t *const channel_config)
Setup a DMA channel configuration.
Definition: qm_dma.c:254
qm_uart_restore_context
int qm_uart_restore_context(const qm_uart_t uart, const qm_uart_context_t *const ctx)
Restore UART context.
Definition: qm_uart.c:701
qm_uart_reg_t::ier_dlh
QM_RW uint32_t ier_dlh
Interrupt Enable / Divisor Latch High.
Definition: qm_soc_regs.h:661
DMA_HW_IF_UART_A_TX
UART_A_TX.
Definition: qm_soc_regs.h:1494
QM_DMA_NUM
Number of DMA controllers.
Definition: qm_soc_regs.h:1482
qm_dma_channel_config_t::source_transfer_width
qm_dma_transfer_width_t source_transfer_width
DMA source transfer width.
Definition: qm_dma.h:88
qm_uart_irq_write_terminate
int qm_uart_irq_write_terminate(const qm_uart_t uart)
Terminate UART IRQ TX transfer.
Definition: qm_uart.c:412
QM_UART_RX_BUSY
RX Busy flag.
Definition: qm_uart.h:59
qm_uart_transfer_t::callback_data
void * callback_data
Callback identifier.
Definition: qm_uart.h:91
qm_dma_transfer_terminate
int qm_dma_transfer_terminate(const qm_dma_t dma, const qm_dma_channel_id_t channel_id)
Terminate a DMA transfer.
Definition: qm_dma.c:618
qm_uart_context_t::dll
uint32_t dll
Divisor Latch Low.
Definition: qm_soc_regs.h:996
qm_dma_channel_direction_t
qm_dma_channel_direction_t
DMA channel direction.
Definition: qm_dma.h:56
qm_uart_config_t::baud_divisor
uint32_t baud_divisor
Baud Divisor.
Definition: qm_uart.h:69
qm_dma_transfer_start
int qm_dma_transfer_start(const qm_dma_t dma, const qm_dma_channel_id_t channel_id)
Start a DMA transfer.
Definition: qm_dma.c:582
qm_uart_reg_t::iir_fcr
QM_RW uint32_t iir_fcr
Interrupt Identification / FIFO Control.
Definition: qm_soc_regs.h:662
QM_DMA_MEMORY_TO_PERIPHERAL
Memory to peripheral transfer.
Definition: qm_dma.h:58
qm_uart_reg_t::scr
QM_RW uint32_t scr
Scratchpad.
Definition: qm_soc_regs.h:667
qm_dma_transfer_t::source_address
uint32_t * source_address
DMA source transfer address.
Definition: qm_dma.h:135
qm_dma_channel_id_t
qm_dma_channel_id_t
DMA channel IDs.
Definition: qm_soc_regs.h:1486
qm_uart_write
int qm_uart_write(const qm_uart_t uart, const uint8_t data)
UART character data write.
Definition: qm_uart.c:280
qm_uart_irq_read_terminate
int qm_uart_irq_read_terminate(const qm_uart_t uart)
Terminate UART IRQ RX transfer.
Definition: qm_uart.c:429
qm_uart_config_t
UART configuration structure type.
Definition: qm_uart.h:67
qm_uart_context_t::lcr
uint32_t lcr
Line Control.
Definition: qm_soc_regs.h:997
qm_uart_transfer_t::data_len
uint32_t data_len
Number of bytes to transfer.
Definition: qm_uart.h:78
qm_dma_channel_config_t::channel_direction
qm_dma_channel_direction_t channel_direction
DMA channel direction.
Definition: qm_dma.h:85
qm_uart_read
int qm_uart_read(const qm_uart_t uart, uint8_t *const data, qm_uart_status_t *status)
UART character data read.
Definition: qm_uart.c:296
qm_uart_context_t::dlf
uint32_t dlf
Divisor Latch Fraction.
Definition: qm_soc_regs.h:1001
QM_UART_TX_BUSY
TX Busy flag.
Definition: qm_uart.h:58
qm_uart_dma_write
int qm_uart_dma_write(const qm_uart_t uart, const qm_uart_transfer_t *const xfer)
Perform a DMA-based TX transfer on the UART bus.
Definition: qm_uart.c:591
qm_uart_context_t::ier
uint32_t ier
Interrupt Enable Register.
Definition: qm_soc_regs.h:994
qm_uart_write_buffer
int qm_uart_write_buffer(const qm_uart_t uart, const uint8_t *const data, uint32_t len)
UART multi-byte data write.
Definition: qm_uart.c:342
qm_dma_channel_config_t::client_callback
void(* client_callback)(void *callback_context, uint32_t len, int error_code)
Client callback for DMA transfer ISR.
Definition: qm_dma.h:109