Back to home page

LXR

 
 

    


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

0001 /*
0002  * SPDX-License-Identifier: BSD-2-Clause
0003  *
0004  * Copyright (C) 2022 Chris Johns <chris@contemporary.software>
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 <dev/serial/versal-uart.h>
0029 #include <dev/serial/arm-pl011.h>
0030 #include <bsp/irq.h>
0031 
0032 #include <bspopts.h>
0033 
0034 static uint32_t versal_uart_intr_all(void)
0035 {
0036   return PL011_UARTI_OEI |
0037     PL011_UARTI_BEI |
0038     PL011_UARTI_PEI |
0039     PL011_UARTI_FEI |
0040     PL011_UARTI_RTI |
0041     PL011_UARTI_TXI |
0042     PL011_UARTI_RXI |
0043     PL011_UARTI_DSRMI |
0044     PL011_UARTI_DCDMI |
0045     PL011_UARTI_CTSMI |
0046     PL011_UARTI_RIMI;
0047 }
0048 
0049 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0050 static void versal_uart_intr_clear(volatile arm_pl011_uart *regs, uint32_t ints)
0051 {
0052   regs->base.uarticr = ints;
0053 }
0054 
0055 static void versal_uart_intr_clearall(volatile arm_pl011_uart *regs)
0056 {
0057   versal_uart_intr_clear(regs, versal_uart_intr_all());
0058 }
0059 
0060 static void versal_uart_intr_enable(volatile arm_pl011_uart *regs, uint32_t ints)
0061 {
0062   regs->base.uartimsc |= ints;
0063 }
0064 #endif
0065 
0066 static void versal_uart_intr_disable(volatile arm_pl011_uart *regs, uint32_t ints)
0067 {
0068   regs->base.uartimsc &= ~ints;
0069 }
0070 
0071 static void versal_uart_intr_disableall(volatile arm_pl011_uart *regs)
0072 {
0073   versal_uart_intr_disable(regs, versal_uart_intr_all());
0074 }
0075 
0076 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0077 static bool versal_uart_flags_clear(volatile arm_pl011_uart *regs, uint32_t flags)
0078 {
0079   return (regs->base.uartfr & flags) == 0;
0080 }
0081 
0082 static void versal_uart_interrupt(void *arg)
0083 {
0084   rtems_termios_tty *tty = arg;
0085   versal_pl011_context *ctx = rtems_termios_get_device_context(tty);
0086   volatile arm_pl011_uart *regs = (volatile arm_pl011_uart *) ctx->pl011_ctx.regs;
0087   uint32_t uartmis = regs->base.uartmis;
0088 
0089   versal_uart_intr_clear(regs, uartmis);
0090 
0091   if ((uartmis & (PL011_UARTI_RTI | PL011_UARTI_RXI)) != 0) {
0092     char buf[32];
0093     int c = 0;
0094     while (c < sizeof(buf) &&
0095            versal_uart_flags_clear(regs, PL011_UARTFR_RXFE)) {
0096       buf[c++] = (char) PL011_UARTDR_DATA_GET(regs->base.uartdr);
0097     }
0098     rtems_termios_enqueue_raw_characters(tty, buf, c);
0099   }
0100 
0101   if (ctx->transmitting) {
0102     int sent = ctx->pl011_ctx.tx_queued_chars;
0103     ctx->transmitting = false;
0104     ctx->pl011_ctx.tx_queued_chars = 0;
0105     versal_uart_intr_disable(regs, PL011_UARTI_TXI);
0106     rtems_termios_dequeue_characters(tty, sent);
0107   }
0108 }
0109 #endif
0110 
0111 void versal_uart_reset_tx_flush(rtems_termios_device_context *base)
0112 {
0113   volatile arm_pl011_uart *regs = (volatile arm_pl011_uart *) arm_pl011_get_regs(base);
0114   int c = 4;
0115 
0116   while (c-- > 0) {
0117     arm_pl011_write_polled(base, '\r');
0118   }
0119 
0120   while ((regs->base.uartfr & PL011_UARTFR_TXFE) == 0) {
0121     /* Wait for empty */
0122   }
0123   while ((regs->base.uartfr & PL011_UARTFR_BUSY) != 0) {
0124     /* Wait for empty */
0125   }
0126 }
0127 
0128 int versal_uart_initialize(rtems_termios_device_context *base)
0129 {
0130   volatile pl011_base *regs = (volatile pl011_base *)arm_pl011_get_regs(base);
0131   arm_pl011_context *ctx = (arm_pl011_context *) base;
0132   uint32_t maxerr = 3;
0133   uint32_t ibauddiv = 0;
0134   uint32_t fbauddiv = 0;
0135   int rv;
0136 
0137   versal_uart_reset_tx_flush(base);
0138 
0139   rv = arm_pl011_compute_baudrate_params(
0140     &ibauddiv,
0141     &fbauddiv,
0142     VERSAL_UART_DEFAULT_BAUD,
0143     ctx->clock,
0144     maxerr
0145   );
0146   if (rv != 0) {
0147     return rv;
0148   }
0149 
0150   /* Line control: 8-bit word length, no parity, no FIFO, 1 stop bit */
0151   regs->uartlcr_h = PL011_UARTLCR_H_WLEN( PL011_UARTLCR_H_WLEN_8 )
0152     | PL011_UARTLCR_H_FEN;
0153 
0154   /* Control: receive, transmit, uart enable, no CTS, no RTS, no loopback */
0155   regs->uartcr = PL011_UARTCR_RXE
0156     | PL011_UARTCR_TXE
0157     | PL011_UARTCR_UARTEN;
0158 
0159   regs->uartibrd = ibauddiv;
0160   regs->uartfbrd = fbauddiv;
0161 
0162   return 0;
0163 }
0164 
0165 static bool versal_uart_first_open(
0166   rtems_termios_tty *tty,
0167   rtems_termios_device_context *base,
0168   struct termios *term,
0169   rtems_libio_open_close_args_t *args
0170 )
0171 {
0172 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0173   versal_pl011_context *ctx = (versal_pl011_context *) base;
0174   volatile arm_pl011_uart *regs = (volatile arm_pl011_uart *) ctx->pl011_ctx.regs;
0175   rtems_status_code sc;
0176 
0177   ctx->transmitting = false;
0178   ctx->pl011_ctx.tx_queued_chars = 0;
0179   ctx->pl011_ctx.needs_sw_triggered_tx_irq = true;
0180 #endif
0181 
0182   rtems_termios_set_initial_baud(tty, VERSAL_UART_DEFAULT_BAUD);
0183   versal_uart_initialize(base);
0184 
0185 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0186   regs->base.uartifls = PL011_UARTIFLS_RXIFLSEL(2) | PL011_UARTIFLS_TXIFLSEL(2);
0187   regs->base.uartlcr_h |= PL011_UARTLCR_H_FEN;
0188   versal_uart_intr_disableall(regs);
0189   sc = rtems_interrupt_handler_install(
0190     ctx->pl011_ctx.irq,
0191     "UART",
0192     RTEMS_INTERRUPT_SHARED,
0193     versal_uart_interrupt,
0194     tty
0195   );
0196   if (sc != RTEMS_SUCCESSFUL) {
0197     return false;
0198   }
0199   versal_uart_intr_clearall(regs);
0200   versal_uart_intr_enable(regs, PL011_UARTI_RTI | PL011_UARTI_RXI);
0201 #endif
0202 
0203   return true;
0204 }
0205 
0206 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0207 static void versal_uart_last_close(
0208   rtems_termios_tty *tty,
0209   rtems_termios_device_context *base,
0210   rtems_libio_open_close_args_t *args
0211 )
0212 {
0213   versal_pl011_context *ctx = (versal_pl011_context *) base;
0214   rtems_interrupt_handler_remove(ctx->pl011_ctx.irq, versal_uart_interrupt, tty);
0215 }
0216 #endif
0217 
0218 static void versal_uart_write_support(
0219   rtems_termios_device_context *base,
0220   const char *buf,
0221   size_t len
0222 )
0223 {
0224 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0225   versal_pl011_context *ctx = (versal_pl011_context *) base;
0226   volatile arm_pl011_uart *regs = (volatile arm_pl011_uart *) ctx->pl011_ctx.regs;
0227 
0228   if (len > 0) {
0229     size_t len_remaining = len;
0230     const char *p = &buf[0];
0231     versal_uart_intr_enable(regs, PL011_UARTI_TXI);
0232     /*
0233      * The PL011 IP in the Versal needs preloading the TX FIFO with
0234      * exactly 17 characters for the first TX interrupt to be
0235      * generated.
0236      */
0237     if (ctx->pl011_ctx.needs_sw_triggered_tx_irq) {
0238       ctx->pl011_ctx.needs_sw_triggered_tx_irq = false;
0239       for (int i = 0; i < 17; ++i) {
0240         regs->base.uartdr = PL011_UARTDR_DATA('\r');
0241       }
0242     }
0243     while (versal_uart_flags_clear(regs, PL011_UARTFR_TXFF) &&
0244            len_remaining > 0) {
0245       regs->base.uartdr = PL011_UARTDR_DATA(*p++);
0246       --len_remaining;
0247     }
0248     ctx->pl011_ctx.tx_queued_chars = len - len_remaining;
0249     ctx->transmitting = true;
0250   }
0251 #else
0252   ssize_t i;
0253   for (i = 0; i < len; ++i) {
0254     arm_pl011_write_polled(base, buf[i]);
0255   }
0256 #endif
0257 }
0258 
0259 static bool versal_uart_set_attributes(
0260   rtems_termios_device_context *context,
0261   const struct termios *term
0262 )
0263 {
0264   versal_pl011_context *ctx = (versal_pl011_context *) context;
0265   volatile arm_pl011_uart *regs = (volatile arm_pl011_uart *) ctx->pl011_ctx.regs;
0266   int32_t baud;
0267   uint32_t ibauddiv = 0;
0268   uint32_t fbauddiv = 0;
0269   uint32_t mode = 0;
0270   int rc;
0271 
0272   /*
0273    * Determine the baud rate
0274    */
0275   baud = rtems_termios_baud_to_number(term->c_ospeed);
0276 
0277   if (baud > 0) {
0278     uint32_t maxerr = 3;
0279 
0280     rc = arm_pl011_compute_baudrate_params(
0281       &ibauddiv,
0282       &fbauddiv,
0283       baud,
0284       ctx->pl011_ctx.clock,
0285       maxerr
0286     );
0287     if (rc != 0) {
0288       return rc;
0289     }
0290   }
0291 
0292   /*
0293    * Configure the mode register
0294    */
0295   mode = regs->base.uartlcr_h & PL011_UARTLCR_H_FEN;
0296 
0297   /*
0298    * Parity
0299    */
0300   if ((term->c_cflag & PARENB) != 0) {
0301     mode |= PL011_UARTLCR_H_PEN;
0302     if ((term->c_cflag & PARODD) == 0) {
0303       mode |= PL011_UARTLCR_H_EPS;
0304     }
0305   }
0306 
0307   /*
0308    * Character Size
0309    */
0310   switch (term->c_cflag & CSIZE)
0311   {
0312   case CS5:
0313     mode = PL011_UARTLCR_H_WLEN_SET(mode, PL011_UARTLCR_H_WLEN_5);
0314     break;
0315   case CS6:
0316     mode = PL011_UARTLCR_H_WLEN_SET(mode, PL011_UARTLCR_H_WLEN_6);
0317     break;
0318   case CS7:
0319     mode = PL011_UARTLCR_H_WLEN_SET(mode, PL011_UARTLCR_H_WLEN_7);
0320     break;
0321   case CS8:
0322   default:
0323     mode = PL011_UARTLCR_H_WLEN_SET(mode, PL011_UARTLCR_H_WLEN_8);
0324     break;
0325   }
0326 
0327   /*
0328    * Stop Bits
0329    */
0330   if (term->c_cflag & CSTOPB) {
0331     /* 2 stop bits */
0332     mode |= PL011_UARTLCR_H_STP2;
0333   }
0334 
0335   versal_uart_intr_disableall(regs);
0336 
0337   /*
0338    * Wait for any data in the TXFIFO to be sent then wait while the
0339    * transmiter is active.
0340    */
0341   while ((regs->base.uartfr & PL011_UARTFR_TXFE) == 0 ||
0342          (regs->base.uartfr & PL011_UARTFR_BUSY) != 0) {
0343     /* Wait */
0344   }
0345 
0346   regs->base.uartcr = PL011_UARTCR_UARTEN;
0347   /* Ignore baud rate of B0. There are no modem control lines to de-assert */
0348   if (baud > 0) {
0349     regs->base.uartibrd = ibauddiv;
0350     regs->base.uartfbrd = fbauddiv;
0351   }
0352   regs->base.uartlcr_h = mode;
0353 
0354   /* Control: receive, transmit, uart enable, no CTS, no RTS, no loopback */
0355   regs->base.uartcr = PL011_UARTCR_RXE
0356     | PL011_UARTCR_TXE
0357     | PL011_UARTCR_UARTEN;
0358 
0359 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0360   versal_uart_intr_clearall(regs);
0361   versal_uart_intr_enable(regs, PL011_UARTI_RTI | PL011_UARTI_RXI);
0362 #endif
0363 
0364   return true;
0365 }
0366 
0367 const rtems_termios_device_handler versal_uart_handler = {
0368   .first_open = versal_uart_first_open,
0369   .set_attributes = versal_uart_set_attributes,
0370   .write = versal_uart_write_support,
0371 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0372   .last_close = versal_uart_last_close,
0373   .mode = TERMIOS_IRQ_DRIVEN
0374 #else
0375   .poll_read = arm_pl011_read_polled,
0376   .mode = TERMIOS_POLLED
0377 #endif
0378 };