From ceb2921d79694c13be2a82db00b031d744f9f1cc Mon Sep 17 00:00:00 2001 From: p-szafonimateusz Date: Thu, 12 Sep 2024 10:12:39 +0200 Subject: [PATCH] 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 --- 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(-) delete mode 100644 arch/or1k/src/common/or1k_uart.c 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 -#include - -#include - -#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 #include #include -#include #include #include #include -#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; +#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 -#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 -#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 -#include + +#include #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 */