Changeset 779847a5 in rtems


Ignore:
Timestamp:
01/12/22 15:12:20 (2 years ago)
Author:
Christian Mauderer <christian.mauderer@…>
Branches:
5
Children:
9942ff80
Parents:
d9c0dd3
git-author:
Christian Mauderer <christian.mauderer@…> (01/12/22 15:12:20)
git-committer:
Christian Mauderer <christian.mauderer@…> (01/18/22 07:50:06)
Message:

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

Location:
bsps/arm/atsam
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • bsps/arm/atsam/console/console.c

    rd9c0dd3 r779847a5  
    1717#include <bsp/fatal.h>
    1818#include <rtems/console.h>
     19#include <rtems/seterr.h>
    1920
    2021#include <rtems/termiostypes.h>
     
    2324
    2425#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;
    2533
    2634typedef struct {
     
    3341#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
    3442  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;
    3548#endif
    3649} atsam_uart_context;
     
    110123  uint32_t sr = regs->UART_SR;
    111124
    112   while ((sr & UART_SR_RXRDY) != 0) {
    113     char c = (char) regs->UART_RHR;
    114 
    115     rtems_termios_enqueue_raw_characters(tty, &c, 1);
    116 
    117     sr = regs->UART_SR;
     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    }
    118146  }
    119147
     
    197225}
    198226
     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
    199339static bool atsam_uart_first_open(
    200340  rtems_termios_tty *tty,
     
    247387#endif
    248388
     389  if (ctx->rx_dma_enabled) {
     390    atsam_uart_disable_rx_dma(ctx);
     391  }
     392
    249393  if (!ctx->console) {
    250394    PMC_DisablePeripheral(ctx->id);
     
    297441#endif
    298442
     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
    299470static const rtems_termios_device_handler atsam_uart_handler = {
    300471  .first_open = atsam_uart_first_open,
     
    303474  .set_attributes = atsam_uart_set_attributes,
    304475#ifdef ATSAM_CONSOLE_USE_INTERRUPTS
    305   .mode = TERMIOS_IRQ_DRIVEN
     476  .mode = TERMIOS_IRQ_DRIVEN,
     477  .ioctl = atsam_uart_ioctl,
    306478#else
    307479  .poll_read = atsam_uart_read,
  • bsps/arm/atsam/include/bsp.h

    rd9c0dd3 r779847a5  
    3434#include <bspopts.h>
    3535#include <bsp/default-initial-extension.h>
     36#include <sys/ioccom.h>
    3637
    3738#include <rtems.h>
     
    110111void bsp_restart( const void *const addr );
    111112
     113/*
     114 * This ioctl enables the receive DMA for an UART. The DMA can be usefull if you
     115 * loose characters in high interrupt load situations.
     116 *
     117 * Disabling the DMA again is only possible by closing all file descriptors of
     118 * that UART.
     119 *
     120 * Note that every UART needs one DMA channel and the system has only a limited
     121 * amount of DMAs. So only use it if you need it.
     122 */
     123#define ATSAM_UART_ENABLE_RX_DMA _IO('d', 0)
     124
    112125#ifdef __cplusplus
    113126}
Note: See TracChangeset for help on using the changeset viewer.