Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:04

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSBSPsARMLPC32XX
0007  *
0008  * @brief High speed UART driver (14-clock).
0009  */
0010 
0011 /*
0012  * Copyright (C) 2010, 2014 embedded brains GmbH & Co. KG
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #include <bsp.h>
0037 #include <bsp/lpc32xx.h>
0038 #include <bsp/irq.h>
0039 #include <bsp/hsu.h>
0040 
0041 #define HSU_FIFO_SIZE 64
0042 
0043 #define HSU_LEVEL_RX_MASK 0xffU
0044 #define HSU_LEVEL_TX_MASK 0xff00U
0045 #define HSU_LEVEL_TX_SHIFT 8
0046 
0047 #define HSU_RX_DATA_MASK 0xffU
0048 #define HSU_RX_EMPTY (1U << 8)
0049 #define HSU_RX_ERROR (1U << 9)
0050 #define HSU_RX_BREAK (1U << 10)
0051 
0052 #define HSU_IIR_TX (1U << 0)
0053 #define HSU_IIR_RX_TRIG (1U << 1)
0054 #define HSU_IIR_RX_TIMEOUT (1U << 2)
0055 
0056 #define HSU_CTRL_INTR_DISABLED 0x1280fU
0057 #define HSU_CTRL_RX_INTR_ENABLED 0x1284fU
0058 #define HSU_CTRL_RX_AND_TX_INTR_ENABLED 0x1286fU
0059 
0060 /* We are interested in RX timeout, RX trigger and TX trigger interrupts */
0061 #define HSU_IIR_MASK 0x7U
0062 
0063 bool lpc32xx_hsu_probe(rtems_termios_device_context *base)
0064 {
0065   lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
0066   volatile lpc32xx_hsu *hsu = ctx->hsu;
0067 
0068   hsu->ctrl = HSU_CTRL_INTR_DISABLED;
0069 
0070   /* Drain FIFOs */
0071   while (hsu->level != 0) {
0072     hsu->fifo;
0073   }
0074 
0075   return true;
0076 }
0077 
0078 static void lpc32xx_hsu_interrupt_handler(void *arg)
0079 {
0080   rtems_termios_tty *tty = arg;
0081   lpc32xx_hsu_context *ctx = rtems_termios_get_device_context(tty);
0082   volatile lpc32xx_hsu *hsu = ctx->hsu;
0083 
0084   /* Iterate until no more interrupts are pending */
0085   do {
0086     int rv = 0;
0087     int i = 0;
0088     char buf [HSU_FIFO_SIZE];
0089 
0090     /* Enqueue received characters */
0091     while (i < HSU_FIFO_SIZE) {
0092       uint32_t in = hsu->fifo;
0093 
0094       if ((in & HSU_RX_EMPTY) == 0) {
0095         if ((in & HSU_RX_BREAK) == 0) {
0096           buf [i] = in & HSU_RX_DATA_MASK;
0097           ++i;
0098         }
0099       } else {
0100         break;
0101       }
0102     }
0103     rtems_termios_enqueue_raw_characters(tty, buf, i);
0104 
0105     /* Dequeue transmitted characters */
0106     rv = rtems_termios_dequeue_characters(tty, (int) ctx->chars_in_transmission);
0107     if (rv == 0) {
0108       /* Nothing to transmit */
0109     }
0110   } while ((hsu->iir & HSU_IIR_MASK) != 0);
0111 }
0112 
0113 static bool lpc32xx_hsu_first_open(
0114   struct rtems_termios_tty *tty,
0115   rtems_termios_device_context *base,
0116   struct termios *term,
0117   rtems_libio_open_close_args_t *args
0118 )
0119 {
0120   lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
0121   volatile lpc32xx_hsu *hsu = ctx->hsu;
0122   rtems_status_code sc;
0123   bool ok;
0124 
0125   sc = rtems_interrupt_handler_install(
0126     ctx->irq,
0127     "HSU",
0128     RTEMS_INTERRUPT_UNIQUE,
0129     lpc32xx_hsu_interrupt_handler,
0130     tty
0131   );
0132   ok = sc == RTEMS_SUCCESSFUL;
0133 
0134   if (ok) {
0135     rtems_termios_set_initial_baud(tty, ctx->initial_baud);
0136     hsu->ctrl = HSU_CTRL_RX_INTR_ENABLED;
0137   }
0138 
0139   return ok;
0140 }
0141 
0142 static void lpc32xx_hsu_last_close(
0143   struct rtems_termios_tty *tty,
0144   rtems_termios_device_context *base,
0145   rtems_libio_open_close_args_t *args
0146 )
0147 {
0148   lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
0149   volatile lpc32xx_hsu *hsu = ctx->hsu;
0150 
0151   hsu->ctrl = HSU_CTRL_INTR_DISABLED;
0152 
0153   rtems_interrupt_handler_remove(
0154     ctx->irq,
0155     lpc32xx_hsu_interrupt_handler,
0156     tty
0157   );
0158 }
0159 
0160 static void lpc32xx_hsu_write(
0161   rtems_termios_device_context *base,
0162   const char *buf,
0163   size_t len
0164 )
0165 {
0166   lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
0167   volatile lpc32xx_hsu *hsu = ctx->hsu;
0168   size_t tx_level = (hsu->level & HSU_LEVEL_TX_MASK) >> HSU_LEVEL_TX_SHIFT;
0169   size_t tx_free = HSU_FIFO_SIZE - tx_level;
0170   size_t i = 0;
0171   size_t out = len > tx_free ? tx_free : len;
0172 
0173   for (i = 0; i < out; ++i) {
0174     hsu->fifo = buf [i];
0175   }
0176 
0177   ctx->chars_in_transmission = out;
0178 
0179   if (len > 0) {
0180     hsu->ctrl = HSU_CTRL_RX_AND_TX_INTR_ENABLED;
0181   } else {
0182     hsu->ctrl = HSU_CTRL_RX_INTR_ENABLED;
0183     hsu->iir = HSU_IIR_TX;
0184   }
0185 }
0186 
0187 static bool lpc32xx_hsu_set_attributes(
0188   rtems_termios_device_context *base,
0189   const struct termios *term
0190 )
0191 {
0192   lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
0193   volatile lpc32xx_hsu *hsu = ctx->hsu;
0194   int baud_flags = term->c_ospeed;
0195 
0196   if (baud_flags != 0) {
0197     int32_t baud = rtems_termios_baud_to_number(baud_flags);
0198 
0199     if (baud > 0) {
0200       uint32_t baud_divisor = 14 * (uint32_t) baud;
0201       uint32_t rate = LPC32XX_PERIPH_CLK / baud_divisor;
0202       uint32_t remainder = LPC32XX_PERIPH_CLK - rate * baud_divisor;
0203 
0204       if (2 * remainder >= baud_divisor) {
0205         ++rate;
0206       }
0207 
0208       hsu->rate = rate - 1;
0209     }
0210   }
0211 
0212   return true;
0213 }
0214 
0215 const rtems_termios_device_handler lpc32xx_hsu_fns = {
0216   .first_open = lpc32xx_hsu_first_open,
0217   .last_close = lpc32xx_hsu_last_close,
0218   .write = lpc32xx_hsu_write,
0219   .set_attributes = lpc32xx_hsu_set_attributes,
0220   .mode = TERMIOS_IRQ_DRIVEN
0221 };