You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2024/09/14 19:38:08 UTC

(nuttx) 05/09: drivers: prepare 16550 UART driver to support PCI

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit ceb2921d79694c13be2a82db00b031d744f9f1cc
Author: p-szafonimateusz <p-...@xiaomi.com>
AuthorDate: Thu Sep 12 10:12:39 2024 +0200

    drivers: prepare 16550 UART driver to support PCI
    
    prepare 16550 UART driver to support PCI:
    
    - [breaking change] change argument of uart_ioctl() from `struct file *filep` to `FAR struct u16550_s *priv`
      Also fix moxart_16550.c build related to this change
    
    - [breaking change] change argument of uart_getreg() and uart_putreg from `uart_addrwidth_t base` to `FAR struct u16550_s *priv`
      Also fix arch/x86/src/qemu/qemu_serial.c and arch/x86_64/src/intel64/intel64_serial.c related to this change
    
    - [breaking change] change argument of uart_dmachan() from `uart_addrwidth_t base` to `FAR struct u16550_s *priv`
    
    - move `struct u16550_s` to public header
    
    - generalize UART_XXX_OFFSET so we can use it with any register increment
    
    - make u16550_bind(), u16550_interrupt(), u16550_interrupt() public
    
    - remove arch/or1k/src/common/or1k_uart.c and use common 16550 MIMO interfacve
    
    - change irq type in `struct u16550_s` from uint8_t to int to match MSI API
    
    Signed-off-by: p-szafonimateusz <p-...@xiaomi.com>
---
 arch/arm/src/moxart/moxart_16550.c            |  13 +-
 arch/or1k/src/common/or1k_uart.c              |  45 ---
 arch/or1k/src/mor1kx/Make.defs                |   1 -
 arch/x86/src/qemu/qemu_serial.c               |   8 +-
 arch/x86_64/src/intel64/intel64_serial.c      |   8 +-
 boards/or1k/mor1kx/or1k/configs/nsh/defconfig |   1 +
 drivers/serial/uart_16550.c                   | 411 +++++++++++++++-----------
 include/nuttx/serial/uart_16550.h             | 151 +++++++---
 8 files changed, 359 insertions(+), 279 deletions(-)

diff --git a/arch/arm/src/moxart/moxart_16550.c b/arch/arm/src/moxart/moxart_16550.c
index f2dc48bf4f..9b679dd210 100644
--- a/arch/arm/src/moxart/moxart_16550.c
+++ b/arch/arm/src/moxart/moxart_16550.c
@@ -69,15 +69,12 @@ void uart_decodeirq(int irq, void *context)
 }
 
 #ifdef CONFIG_SERIAL_UART_ARCH_IOCTL
