File indexing completed on 2025-05-11 08:22:44
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
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
0266
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
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 }