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 */