Back to home page

LXR

 
 

    


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

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (C) 2013, 2014 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/arm-pl011.h>
0029 #include <bspopts.h>
0030 
0031 static inline int arm_pl011_read_char(volatile pl011_base *regs_base)
0032 {
0033   return PL011_UARTDR_DATA_GET(regs_base->uartdr);
0034 }
0035 
0036 static inline void arm_pl011_write_char(
0037   volatile pl011_base *regs_base,
0038   const char ch
0039 )
0040 {
0041   regs_base->uartdr = PL011_UARTDR_DATA_SET(regs_base->uartdr, ch);
0042 }
0043 
0044 static inline bool arm_pl011_is_rxfifo_empty(volatile pl011_base *regs_base)
0045 {
0046   return (regs_base->uartfr & PL011_UARTFR_RXFE) != 0;
0047 }
0048 
0049 static inline bool arm_pl011_is_txfifo_full(volatile pl011_base *regs_base)
0050 {
0051   return (regs_base->uartfr & PL011_UARTFR_TXFF) != 0;
0052 }
0053 
0054 volatile arm_pl011_uart *arm_pl011_get_regs(rtems_termios_device_context *base)
0055 {
0056   return ((arm_pl011_context *)base)->regs;
0057 }
0058 
0059 bool arm_pl011_probe(rtems_termios_device_context *base)
0060 {
0061   volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0062 
0063   regs->base.uartlcr_h = PL011_UARTLCR_H_WLEN(PL011_UARTLCR_H_WLEN_8);
0064   regs->base.uartcr = PL011_UARTCR_RXE
0065     | PL011_UARTCR_TXE
0066     | PL011_UARTCR_UARTEN;
0067 
0068   return true;
0069 }
0070 
0071 static void arm_pl011_flush_fifos(const arm_pl011_context *context)
0072 {
0073   volatile pl011_base *regs = (volatile pl011_base *) context->regs;
0074 
0075   regs->uartlcr_h &= ~PL011_UARTLCR_H_FEN;
0076   regs->uartlcr_h |= PL011_UARTLCR_H_FEN;
0077 }
0078 
0079 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0080 static inline void arm_pl011_clear_irq(
0081   volatile pl011_base *regs_base,
0082   const uint32_t irq_mask
0083 )
0084 {
0085   /* ICR is a write-only register */
0086   regs_base->uarticr = irq_mask;
0087 }
0088 
0089 static inline void arm_pl011_enable_irq(
0090   volatile pl011_base *regs_base,
0091   const uint32_t irq_mask
0092 )
0093 {
0094   regs_base->uartimsc |= irq_mask;
0095 }
0096 #endif
0097 
0098 static inline void arm_pl011_disable_irq(
0099   volatile pl011_base *regs_base,
0100   uint32_t irq_mask
0101 )
0102 {
0103   regs_base->uartimsc &= ~irq_mask;
0104 }
0105 
0106 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0107 static void arm_pl011_irq_handler(void *arg)
0108 {
0109   arm_pl011_context *context = (void *) arg;
0110   volatile arm_pl011_uart *regs =
0111     arm_pl011_get_regs((rtems_termios_device_context *) context);
0112   const uint32_t irqs = regs->base.uartmis;
0113   char buffer[ARM_PL011_FIFO_DEPTH];
0114   /* RXFIFO got data */
0115   uint32_t rx_irq_mask = PL011_UARTI_RTI | PL011_UARTI_RXI;
0116   if ((irqs & rx_irq_mask) != 0) {
0117     arm_pl011_clear_irq(&regs->base, rx_irq_mask);
0118     
0119     unsigned int i = 0;
0120     while (i < sizeof(buffer) && !arm_pl011_is_rxfifo_empty(&regs->base)) {
0121       buffer[i] = arm_pl011_read_char(&regs->base);
0122       i++;
0123     }
0124 
0125     (void) rtems_termios_enqueue_raw_characters(context->tty, buffer, i);
0126   }
0127 
0128   /*
0129    * Some characters got queued in the TXFIFO, so dequeue them from Termios'
0130    * structures.
0131    */
0132   if ((irqs & PL011_UARTI_TXI) != 0) {
0133     /*
0134      * First interrupt was raised, so no need to trigger the handler
0135      * through software anymore.
0136      */
0137     if (context->needs_sw_triggered_tx_irq == true) {
0138       context->needs_sw_triggered_tx_irq = false;
0139     }
0140 
0141     (void) rtems_termios_dequeue_characters(context->tty,
0142       context->tx_queued_chars);
0143 
0144     /*
0145      * Explicitly clear the transmit interrupt.  This is necessary
0146      * because there may not be enough bytes in the output buffer to
0147      * fill the FIFO greater than the transmit interrupt trigger level.
0148      * If FIFOs are disabled, this applies if there are 0 bytes to
0149      * transmit and therefore nothing to fill the Tx holding register
0150      * with.
0151      */
0152     arm_pl011_clear_irq(&regs->base, PL011_UARTI_TXI);
0153   }
0154 }
0155 
0156 static bool arm_pl011_first_open_irq(
0157   rtems_termios_device_context *base,
0158   arm_pl011_context *context
0159 )
0160 {
0161   rtems_status_code sc;
0162   volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0163   /* Set FIFO trigger levels for interrupts */
0164   regs->base.uartifls =
0165     PL011_UARTIFLS_TXIFLSEL_SET(regs->base.uartifls, TXFIFO_IRQ_TRIGGER_LEVEL);
0166   regs->base.uartifls =
0167     PL011_UARTIFLS_RXIFLSEL_SET(regs->base.uartifls, RXFIFO_IRQ_TRIGGER_LEVEL);
0168   regs->base.uartlcr_h |= PL011_UARTLCR_H_FEN;
0169   arm_pl011_disable_irq(&regs->base, PL011_UARTI_MASK);
0170   sc = rtems_interrupt_handler_install(
0171     context->irq,
0172     "UART",
0173     RTEMS_INTERRUPT_SHARED,
0174     arm_pl011_irq_handler,
0175     context
0176   );
0177   if (sc != RTEMS_SUCCESSFUL) {
0178     return false;
0179   }
0180 
0181   /* Clear all interrupts  */
0182   arm_pl011_clear_irq(&regs->base, PL011_UARTI_MASK);
0183   arm_pl011_enable_irq(&regs->base, PL011_UARTI_RTI | PL011_UARTI_RXI);
0184   return RTEMS_SUCCESSFUL;
0185 }
0186 #endif
0187 
0188 int arm_pl011_compute_baudrate_params(
0189   uint32_t *ibrd,
0190   uint32_t *fbrd,
0191   const uint32_t baudrate,
0192   const uint32_t clock,
0193   const unsigned short max_error
0194 )
0195 {
0196   uint16_t scalar;
0197   uint32_t computed_bauddiv, computed_baudrate, baud_error;
0198   uint64_t scaled_bauddiv;
0199   unsigned short round_off, percent_error;
0200   /*
0201    * The integer baudrate divisor, i.e. (clock / (baudrate * 16)), value
0202    * should lie on [1, 2^16 - 1]. To ensure this, clock and baudrate have to
0203    * be validated.
0204    */
0205   *ibrd = clock / 16 / baudrate;
0206   if (*ibrd < 1 || *ibrd > PL011_UARTIBRD_BAUD_DIVINT_MASK) {
0207     return 2;
0208   }
0209 
0210   /* Find the fractional part */
0211   scalar = 1 << (PL011_UARTFBRD_BAUD_DIVFRAC_WIDTH + 1);
0212   scaled_bauddiv =
0213     ((uint64_t) clock * scalar) / 16 / baudrate;
0214   round_off = scaled_bauddiv & 0x1;
0215   *fbrd =
0216     ((scaled_bauddiv >> 1) & PL011_UARTFBRD_BAUD_DIVFRAC_MASK) + round_off;
0217 
0218   /* Calculate the baudrate and check if the error is too large */
0219   computed_bauddiv =
0220     (*ibrd << PL011_UARTFBRD_BAUD_DIVFRAC_WIDTH) | *fbrd;
0221   computed_baudrate =
0222     ((uint64_t) clock << PL011_UARTFBRD_BAUD_DIVFRAC_WIDTH)
0223     / 16 / computed_bauddiv;
0224 
0225   baud_error = computed_baudrate - baudrate;
0226   if (baudrate > computed_baudrate) {
0227     baud_error = baudrate - computed_baudrate;
0228   }
0229 
0230   percent_error = (baud_error * 100) / baudrate;
0231   if (percent_error >= max_error) {
0232     return 1;
0233   }
0234 
0235   return 0;
0236 }
0237 
0238 static uint32_t arm_pl011_set_lcrh(const struct termios *term)
0239 {
0240   /* enable FIFO */ 
0241   uint32_t lcrh = PL011_UARTLCR_H_FEN;
0242 
0243   /* Mode: parity */
0244   if ((term->c_cflag & PARENB) != 0) {
0245     lcrh |= PL011_UARTLCR_H_PEN;
0246     if ((term->c_cflag & PARODD) == 0) {
0247       lcrh |= PL011_UARTLCR_H_EPS;
0248     }
0249   }
0250 
0251   /* Mode: character size */
0252   switch (term->c_cflag & CSIZE) {
0253     case CS5:
0254       lcrh |= PL011_UARTLCR_H_WLEN_SET(lcrh, PL011_UARTLCR_H_WLEN_5);
0255       break;
0256     case CS6:
0257       lcrh |= PL011_UARTLCR_H_WLEN_SET(lcrh, PL011_UARTLCR_H_WLEN_6);
0258       break;
0259     case CS7:
0260       lcrh |= PL011_UARTLCR_H_WLEN_SET(lcrh, PL011_UARTLCR_H_WLEN_7);
0261       break;
0262     case CS8:
0263     default:
0264       lcrh |= PL011_UARTLCR_H_WLEN_SET(lcrh, PL011_UARTLCR_H_WLEN_8);
0265   }
0266 
0267   /* Mode: stop bits */
0268   if ((term->c_cflag & CSTOPB) != 0) {
0269     /* 2 stop bits */
0270     lcrh |= PL011_UARTLCR_H_STP2;
0271   }
0272   return lcrh;
0273 }
0274 
0275 static uint32_t arm_pl011_set_cr(const struct termios *term, volatile pl011_base *regs)
0276 {
0277   uint32_t cr = regs->uartcr;
0278   /*
0279    * Control: Configure flow control
0280    * NOTE: Flow control is untested
0281    */
0282   cr &= ~(PL011_UARTCR_CTSEN | PL011_UARTCR_RTSEN);
0283   if ((term->c_cflag & CCTS_OFLOW) != 0) {
0284     cr |= PL011_UARTCR_CTSEN;
0285   }
0286   if ((term->c_cflag & CRTS_IFLOW) != 0) {
0287     cr |= PL011_UARTCR_RTSEN;
0288   }
0289 
0290   /* Control: Configure receiver */
0291   if ((term->c_cflag & CREAD) != 0) {
0292     cr |= PL011_UARTCR_RXE;
0293   }
0294 
0295   /* Control: Re-enable UART */
0296   cr |= PL011_UARTCR_UARTEN | PL011_UARTCR_TXE;
0297 
0298   return cr;
0299 }
0300 
0301 static bool arm_pl011_set_attributes(
0302   rtems_termios_device_context *base,
0303   const struct termios *term
0304 )
0305 {
0306   uint32_t ibrd, fbrd, lcrh, baud, cr;
0307   int err;
0308   arm_pl011_context *context = (arm_pl011_context *) base;
0309   volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0310 
0311   /* Determine baudrate parameters */
0312   baud = rtems_termios_number_to_baud(term->c_ospeed);
0313   if (baud == B0) {
0314     return false;
0315   }
0316 
0317   err = arm_pl011_compute_baudrate_params(&ibrd,&fbrd,baud,context->clock,3);
0318   if (err != 0) {
0319     return false;
0320   }
0321 
0322   lcrh = arm_pl011_set_lcrh(term);
0323 
0324   /* Disable all interrupts */
0325   arm_pl011_disable_irq(&regs->base, PL011_UARTI_MASK);
0326 
0327   /*
0328    * Wait for any data in the TXFIFO to be sent then wait while the
0329    * transmiter is active.
0330    */
0331   while (
0332     (regs->base.uartfr & PL011_UARTFR_TXFE) == 0 ||
0333     (regs->base.uartfr & PL011_UARTFR_BUSY) != 0
0334   ) {
0335       /* Wait */
0336   }
0337 
0338   regs->base.uartcr = PL011_UARTCR_UARTEN;
0339 
0340   /* Set the baudrate */
0341   regs->base.uartibrd = ibrd;
0342   regs->base.uartfbrd = fbrd;
0343 
0344   /*
0345    * Commit mode configurations
0346    * NOTE:
0347    * This has to happen after IBRD and FBRD as writing to LCRH is
0348    * required to trigger the baudrate update.
0349    */
0350   regs->base.uartlcr_h = lcrh;
0351 
0352   /* Control: Disable UART */
0353   regs->base.uartcr &=
0354     ~(PL011_UARTCR_UARTEN | PL011_UARTCR_RXE | PL011_UARTCR_TXE);
0355   cr = arm_pl011_set_cr(term, &regs->base);
0356   arm_pl011_flush_fifos(context);
0357   /* Commit changes to control register */
0358   regs->base.uartcr = cr;
0359 
0360 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0361   /* Clear all interrupts  */
0362   arm_pl011_clear_irq(&regs->base, PL011_UARTI_MASK);
0363 
0364   /* Re-enable RX interrupts */
0365   if ((term->c_cflag & CREAD) != 0) {
0366     arm_pl011_enable_irq(&regs->base, PL011_UARTI_RTI | PL011_UARTI_RXI);
0367   }
0368 
0369   /*
0370    * UART is freshly enabled, TXFIFO is empty, and interrupt will be enabled,
0371    * so the next transmission will required software-trigger interrupt.
0372    */
0373   context->needs_sw_triggered_tx_irq = true;
0374 #endif
0375   return true;
0376 }
0377 
0378 static bool arm_pl011_first_open(
0379   struct rtems_termios_tty *tty,
0380   rtems_termios_device_context *base,
0381   struct termios *term,
0382   rtems_libio_open_close_args_t *args
0383 )
0384 {
0385   arm_pl011_context *context = (arm_pl011_context *) base;
0386 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0387   rtems_status_code sc;
0388 
0389   context->tty = tty;
0390 #endif
0391 
0392   if (rtems_termios_set_initial_baud(tty, context->initial_baud) != 0) {
0393     return false;
0394   }
0395 
0396   if (!arm_pl011_set_attributes(base, term)) {
0397     return false;
0398   }
0399   
0400 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0401   sc = arm_pl011_first_open_irq(base, context);
0402   if (sc != RTEMS_SUCCESSFUL) {
0403     return false;
0404   }
0405 #endif
0406   
0407   return true;
0408 }
0409 
0410 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0411 static void arm_pl011_last_close(
0412   rtems_termios_tty *tty,
0413   rtems_termios_device_context *base,
0414   rtems_libio_open_close_args_t *args
0415 )
0416 {
0417   const arm_pl011_context *context = (void *) base;
0418   (void) rtems_interrupt_handler_remove(
0419     context->irq,
0420     arm_pl011_irq_handler,
0421     tty
0422   );
0423 }
0424 #endif
0425 
0426 int arm_pl011_read_polled(rtems_termios_device_context *base)
0427 {
0428   volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0429 
0430   if (arm_pl011_is_rxfifo_empty(&regs->base)) {
0431     return -1;
0432   } else {
0433     return arm_pl011_read_char(&regs->base);
0434   }
0435 }
0436 
0437 void arm_pl011_write_polled(rtems_termios_device_context *base, char c)
0438 {
0439   volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0440 
0441   /* Wait until TXFIFO has space */
0442   while (arm_pl011_is_txfifo_full(&regs->base)) {
0443     /* Wait */
0444   }
0445 
0446   arm_pl011_write_char(&regs->base, c);
0447 }
0448 
0449 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0450 static void arm_pl011_write_support_interrupt(
0451   rtems_termios_device_context *base,
0452   const char *buffer,
0453   size_t buffer_len
0454 )
0455 {
0456   arm_pl011_context *context = (arm_pl011_context *) base;
0457   volatile arm_pl011_uart *regs = arm_pl011_get_regs(base);
0458   size_t i = 0;
0459 
0460   if (buffer_len > 0) {
0461     arm_pl011_enable_irq(&regs->base, PL011_UARTI_TXI);
0462     /*
0463      * When arm_pl011_write_support_interrupt writes the first character,
0464      * if txfifo is full, it will return directly. This will cause this
0465      * character to be lost. If txfifo is full, wait for it to be not full.
0466      */
0467     while (arm_pl011_is_txfifo_full(&regs->base)) {
0468       /* wait for txfifo not full */
0469     }
0470     while (!arm_pl011_is_txfifo_full(&regs->base) && i < buffer_len) {
0471       arm_pl011_write_char(&regs->base, buffer[i]);
0472       i++;
0473     }
0474     context->tx_queued_chars = i;
0475 
0476     if (context->needs_sw_triggered_tx_irq) {
0477       /*
0478        * The first write may not trigger the TX interrupt due to insufficient
0479        * characters being written. Call rtems_termios_dequeue_characters() to
0480        * continue writing.
0481        */
0482       (void) rtems_termios_dequeue_characters(context->tty,
0483                                               context->tx_queued_chars);
0484     }
0485   } else {
0486     /*
0487      * Termios will set n to zero to indicate that the transmitter is now
0488      * inactive. The output buffer is empty in this case. The driver may
0489      * disable the transmit interrupts now.
0490      */
0491     arm_pl011_disable_irq(&regs->base, PL011_UARTI_TXI);
0492   }
0493 }
0494 #else
0495 static void arm_pl011_write_support_polled(
0496   rtems_termios_device_context *base,
0497   const char *s,
0498   size_t n
0499 )
0500 {
0501   size_t i;
0502 
0503   for (i = 0; i < n; ++i) {
0504     arm_pl011_write_polled(base, s[i]);
0505   }
0506 }
0507 #endif
0508 
0509 static void arm_pl011_write_buffer(
0510   rtems_termios_device_context *base,
0511   const char *buffer,
0512   size_t n
0513 )
0514 {
0515 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0516   arm_pl011_write_support_interrupt(base, buffer, n);
0517 #else
0518   arm_pl011_write_support_polled(base, buffer, n);
0519 #endif
0520 }
0521 
0522 const rtems_termios_device_handler arm_pl011_fns = {
0523   .first_open = arm_pl011_first_open,
0524   .write = arm_pl011_write_buffer,
0525   .set_attributes = arm_pl011_set_attributes,
0526   .ioctl = NULL,
0527 #ifdef BSP_CONSOLE_USE_INTERRUPTS
0528   .last_close = arm_pl011_last_close,
0529   .poll_read = NULL,
0530   .mode = TERMIOS_IRQ_DRIVEN,
0531 #else
0532   .last_close = NULL,
0533   .poll_read = arm_pl011_read_polled,
0534   .mode = TERMIOS_POLLED,
0535 #endif
0536 };