Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:22:44

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (c) 2016 embedded brains GmbH & Co. KG
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions
0008  * are met:
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0025  * POSSIBILITY OF SUCH DAMAGE.
0026  */
0027 
0028 #include <bsp.h>
0029 #include <bsp/irq.h>
0030 #include <bsp/fatal.h>
0031 #include <rtems/console.h>
0032 #include <rtems/seterr.h>
0033 
0034 #include <rtems/termiostypes.h>
0035 
0036 #include <chip.h>
0037 
0038 #include <unistd.h>
0039 
0040 #define UART_RX_DMA_BUF_SIZE 32l
0041 
0042 typedef struct {
0043   char buf[UART_RX_DMA_BUF_SIZE];
0044   LinkedListDescriporView3 desc;
0045 } atsam_uart_rx_dma;
0046 
0047 typedef struct {
0048   rtems_termios_device_context base;
0049   Uart *regs;
0050   rtems_vector_number irq;
0051   uint32_t id;
0052   bool console;
0053   bool is_usart;
0054 #ifdef ATSAM_CONSOLE_USE_INTERRUPTS
0055   bool transmitting;
0056   bool rx_dma_enabled;
0057   uint32_t rx_dma_channel;
0058   atsam_uart_rx_dma *rx_dma;
0059   char *volatile*rx_dma_da;
0060   char *rx_next_read_pos;
0061 #endif
0062 } atsam_uart_context;
0063 
0064 static atsam_uart_context atsam_usart_instances[] = {
0065   {
0066     .regs = (Uart *)USART0,
0067     .irq = USART0_IRQn,
0068     .id = ID_USART0,
0069     .is_usart = true,
0070   }
0071 #ifdef USART1
0072   , {
0073     .regs = (Uart *)USART1,
0074     .irq = USART1_IRQn,
0075     .id = ID_USART1,
0076     .is_usart = true,
0077   }
0078 #endif
0079 #ifdef USART2
0080   , {
0081     .regs = (Uart *)USART2,
0082     .irq = USART2_IRQn,
0083     .id = ID_USART2,
0084     .is_usart = true,
0085   }
0086 #endif
0087 };
0088 
0089 static atsam_uart_context atsam_uart_instances[] = {
0090   {
0091     .regs = UART0,
0092     .irq = UART0_IRQn,
0093     .id = ID_UART0,
0094     .is_usart = false,
0095   }
0096 #ifdef UART1
0097   , {
0098     .regs = UART1,
0099     .irq = UART1_IRQn,
0100     .id = ID_UART1,
0101     .is_usart = false,
0102   }
0103 #endif
0104 #ifdef UART2
0105   , {
0106     .regs = UART2,
0107     .irq = UART2_IRQn,
0108     .id = ID_UART2,
0109     .is_usart = false,
0110   }
0111 #endif
0112 #ifdef UART3
0113   , {
0114     .regs = UART3,
0115     .irq = UART3_IRQn,
0116     .id = ID_UART3,
0117     .is_usart = false,
0118   }
0119 #endif
0120 #ifdef UART4
0121   , {
0122     .regs = UART4,
0123     .irq = UART4_IRQn,
0124     .id = ID_UART4,
0125     .is_usart = false,
0126   }
0127 #endif
0128 };
0129 
0130 #ifdef ATSAM_CONSOLE_USE_INTERRUPTS
0131 static void atsam_uart_interrupt(void *arg)
0132 {
0133   rtems_termios_tty *tty = arg;
0134   atsam_uart_context *ctx = rtems_termios_get_device_context(tty);
0135   Uart *regs = ctx->regs;
0136   uint32_t sr = regs->UART_SR;
0137 
0138   if (!ctx->rx_dma_enabled) {
0139     while ((sr & UART_SR_RXRDY) != 0) {
0140       char c = (char) regs->UART_RHR;
0141 
0142       rtems_termios_enqueue_raw_characters(tty, &c, 1);
0143 
0144       sr = regs->UART_SR;
0145     }
0146   } else {
0147     while (*ctx->rx_dma_da != ctx->rx_next_read_pos) {
0148       char c;
0149 
0150       c = *ctx->rx_next_read_pos;
0151 
0152       ++ctx->rx_next_read_pos;
0153       if (ctx->rx_next_read_pos >= &ctx->rx_dma->buf[UART_RX_DMA_BUF_SIZE]) {
0154         ctx->rx_next_read_pos = &ctx->rx_dma->buf[0];
0155       }
0156 
0157       rtems_termios_enqueue_raw_characters(tty, &c, 1);
0158     }
0159   }
0160 
0161   while (ctx->transmitting && (sr & UART_SR_TXRDY) != 0) {
0162     rtems_termios_dequeue_characters(tty, 1);
0163     sr = regs->UART_SR;
0164   }
0165 }
0166 #endif
0167 
0168 static bool atsam_uart_set_attributes(
0169   rtems_termios_device_context *base,
0170   const struct termios *term
0171 )
0172 {
0173   atsam_uart_context *ctx = (atsam_uart_context *) base;
0174   Uart *regs = ctx->regs;
0175   rtems_termios_baud_t baud;
0176   uint32_t mr;
0177 
0178   baud = rtems_termios_baud_to_number(term->c_ospeed);
0179   regs->UART_BRGR = (BOARD_MCK / baud) / 16;
0180 
0181   if ((term->c_cflag & CREAD) != 0) {
0182     regs->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
0183   } else {
0184     regs->UART_CR = UART_CR_TXEN;
0185   }
0186 
0187   if (ctx->is_usart) {
0188     mr = US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK;
0189   } else {
0190     mr = UART_MR_FILTER_DISABLED | UART_MR_BRSRCCK_PERIPH_CLK;
0191   }
0192 
0193   if (ctx->is_usart) {
0194     switch (term->c_cflag & CSIZE) {
0195       case CS5:
0196         mr |= US_MR_CHRL_5_BIT;
0197         break;
0198       case CS6:
0199         mr |= US_MR_CHRL_6_BIT;
0200         break;
0201       case CS7:
0202         mr |= US_MR_CHRL_7_BIT;
0203         break;
0204       default:
0205         mr |= US_MR_CHRL_8_BIT;
0206         break;
0207     }
0208   } else {
0209     if ((term->c_cflag & CSIZE) != CS8) {
0210       return false;
0211     }
0212   }
0213 
0214   if ((term->c_cflag & PARENB) != 0) {
0215     if ((term->c_cflag & PARODD) != 0) {
0216       mr |= UART_MR_PAR_ODD;
0217     } else {
0218       mr |= UART_MR_PAR_EVEN;
0219     }
0220   } else {
0221     mr |= UART_MR_PAR_NO;
0222   }
0223 
0224   if (ctx->is_usart) {
0225     if ((term->c_cflag & CSTOPB) != 0) {
0226       mr |= US_MR_NBSTOP_2_BIT;
0227     } else {
0228       mr |= US_MR_NBSTOP_1_BIT;
0229     }
0230   } else {
0231     if ((term->c_cflag & CSTOPB) != 0) {
0232       return false;
0233     }
0234   }
0235 
0236   regs->UART_MR = mr;
0237 
0238   return true;
0239 }
0240 
0241 static void atsam_uart_disable_rx_dma(atsam_uart_context *ctx)
0242 {
0243   if (ctx->rx_dma) {
0244     rtems_cache_coherent_free(ctx->rx_dma);
0245     ctx->rx_dma = NULL;
0246   }
0247 
0248   if (ctx->rx_dma_channel != XDMAD_ALLOC_FAILED) {
0249     XDMAD_FreeChannel(&XDMAD_Instance, ctx->rx_dma_channel);
0250   }
0251 
0252   ctx->rx_dma_enabled = false;
0253 }
0254 
0255 static rtems_status_code atsam_uart_enable_rx_dma(atsam_uart_context *ctx)
0256 {
0257   eXdmadRC rc;
0258   int channel_id;
0259 
0260   if (ctx->rx_dma_enabled) {
0261     return RTEMS_SUCCESSFUL;
0262   }
0263 
0264   /*
0265    * Make sure everything is in a clean default state so that the cleanup works
0266    * in an error case.
0267    */
0268   ctx->rx_dma = NULL;
0269   ctx->rx_dma_channel = XDMAD_ALLOC_FAILED;
0270 
0271   ctx->rx_dma = rtems_cache_coherent_allocate(sizeof(*ctx->rx_dma), 0, 0);
0272   if (ctx->rx_dma == NULL) {
0273     atsam_uart_disable_rx_dma(ctx);
0274     return RTEMS_NO_MEMORY;
0275   }
0276 
0277   ctx->rx_next_read_pos = &ctx->rx_dma->buf[0];
0278 
0279   ctx->rx_dma_channel = XDMAD_AllocateChannel(
0280     &XDMAD_Instance,
0281     XDMAD_TRANSFER_MEMORY,
0282     ctx->id
0283   );
0284 
0285   if (ctx->rx_dma_channel == XDMAD_ALLOC_FAILED) {
0286     atsam_uart_disable_rx_dma(ctx);
0287     return RTEMS_IO_ERROR;
0288   }
0289 
0290   rc = XDMAD_PrepareChannel(&XDMAD_Instance, ctx->rx_dma_channel);
0291   if (rc != XDMAD_OK) {
0292     atsam_uart_disable_rx_dma(ctx);
0293     return RTEMS_IO_ERROR;
0294   }
0295 
0296   channel_id = ctx->rx_dma_channel & 0xff;
0297   ctx->rx_dma_da =
0298     (char *volatile*) &XDMAD_Instance.pXdmacs->XDMAC_CHID[channel_id].XDMAC_CDA;
0299 
0300   ctx->rx_dma->desc.mbr_nda = (uint32_t)&ctx->rx_dma->desc;
0301   ctx->rx_dma->desc.mbr_ubc =
0302     1 |
0303     XDMA_UBC_NVIEW_NDV3 |
0304     XDMA_UBC_NDE_FETCH_EN |
0305     XDMA_UBC_NDEN_UPDATED |
0306     XDMA_UBC_NSEN_UPDATED;
0307   ctx->rx_dma->desc.mbr_sa = (uint32_t) &ctx->regs->UART_RHR;
0308   ctx->rx_dma->desc.mbr_da = (uint32_t) &ctx->rx_dma->buf[0];
0309   ctx->rx_dma->desc.mbr_cfg =
0310     XDMAC_CC_TYPE_PER_TRAN |
0311     XDMAC_CC_MBSIZE_SINGLE |
0312     XDMAC_CC_DSYNC_PER2MEM |
0313     XDMAC_CC_SWREQ_HWR_CONNECTED |
0314     XDMAC_CC_MEMSET_NORMAL_MODE |
0315     XDMAC_CC_CSIZE_CHK_1 |
0316     XDMAC_CC_DWIDTH_BYTE |
0317     XDMAC_CC_SIF_AHB_IF1 |
0318     XDMAC_CC_DIF_AHB_IF1 |
0319     XDMAC_CC_SAM_FIXED_AM |
0320     XDMAC_CC_DAM_UBS_AM |
0321     XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(ctx->id, XDMAD_TRANSFER_RX));
0322   ctx->rx_dma->desc.mbr_bc = UART_RX_DMA_BUF_SIZE - 1;
0323   ctx->rx_dma->desc.mbr_ds = 0;
0324   ctx->rx_dma->desc.mbr_sus = 0;
0325   ctx->rx_dma->desc.mbr_dus = 0;
0326 
0327   rc = XDMAD_ConfigureTransfer(
0328     &XDMAD_Instance,
0329     ctx->rx_dma_channel,
0330     NULL,
0331     XDMAC_CNDC_NDE_DSCR_FETCH_EN |
0332     XDMAC_CNDC_NDVIEW_NDV3 |
0333     XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
0334     XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED,
0335     (uint32_t)&ctx->rx_dma->desc,
0336     0);
0337   if (rc != XDMAD_OK) {
0338     atsam_uart_disable_rx_dma(ctx);
0339     return RTEMS_IO_ERROR;
0340   }
0341 
0342   rc = XDMAD_StartTransfer(&XDMAD_Instance, ctx->rx_dma_channel);
0343   if (rc != XDMAD_OK) {
0344     atsam_uart_disable_rx_dma(ctx);
0345     return RTEMS_IO_ERROR;
0346   }
0347 
0348   ctx->rx_dma_enabled = true;
0349 
0350   return RTEMS_SUCCESSFUL;
0351 }
0352 
0353 static bool atsam_uart_first_open(
0354   rtems_termios_tty *tty,
0355   rtems_termios_device_context *base,
0356   struct termios *term,
0357   rtems_libio_open_close_args_t *args
0358 )
0359 {
0360   atsam_uart_context *ctx = (atsam_uart_context *) base;
0361   Uart *regs = ctx->regs;
0362 #ifdef ATSAM_CONSOLE_USE_INTERRUPTS
0363   rtems_status_code sc;
0364 #endif
0365 
0366   regs->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RSTSTA;
0367   regs->UART_IDR = 0xffffffff;
0368 
0369   PMC_EnablePeripheral(ctx->id);
0370 
0371   rtems_termios_set_initial_baud(tty, ATSAM_CONSOLE_BAUD);
0372   atsam_uart_set_attributes(base, term);
0373 
0374 #ifdef ATSAM_CONSOLE_USE_INTERRUPTS
0375   regs->UART_IER = UART_IDR_RXRDY;
0376   sc = rtems_interrupt_handler_install(
0377     ctx->irq,
0378     ctx->is_usart ? "USART" : "UART",
0379     RTEMS_INTERRUPT_SHARED,
0380     atsam_uart_interrupt,
0381     tty
0382   );
0383   if (sc != RTEMS_SUCCESSFUL) {
0384     return false;
0385   }
0386 #endif
0387 
0388   return true;
0389 }
0390 
0391 static void atsam_uart_last_close(
0392   rtems_termios_tty *tty,
0393   rtems_termios_device_context *base,
0394   rtems_libio_open_close_args_t *args
0395 )
0396 {
0397   atsam_uart_context *ctx = (atsam_uart_context *) base;
0398 
0399 #ifdef ATSAM_CONSOLE_USE_INTERRUPTS
0400   rtems_interrupt_handler_remove(ctx->irq, atsam_uart_interrupt, tty);
0401 #endif
0402 
0403   if (ctx->rx_dma_enabled) {
0404     atsam_uart_disable_rx_dma(ctx);
0405   }
0406 
0407   if (!ctx->console) {
0408     PMC_DisablePeripheral(ctx->id);
0409   }
0410 }
0411 
0412 static void atsam_uart_write(
0413   rtems_termios_device_context *base,
0414   const char *buf,
0415   size_t len
0416 )
0417 {
0418   atsam_uart_context *ctx = (atsam_uart_context *) base;
0419   Uart *regs = ctx->regs;
0420 
0421 #ifdef ATSAM_CONSOLE_USE_INTERRUPTS
0422   if (len > 0) {
0423     ctx->transmitting = true;
0424     regs->UART_THR = buf[0];
0425     regs->UART_IER = UART_IDR_TXRDY;
0426   } else {
0427     ctx->transmitting = false;
0428     regs->UART_IDR = UART_IDR_TXRDY;
0429   }
0430 #else
0431   size_t i;
0432 
0433   for (i = 0; i < len; ++i) {
0434     while ((regs->UART_SR & UART_SR_TXRDY) == 0) {
0435       /* Wait */
0436     }
0437 
0438     regs->UART_THR = buf[i];
0439   }
0440 #endif
0441 }
0442 
0443 #ifndef ATSAM_CONSOLE_USE_INTERRUPTS
0444 static int atsam_uart_read(rtems_termios_device_context *base)
0445 {
0446   atsam_uart_context *ctx = (atsam_uart_context *) base;
0447   Uart *regs = ctx->regs;
0448 
0449   if ((regs->UART_SR & UART_SR_RXRDY) != 0) {
0450     return (char) regs->UART_RHR;
0451   } else {
0452     return -1;
0453   }
0454 }
0455 #endif
0456 
0457 #ifdef ATSAM_CONSOLE_USE_INTERRUPTS
0458 static int atsam_uart_ioctl(
0459   rtems_termios_device_context *base,
0460   ioctl_command_t               request,
0461   void                         *buffer
0462 )
0463 {
0464   atsam_uart_context *ctx = (atsam_uart_context *) base;
0465   rtems_status_code sc;
0466 
0467   switch (request) {
0468     case ATSAM_UART_ENABLE_RX_DMA:
0469       sc = atsam_uart_enable_rx_dma(ctx);
0470       if (sc != RTEMS_SUCCESSFUL) {
0471         rtems_set_errno_and_return_minus_one(EIO);
0472       } else {
0473         ctx->rx_dma_enabled = true;
0474       }
0475       break;
0476     default:
0477       rtems_set_errno_and_return_minus_one(EINVAL);
0478   }
0479 
0480   return 0;
0481 }
0482 #endif
0483 
0484 static const rtems_termios_device_handler atsam_uart_handler = {
0485   .first_open = atsam_uart_first_open,
0486   .last_close = atsam_uart_last_close,
0487   .write = atsam_uart_write,
0488   .set_attributes = atsam_uart_set_attributes,
0489 #ifdef ATSAM_CONSOLE_USE_INTERRUPTS
0490   .mode = TERMIOS_IRQ_DRIVEN,
0491   .ioctl = atsam_uart_ioctl,
0492 #else
0493   .poll_read = atsam_uart_read,
0494   .mode = TERMIOS_POLLED
0495 #endif
0496 };
0497 
0498 rtems_status_code console_initialize(
0499   rtems_device_major_number major,
0500   rtems_device_minor_number minor,
0501   void *arg
0502 )
0503 {
0504   size_t i;
0505 
0506   rtems_termios_initialize();
0507 
0508   for (i = 0; i < RTEMS_ARRAY_SIZE(atsam_usart_instances); ++i) {
0509     char usart[] = "/dev/ttyUSARTX";
0510 
0511     usart[sizeof(usart) - 2] = (char) ('0' + i);
0512     rtems_termios_device_install(
0513       &usart[0],
0514       &atsam_uart_handler,
0515       NULL,
0516       &atsam_usart_instances[i].base
0517     );
0518 
0519 #if ATSAM_CONSOLE_DEVICE_TYPE == 0
0520     if (i == ATSAM_CONSOLE_DEVICE_INDEX) {
0521       atsam_usart_instances[i].console = true;
0522       link(&usart[0], CONSOLE_DEVICE_NAME);
0523     }
0524 #endif
0525   }
0526 
0527   for (i = 0; i < RTEMS_ARRAY_SIZE(atsam_uart_instances); ++i) {
0528     char uart[] = "/dev/ttyUARTX";
0529 
0530     uart[sizeof(uart) - 2] = (char) ('0' + i);
0531     rtems_termios_device_install(
0532       &uart[0],
0533       &atsam_uart_handler,
0534       NULL,
0535       &atsam_uart_instances[i].base
0536     );
0537 
0538 #if ATSAM_CONSOLE_DEVICE_TYPE == 1
0539     if (i == ATSAM_CONSOLE_DEVICE_INDEX) {
0540       atsam_uart_instances[i].console = true;
0541       link(&uart[0], CONSOLE_DEVICE_NAME);
0542     }
0543 #endif
0544   }
0545 
0546   return RTEMS_SUCCESSFUL;
0547 }