Back to home page

LXR

 
 

    


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

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (c) 2017 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 <sys/param.h>
0029 
0030 #include <rtems/bspIo.h>
0031 #include <rtems/console.h>
0032 #include <rtems/sysinit.h>
0033 #include <rtems/termiostypes.h>
0034 
0035 #include <bsp.h>
0036 #include <bsp/fdt.h>
0037 #include <bsp/irq.h>
0038 
0039 #include <arm/freescale/imx/imx_ccmvar.h>
0040 #include <arm/freescale/imx/imx_uartreg.h>
0041 
0042 #include <libfdt.h>
0043 
0044 #define IMX_UART_TX_FIFO_LEVEL 16
0045 
0046 typedef struct {
0047   rtems_termios_device_context base;
0048   volatile imx_uart *regs;
0049 #ifdef CONSOLE_USE_INTERRUPTS
0050   rtems_vector_number irq;
0051   int tx_in_progress;
0052 #endif
0053 } imx_uart_context;
0054 
0055 static imx_uart_context imx_uart_instances[7];
0056 
0057 static imx_uart_context *imx_uart_console = &imx_uart_instances[0];
0058 
0059 static volatile imx_uart *imx_uart_get_regs(rtems_termios_device_context *base)
0060 {
0061   imx_uart_context *ctx;
0062 
0063   ctx = (imx_uart_context *) base;
0064   return ctx->regs;
0065 }
0066 
0067 static void imx_uart_write_polled(rtems_termios_device_context *base, char c)
0068 {
0069   volatile imx_uart *regs;
0070 
0071   regs = imx_uart_get_regs(base);
0072 
0073   while ((regs->usr1 & IMX_UART_USR1_TRDY) == 0) {
0074     /* Wait */
0075   }
0076 
0077   regs->utxd = IMX_UART_UTXD_TX_DATA(c);
0078 }
0079 
0080 static int imx_uart_read_polled(rtems_termios_device_context *base)
0081 {
0082   volatile imx_uart *regs;
0083 
0084   regs = imx_uart_get_regs(base);
0085 
0086   if ((regs->usr2 & IMX_UART_USR2_RDR) != 0) {
0087     return IMX_UART_URXD_RX_DATA_GET(regs->urxd);
0088   } else {
0089     return -1;
0090   }
0091 }
0092 
0093 void imx_uart_console_drain(void)
0094 {
0095   volatile imx_uart *regs;
0096 
0097   regs = imx_uart_get_regs(&imx_uart_console->base);
0098 
0099   if (regs != NULL) {
0100     while ((regs->usr2 & IMX_UART_USR2_TXFE) == 0) {
0101       /* Wait */
0102     }
0103   }
0104 }
0105 
0106 static void imx_output_char(char c)
0107 {
0108   imx_uart_write_polled(&imx_uart_console->base, c);
0109 }
0110 
0111 static int imx_poll_char(void)
0112 {
0113   return imx_uart_read_polled(&imx_uart_console->base);
0114 }
0115 
0116 static void imx_uart_init_context(
0117   imx_uart_context *ctx,
0118   const char *fdt,
0119   const char *serial
0120 )
0121 {
0122   int node;
0123 
0124   rtems_termios_device_context_initialize(&ctx->base, "UART");
0125   node = fdt_path_offset(fdt, serial);
0126   ctx->regs = imx_get_reg_of_node(fdt, node);
0127 #ifdef CONSOLE_USE_INTERRUPTS
0128   ctx->irq = imx_get_irq_of_node(fdt, node, 0);
0129 #endif
0130 }
0131 
0132 static void imx_uart_probe(void)
0133 {
0134   const void *fdt;
0135   int node;
0136   int offset;
0137   const char *console;
0138   size_t i;
0139 
0140   fdt = bsp_fdt_get();
0141   node = fdt_path_offset(fdt, "/chosen");
0142 
0143   console = fdt_getprop(fdt, node, "stdout-path", NULL);
0144   if (console == NULL) {
0145     console = "";
0146   }
0147 
0148   node = fdt_path_offset(fdt, "/aliases");
0149   offset = fdt_first_property_offset(fdt, node);
0150   i = 0;
0151 
0152   while (offset >= 0 && i < RTEMS_ARRAY_SIZE(imx_uart_instances)) {
0153     const struct fdt_property *prop;
0154 
0155     prop = fdt_get_property_by_offset(fdt, offset, NULL);
0156 
0157     if (prop != NULL) {
0158       const char *name;
0159 
0160       name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
0161       if (strstr(name, "serial") != NULL) {
0162         imx_uart_context *ctx;
0163         const char *serial;
0164 
0165         ctx = &imx_uart_instances[i];
0166         serial = prop->data;
0167 
0168         if (strcmp(serial, console) == 0) {
0169           imx_uart_console = ctx;
0170         }
0171 
0172         imx_uart_init_context(ctx, fdt, serial);
0173         ++i;
0174       }
0175     }
0176 
0177     offset = fdt_next_property_offset(fdt, offset);
0178   }
0179 
0180   BSP_output_char = imx_output_char;
0181   BSP_poll_char = imx_poll_char;
0182 }
0183 
0184 static void imx_output_char_init(char c)
0185 {
0186   imx_uart_probe();
0187   imx_output_char(c);
0188 }
0189 
0190 BSP_output_char_function_type BSP_output_char = imx_output_char_init;
0191 
0192 BSP_polling_getchar_function_type BSP_poll_char = NULL;
0193 
0194 #ifdef CONSOLE_USE_INTERRUPTS
0195 static void imx_uart_interrupt(void *arg)
0196 {
0197   rtems_termios_tty *tty;
0198   imx_uart_context *ctx;
0199   volatile imx_uart *regs;
0200   uint32_t usr2;
0201 
0202   tty = arg;
0203   ctx = rtems_termios_get_device_context(tty);
0204   regs = ctx->regs;
0205   usr2 = regs->usr2;
0206 
0207   regs->usr1 = IMX_UART_USR1_AGTIM;
0208 
0209   while ((usr2 & IMX_UART_USR2_RDR) != 0) {
0210     char c;
0211 
0212     c = IMX_UART_URXD_RX_DATA_GET(regs->urxd);
0213     rtems_termios_enqueue_raw_characters(tty, &c, 1);
0214     usr2 = regs->usr2;
0215   }
0216 
0217   if (ctx->tx_in_progress > 0 && (regs->usr1 & IMX_UART_USR1_TRDY) != 0) {
0218     rtems_termios_dequeue_characters(tty, ctx->tx_in_progress);
0219   }
0220 }
0221 #endif
0222 
0223 static bool imx_uart_set_attributes(
0224   rtems_termios_device_context *base,
0225   const struct termios *term
0226 )
0227 {
0228   imx_uart_context *ctx;
0229   volatile imx_uart *regs;
0230   uint32_t ufcr;
0231   uint32_t baud;
0232 
0233   ctx = (imx_uart_context *) base;
0234   regs = imx_uart_get_regs(&ctx->base);
0235 
0236   baud = rtems_termios_baud_to_number(term->c_ospeed);
0237 
0238   if (baud != 0) {
0239     ufcr = regs->ufcr;
0240     ufcr = IMX_UART_UFCR_RFDIV_SET(ufcr, 0x5);
0241     regs->ufcr = ufcr;
0242     regs->ubir = 15;
0243     regs->ubmr = imx_ccm_uart_hz() / baud - 1;
0244   }
0245 
0246   return true;
0247 }
0248 
0249 static bool imx_uart_first_open(
0250   rtems_termios_tty *tty,
0251   rtems_termios_device_context *base,
0252   struct termios *term,
0253   rtems_libio_open_close_args_t *args
0254 )
0255 {
0256   imx_uart_context *ctx;
0257   volatile imx_uart *regs;
0258 #ifdef CONSOLE_USE_INTERRUPTS
0259   rtems_status_code sc;
0260   uint32_t ufcr;
0261 #endif
0262 
0263   ctx = (imx_uart_context *) base;
0264   regs = imx_uart_get_regs(&ctx->base);
0265 
0266   regs->ucr1 = IMX_UART_UCR1_UARTEN;
0267   regs->ucr2 = IMX_UART_UCR2_IRTS | IMX_UART_UCR2_WS | IMX_UART_UCR2_RXEN
0268     | IMX_UART_UCR2_TXEN | IMX_UART_UCR2_SRST;
0269   regs->ucr3 |= IMX_UART_UCR3_ADNIMP | IMX_UART_UCR3_RXDMUXSEL;
0270 
0271   rtems_termios_set_initial_baud(tty, 115200);
0272   imx_uart_set_attributes(base, term);
0273 
0274 #ifdef CONSOLE_USE_INTERRUPTS
0275   ufcr = regs->ufcr;
0276   ufcr = IMX_UART_UFCR_RXTL_SET(ufcr, 16);
0277   ufcr = IMX_UART_UFCR_TXTL_SET(ufcr, IMX_UART_TX_FIFO_LEVEL);
0278   regs->ufcr = ufcr;
0279   regs->ucr1 |= IMX_UART_UCR1_RRDYEN;
0280   regs->ucr2 |= IMX_UART_UCR2_ATEN;
0281   sc = rtems_interrupt_handler_install(
0282     ctx->irq,
0283     "UART",
0284     RTEMS_INTERRUPT_SHARED,
0285     imx_uart_interrupt,
0286     tty
0287   );
0288   if (sc != RTEMS_SUCCESSFUL) {
0289     return false;
0290   }
0291 #endif
0292 
0293   return true;
0294 }
0295 
0296 static void imx_uart_last_close(
0297   rtems_termios_tty *tty,
0298   rtems_termios_device_context *base,
0299   rtems_libio_open_close_args_t *args
0300 )
0301 {
0302 #ifdef CONSOLE_USE_INTERRUPTS
0303   imx_uart_context *ctx;
0304 
0305   ctx = (imx_uart_context *) base;
0306   rtems_interrupt_handler_remove(ctx->irq, imx_uart_interrupt, tty);
0307 #endif
0308 }
0309 
0310 static void imx_uart_write(
0311   rtems_termios_device_context *base,
0312   const char *buf,
0313   size_t len
0314 )
0315 {
0316 #ifdef CONSOLE_USE_INTERRUPTS
0317   imx_uart_context *ctx;
0318   volatile imx_uart *regs;
0319   int n;
0320   uint32_t ucr1;
0321 
0322   ctx = (imx_uart_context *) base;
0323   regs = imx_uart_get_regs(&ctx->base);
0324   ucr1 = regs->ucr1;
0325 
0326   if (len > 0) {
0327     int i;
0328 
0329     n = (int) MIN(len, IMX_UART_TX_FIFO_LEVEL);
0330     ucr1 |= IMX_UART_UCR1_TRDYEN;
0331 
0332     for (i = 0; i < n; ++i) {
0333       regs->utxd = IMX_UART_UTXD_TX_DATA(buf[i]);
0334     }
0335   } else {
0336     n = 0;
0337     ucr1 &= ~IMX_UART_UCR1_TRDYEN;
0338   }
0339 
0340   regs->ucr1 = ucr1;
0341   ctx->tx_in_progress = n;
0342 #else
0343   size_t i;
0344 
0345   for (i = 0; i < len; ++i) {
0346     imx_uart_write_polled(base, buf[i]);
0347   }
0348 #endif
0349 }
0350 
0351 static const rtems_termios_device_handler imx_uart_handler = {
0352   .first_open = imx_uart_first_open,
0353   .last_close = imx_uart_last_close,
0354   .write = imx_uart_write,
0355   .set_attributes = imx_uart_set_attributes,
0356 #ifdef CONSOLE_USE_INTERRUPTS
0357   .mode = TERMIOS_IRQ_DRIVEN
0358 #else
0359   .poll_read = imx_uart_read_polled,
0360   .mode = TERMIOS_POLLED
0361 #endif
0362 };
0363 
0364 rtems_status_code console_initialize(
0365   rtems_device_major_number major,
0366   rtems_device_minor_number minor,
0367   void *arg
0368 )
0369 {
0370   char path[] = "/dev/ttyS?";
0371   size_t i;
0372 
0373   rtems_termios_initialize();
0374 
0375   for (i = 0; i < RTEMS_ARRAY_SIZE(imx_uart_instances); ++i) {
0376     imx_uart_context *ctx;
0377 
0378     ctx = &imx_uart_instances[i];
0379     path[sizeof(path) - 2] = (char) ('0' + i);
0380 
0381     rtems_termios_device_install(
0382       path,
0383       &imx_uart_handler,
0384       NULL,
0385       &ctx->base
0386     );
0387 
0388     if (ctx == imx_uart_console) {
0389       link(path, CONSOLE_DEVICE_NAME);
0390     }
0391   }
0392 
0393   return RTEMS_SUCCESSFUL;
0394 }
0395 
0396 RTEMS_SYSINIT_ITEM(
0397   imx_uart_probe,
0398   RTEMS_SYSINIT_BSP_START,
0399   RTEMS_SYSINIT_ORDER_LAST_BUT_5
0400 );