Back to home page

LXR

 
 

    


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

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (C) 2020 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/fdt.h>
0030 #include <bsp/fatal.h>
0031 #include <bsp/irq.h>
0032 #include <rtems/bspIo.h>
0033 #include <rtems/console.h>
0034 #include <rtems/termiostypes.h>
0035 
0036 #include <inttypes.h>
0037 #include <libfdt.h>
0038 #include <stdio.h>
0039 
0040 #include <chip.h>
0041 #include <fsl_lpuart.h>
0042 
0043 #define LPUART_MAX_INSTANCES 8
0044 
0045 #define LPUART_DATA_RT (LPUART_DATA_R0T0_MASK | LPUART_DATA_R1T1_MASK | \
0046                         LPUART_DATA_R2T2_MASK | LPUART_DATA_R3T3_MASK | \
0047                         LPUART_DATA_R4T4_MASK | LPUART_DATA_R5T5_MASK | \
0048                         LPUART_DATA_R6T6_MASK | LPUART_DATA_R7T7_MASK | \
0049                         LPUART_DATA_R8T8_MASK | LPUART_DATA_R9T9_MASK)
0050 
0051 typedef struct {
0052   rtems_termios_device_context base;
0053   volatile LPUART_Type *regs;
0054   rtems_vector_number irq;
0055   const char *path;
0056   clock_ip_name_t clock_ip;
0057   uint32_t src_clock_hz;
0058   lpuart_config_t config;
0059 } imxrt_lpuart_context;
0060 
0061 /* Static memory for the console UART because it might is initialized early. */
0062 static imxrt_lpuart_context imxrt_lpuart_console_instance;
0063 static imxrt_lpuart_context *imxrt_lpuart_console;
0064 
0065 static void imxrt_output_char(char c);
0066 static int imxrt_poll_char(void);
0067 
0068 static imxrt_lpuart_context *imxrt_lpuart_get_context(
0069   rtems_termios_device_context *base
0070 )
0071 {
0072   return RTEMS_CONTAINER_OF(base, imxrt_lpuart_context, base);
0073 }
0074 
0075 static void imxrt_lpuart_write_polled(
0076     rtems_termios_device_context *base,
0077     char c
0078 )
0079 {
0080   imxrt_lpuart_context *ctx = imxrt_lpuart_get_context(base);
0081   volatile LPUART_Type *regs = ctx->regs;
0082   rtems_interrupt_level isr_cookie;
0083   uint32_t ctrl;
0084 
0085   rtems_interrupt_disable(isr_cookie);
0086   ctrl = ctx->regs->CTRL;
0087   ctx->regs->CTRL = ctrl & ~LPUART_CTRL_TIE_MASK;
0088   rtems_interrupt_enable(isr_cookie);
0089 
0090   while ((regs->STAT & LPUART_STAT_TDRE_MASK) == 0) {
0091     /* Wait */
0092   }
0093 
0094   regs->DATA = c;
0095 
0096   if ((ctrl & LPUART_CTRL_TIE_MASK) != 0) {
0097     ctx->regs->CTRL |= LPUART_CTRL_TIE_MASK;
0098   }
0099 }
0100 
0101 static int imxrt_lpuart_read_polled(rtems_termios_device_context *base)
0102 {
0103   uint32_t data;
0104   imxrt_lpuart_context *ctx = imxrt_lpuart_get_context(base);
0105   volatile LPUART_Type *regs = ctx->regs;
0106 
0107   data = regs->DATA;
0108 
0109   if ( data & (LPUART_DATA_PARITYE_MASK | LPUART_DATA_FRETSC_MASK |
0110       LPUART_DATA_RXEMPT_MASK) ) {
0111     return -1;
0112   } else {
0113     return data & LPUART_DATA_RT;
0114   }
0115 }
0116 
0117 static bool imxrt_lpuart_set_attributes(
0118   rtems_termios_device_context *base,
0119   const struct termios *term
0120 )
0121 {
0122   imxrt_lpuart_context *ctx = imxrt_lpuart_get_context(base);
0123 
0124   switch (term->c_cflag & CSIZE) {
0125     case CS7:
0126       ctx->config.dataBitsCount = kLPUART_SevenDataBits;
0127       break;
0128     case CS8:
0129       ctx->config.dataBitsCount = kLPUART_EightDataBits;
0130       break;
0131     default:
0132       return false;
0133       break;
0134   }
0135 
0136   ctx->config.baudRate_Bps = rtems_termios_baud_to_number(term->c_ospeed);
0137 
0138   if ((term->c_cflag & CSTOPB) != 0) {
0139     ctx->config.stopBitCount = kLPUART_TwoStopBit;
0140   } else {
0141     ctx->config.stopBitCount = kLPUART_OneStopBit;
0142   }
0143 
0144   if ((term->c_cflag & PARENB) != 0) {
0145     if ((term->c_cflag & PARODD) != 0) {
0146       ctx->config.parityMode = kLPUART_ParityOdd;
0147     } else {
0148       ctx->config.parityMode = kLPUART_ParityEven;
0149     }
0150   } else {
0151     ctx->config.parityMode = kLPUART_ParityDisabled;
0152   }
0153 
0154   if ((term->c_cflag & CREAD) != 0) {
0155     ctx->config.enableRx = true;
0156   } else {
0157     ctx->config.enableRx = false;
0158   }
0159 
0160   if ((term->c_cflag & CCTS_OFLOW) != 0) {
0161     ctx->config.enableTxCTS = true;
0162   } else {
0163     ctx->config.enableTxCTS = false;
0164   }
0165 
0166   if ((term->c_cflag & CRTS_IFLOW) != 0) {
0167     ctx->config.enableRxRTS = true;
0168   } else {
0169     ctx->config.enableRxRTS = false;
0170   }
0171 
0172   (void) LPUART_Init((LPUART_Type *)ctx->regs, &ctx->config,
0173       ctx->src_clock_hz, false);
0174 
0175   return true;
0176 }
0177 
0178 static uint32_t imxrt_lpuart_get_src_freq(clock_ip_name_t clock_ip)
0179 {
0180   uint32_t freq;
0181 #if IMXRT_IS_MIMXRT10xx
0182   uint32_t mux;
0183   uint32_t divider;
0184 
0185   (void) clock_ip; /* Not necessary for i.MXRT1050 */
0186 
0187   mux = CLOCK_GetMux(kCLOCK_UartMux);
0188   divider = 1;
0189 
0190   switch (mux) {
0191   case 0: /* pll3_sw_clk */
0192     freq = CLOCK_GetFreq(kCLOCK_Usb1PllClk);
0193     divider = 6;
0194     break;
0195   case 1: /* OSC */
0196     freq = CLOCK_GetFreq(kCLOCK_OscClk);
0197     break;
0198   default:
0199     freq = 0;
0200   }
0201 
0202   divider *= CLOCK_GetDiv(kCLOCK_UartDiv) + 1U;
0203   freq /= divider;
0204 #elif IMXRT_IS_MIMXRT11xx
0205   /*
0206    * FIXME: A future version of the mcux_sdk might provide a better method to
0207    * get the clock instead of this hack.
0208    */
0209   clock_root_t clock_root = clock_ip + kCLOCK_Root_Lpuart1 - kCLOCK_Lpuart1;
0210 
0211   freq = CLOCK_GetRootClockFreq(clock_root);
0212 #else
0213   #error Getting UART clock frequency is not implemented for this chip
0214 #endif
0215 
0216   return freq;
0217 }
0218 
0219 static clock_ip_name_t imxrt_lpuart_clock_ip(volatile LPUART_Type *regs)
0220 {
0221   LPUART_Type *const base_addresses[] = LPUART_BASE_PTRS;
0222   static const clock_ip_name_t lpuart_clocks[] = LPUART_CLOCKS;
0223   size_t i;
0224 
0225   for (i = 0; i < RTEMS_ARRAY_SIZE(base_addresses); ++i) {
0226     if (base_addresses[i] == regs) {
0227       return lpuart_clocks[i];
0228     }
0229   }
0230 
0231   return kCLOCK_IpInvalid;
0232 }
0233 
0234 static void imxrt_lpuart_init_hardware(imxrt_lpuart_context *ctx)
0235 {
0236   (void) LPUART_Init((LPUART_Type *)ctx->regs, &ctx->config,
0237       ctx->src_clock_hz, true);
0238 }
0239 
0240 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0241 static void imxrt_lpuart_interrupt(void *arg)
0242 {
0243   rtems_termios_tty *tty = arg;
0244   rtems_termios_device_context *base = rtems_termios_get_device_context(tty);
0245   imxrt_lpuart_context *ctx = imxrt_lpuart_get_context(base);
0246   uint32_t stat;
0247   uint32_t data;
0248 
0249   stat = ctx->regs->STAT;
0250 
0251   if ((stat & LPUART_STAT_RDRF_MASK) != 0) {
0252     do {
0253       char c;
0254       data = ctx->regs->DATA;
0255 
0256       if ((data & (LPUART_DATA_PARITYE_MASK | LPUART_DATA_FRETSC_MASK |
0257           LPUART_DATA_RXEMPT_MASK)) == 0) {
0258         c = data & LPUART_DATA_RT;
0259         rtems_termios_enqueue_raw_characters(tty, &c, 1);
0260       }
0261     } while ((data & LPUART_DATA_RXEMPT_MASK) == 0);
0262   }
0263 
0264   if ((ctx->regs->CTRL & LPUART_CTRL_TIE_MASK) != 0
0265       && (stat & LPUART_STAT_TDRE_MASK) != 0) {
0266     /* Note: This will call imxrt_lpuart_write */
0267     rtems_termios_dequeue_characters(tty, 1);
0268   }
0269 }
0270 #endif
0271 
0272 static bool imxrt_lpuart_first_open(
0273   rtems_termios_tty *tty,
0274   rtems_termios_device_context *base,
0275   struct termios *term,
0276   rtems_libio_open_close_args_t *args
0277 )
0278 {
0279 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0280   rtems_status_code sc;
0281 #endif
0282   imxrt_lpuart_context *ctx = imxrt_lpuart_get_context(base);
0283   rtems_termios_set_initial_baud(tty, BSP_CONSOLE_BAUD);
0284 
0285 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0286   sc = rtems_interrupt_handler_install(
0287     ctx->irq,
0288     "LPUART",
0289     RTEMS_INTERRUPT_SHARED,
0290     imxrt_lpuart_interrupt,
0291     tty
0292   );
0293   if (sc != RTEMS_SUCCESSFUL) {
0294     return false;
0295   }
0296 #endif
0297 
0298   imxrt_lpuart_init_hardware(ctx);
0299 
0300 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0301   ctx->regs->CTRL |= LPUART_CTRL_RIE_MASK;
0302 #endif
0303 
0304   imxrt_lpuart_set_attributes(base, term);
0305 
0306   return true;
0307 }
0308 
0309 static void imxrt_lpuart_last_close(
0310   rtems_termios_tty *tty,
0311   rtems_termios_device_context *base,
0312   rtems_libio_open_close_args_t *args
0313 )
0314 {
0315   imxrt_lpuart_context *ctx = imxrt_lpuart_get_context(base);
0316 
0317   LPUART_Deinit((LPUART_Type *)ctx->regs);
0318 
0319 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0320   (void) rtems_interrupt_handler_remove(
0321     ctx->irq,
0322     imxrt_lpuart_interrupt,
0323     tty
0324   );
0325 #endif
0326 }
0327 
0328 static void imxrt_lpuart_write(
0329   rtems_termios_device_context *base,
0330   const char *buf,
0331   size_t len
0332 )
0333 {
0334 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0335   imxrt_lpuart_context *ctx = imxrt_lpuart_get_context(base);
0336 
0337   if (len > 0) {
0338     ctx->regs->DATA = (uint8_t) buf[0];
0339     ctx->regs->CTRL |= LPUART_CTRL_TIE_MASK;
0340   } else {
0341     ctx->regs->CTRL &= ~LPUART_CTRL_TIE_MASK;
0342   }
0343 #else
0344   size_t i;
0345 
0346   for (i = 0; i < len; ++i) {
0347     imxrt_lpuart_write_polled(base, buf[i]);
0348   }
0349 #endif
0350 }
0351 
0352 static const rtems_termios_device_handler imxrt_lpuart_handler = {
0353   .first_open = imxrt_lpuart_first_open,
0354   .last_close = imxrt_lpuart_last_close,
0355   .write = imxrt_lpuart_write,
0356   .set_attributes = imxrt_lpuart_set_attributes,
0357 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0358   .mode = TERMIOS_IRQ_DRIVEN,
0359 #else
0360   .poll_read = imxrt_lpuart_read_polled,
0361   .mode = TERMIOS_POLLED,
0362 #endif
0363 };
0364 
0365 static int imxrt_lpuart_get_stdout_node(const void *fdt)
0366 {
0367   int node;
0368   const char *console;
0369 
0370   node = fdt_path_offset(fdt, "/chosen");
0371   if (node < 0) {
0372     bsp_fatal(IMXRT_FATAL_NO_CONSOLE);
0373   }
0374 
0375   console = fdt_getprop(fdt, node, "stdout-path", NULL);
0376   if (console == NULL) {
0377     bsp_fatal(IMXRT_FATAL_NO_CONSOLE);
0378   }
0379 
0380   node = fdt_path_offset(fdt, console);
0381   if (node < 0) {
0382     bsp_fatal(IMXRT_FATAL_NO_CONSOLE);
0383   }
0384 
0385   return node;
0386 }
0387 
0388 static void imxrt_lpuart_init_context_from_fdt(
0389   imxrt_lpuart_context *ctx,
0390   const void *fdt,
0391   int node
0392 )
0393 {
0394   memset(&ctx->base, 0, sizeof(ctx->base));
0395 
0396   ctx->regs = imx_get_reg_of_node(fdt, node);
0397   if (ctx->regs == NULL) {
0398     bsp_fatal(IMXRT_FATAL_LPUART_INVALID_FDT);
0399   }
0400 
0401   ctx->irq = imx_get_irq_of_node(fdt, node, 0);
0402   if (ctx->irq == BSP_INTERRUPT_VECTOR_INVALID) {
0403     bsp_fatal(IMXRT_FATAL_LPUART_INVALID_FDT);
0404   }
0405 
0406   ctx->path = fdt_getprop(fdt, node, "rtems,path", NULL);
0407   if (ctx->path == NULL) {
0408     bsp_fatal(IMXRT_FATAL_LPI2C_INVALID_FDT);
0409   }
0410 
0411   ctx->clock_ip = imxrt_lpuart_clock_ip(ctx->regs);
0412   ctx->src_clock_hz = imxrt_lpuart_get_src_freq(ctx->clock_ip);
0413 
0414   LPUART_GetDefaultConfig(&ctx->config);
0415   ctx->config.enableTx = true;
0416   ctx->config.enableRx = true;
0417 
0418   rtems_termios_device_context_initialize(&ctx->base, "LPUART");
0419 }
0420 
0421 static void imxrt_lpuart_console_probe_early(void)
0422 {
0423   int node;
0424   const void *fdt;
0425 
0426   imxrt_lpuart_console = NULL;
0427 
0428   fdt = bsp_fdt_get();
0429   node = imxrt_lpuart_get_stdout_node(fdt);
0430   imxrt_lpuart_init_context_from_fdt(&imxrt_lpuart_console_instance, fdt, node);
0431   (void) LPUART_Init(
0432     (LPUART_Type *)imxrt_lpuart_console_instance.regs,
0433     &imxrt_lpuart_console_instance.config,
0434     imxrt_lpuart_console_instance.src_clock_hz,
0435     true
0436   );
0437   imxrt_lpuart_console = &imxrt_lpuart_console_instance;
0438 
0439   BSP_output_char = imxrt_output_char;
0440   BSP_poll_char = imxrt_poll_char;
0441 }
0442 
0443 rtems_status_code console_initialize(
0444   rtems_device_major_number major,
0445   rtems_device_minor_number minor,
0446   void *arg
0447 )
0448 {
0449   const void *fdt;
0450   int stdout_node;
0451   int node;
0452   rtems_status_code sc;
0453 
0454   fdt = bsp_fdt_get();
0455   stdout_node = imxrt_lpuart_get_stdout_node(fdt);
0456   node = -1;
0457 
0458   rtems_termios_initialize();
0459 
0460   do {
0461     node = fdt_node_offset_by_compatible(fdt, node, "nxp,imxrt-lpuart");
0462 
0463     if (node >= 0 && imxrt_fdt_node_is_enabled(fdt, node)) {
0464       imxrt_lpuart_context *ctx;
0465 
0466       if (node != stdout_node) {
0467         ctx = calloc(1, sizeof(imxrt_lpuart_context));
0468         if (ctx == NULL) {
0469           bsp_fatal(IMXRT_FATAL_LPUART_ALLOC_FAILED);
0470         }
0471 
0472         imxrt_lpuart_init_context_from_fdt(ctx, fdt, node);
0473 
0474       } else {
0475         ctx = imxrt_lpuart_console;
0476         if (ctx == NULL) {
0477           imxrt_lpuart_console_probe_early();
0478           ctx = imxrt_lpuart_console;
0479         }
0480       }
0481 
0482       sc = rtems_termios_device_install(
0483         ctx->path,
0484         &imxrt_lpuart_handler,
0485         NULL,
0486         &ctx->base
0487       );
0488       if (sc != RTEMS_SUCCESSFUL) {
0489         bsp_fatal(IMXRT_FATAL_LPUART_INSTALL_FAILED);
0490       }
0491 
0492       if (node == stdout_node) {
0493         link(ctx->path, CONSOLE_DEVICE_NAME);
0494       }
0495     }
0496   } while (node >= 0);
0497 
0498   return RTEMS_SUCCESSFUL;
0499 }
0500 
0501 static void imxrt_output_char(char c)
0502 {
0503   if (imxrt_lpuart_console != NULL) {
0504     imxrt_lpuart_write_polled(&imxrt_lpuart_console->base, c);
0505   }
0506 }
0507 
0508 static int imxrt_poll_char(void)
0509 {
0510   return imxrt_lpuart_read_polled(&imxrt_lpuart_console->base);
0511 }
0512 
0513 static void imxrt_output_char_init(char c)
0514 {
0515   imxrt_lpuart_console_probe_early();
0516   imxrt_output_char(c);
0517 }
0518 
0519 BSP_output_char_function_type BSP_output_char = imxrt_output_char_init;
0520 
0521 BSP_polling_getchar_function_type BSP_poll_char = NULL;