source: rtems/bsps/arm/atsam/console/console.c @ 779847a5

5
Last change on this file since 779847a5 was 779847a5, checked in by Christian Mauderer <christian.mauderer@…>, on 01/12/22 at 15:12:20

bsp/atsam: Optionally use DMA for UART Rx

If the system is busy with other interrupts and the UART is set to a
fast baud rate, it's possible to loose UART interrupts and therefore
characters. This allows to optionally enable a DMA for the UARTs so that
a number of lost interrupts can be tolerated.

The number of DMAs on this chip is limited and not not all applications
need that feature. Therefore the DMA is disabled by default.

Close #4577

  • Property mode set to 100644
File size: 11.5 KB
Line 
1/*
2 * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#include <bsp.h>
16#include <bsp/irq.h>
17#include <bsp/fatal.h>
18#include <rtems/console.h>
19#include <rtems/seterr.h>
20
21#include <rtems/termiostypes.h>
22
23#include <chip.h>
24
25#include <unistd.h>
26
27#define UART_RX_DMA_BUF_SIZE 32l
28
29typedef struct {
30  char buf[UART_RX_DMA_BUF_SIZE];
31  LinkedListDescriporView3 desc;
32} atsam_uart_rx_dma;
33
34typedef struct {
35  rtems_termios_device_context base;
36  Uart *regs;
37  rtems_vector_number irq;
38  uint32_t id;
39  bool console;
40  bool is_usart;
41#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
42  bool transmitting;
43  bool rx_dma_enabled;
44  uint32_t rx_dma_channel;
45  atsam_uart_rx_dma *rx_dma;
46  char *volatile*rx_dma_da;
47  char *rx_next_read_pos;
48#endif
49} atsam_uart_context;
50
51static atsam_uart_context atsam_usart_instances[] = {
52  {
53    .regs = (Uart *)USART0,
54    .irq = USART0_IRQn,
55    .id = ID_USART0,
56    .is_usart = true,
57  }
58#ifdef USART1
59  , {
60    .regs = (Uart *)USART1,
61    .irq = USART1_IRQn,
62    .id = ID_USART1,
63    .is_usart = true,
64  }
65#endif
66#ifdef USART2
67  , {
68    .regs = (Uart *)USART2,
69    .irq = USART2_IRQn,
70    .id = ID_USART2,
71    .is_usart = true,
72  }
73#endif
74};
75
76static atsam_uart_context atsam_uart_instances[] = {
77  {
78    .regs = UART0,
79    .irq = UART0_IRQn,
80    .id = ID_UART0,
81    .is_usart = false,
82  }
83#ifdef UART1
84  , {
85    .regs = UART1,
86    .irq = UART1_IRQn,
87    .id = ID_UART1,
88    .is_usart = false,
89  }
90#endif
91#ifdef UART2
92  , {
93    .regs = UART2,
94    .irq = UART2_IRQn,
95    .id = ID_UART2,
96    .is_usart = false,
97  }
98#endif
99#ifdef UART3
100  , {
101    .regs = UART3,
102    .irq = UART3_IRQn,
103    .id = ID_UART3,
104    .is_usart = false,
105  }
106#endif
107#ifdef UART4
108  , {
109    .regs = UART4,
110    .irq = UART4_IRQn,
111    .id = ID_UART4,
112    .is_usart = false,
113  }
114#endif
115};
116
117#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
118static void atsam_uart_interrupt(void *arg)
119{
120  rtems_termios_tty *tty = arg;
121  atsam_uart_context *ctx = rtems_termios_get_device_context(tty);
122  Uart *regs = ctx->regs;
123  uint32_t sr = regs->UART_SR;
124
125  if (!ctx->rx_dma_enabled) {
126    while ((sr & UART_SR_RXRDY) != 0) {
127      char c = (char) regs->UART_RHR;
128
129      rtems_termios_enqueue_raw_characters(tty, &c, 1);
130
131      sr = regs->UART_SR;
132    }
133  } else {
134    while (*ctx->rx_dma_da != ctx->rx_next_read_pos) {
135      char c;
136
137      c = *ctx->rx_next_read_pos;
138
139      ++ctx->rx_next_read_pos;
140      if (ctx->rx_next_read_pos >= &ctx->rx_dma->buf[UART_RX_DMA_BUF_SIZE]) {
141        ctx->rx_next_read_pos = &ctx->rx_dma->buf[0];
142      }
143
144      rtems_termios_enqueue_raw_characters(tty, &c, 1);
145    }
146  }
147
148  if (ctx->transmitting && (sr & UART_SR_TXEMPTY) != 0) {
149    rtems_termios_dequeue_characters(tty, 1);
150  }
151}
152#endif
153
154static bool atsam_uart_set_attributes(
155  rtems_termios_device_context *base,
156  const struct termios *term
157)
158{
159  atsam_uart_context *ctx = (atsam_uart_context *) base;
160  Uart *regs = ctx->regs;
161  rtems_termios_baud_t baud;
162  uint32_t mr;
163
164  baud = rtems_termios_baud_to_number(term->c_ospeed);
165  regs->UART_BRGR = (BOARD_MCK / baud) / 16;
166
167  if ((term->c_cflag & CREAD) != 0) {
168    regs->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
169  } else {
170    regs->UART_CR = UART_CR_TXEN;
171  }
172
173  if (ctx->is_usart) {
174    mr = US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK;
175  } else {
176    mr = UART_MR_FILTER_DISABLED | UART_MR_BRSRCCK_PERIPH_CLK;
177  }
178
179  if (ctx->is_usart) {
180    switch (term->c_cflag & CSIZE) {
181      case CS5:
182        mr |= US_MR_CHRL_5_BIT;
183        break;
184      case CS6:
185        mr |= US_MR_CHRL_6_BIT;
186        break;
187      case CS7:
188        mr |= US_MR_CHRL_7_BIT;
189        break;
190      default:
191        mr |= US_MR_CHRL_8_BIT;
192        break;
193    }
194  } else {
195    if ((term->c_cflag & CSIZE) != CS8) {
196      return false;
197    }
198  }
199
200  if ((term->c_cflag & PARENB) != 0) {
201    if ((term->c_cflag & PARODD) != 0) {
202      mr |= UART_MR_PAR_ODD;
203    } else {
204      mr |= UART_MR_PAR_EVEN;
205    }
206  } else {
207    mr |= UART_MR_PAR_NO;
208  }
209
210  if (ctx->is_usart) {
211    if ((term->c_cflag & CSTOPB) != 0) {
212      mr |= US_MR_NBSTOP_2_BIT;
213    } else {
214      mr |= US_MR_NBSTOP_1_BIT;
215    }
216  } else {
217    if ((term->c_cflag & CSTOPB) != 0) {
218      return false;
219    }
220  }
221
222  regs->UART_MR = mr;
223
224  return true;
225}
226
227static void atsam_uart_disable_rx_dma(atsam_uart_context *ctx)
228{
229  if (ctx->rx_dma) {
230    rtems_cache_coherent_free(ctx->rx_dma);
231    ctx->rx_dma = NULL;
232  }
233
234  if (ctx->rx_dma_channel != XDMAD_ALLOC_FAILED) {
235    XDMAD_FreeChannel(&XDMAD_Instance, ctx->rx_dma_channel);
236  }
237
238  ctx->rx_dma_enabled = false;
239}
240
241static rtems_status_code atsam_uart_enable_rx_dma(atsam_uart_context *ctx)
242{
243  eXdmadRC rc;
244  int channel_id;
245
246  if (ctx->rx_dma_enabled) {
247    return RTEMS_SUCCESSFUL;
248  }
249
250  /*
251   * Make sure everything is in a clean default state so that the cleanup works
252   * in an error case.
253   */
254  ctx->rx_dma = NULL;
255  ctx->rx_dma_channel = XDMAD_ALLOC_FAILED;
256
257  ctx->rx_dma = rtems_cache_coherent_allocate(sizeof(*ctx->rx_dma), 0, 0);
258  if (ctx->rx_dma == NULL) {
259    atsam_uart_disable_rx_dma(ctx);
260    return RTEMS_NO_MEMORY;
261  }
262
263  ctx->rx_next_read_pos = &ctx->rx_dma->buf[0];
264
265  ctx->rx_dma_channel = XDMAD_AllocateChannel(
266    &XDMAD_Instance,
267    XDMAD_TRANSFER_MEMORY,
268    ctx->id
269  );
270
271  if (ctx->rx_dma_channel == XDMAD_ALLOC_FAILED) {
272    atsam_uart_disable_rx_dma(ctx);
273    return RTEMS_IO_ERROR;
274  }
275
276  rc = XDMAD_PrepareChannel(&XDMAD_Instance, ctx->rx_dma_channel);
277  if (rc != XDMAD_OK) {
278    atsam_uart_disable_rx_dma(ctx);
279    return RTEMS_IO_ERROR;
280  }
281
282  channel_id = ctx->rx_dma_channel & 0xff;
283  ctx->rx_dma_da =
284    (char *volatile*) &XDMAD_Instance.pXdmacs->XDMAC_CHID[channel_id].XDMAC_CDA;
285
286  ctx->rx_dma->desc.mbr_nda = (uint32_t)&ctx->rx_dma->desc;
287  ctx->rx_dma->desc.mbr_ubc =
288    1 |
289    XDMA_UBC_NVIEW_NDV3 |
290    XDMA_UBC_NDE_FETCH_EN |
291    XDMA_UBC_NDEN_UPDATED |
292    XDMA_UBC_NSEN_UPDATED;
293  ctx->rx_dma->desc.mbr_sa = (uint32_t) &ctx->regs->UART_RHR;
294  ctx->rx_dma->desc.mbr_da = (uint32_t) &ctx->rx_dma->buf[0];
295  ctx->rx_dma->desc.mbr_cfg =
296    XDMAC_CC_TYPE_PER_TRAN |
297    XDMAC_CC_MBSIZE_SINGLE |
298    XDMAC_CC_DSYNC_PER2MEM |
299    XDMAC_CC_SWREQ_HWR_CONNECTED |
300    XDMAC_CC_MEMSET_NORMAL_MODE |
301    XDMAC_CC_CSIZE_CHK_1 |
302    XDMAC_CC_DWIDTH_BYTE |
303    XDMAC_CC_SIF_AHB_IF1 |
304    XDMAC_CC_DIF_AHB_IF1 |
305    XDMAC_CC_SAM_FIXED_AM |
306    XDMAC_CC_DAM_UBS_AM |
307    XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(ctx->id, XDMAD_TRANSFER_RX));
308  ctx->rx_dma->desc.mbr_bc = UART_RX_DMA_BUF_SIZE - 1;
309  ctx->rx_dma->desc.mbr_ds = 0;
310  ctx->rx_dma->desc.mbr_sus = 0;
311  ctx->rx_dma->desc.mbr_dus = 0;
312
313  rc = XDMAD_ConfigureTransfer(
314    &XDMAD_Instance,
315    ctx->rx_dma_channel,
316    NULL,
317    XDMAC_CNDC_NDE_DSCR_FETCH_EN |
318    XDMAC_CNDC_NDVIEW_NDV3 |
319    XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
320    XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED,
321    (uint32_t)&ctx->rx_dma->desc,
322    0);
323  if (rc != XDMAD_OK) {
324    atsam_uart_disable_rx_dma(ctx);
325    return RTEMS_IO_ERROR;
326  }
327
328  rc = XDMAD_StartTransfer(&XDMAD_Instance, ctx->rx_dma_channel);
329  if (rc != XDMAD_OK) {
330    atsam_uart_disable_rx_dma(ctx);
331    return RTEMS_IO_ERROR;
332  }
333
334  ctx->rx_dma_enabled = true;
335
336  return RTEMS_SUCCESSFUL;
337}
338
339static bool atsam_uart_first_open(
340  rtems_termios_tty *tty,
341  rtems_termios_device_context *base,
342  struct termios *term,
343  rtems_libio_open_close_args_t *args
344)
345{
346  atsam_uart_context *ctx = (atsam_uart_context *) base;
347  Uart *regs = ctx->regs;
348#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
349  rtems_status_code sc;
350#endif
351
352  regs->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RSTSTA;
353  regs->UART_IDR = 0xffffffff;
354
355  PMC_EnablePeripheral(ctx->id);
356
357  rtems_termios_set_initial_baud(tty, ATSAM_CONSOLE_BAUD);
358  atsam_uart_set_attributes(base, term);
359
360#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
361  regs->UART_IER = UART_IDR_RXRDY;
362  sc = rtems_interrupt_handler_install(
363    ctx->irq,
364    ctx->is_usart ? "USART" : "UART",
365    RTEMS_INTERRUPT_SHARED,
366    atsam_uart_interrupt,
367    tty
368  );
369  if (sc != RTEMS_SUCCESSFUL) {
370    return false;
371  }
372#endif
373
374  return true;
375}
376
377static void atsam_uart_last_close(
378  rtems_termios_tty *tty,
379  rtems_termios_device_context *base,
380  rtems_libio_open_close_args_t *args
381)
382{
383  atsam_uart_context *ctx = (atsam_uart_context *) base;
384
385#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
386  rtems_interrupt_handler_remove(ctx->irq, atsam_uart_interrupt, tty);
387#endif
388
389  if (ctx->rx_dma_enabled) {
390    atsam_uart_disable_rx_dma(ctx);
391  }
392
393  if (!ctx->console) {
394    PMC_DisablePeripheral(ctx->id);
395  }
396}
397
398static void atsam_uart_write(
399  rtems_termios_device_context *base,
400  const char *buf,
401  size_t len
402)
403{
404  atsam_uart_context *ctx = (atsam_uart_context *) base;
405  Uart *regs = ctx->regs;
406
407#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
408  if (len > 0) {
409    ctx->transmitting = true;
410    regs->UART_THR = buf[0];
411    regs->UART_IER = UART_IDR_TXEMPTY;
412  } else {
413    ctx->transmitting = false;
414    regs->UART_IDR = UART_IDR_TXEMPTY;
415  }
416#else
417  size_t i;
418
419  for (i = 0; i < len; ++i) {
420    while ((regs->UART_SR & UART_SR_TXEMPTY) == 0) {
421      /* Wait */
422    }
423
424    regs->UART_THR = buf[i];
425  }
426#endif
427}
428
429#ifndef ATSAM_CONSOLE_USE_INTERRUPTS
430static int atsam_uart_read(rtems_termios_device_context *base)
431{
432  atsam_uart_context *ctx = (atsam_uart_context *) base;
433  Uart *regs = ctx->regs;
434
435  if ((regs->UART_SR & UART_SR_RXRDY) != 0) {
436    return (char) regs->UART_RHR;
437  } else {
438    return -1;
439  }
440}
441#endif
442
443#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
444static int atsam_uart_ioctl(
445  rtems_termios_device_context *base,
446  ioctl_command_t               request,
447  void                         *buffer
448)
449{
450  atsam_uart_context *ctx = (atsam_uart_context *) base;
451  rtems_status_code sc;
452
453  switch (request) {
454    case ATSAM_UART_ENABLE_RX_DMA:
455      sc = atsam_uart_enable_rx_dma(ctx);
456      if (sc != RTEMS_SUCCESSFUL) {
457        rtems_set_errno_and_return_minus_one(EIO);
458      } else {
459        ctx->rx_dma_enabled = true;
460      }
461      break;
462    default:
463      rtems_set_errno_and_return_minus_one(EINVAL);
464  }
465
466  return 0;
467}
468#endif
469
470static const rtems_termios_device_handler atsam_uart_handler = {
471  .first_open = atsam_uart_first_open,
472  .last_close = atsam_uart_last_close,
473  .write = atsam_uart_write,
474  .set_attributes = atsam_uart_set_attributes,
475#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
476  .mode = TERMIOS_IRQ_DRIVEN,
477  .ioctl = atsam_uart_ioctl,
478#else
479  .poll_read = atsam_uart_read,
480  .mode = TERMIOS_POLLED
481#endif
482};
483
484rtems_status_code console_initialize(
485  rtems_device_major_number major,
486  rtems_device_minor_number minor,
487  void *arg
488)
489{
490  size_t i;
491
492  rtems_termios_initialize();
493
494  for (i = 0; i < RTEMS_ARRAY_SIZE(atsam_usart_instances); ++i) {
495    char usart[] = "/dev/ttyUSARTX";
496
497    usart[sizeof(usart) - 2] = (char) ('0' + i);
498    rtems_termios_device_install(
499      &usart[0],
500      &atsam_uart_handler,
501      NULL,
502      &atsam_usart_instances[i].base
503    );
504
505#if ATSAM_CONSOLE_DEVICE_TYPE == 0
506    if (i == ATSAM_CONSOLE_DEVICE_INDEX) {
507      atsam_usart_instances[i].console = true;
508      link(&usart[0], CONSOLE_DEVICE_NAME);
509    }
510#endif
511  }
512
513  for (i = 0; i < RTEMS_ARRAY_SIZE(atsam_uart_instances); ++i) {
514    char uart[] = "/dev/ttyUARTX";
515
516    uart[sizeof(uart) - 2] = (char) ('0' + i);
517    rtems_termios_device_install(
518      &uart[0],
519      &atsam_uart_handler,
520      NULL,
521      &atsam_uart_instances[i].base
522    );
523
524#if ATSAM_CONSOLE_DEVICE_TYPE == 1
525    if (i == ATSAM_CONSOLE_DEVICE_INDEX) {
526      atsam_uart_instances[i].console = true;
527      link(&uart[0], CONSOLE_DEVICE_NAME);
528    }
529#endif
530  }
531
532  return RTEMS_SUCCESSFUL;
533}
Note: See TracBrowser for help on using the repository browser.