Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:05

0001 /*
0002  * SPDX-License-Identifier: BSD-2-Clause
0003  *
0004  * Copyright (C) 2013, 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 <dev/serial/zynq-uart.h>
0029 #include <dev/serial/zynq-uart-regs.h>
0030 #include <bsp/irq.h>
0031 #include <rtems/termiostypes.h>
0032 
0033 #include <bspopts.h>
0034 
0035 #ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
0036 static void zynq_uart_interrupt(void *arg)
0037 {
0038   rtems_termios_tty *tty = arg;
0039   zynq_uart_context *ctx = rtems_termios_get_device_context(tty);
0040   volatile zynq_uart *regs = ctx->regs;
0041 
0042   if ((regs->irq_sts & ZYNQ_UART_RTRIG) != 0) {
0043     char buf[32];
0044     int c = 0;
0045     regs->irq_sts = ZYNQ_UART_RTRIG;
0046     while (c < sizeof(buf) &&
0047            (regs->channel_sts & ZYNQ_UART_CHANNEL_STS_REMPTY) == 0) {
0048        buf[c++] = (char) ZYNQ_UART_TX_RX_FIFO_FIFO_GET(regs->tx_rx_fifo);
0049     }
0050     rtems_termios_enqueue_raw_characters(tty, buf, c);
0051   }
0052 
0053   if (ctx->transmitting) {
0054     int sent = ctx->tx_queued;
0055     regs->irq_dis = ZYNQ_UART_TEMPTY;
0056     ctx->transmitting = false;
0057     ctx->tx_queued = 0;
0058     rtems_termios_dequeue_characters(tty, sent);
0059   }
0060 }
0061 #endif
0062 
0063 static bool zynq_uart_first_open(
0064   rtems_termios_tty *tty,
0065   rtems_termios_device_context *base,
0066   struct termios *term,
0067   rtems_libio_open_close_args_t *args
0068 )
0069 {
0070   zynq_uart_context *ctx = (zynq_uart_context *) base;
0071   volatile zynq_uart *regs = ctx->regs;
0072 #ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
0073   rtems_status_code sc;
0074 #endif
0075 
0076   rtems_termios_set_initial_baud(tty, ZYNQ_UART_DEFAULT_BAUD);
0077   zynq_uart_initialize(regs);
0078 
0079 #ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
0080   regs->rx_fifo_trg_lvl = 1;
0081   regs->irq_dis = 0xffffffff;
0082   regs->irq_sts = 0xffffffff;
0083   regs->irq_en = ZYNQ_UART_RTRIG;
0084   sc = rtems_interrupt_handler_install(
0085     ctx->irq,
0086     "UART",
0087     RTEMS_INTERRUPT_SHARED,
0088     zynq_uart_interrupt,
0089     tty
0090   );
0091   if (sc != RTEMS_SUCCESSFUL) {
0092     return false;
0093   }
0094 #endif
0095 
0096   return true;
0097 }
0098 
0099 #ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
0100 static void zynq_uart_last_close(
0101   rtems_termios_tty *tty,
0102   rtems_termios_device_context *base,
0103   rtems_libio_open_close_args_t *args
0104 )
0105 {
0106   zynq_uart_context *ctx = (zynq_uart_context *) base;
0107 
0108   rtems_interrupt_handler_remove(ctx->irq, zynq_uart_interrupt, tty);
0109 }
0110 #endif
0111 
0112 #ifndef ZYNQ_CONSOLE_USE_INTERRUPTS
0113 static int zynq_uart_read_polled(rtems_termios_device_context *base)
0114 {
0115   zynq_uart_context *ctx = (zynq_uart_context *) base;
0116   return zynq_uart_read_char_polled(ctx->regs);
0117 }
0118 #endif
0119 
0120 static void zynq_uart_write_support(
0121   rtems_termios_device_context *base,
0122   const char *buf,
0123   size_t len
0124 )
0125 {
0126   zynq_uart_context *ctx = (zynq_uart_context *) base;
0127   volatile zynq_uart *regs = ctx->regs;
0128 #ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
0129 
0130   regs->irq_dis = ZYNQ_UART_TEMPTY;
0131 
0132   if (len > 0) {
0133     const char *p = &buf[0];
0134     regs->irq_sts = ZYNQ_UART_TEMPTY;
0135     while (((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TNFUL) == 0) &&
0136            len > 0) {
0137       regs->tx_rx_fifo = ZYNQ_UART_TX_RX_FIFO_FIFO(*p);
0138       ++p;
0139       ++ctx->tx_queued;
0140       --len;
0141     }
0142     ctx->transmitting = true;
0143     regs->irq_en = ZYNQ_UART_TEMPTY;
0144   }
0145 #else
0146   size_t i;
0147   for (i = 0; i < len; ++i) {
0148     zynq_uart_write_char_polled(regs, buf[i]);
0149   }
0150 #endif
0151 }
0152 
0153 static bool zynq_uart_set_attributes(
0154   rtems_termios_device_context *context,
0155   const struct termios *term
0156 )
0157 {
0158   zynq_uart_context *ctx = (zynq_uart_context *) context;
0159   volatile zynq_uart *regs = ctx->regs;
0160   uint32_t desired_baud;
0161   uint32_t cd;
0162   uint32_t bdiv;
0163   uint32_t mode;
0164 
0165   /*
0166    * Determine the baud
0167    */
0168   desired_baud = rtems_termios_baud_to_number(term->c_ospeed);
0169   mode = regs->mode & ZYNQ_UART_MODE_CLKS;
0170 
0171   if (desired_baud > 0) {
0172     uint32_t error = zynq_uart_calculate_baud(desired_baud, mode, &cd, &bdiv);
0173     uint32_t margin;
0174 
0175     if ( desired_baud >= 100 ) {
0176       margin = 3 * (desired_baud / 100);
0177     } else {
0178       margin = 1;
0179     }
0180 
0181     if (error > margin) {
0182       return false;
0183     }
0184   }
0185 
0186   /*
0187    * Configure the mode register
0188    */
0189   mode |= ZYNQ_UART_MODE_CHMODE(ZYNQ_UART_MODE_CHMODE_NORMAL);
0190 
0191   /*
0192    * Parity
0193    */
0194   mode |= ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_NONE);
0195   if (term->c_cflag & PARENB) {
0196     if (!(term->c_cflag & PARODD)) {
0197       mode |= ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_ODD);
0198     } else {
0199       mode |= ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_EVEN);
0200     }
0201   }
0202 
0203   /*
0204    * Character Size
0205    */
0206   switch (term->c_cflag & CSIZE)
0207   {
0208   case CS5:
0209     return false;
0210   case CS6:
0211     mode |= ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_6);
0212     break;
0213   case CS7:
0214     mode |= ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_7);
0215     break;
0216   case CS8:
0217   default:
0218     mode |= ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_8);
0219     break;
0220   }
0221 
0222   /*
0223    * Stop Bits
0224    */
0225   if (term->c_cflag & CSTOPB) {
0226     /* 2 stop bits */
0227     mode |= ZYNQ_UART_MODE_NBSTOP(ZYNQ_UART_MODE_NBSTOP_STOP_2);
0228   } else {
0229     /* 1 stop bit */
0230     mode |= ZYNQ_UART_MODE_NBSTOP(ZYNQ_UART_MODE_NBSTOP_STOP_1);
0231   }
0232 
0233   /*
0234    * Wait for any data in the TXFIFO to be sent then wait while the
0235    * transmiter is active.
0236    */
0237   while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) == 0 ||
0238          (regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TACTIVE) != 0) {
0239     /* Wait */
0240   }
0241 
0242   regs->control = ZYNQ_UART_CONTROL_RXDIS | ZYNQ_UART_CONTROL_TXDIS;
0243   /* Ignore baud rate of B0. There are no modem control lines to de-assert */
0244   if (desired_baud > 0) {
0245     regs->baud_rate_gen = ZYNQ_UART_BAUD_RATE_GEN_CD(cd);
0246     regs->baud_rate_div = ZYNQ_UART_BAUD_RATE_DIV_BDIV(bdiv);
0247   }
0248   regs->control = ZYNQ_UART_CONTROL_RXRES | ZYNQ_UART_CONTROL_TXRES;
0249   regs->mode = mode;
0250   regs->irq_sts = 0xffffffff;
0251   regs->control = ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN;
0252 
0253   return true;
0254 }
0255 
0256 const rtems_termios_device_handler zynq_uart_handler = {
0257   .first_open = zynq_uart_first_open,
0258   .set_attributes = zynq_uart_set_attributes,
0259   .write = zynq_uart_write_support,
0260 #ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
0261   .last_close = zynq_uart_last_close,
0262   .mode = TERMIOS_IRQ_DRIVEN
0263 #else
0264   .poll_read = zynq_uart_read_polled,
0265   .mode = TERMIOS_POLLED
0266 #endif
0267 };