-int uart_ioctl(struct file *filep, int cmd, unsigned long arg)
+int uart_ioctl(FAR struct u16550_s *priv, int cmd, unsigned long arg)
 {
-  struct inode      *inode = filep->f_inode;
-  struct uart_dev_s *dev   = inode->i_private;
-  struct u16550_s   *priv  = (struct u16550_s *)dev->priv;
-  int                ret   = -ENOTTY;
-  uint32_t           vmode;
-  unsigned int       opmode;
-  int                bitm_off;
+  int          ret = -ENOTTY;
+  uint32_t     vmode;
+  unsigned int opmode;
+  int          bitm_off;
 
   /* TODO: calculate bit offset from UART_BASE address.
    *  E.g.:
diff --git a/arch/or1k/src/common/or1k_uart.c b/arch/or1k/src/common/or1k_uart.c
deleted file mode 100644
index d6c15157df..0000000000
--- a/arch/or1k/src/common/or1k_uart.c
+++ /dev/null
@@ -1,45 +0,0 @@
-/****************************************************************************
- * arch/or1k/src/common/or1k_uart.c
- *
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.  The
- * ASF licenses this file to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the
- * License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
- * License for the specific language governing permissions and limitations
- * under the License.
- *
- ****************************************************************************/
-
-/****************************************************************************
- * Included Files
- ****************************************************************************/
-
-#include <nuttx/config.h>
-#include <nuttx/arch.h>
-
-#include <nuttx/serial/uart_16550.h>
-
-#include "or1k_internal.h"
-
-/****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-uart_datawidth_t uart_getreg(uart_addrwidth_t base, unsigned int offset)
-{
-  return *(uint8_t *)(base + offset);
-}
-
-void uart_putreg(uart_addrwidth_t base, unsigned int offset,
-                 uart_datawidth_t value)
-{
-  *(uint8_t *)(base + offset) = value;
-}
diff --git a/arch/or1k/src/mor1kx/Make.defs b/arch/or1k/src/mor1kx/Make.defs
index 5f87c7dfd6..93556fea87 100644
--- a/arch/or1k/src/mor1kx/Make.defs
+++ b/arch/or1k/src/mor1kx/Make.defs
@@ -40,7 +40,6 @@ CMN_CSRCS  = or1k_initialize.c \
              or1k_idle.c \
              or1k_irq.c \
              or1k_nputs.c \
-             or1k_uart.c \
              or1k_timer.c \
              or1k_doirq.c \
              or1k_cpuinfo.c \
diff --git a/arch/x86/src/qemu/qemu_serial.c b/arch/x86/src/qemu/qemu_serial.c
index cc90e40b33..ae7a6736c6 100644
--- a/arch/x86/src/qemu/qemu_serial.c
+++ b/arch/x86/src/qemu/qemu_serial.c
@@ -52,15 +52,15 @@
  *
  ****************************************************************************/
 
-uart_datawidth_t uart_getreg(uart_addrwidth_t base, unsigned int offset)
+uart_datawidth_t uart_getreg(struct u16550_s *priv, unsigned int offset)
 {
-  return inb(base + offset);
+  return inb(priv->uartbase + offset);
 }
 
-void uart_putreg(uart_addrwidth_t base,
+void uart_putreg(struct u16550_s *priv,
                  unsigned int offset, uart_datawidth_t value)
 {
-  outb(value, base + offset);
+  outb(value, priv->uartbase + offset);
 }
 
 #else /* USE_SERIALDRIVER */
diff --git a/arch/x86_64/src/intel64/intel64_serial.c b/arch/x86_64/src/intel64/intel64_serial.c
index d8fe7e81ee..2daff248b2 100644
--- a/arch/x86_64/src/intel64/intel64_serial.c
+++ b/arch/x86_64/src/intel64/intel64_serial.c
@@ -52,12 +52,12 @@
  *
  ****************************************************************************/
 
-uart_datawidth_t uart_getreg(uart_addrwidth_t base, unsigned int offset)
+uart_datawidth_t uart_getreg(struct u16550_s *priv, unsigned int offset)
 {
-  return inb(base + offset);
+  return inb(priv->uartbase + offset);
 }
 
-void uart_putreg(uart_addrwidth_t base, unsigned int offset,
+void uart_putreg(struct u16550_s *priv, unsigned int offset,
                  uart_datawidth_t value)
 {
   /* Intel x86 platform require OUT2 of MCR being set
@@ -69,7 +69,7 @@ void uart_putreg(uart_addrwidth_t base, unsigned int offset,
       value |= UART_MCR_OUT2;
     }
 
-  outb(value, base + offset);
+  outb(value, priv->uartbase + offset);
 }
 
 #else /* USE_SERIALDRIVER */
diff --git a/boards/or1k/mor1kx/or1k/configs/nsh/defconfig b/boards/or1k/mor1kx/or1k/configs/nsh/defconfig
index a27df909bd..a5304afe6c 100644
--- a/boards/or1k/mor1kx/or1k/configs/nsh/defconfig
+++ b/boards/or1k/mor1kx/or1k/configs/nsh/defconfig
@@ -84,6 +84,7 @@ CONFIG_PTHREAD_STACK_DEFAULT=1024
 CONFIG_RAM_SIZE=4194304
 CONFIG_RAM_START=0x0000
 CONFIG_RAW_BINARY=y
+CONFIG_SERIAL_UART_ARCH_MMIO=y
 CONFIG_START_DAY=7
 CONFIG_START_MONTH=12
 CONFIG_START_YEAR=2012
diff --git a/drivers/serial/uart_16550.c b/drivers/serial/uart_16550.c
index b0d1c2223c..58dc514e89 100644
--- a/drivers/serial/uart_16550.c
+++ b/drivers/serial/uart_16550.c
@@ -39,75 +39,55 @@
 #include <nuttx/arch.h>
 #include <nuttx/clk/clk.h>
 #include <nuttx/dma/dma.h>
-#include <nuttx/serial/serial.h>
 #include <nuttx/fs/ioctl.h>
 #include <nuttx/serial/uart_16550.h>
 
 #include <arch/board/board.h>
 
-#if defined(CONFIG_16550_UART0_DMA) || defined(CONFIG_16550_UART1_DMA) \
-      || defined(CONFIG_16550_UART2_DMA) || defined(CONFIG_16550_UART3_DMA)
-#  define HAVE_16550_UART_DMA 1
-#endif
-
 #ifdef CONFIG_16550_UART
 
 /****************************************************************************
  * Pre-processor Definitions
  ****************************************************************************/
 
+/* Are any UARTs enabled? */
+
+#undef HAVE_16550_UART
+#if defined(CONFIG_16550_UART0) || defined(CONFIG_16550_UART1)  \
+  || defined(CONFIG_16550_UART2) || defined(CONFIG_16550_UART3)
+#  define HAVE_16550_UART 1
+#endif
+
 /* Timeout for UART Busy Wait, in milliseconds */
 
 #define UART_TIMEOUT_MS 100
 
-/****************************************************************************
- * Private Types
- ****************************************************************************/
+/* Default getreg/putreg operations */
 
-struct u16550_s
-{
-  uart_addrwidth_t       uartbase;  /* Base address of UART registers */
-#ifdef HAVE_16550_UART_DMA
-  int32_t                dmatx;
-  FAR struct dma_chan_s *chantx;
-  int32_t                dmarx;
-  FAR struct dma_chan_s *chanrx;
-  FAR char              *dmarxbuf;
-  size_t                 dmarxsize;
-  volatile size_t        dmarxhead;
-  volatile size_t        dmarxtail;
-  int32_t                dmarxtimeout;
-#endif
-#if !defined(CONFIG_16550_SUPRESS_CONFIG) || defined(HAVE_16550_UART_DMA)
-  uint32_t               baud;      /* Configured baud */
-  uint32_t               uartclk;   /* UART clock frequency */
-#endif
-#ifdef CONFIG_CLK
-  FAR const char        *clk_name;  /* UART clock name */
-  FAR struct clk_s      *mclk;      /* UART clock descriptor */
-#endif
-  uart_datawidth_t       ier;       /* Saved IER value */
-  uint8_t                irq;       /* IRQ associated with this UART */
-#ifndef CONFIG_16550_SUPRESS_CONFIG
-  uint8_t                parity;    /* 0=none, 1=odd, 2=even */
-  uint8_t                bits;      /* Number of bits (7 or 8) */
-  bool                   stopbits2; /* true: Configure with 2 stop bits instead of 1 */
-#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
-  bool                   flow;      /* flow control (RTS/CTS) enabled */
-#endif
+#ifdef CONFIG_SERIAL_UART_ARCH_MMIO
+#  define u16550_getreg u16550_mmio_getreg
+#  define u16550_putreg u16550_mmio_putreg
+#else
+#  define u16550_getreg uart_getreg
+#  define u16550_putreg uart_putreg
 #endif
-  uart_datawidth_t       rxtrigger; /* RX trigger level */
-};
 
 /****************************************************************************
  * Private Function Prototypes
  ****************************************************************************/
 
+#ifdef CONFIG_SERIAL_UART_ARCH_MMIO
+static uart_datawidth_t u16550_mmio_getreg(FAR struct u16550_s *priv,
+                                           unsigned int offset);
+static void u16550_mmio_putreg(FAR struct u16550_s *priv,
+                               unsigned int offset,
+                               uart_datawidth_t value);
+#endif
+
 static int  u16550_setup(FAR struct uart_dev_s *dev);
 static void u16550_shutdown(FAR struct uart_dev_s *dev);
 static int  u16550_attach(FAR struct uart_dev_s *dev);
 static void u16550_detach(FAR struct uart_dev_s *dev);
-static int  u16550_interrupt(int irq, FAR void *context, FAR void *arg);
 static int  u16550_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
 static int  u16550_receive(FAR struct uart_dev_s *dev, unsigned int *status);
 static void u16550_rxint(FAR struct uart_dev_s *dev, bool enable);
@@ -133,6 +113,21 @@ static bool u16550_txempty(FAR struct uart_dev_s *dev);
  * Private Data
  ****************************************************************************/
 
+#ifdef HAVE_16550_UART
+static const struct u16550_ops_s g_u16550_ops =
+{
+  .isr        = u16550_interrupt,
+  .getreg     = u16550_getreg,
+  .putreg     = u16550_putreg,
+#  ifdef CONFIG_SERIAL_UART_ARCH_IOCTL
+  .ioctl      = uart_ioctl,
+#  endif
+#  ifdef HAVE_16550_UART_DMA
+  .dmachan    = uart_dmachan,
+#  endif
+};
+#endif
+
 static const struct uart_ops_s g_uart_ops =
 {
   .setup          = u16550_setup,
@@ -197,7 +192,9 @@ static char g_uart3dmarxbuf[CONFIG_16550_UART3_DMA_RXBUFSIZE];
 #ifdef CONFIG_16550_UART0
 static struct u16550_s g_uart0priv =
 {
+  .ops            = &g_u16550_ops,
   .uartbase       = CONFIG_16550_UART0_BASE,
+  .regincr        = CONFIG_16550_REGINCR,
 #ifdef CONFIG_16550_UART0_DMA
   .dmatx          = CONFIG_16550_UART0_DMA_TX,
   .dmarx          = CONFIG_16550_UART0_DMA_RX,
@@ -251,7 +248,9 @@ static uart_dev_t g_uart0port =
 #ifdef CONFIG_16550_UART1
 static struct u16550_s g_uart1priv =
 {
+  .ops            = &g_u16550_ops,
   .uartbase       = CONFIG_16550_UART1_BASE,
+  .regincr        = CONFIG_16550_REGINCR,
 #ifdef CONFIG_16550_UART1_DMA
   .dmatx          = CONFIG_16550_UART1_DMA_TX,
   .dmarx          = CONFIG_16550_UART1_DMA_RX,
@@ -305,7 +304,9 @@ static uart_dev_t g_uart1port =
 #ifdef CONFIG_16550_UART2
 static struct u16550_s g_uart2priv =
 {
+  .ops            = &g_u16550_ops,
   .uartbase       = CONFIG_16550_UART2_BASE,
+  .regincr        = CONFIG_16550_REGINCR,
 #ifdef CONFIG_16550_UART2_DMA
   .dmatx          = CONFIG_16550_UART2_DMA_TX,
   .dmarx          = CONFIG_16550_UART2_DMA_RX,
@@ -359,7 +360,9 @@ static uart_dev_t g_uart2port =
 #ifdef CONFIG_16550_UART3
 static struct u16550_s g_uart3priv =
 {
+  .ops            = &g_u16550_ops,
   .uartbase       = CONFIG_16550_UART3_BASE,
+  .regincr        = CONFIG_16550_REGINCR,
 #ifdef CONFIG_16550_UART3_DMA
   .dmatx          = CONFIG_16550_UART3_DMA_TX,
   .dmarx          = CONFIG_16550_UART3_DMA_RX,
@@ -607,6 +610,31 @@ static uart_dev_t g_uart3port =
  * Private Functions
  ****************************************************************************/
 
+#ifdef CONFIG_SERIAL_UART_ARCH_MMIO
+/****************************************************************************
+ * Name: u16550_mmio_getreg
+ ****************************************************************************/
+
+static uart_datawidth_t u16550_mmio_getreg(FAR struct u16550_s *priv,
+                                           unsigned int offset)
+{
+  uintptr_t addr = priv->uartbase + offset;
+  return *((FAR volatile uart_datawidth_t *)addr);
+}
+
+/****************************************************************************
+ * Name: u16550_mmio_putreg
+ ****************************************************************************/
+
+static void u16550_mmio_putreg(FAR struct u16550_s *priv,
+                               unsigned int offset,
+                               uart_datawidth_t value)
+{
+  uintptr_t addr = priv->uartbase + offset;
+  *((FAR volatile uart_datawidth_t *)addr) = value;
+}
+#endif
+
 /****************************************************************************
  * Name: u16550_serialin
  ****************************************************************************/
@@ -614,11 +642,10 @@ static uart_dev_t g_uart3port =
 static inline uart_datawidth_t u16550_serialin(FAR struct u16550_s *priv,
                                                int offset)
 {
-#ifdef CONFIG_SERIAL_UART_ARCH_MMIO
-  return *((FAR volatile uart_datawidth_t *)priv->uartbase + offset);
-#else
-  return uart_getreg(priv->uartbase, offset);
-#endif
+  /* Get correct offset */
+
+  offset *= (priv->regincr * sizeof(uart_datawidth_t));
+  return priv->ops->getreg(priv, offset);
 }
 
 /****************************************************************************
@@ -628,11 +655,10 @@ static inline uart_datawidth_t u16550_serialin(FAR struct u16550_s *priv,
 static inline void u16550_serialout(FAR struct u16550_s *priv, int offset,
                                     uart_datawidth_t value)
 {
-#ifdef CONFIG_SERIAL_UART_ARCH_MMIO
-  *((FAR volatile uart_datawidth_t *)priv->uartbase + offset) = value;
-#else
-  uart_putreg(priv->uartbase, offset, value);
-#endif
+  /* Get correct offset */
+
+  offset *= (priv->regincr * sizeof(uart_datawidth_t));
+  priv->ops->putreg(priv, offset, value);
 }
 
 #ifdef CONFIG_16550_WAIT_LCR
@@ -759,6 +785,13 @@ static int u16550_setup(FAR struct uart_dev_s *dev)
   uint32_t mcr;
 #endif
 
+  if (priv->uartbase == 0)
+    {
+      /* Device must be initialized */
+
+      return -EPERM;
+    }
+
   /* Clear fifos */
 
   u16550_serialout(priv, UART_FCR_OFFSET,
@@ -930,7 +963,7 @@ static int u16550_attach(struct uart_dev_s *dev)
 
   /* Attach and enable the IRQ */
 
-  ret = irq_attach(priv->irq, u16550_interrupt, dev);
+  ret = irq_attach(priv->irq, priv->ops->isr, dev);
 #ifndef CONFIG_ARCH_NOINTC
   if (ret == OK)
     {
@@ -974,7 +1007,7 @@ static void u16550_detach(FAR struct uart_dev_s *dev)
 #endif
 
   up_disable_irq(priv->irq);
-  irq_detach(priv->irq);
+  irqchain_detach(priv->irq, priv->ops->isr, dev);
 
 #ifdef CONFIG_CLK
   /* Clk disaable */
@@ -986,109 +1019,6 @@ static void u16550_detach(FAR struct uart_dev_s *dev)
 #endif
 }
 
-/****************************************************************************
- * Name: u16550_interrupt
- *
- * Description:
- *   This is the UART interrupt handler.  It will be invoked when an
- *   interrupt is received on the 'irq'.  It should call uart_xmitchars or
- *   uart_recvchars to perform the appropriate data transfers.  The
- *   interrupt handling logic must be able to map the 'arg' to the
- *   appropriate uart_dev_s structure in order to call these functions.
- *
- ****************************************************************************/
-
-static int u16550_interrupt(int irq, FAR void *context, FAR void *arg)
-{
-  FAR struct uart_dev_s *dev = (struct uart_dev_s *)arg;
-  FAR struct u16550_s *priv;
-  uint32_t status;
-  int passes;
-
-  DEBUGASSERT(dev != NULL && dev->priv != NULL);
-  priv = (FAR struct u16550_s *)dev->priv;
-
-  /* Loop until there are no characters to be transferred or,
-   * until we have been looping for a long time.
-   */
-
-  for (passes = 0; passes < 256; passes++)
-    {
-      /* Get the current UART status and check for loop
-       * termination conditions
-       */
-
-      status = u16550_serialin(priv, UART_IIR_OFFSET);
-
-      /* The UART_IIR_INTSTATUS bit should be zero if there are pending
-       * interrupts
-       */
-
-      if ((status & UART_IIR_INTSTATUS) != 0)
-        {
-          /* Break out of the loop when there is no longer a
-           * pending interrupt
-           */
-
-          break;
-        }
-
-      /* Handle the interrupt by its interrupt ID field */
-
-      switch (status & UART_IIR_INTID_MASK)
-        {
-          /* Handle incoming, receive bytes (with or without timeout) */
-
-          case UART_IIR_INTID_RDA:
-          case UART_IIR_INTID_CTI:
-            {
-              uart_recvchars(dev);
-              break;
-            }
-
-          /* Handle outgoing, transmit bytes */
-
-          case UART_IIR_INTID_THRE:
-            {
-              uart_xmitchars(dev);
-              break;
-            }
-
-          /* Just clear modem status interrupts (UART1 only) */
-
-          case UART_IIR_INTID_MSI:
-            {
-              /* Read the modem status register (MSR) to clear */
-
-              status = u16550_serialin(priv, UART_MSR_OFFSET);
-              sinfo("MSR: %02"PRIx32"\n", status);
-              break;
-            }
-
-          /* Just clear any line status interrupts */
-
-          case UART_IIR_INTID_RLS:
-            {
-              /* Read the line status register (LSR) to clear */
-
-              status = u16550_serialin(priv, UART_LSR_OFFSET);
-              sinfo("LSR: %02"PRIx32"\n", status);
-              break;
-            }
-
-          /* There should be no other values */
-
-          default:
-            {
-              serr("ERROR: Unexpected IIR: %02"PRIx32"\n", status);
-              break;
-            }
-        }
-    }
-
-  return OK;
-}
-
 /****************************************************************************
  * Name: u16550_ioctl
  *
@@ -1105,7 +1035,7 @@ static int u16550_ioctl(struct file *filep, int cmd, unsigned long arg)
   int ret;
 
 #ifdef CONFIG_SERIAL_UART_ARCH_IOCTL
-  ret = uart_ioctl(filep, cmd, arg);
+  ret = priv->ops->ioctl(priv, cmd, arg);
 
   if (ret != -ENOTTY)
     {
@@ -1538,7 +1468,7 @@ static void u16550_dmarxfree(FAR struct uart_dev_s *dev)
 
   if (priv->chanrx == NULL)
     {
-      priv->chanrx = uart_dmachan(priv->uartbase, priv->dmarx);
+      priv->chanrx = priv->ops->dmachan(priv, priv->dmarx)
       if (priv->chanrx == NULL)
         {
           return; /* Fail to get DMA channel */
@@ -1589,7 +1519,7 @@ static void u16550_dmatxavail(FAR struct uart_dev_s *dev)
 
   if (priv->chantx == NULL)
     {
-      priv->chantx = uart_dmachan(priv->uartbase, priv->dmatx);
+      priv->chantx = priv->ops->dmachan(priv, priv->dmatx);
       if (priv->chantx == NULL)
         {
           return; /* Fail to get DMA channel */
@@ -1692,22 +1622,6 @@ static bool u16550_txempty(struct uart_dev_s *dev)
   return ((u16550_serialin(priv, UART_LSR_OFFSET) & UART_LSR_TEMT) != 0);
 }
 
-/****************************************************************************
- * Name: u16550_putc
- *
- * Description:
- *   Write one character to the UART (polled)
- *
- ****************************************************************************/
-
-#ifdef HAVE_16550_CONSOLE
-static void u16550_putc(FAR struct u16550_s *priv, int ch)
-{
-  while ((u16550_serialin(priv, UART_LSR_OFFSET) & UART_LSR_THRE) == 0);
-  u16550_serialout(priv, UART_THR_OFFSET, (uart_datawidth_t)ch);
-}
-#endif
-
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -1769,7 +1683,7 @@ void u16550_serialinit(void)
  * Name: up_putc
  *
  * Description:
- *   Provide priority, low-level access to support OS debug  writes
+ *   Provide priority, low-level access to support OS debug writes
  *
  ****************************************************************************/
 
@@ -1793,4 +1707,145 @@ int up_putc(int ch)
 }
 #endif
 
+/****************************************************************************
+ * Name: u16550_bind
+ *
+ * Description:
+ *   Bind 16550 compatible device with this driver.
+ *
+ ****************************************************************************/
+
+int u16550_bind(FAR uart_dev_t *dev)
+{
+  dev->ops = &g_uart_ops;
+
+#ifdef CONFIG_16550_PCI_CONSOLE
+  if (dev->isconsole)
+    {
+      /* Setup console device */
+
+      u16550_setup(dev);
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: u16550_interrupt
+ *
+ * Description:
+ *   This is the UART interrupt handler.  It will be invoked when an
+ *   interrupt is received on the 'irq'.  It should call uart_xmitchars or
+ *   uart_recvchars to perform the appropriate data transfers.  The
+ *   interrupt handling logic must be able to map the 'arg' to the
+ *   appropriate uart_dev_s structure in order to call these functions.
+ *
+ ****************************************************************************/
+
+int u16550_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct uart_dev_s *dev = (struct uart_dev_s *)arg;
+  FAR struct u16550_s *priv;
+  uint32_t status;
+  int passes;
+
+  DEBUGASSERT(dev != NULL && dev->priv != NULL);
+  priv = (FAR struct u16550_s *)dev->priv;
+
+  /* Loop until there are no characters to be transferred or,
+   * until we have been looping for a long time.
+   */
+
+  for (passes = 0; passes < 256; passes++)
+    {
+      /* Get the current UART status and check for loop
+       * termination conditions
+       */
+
+      status = u16550_serialin(priv, UART_IIR_OFFSET);
+
+      /* The UART_IIR_INTSTATUS bit should be zero if there are pending
+       * interrupts
+       */
+
+      if ((status & UART_IIR_INTSTATUS) != 0)
+        {
+          /* Break out of the loop when there is no longer a
+           * pending interrupt
+           */
+
+          break;
+        }
+
+      /* Handle the interrupt by its interrupt ID field */
+
+      switch (status & UART_IIR_INTID_MASK)
+        {
+          /* Handle incoming, receive bytes (with or without timeout) */
+
+          case UART_IIR_INTID_RDA:
+          case UART_IIR_INTID_CTI:
+            {
+              uart_recvchars(dev);
+              break;
+            }
+
+          /* Handle outgoing, transmit bytes */
+
+          case UART_IIR_INTID_THRE:
+            {
+              uart_xmitchars(dev);
+              break;
+            }
+
+          /* Just clear modem status interrupts (UART1 only) */
+
+          case UART_IIR_INTID_MSI:
+            {
+              /* Read the modem status register (MSR) to clear */
+
+              status = u16550_serialin(priv, UART_MSR_OFFSET);
+              sinfo("MSR: %02"PRIx32"\n", status);
+              break;
+            }
+
+          /* Just clear any line status interrupts */
+
+          case UART_IIR_INTID_RLS:
+            {
+              /* Read the line status register (LSR) to clear */
+
+              status = u16550_serialin(priv, UART_LSR_OFFSET);
+              sinfo("LSR: %02"PRIx32"\n", status);
+              break;
+            }
+
+          /* There should be no other values */
+
+          default:
+            {
+              serr("ERROR: Unexpected IIR: %02"PRIx32"\n", status);
+              break;
+            }
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: u16550_putc
+ *
+ * Description:
+ *   Write one character to the UART (polled)
+ *
+ ****************************************************************************/
+
+void u16550_putc(FAR struct u16550_s *priv, int ch)
+{
+  while ((u16550_serialin(priv, UART_LSR_OFFSET) & UART_LSR_THRE) == 0);
+  u16550_serialout(priv, UART_THR_OFFSET, (uart_datawidth_t)ch);
+}
+
 #endif /* CONFIG_16550_UART */
diff --git a/include/nuttx/serial/uart_16550.h b/include/nuttx/serial/uart_16550.h
index 32e95759d8..688281702f 100644
--- a/include/nuttx/serial/uart_16550.h
+++ b/include/nuttx/serial/uart_16550.h
@@ -27,7 +27,8 @@
  ****************************************************************************/
 
 #include <nuttx/config.h>
-#include <stdint.h>
+
+#include <nuttx/serial/serial.h>
 
 #ifdef CONFIG_16550_UART
 
@@ -37,12 +38,10 @@
 
 /* CONFIGURATION ************************************************************/
 
-/* Are any UARTs enabled? */
-
-#undef HAVE_UART
-#if defined(CONFIG_16550_UART0) || defined(CONFIG_16550_UART1) || \
-    defined(CONFIG_16550_UART2) || defined(CONFIG_16550_UART3)
-#  define HAVE_UART 1
+#undef HAVE_16550_UART_DMA
+#if defined(CONFIG_16550_UART0_DMA) || defined(CONFIG_16550_UART1_DMA) || \
+    defined(CONFIG_16550_UART2_DMA) || defined(CONFIG_16550_UART3_DMA)
+#  define HAVE_16550_UART_DMA 1
 #endif
 
 /* We need to be told the address increment between registers and the
@@ -173,33 +172,20 @@
 
 /* Register offsets *********************************************************/
 
-#define UART_RBR_INCR          0  /* (DLAB =0) Receiver Buffer Register */
-#define UART_THR_INCR          0  /* (DLAB =0) Transmit Holding Register */
-#define UART_DLL_INCR          0  /* (DLAB =1) Divisor Latch LSB */
-#define UART_DLM_INCR          1  /* (DLAB =1) Divisor Latch MSB */
-#define UART_IER_INCR          1  /* (DLAB =0) Interrupt Enable Register */
-#define UART_IIR_INCR          2  /* Interrupt ID Register */
-#define UART_FCR_INCR          2  /* FIFO Control Register */
-#define UART_LCR_INCR          3  /* Line Control Register */
-#define UART_MCR_INCR          4  /* Modem Control Register */
-#define UART_LSR_INCR          5  /* Line Status Register */
-#define UART_MSR_INCR          6  /* Modem Status Register */
-#define UART_SCR_INCR          7  /* Scratch Pad Register */
-#define UART_USR_INCR          31 /* UART Status Register */
-
-#define UART_RBR_OFFSET        (CONFIG_16550_REGINCR*UART_RBR_INCR)
-#define UART_THR_OFFSET        (CONFIG_16550_REGINCR*UART_THR_INCR)
-#define UART_DLL_OFFSET        (CONFIG_16550_REGINCR*UART_DLL_INCR)
-#define UART_DLM_OFFSET        (CONFIG_16550_REGINCR*UART_DLM_INCR)
-#define UART_IER_OFFSET        (CONFIG_16550_REGINCR*UART_IER_INCR)
-#define UART_IIR_OFFSET        (CONFIG_16550_REGINCR*UART_IIR_INCR)
-#define UART_FCR_OFFSET        (CONFIG_16550_REGINCR*UART_FCR_INCR)
-#define UART_LCR_OFFSET        (CONFIG_16550_REGINCR*UART_LCR_INCR)
-#define UART_MCR_OFFSET        (CONFIG_16550_REGINCR*UART_MCR_INCR)
-#define UART_LSR_OFFSET        (CONFIG_16550_REGINCR*UART_LSR_INCR)
-#define UART_MSR_OFFSET        (CONFIG_16550_REGINCR*UART_MSR_INCR)
-#define UART_SCR_OFFSET        (CONFIG_16550_REGINCR*UART_SCR_INCR)
-#define UART_USR_OFFSET        (CONFIG_16550_REGINCR*UART_USR_INCR)
+#define UART_RBR_OFFSET        0  /* (DLAB =0) Receiver Buffer Register */
+#define UART_THR_OFFSET        0  /* (DLAB =0) Transmit Holding Register */
+#define UART_DLL_OFFSET        0  /* (DLAB =1) Divisor Latch LSB */
+#define UART_DLM_OFFSET        1  /* (DLAB =1) Divisor Latch MSB */
+#define UART_IER_OFFSET        1  /* (DLAB =0) Interrupt Enable Register */
+#define UART_IIR_OFFSET        2  /* Interrupt ID Register */
+#define UART_FCR_OFFSET        2  /* FIFO Control Register */
+#define UART_LCR_OFFSET        3  /* Line Control Register */
+#define UART_MCR_OFFSET        4  /* Modem Control Register */
+#define UART_LSR_OFFSET        5  /* Line Status Register */
+#define UART_MSR_OFFSET        6  /* Modem Status Register */
+#define UART_SCR_OFFSET        7  /* Scratch Pad Register */
+#define UART_USR_OFFSET        31 /* UART Status Register */
+#define UART_DLF_OFFSET        48 /* Divisor Latch Fraction Register */
 
 /* Register bit definitions *************************************************/
 
@@ -333,6 +319,64 @@ typedef uint64_t uart_addrwidth_t;
  * Public Data
  ****************************************************************************/
 
+/* UART 16550 ops */
+
+struct u16550_s;
+struct u16550_ops_s
+{
+  CODE int (*isr)(int irq, FAR void *context, FAR void *arg);
+  CODE uart_datawidth_t (*getreg)(FAR struct u16550_s *priv,
+                                  unsigned int offset);
+  CODE void (*putreg)(FAR struct u16550_s *priv,
+                      unsigned int offset,
+                      uart_datawidth_t value);
+  CODE int (*ioctl)(FAR struct u16550_s *priv, int cmd, unsigned long arg);
+  CODE FAR struct dma_chan_s *(*dmachan)(FAR struct u16550_s *priv,
+                                         unsigned int ident);
+};
+
+/* UART 16550 private data */
+
+struct u16550_s
+{
+  /* UART 16550 operations */
+
+  FAR const struct u16550_ops_s *ops;
+
+  uart_addrwidth_t       uartbase;  /* Base address of UART registers */
+  uint8_t                regincr;
+#ifdef HAVE_16550_UART_DMA
+  int32_t                dmatx;
+  FAR struct dma_chan_s *chantx;
+  int32_t                dmarx;
+  FAR struct dma_chan_s *chanrx;
+  FAR char              *dmarxbuf;
+  size_t                 dmarxsize;
+  volatile size_t        dmarxhead;
+  volatile size_t        dmarxtail;
+  int32_t                dmarxtimeout;
+#endif
+#if !defined(CONFIG_16550_SUPRESS_CONFIG) || defined(HAVE_16550_UART_DMA)
+  uint32_t               baud;      /* Configured baud */
+  uint32_t               uartclk;   /* UART clock frequency */
+#endif
+#ifdef CONFIG_CLK
+  FAR const char        *clk_name;  /* UART clock name */
+  FAR struct clk_s      *mclk;      /* UART clock descriptor */
+#endif
+  uart_datawidth_t       ier;       /* Saved IER value */
+  int                    irq;       /* IRQ associated with this UART */
+#ifndef CONFIG_16550_SUPRESS_CONFIG
+  uint8_t                parity;    /* 0=none, 1=odd, 2=even */
+  uint8_t                bits;      /* Number of bits (7 or 8) */
+  bool                   stopbits2; /* true: Configure with 2 stop bits instead of 1 */
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL)
+  bool                   flow;      /* flow control (RTS/CTS) enabled */
+#endif
+#endif
+  uart_datawidth_t       rxtrigger; /* RX trigger level */
+};
+
 /****************************************************************************
  * Public Functions Definitions
  ****************************************************************************/
@@ -363,6 +407,36 @@ void u16550_earlyserialinit(void);
 
 void u16550_serialinit(void);
 
+/****************************************************************************
+ * Name: u16550_bind
+ *
+ * Description:
+ *   Bind 16550 compatible device with this driver.
+ *
+ ****************************************************************************/
+
+int u16550_bind(FAR uart_dev_t *dev);
+
+/****************************************************************************
+ * Name: u16550_interrupt
+ *
+ * Description:
+ *   Handle UART 16550 interrupt.
+ *
+ ****************************************************************************/
+
+int u16550_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/****************************************************************************
+ * Name: u16550_putc
+ *
+ * Description:
+ *   Write one character to the UART (polled)
+ *
+ ****************************************************************************/
+
+void u16550_putc(FAR struct u16550_s *priv, int ch);
+
 /****************************************************************************
  * Name: uart_getreg(), uart_putreg(), uart_ioctl()
  *
@@ -374,17 +448,16 @@ void u16550_serialinit(void);
  ****************************************************************************/
 
 #ifndef CONFIG_SERIAL_UART_ARCH_MMIO
-uart_datawidth_t uart_getreg(uart_addrwidth_t base, unsigned int offset);
-void uart_putreg(uart_addrwidth_t base,
+uart_datawidth_t uart_getreg(FAR struct u16550_s *priv, unsigned int offset);
+void uart_putreg(FAR struct u16550_s *priv,
                  unsigned int offset,
                  uart_datawidth_t value);
 #endif
 
-struct file;  /* Forward reference */
-int uart_ioctl(struct file *filep, int cmd, unsigned long arg);
+int uart_ioctl(FAR struct u16550_s *priv, int cmd, unsigned long arg);
 
 struct dma_chan_s;
-FAR struct dma_chan_s *uart_dmachan(uart_addrwidth_t base,
+FAR struct dma_chan_s *uart_dmachan(FAR struct u16550_s *priv,
                                     unsigned int ident);
 
 #endif /* CONFIG_16550_UART */