From 16e93f5d4194cccd2e9b8ad01737c3626832ae3c Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 3 Mar 2016 16:31:56 -0600 Subject: [PATCH] i.MX6: Bring i.MX1 serial driver into i.MX6. Basically the same IP but does not yet compile due to some small differences, missign GPIO configuration logic, and missing clocking logic --- arch/arm/src/imx1/imx_serial.c | 30 +- arch/arm/src/imx6/Make.defs | 2 +- arch/arm/src/imx6/imx_serial.c | 1305 ++++++++++++++++++++++++++++++++ 3 files changed, 1321 insertions(+), 16 deletions(-) create mode 100644 arch/arm/src/imx6/imx_serial.c diff --git a/arch/arm/src/imx1/imx_serial.c b/arch/arm/src/imx1/imx_serial.c index ac93aa949b5..5a5ac78bc2d 100644 --- a/arch/arm/src/imx1/imx_serial.c +++ b/arch/arm/src/imx1/imx_serial.c @@ -80,19 +80,19 @@ struct up_dev_s { - uint32_t uartbase; /* Base address of UART registers */ - uint32_t baud; /* Configured baud */ - uint32_t ucr1; /* Saved UCR1 value */ + uint32_t uartbase; /* Base address of UART registers */ + uint32_t baud; /* Configured baud */ + uint32_t ucr1; /* Saved UCR1 value */ #if defined(CONFIG_ARCH_CHIP_IMX1) || defined(CONFIG_ARCH_CHIP_IMXL) - uint8_t rxirq; /* Rx IRQ associated with this UART */ - uint8_t txirq; /* Tx IRQ associated with this UART */ + uint8_t rxirq; /* Rx IRQ associated with this UART */ + uint8_t txirq; /* Tx IRQ associated with this UART */ #else - uint8_t irq; /* IRQ associated with this UART */ + uint8_t irq; /* IRQ associated with this UART */ #endif - uint8_t parity; /* 0=none, 1=odd, 2=even */ - uint8_t bits; /* Number of bits (7 or 8) */ - uint8_t stopbits2:1; /* 1: Configure with 2 stop bits vs 1 */ - uint8_t hwfc:1; /* 1: Hardware flow control */ + uint8_t parity; /* 0=none, 1=odd, 2=even */ + uint8_t bits; /* Number of bits (7 or 8) */ + uint8_t stopbits2:1; /* 1: Configure with 2 stop bits vs 1 */ + uint8_t hwfc:1; /* 1: Hardware flow control */ uint8_t reserved:6; }; @@ -157,8 +157,8 @@ static char g_uart2txbuffer[CONFIG_UART2_TXBUFSIZE]; #endif #ifdef CONFIG_IMX1_UART3 -static char g_uart3rxbuffer[CONFIG_UART2_RXBUFSIZE]; -static char g_uart3txbuffer[CONFIG_UART2_TXBUFSIZE]; +static char g_uart3rxbuffer[CONFIG_UART3_RXBUFSIZE]; +static char g_uart3txbuffer[CONFIG_UART3_TXBUFSIZE]; #endif /* This describes the state of the IMX uart1 port. */ @@ -179,7 +179,7 @@ static struct up_dev_s g_uart1priv = .stopbits2 = CONFIG_UART1_2STOP, }; -static uart_dev_t g_uart1port = +static struct uart_dev_s g_uart1port = { .recv = { @@ -214,7 +214,7 @@ static struct up_dev_s g_uart2priv = .stopbits2 = CONFIG_UART2_2STOP, }; -static uart_dev_t g_uart2port = +static struct uart_dev_s g_uart2port = { .recv = { @@ -247,7 +247,7 @@ static struct up_dev_s g_uart3priv = .stopbits2 = CONFIG_UART3_2STOP, }; -static uart_dev_t g_uart3port = +static struct uart_dev_s g_uart3port = { .recv = { diff --git a/arch/arm/src/imx6/Make.defs b/arch/arm/src/imx6/Make.defs index 845966fd8e7..e7aded5a4e8 100644 --- a/arch/arm/src/imx6/Make.defs +++ b/arch/arm/src/imx6/Make.defs @@ -132,4 +132,4 @@ CHIP_ASRCS = # i.MX6-specific C source files -CHIP_CSRCS = imx_boot.c imx_memorymap.c imx_irq.c +CHIP_CSRCS = imx_boot.c imx_memorymap.c imx_irq.c # imx_serial.c diff --git a/arch/arm/src/imx6/imx_serial.c b/arch/arm/src/imx6/imx_serial.c new file mode 100644 index 00000000000..cacf39a8dcd --- /dev/null +++ b/arch/arm/src/imx6/imx_serial.c @@ -0,0 +1,1305 @@ +/**************************************************************************** + * arch/arm/src/imx6/imx_serial.c + * + * Copyright (C) 2016 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "chip.h" +#include "up_arch.h" +#include "up_internal.h" + +#include "imx_config.h" +#include "chip/imx_uart.h" + +#ifdef USE_SERIALDRIVER + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Which UART with be tty0/console and which tty1-4? The console will always + * be ttyS0. If there is no console then will use the lowest numbered UART. + */ + +/* First pick the console and ttys0. This could be any of UART1-5 */ + +#if defined(CONFIG_UART1_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart1port /* UART1 is console */ +# define TTYS0_DEV g_uart1port /* UART1 is ttyS0 */ +# define UART1_ASSIGNED 1 +#elif defined(CONFIG_UART2_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart2port /* UART2 is console */ +# define TTYS0_DEV g_uart2port /* UART2 is ttyS0 */ +# define UART2_ASSIGNED 1 +#elif defined(CONFIG_UART3_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart3port /* UART3 is console */ +# define TTYS0_DEV g_uart3port /* UART3 is ttyS0 */ +# define UART3_ASSIGNED 1 +#elif defined(CONFIG_UART4_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart4port /* UART4 is console */ +# define TTYS0_DEV g_uart4port /* UART4 is ttyS0 */ +# define UART4_ASSIGNED 1 +#elif defined(CONFIG_UART5_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart5port /* UART5 is console */ +# define TTYS5_DEV g_uart5port /* UART5 is ttyS0 */ +# define UART5_ASSIGNED 1 +#else +# undef CONSOLE_DEV /* No console */ +# if defined(CONFIG_IMX6_UART1) +# define TTYS0_DEV g_uart1port /* UART1 is ttyS0 */ +# define UART1_ASSIGNED 1 +# elif defined(CONFIG_IMX6_UART2) +# define TTYS0_DEV g_uart2port /* UART2 is ttyS0 */ +# define UART2_ASSIGNED 1 +# elif defined(CONFIG_IMX6_UART3) +# define TTYS0_DEV g_uart3port /* UART3 is ttyS0 */ +# define UART3_ASSIGNED 1 +# elif defined(CONFIG_IMX6_UART4) +# define TTYS0_DEV g_uart4port /* UART4 is ttyS0 */ +# define UART4_ASSIGNED 1 +# elif defined(CONFIG_IMX6_UART5) +# define TTYS0_DEV g_uart5port /* UART5 is ttyS0 */ +# define UART5_ASSIGNED 1 +# endif +#endif + +/* Pick ttys1. This could be any of UART1-5 excluding the console UART. + * One of UART1-5 could be the console; one of UART1-5 has already been + * assigned to ttys0. + */ + +#if defined(CONFIG_IMX6_UART1) && !defined(UART1_ASSIGNED) +# define TTYS1_DEV g_uart1port /* UART1 is ttyS1 */ +# define UART1_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART2) && !defined(UART2_ASSIGNED) +# define TTYS1_DEV g_uart2port /* UART2 is ttyS1 */ +# define UART2_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART3) && !defined(UART3_ASSIGNED) +# define TTYS1_DEV g_uart3port /* UART3 is ttyS1 */ +# define UART3_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART4) && !defined(UART4_ASSIGNED) +# define TTYS1_DEV g_uart4port /* UART4 is ttyS1 */ +# define UART4_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART5) && !defined(UART5_ASSIGNED) +# define TTYS1_DEV g_uart5port /* UART5 is ttyS1 */ +# define UART5_ASSIGNED 1 +#endif + +/* Pick ttys2. This could be one of UART2-5. It can't be UART1 because that + * was either assigned as ttyS0 or ttys1. One of UART 1-5 could be the + * console. One of UART2-5 has already been assigned to ttys0 or ttyS1. + */ + +#if defined(CONFIG_IMX6_UART2) && !defined(UART2_ASSIGNED) +# define TTYS2_DEV g_uart2port /* UART2 is ttyS2 */ +# define UART2_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART3) && !defined(UART3_ASSIGNED) +# define TTYS2_DEV g_uart3port /* UART3 is ttyS2 */ +# define UART3_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART4) && !defined(UART4_ASSIGNED) +# define TTYS2_DEV g_uart4port /* UART4 is ttyS2 */ +# define UART4_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART5) && !defined(UART5_ASSIGNED) +# define TTYS2_DEV g_uart5port /* UART5 is ttyS2 */ +# define UART5_ASSIGNED 1 +#endif + +/* Pick ttys3. This could be one of UART3-5. It can't be UART1-2 because + * those have already been assigned to ttsyS0, 1, or 2. One of + * UART32-5 could also be the console. One of UART3-5 has already + * been assigned to ttys0, 1, or 3. + */ + +#if defined(CONFIG_IMX6_UART3) && !defined(UART3_ASSIGNED) +# define TTYS3_DEV g_uart3port /* UART3 is ttyS3 */ +# define UART3_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART4) && !defined(UART4_ASSIGNED) +# define TTYS3_DEV g_uart4port /* UART4 is ttyS3 */ +# define UART4_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART5) && !defined(UART5_ASSIGNED) +# define TTYS3_DEV g_uart5port /* UART5 is ttyS3 */ +# define UART5_ASSIGNED 1 +#endif + +/* Pick ttys4. This could be one of UART4-5. It can't be UART1-3 because + * those have already been assigned to ttsyS0, 1, 2 or 3. One of + * UART 4-5 could be the console. One of UART4-5 has already been + * assigned to ttys0, 1, 3, or 4. + */ + +#if defined(CONFIG_IMX6_UART4) && !defined(UART4_ASSIGNED) +# define TTYS4_DEV g_uart4port /* UART4 is ttyS4 */ +# define UART4_ASSIGNED 1 +#elif defined(CONFIG_IMX6_UART5) && !defined(UART5_ASSIGNED) +# define TTYS4_DEV g_uart5port /* UART5 is ttyS4 */ +# define UART5_ASSIGNED 1 +#endif + +/* UART, if avaialble, should have been assigned to ttyS0-4. */ + +#if defined(CONFIG_IMX6_UART5) && !defined(UART5_ASSIGNED) +# errnor UART5 was not assigned to a TTY. +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct imx_uart_s +{ + xcpt_t handler; /* Interrupt handler */ + uint32_t uartbase; /* Base address of UART registers */ + uint32_t baud; /* Configured baud */ + uint32_t ucr1; /* Saved UCR1 value */ + uint8_t irq; /* IRQ associated with this UART */ + uint8_t parity; /* 0=none, 1=odd, 2=even */ + uint8_t bits; /* Number of bits (7 or 8) */ + uint8_t stopbits2:1; /* 1: Configure with 2 stop bits vs 1 */ + uint8_t hwfc:1; /* 1: Hardware flow control */ + uint8_t reserved:6; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline uint32_t imx_serialin(struct imx_uart_s *priv, + uint32_t offset); +static inline void imx_serialout(struct imx_uart_s *priv, uint32_t offset, + uint32_t value); +static inline void imx_disableuartint(struct imx_uart_s *priv, + uint32_t *ucr1); +static inline void imx_restoreuartint(struct imx_uart_s *priv, + uint32_t ucr1); +static inline void imx_waittxready(struct imx_uart_s *priv); + +static int imx_setup(struct uart_dev_s *dev); +static void imx_shutdown(struct uart_dev_s *dev); +static int imx_attach(struct uart_dev_s *dev); +static void imx_detach(struct uart_dev_s *dev); + +static int imx_interrupt(struct uart_dev_s *priv); +#ifdef CONFIG_IMX6_UART1 +static int imx_uart1_interrupt(int irq, void *context); +#endif +#ifdef CONFIG_IMX6_UART2 +static int imx_uart2_interrupt(int irq, void *context); +#endif +#ifdef CONFIG_IMX6_UART3 +static int imx_uart3_interrupt(int irq, void *context); +#endif +#ifdef CONFIG_IMX6_UART4 +static int imx_uart4_interrupt(int irq, void *context); +#endif +#ifdef CONFIG_IMX6_UART5 +static int imx_uart5_interrupt(int irq, void *context); +#endif + +static int imx_ioctl(struct file *filep, int cmd, unsigned long arg); +static int imx_receive(struct uart_dev_s *dev, uint32_t *status); +static void imx_rxint(struct uart_dev_s *dev, bool enable); +static bool imx_rxavailable(struct uart_dev_s *dev); +static void imx_send(struct uart_dev_s *dev, int ch); +static void imx_txint(struct uart_dev_s *dev, bool enable); +static bool imx_txready(struct uart_dev_s *dev); +static bool imx_txempty(struct uart_dev_s *dev); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct uart_ops_s g_uart_ops = +{ + .setup = imx_setup, + .shutdown = imx_shutdown, + .attach = imx_attach, + .detach = imx_detach, + .ioctl = imx_ioctl, + .receive = imx_receive, + .rxint = imx_rxint, + .rxavailable = imx_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = NULL, +#endif + .send = imx_send, + .txint = imx_txint, + .txready = imx_txready, + .txempty = imx_txempty, +}; + +/* I/O buffers */ + +#ifdef CONFIG_IMX6_UART1 +static char g_uart1rxbuffer[CONFIG_UART1_RXBUFSIZE]; +static char g_uart1txbuffer[CONFIG_UART1_TXBUFSIZE]; +#endif + +#ifdef CONFIG_IMX6_UART2 +static char g_uart2rxbuffer[CONFIG_UART2_RXBUFSIZE]; +static char g_uart2txbuffer[CONFIG_UART2_TXBUFSIZE]; +#endif + +#ifdef CONFIG_IMX6_UART3 +static char g_uart3rxbuffer[CONFIG_UART3_RXBUFSIZE]; +static char g_uart3txbuffer[CONFIG_UART3_TXBUFSIZE]; +#endif + +#ifdef CONFIG_IMX6_UART4 +static char g_uart4rxbuffer[CONFIG_UART4_RXBUFSIZE]; +static char g_uart4txbuffer[CONFIG_UART4_TXBUFSIZE]; +#endif + +#ifdef CONFIG_IMX6_UART5 +static char g_uart5rxbuffer[CONFIG_UART5_RXBUFSIZE]; +static char g_uart5txbuffer[CONFIG_UART5_TXBUFSIZE]; +#endif + +/* This describes the state of the IMX uart1 port. */ + +#ifdef CONFIG_IMX6_UART1 +static struct imx_uart_s g_uart1priv = +{ + .handler = imx_uart1_interrupt, + .uartbase = IMX_UART1_VBASE, + .baud = CONFIG_UART1_BAUD, + .irq = IMX_IRQ_UART1, + .parity = CONFIG_UART1_PARITY, + .bits = CONFIG_UART1_BITS, + .stopbits2 = CONFIG_UART1_2STOP, +}; + +static struct uart_dev_s g_uart1port = +{ + .recv = + { + .size = CONFIG_UART1_RXBUFSIZE, + .buffer = g_uart1rxbuffer, + }, + .xmit = + { + .size = CONFIG_UART1_TXBUFSIZE, + .buffer = g_uart1txbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_uart1priv, +}; +#endif + +/* This describes the state of the IMX uart2 port. */ + +#ifdef CONFIG_IMX6_UART2 +static struct imx_uart_s g_uart2priv = +{ + .handler = imx_uart2_interrupt, + .uartbase = IMX_UART2_VBASE, + .baud = CONFIG_UART2_BAUD, + .irq = IMX_IRQ_UART2, + .parity = CONFIG_UART2_PARITY, + .bits = CONFIG_UART2_BITS, + .stopbits2 = CONFIG_UART2_2STOP, +}; + +static struct uart_dev_s g_uart2port = +{ + .recv = + { + .size = CONFIG_UART2_RXBUFSIZE, + .buffer = g_uart2rxbuffer, + }, + .xmit = + { + .size = CONFIG_UART2_TXBUFSIZE, + .buffer = g_uart2txbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_uart2priv, +}; +#endif + +#ifdef CONFIG_IMX6_UART3 +static struct imx_uart_s g_uart3priv = +{ + .handler = imx_uart3_interrupt, + .uartbase = IMX_UART3_REGISTER_BASE, + .baud = IMX_UART3_VBASE, + .irq = IMX_IRQ_UART3, + .parity = CONFIG_UART3_PARITY, + .bits = CONFIG_UART3_BITS, + .stopbits2 = CONFIG_UART3_2STOP, +}; + +static struct uart_dev_s g_uart3port = +{ + .recv = + { + .size = CONFIG_UART3_RXBUFSIZE, + .buffer = g_uart3rxbuffer, + }, + .xmit = + { + .size = CONFIG_UART3_TXBUFSIZE, + .buffer = g_uart3txbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_uart3priv, +}; +#endif + +#ifdef CONFIG_IMX6_UART4 +static struct imx_uart_s g_uart4priv = +{ + .handler = imx_uart4_interrupt, + .uartbase = IMX_UART4_REGISTER_BASE, + .baud = IMX_UART4_VBASE, + .irq = IMX_IRQ_UART4, + .parity = CONFIG_UART4_PARITY, + .bits = CONFIG_UART4_BITS, + .stopbits2 = CONFIG_UART4_2STOP, +}; + +static struct uart_dev_s g_uart4port = +{ + .recv = + { + .size = CONFIG_UART4_RXBUFSIZE, + .buffer = g_uart4rxbuffer, + }, + .xmit = + { + .size = CONFIG_UART4_TXBUFSIZE, + .buffer = g_uart4txbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_uart4priv, +}; +#endif + +#ifdef CONFIG_IMX6_UART5 +static struct imx_uart_s g_uart5priv = +{ + .handler = imx_uart5_interrupt, + .uartbase = IMX_UART5_REGISTER_BASE, + .baud = IMX_UART5_VBASE, + .irq = IMX_IRQ_UART5, + .parity = CONFIG_UART5_PARITY, + .bits = CONFIG_UART5_BITS, + .stopbits2 = CONFIG_UART5_2STOP, +}; + +static struct uart_dev_s g_uart5port = +{ + .recv = + { + .size = CONFIG_UART5_RXBUFSIZE, + .buffer = g_uart5rxbuffer, + }, + .xmit = + { + .size = CONFIG_UART5_TXBUFSIZE, + .buffer = g_uart5txbuffer, + }, + .ops = &g_uart_ops, + .priv = &g_uart5priv, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx_serialin + ****************************************************************************/ + +static inline uint32_t imx_serialin(struct imx_uart_s *priv, uint32_t offset) +{ + return getreg32(priv->uartbase + offset); +} + +/**************************************************************************** + * Name: imx_serialout + ****************************************************************************/ + +static inline void imx_serialout(struct imx_uart_s *priv, uint32_t offset, + uint32_t value) +{ + putreg32(value, priv->uartbase + offset); +} + +/**************************************************************************** + * Name: imx_disableuartint + ****************************************************************************/ + +static inline void imx_disableuartint(struct imx_uart_s *priv, + uint32_t *ucr1) +{ + /* Return the current Rx and Tx interrupt state */ + + if (ucr1 != NULL) + { + *ucr1 = priv->ucr1 & (UART_UCR1_RRDYEN | UART_UCR1_TXEMPTYEN); + } + + /* Then disable both Rx and Tx interrupts */ + + priv->ucr1 &= ~(UART_UCR1_RRDYEN | UART_UCR1_TXEMPTYEN); + imx_serialout(priv, UART_UCR1_OFFSET, priv->ucr1); +} + +/**************************************************************************** + * Name: imx_restoreuartint + ****************************************************************************/ + +static inline void imx_restoreuartint(struct imx_uart_s *priv, uint32_t ucr1) +{ + /* Enable/disable any interrupts that are currently disabled but should be + * enabled/disabled. + */ + + priv->ucr1 &= ~(UART_UCR1_RRDYEN | UART_UCR1_TXEMPTYEN); + priv->ucr1 |= ucr1 & (UART_UCR1_RRDYEN | UART_UCR1_TXEMPTYEN); + imx_serialout(priv, UART_UCR1_OFFSET, priv->ucr1); +} + +/**************************************************************************** + * Name: imx_waittxready + ****************************************************************************/ + +static inline void imx_waittxready(struct imx_uart_s *priv) +{ + int tmp; + + for (tmp = 1000 ; tmp > 0 ; tmp--) + { + if ((imx_serialin(priv, UART_UTS_OFFSET) & UART_UTS_TXFULL) == 0) + { + break; + } + } +} + +/**************************************************************************** + * Name: imx_setup + * + * Description: + * Configure the UART baud, bits, parity, fifos, etc. This + * method is called the first time that the serial port is + * opened. + * + ****************************************************************************/ + +static int imx_setup(struct uart_dev_s *dev) +{ +#ifndef CONFIG_SUPPRESS_UART_CONFIG + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + uint32_t regval; + uint32_t ucr2; + uint32_t div; + uint32_t num; + uint32_t den; + + /* Disable the UART */ + + imx_serialout(priv, UART_UCR1_OFFSET, 0); + imx_serialout(priv, UART_UCR2_OFFSET, 0); + imx_serialout(priv, UART_UCR3_OFFSET, 0); + imx_serialout(priv, UART_UCR4_OFFSET, 0); + + /* Set up UCR2 */ + + ucr2 = imx_serialin(priv, UART_UCR2_OFFSET); + ucr2 |= (UART_UCR2_SRST | UART_UCR2_IRTS) + + /* Select the number of data bits */ + + DEBUGASSERT(priv->bits == 7 || priv->bits == 8); + if (priv->bits == 8) + { + ucr2 |= UART_UCR2_WS; + } + + /* Select the number of stop bits */ + + if (priv->stopbits2) + { + ucr2 |= UART_UCR2_STPB; + } + + /* Select even/odd parity */ + + if (priv->parity) + { + DEBUGASSERT(priv->parity == 1 || priv->parity == 2); + ucr2 |= UART_UCR2_PREN; + if (priv->parity == 1) + { + ucr2 |= UART_UCR2_PROE; + } + } + + /* Select RTS */ + +#if 0 + ucr2 &= ~UCR2_IRTS; + ucr2 |= UCR2_CTSC; +#endif + + /* Setup hardware flow control */ + + regval = 0; +#if 0 + if (priv->hwfc) + { + ucr2 |= UART_UCR2_IRTS; + + /* CTS controled by Rx FIFO */ + + ucr2 |= UART_UCR2_CTSC; + + /* Set CTS trigger level */ + + regval |= 30 << UART_UCR4_CTSTL_SHIFT; + } +#endif + + /* i.MX reference clock (PERCLK1) is configured for 16MHz */ + + imx_serialout(priv, UART_UCR4_OFFSET, regval | UART_UCR4_REF16); + + /* Setup the new UART configuration */ + + imx_serialout(priv, UART_UCR2_OFFSET, ucr2); + + /* Set the baud. + * + * baud * 16 / REFFREQ = NUM/DEN + * UBIR = NUM-1; + * UMBR = DEN-1 + * REFFREQ = PERCLK1 / DIV + * DIV = RFDIV[2:0] + * + * First, select a closest value we can for the divider + */ + + div = (BOARD_PERCLK1_FREQUENCY >> 4) / priv->baud; + if (div > 7) + { + div = 7; + } + else if (div < 1) + { + div = 1; + } + + /* Now find the numerator and denominator. These must have + * the ratio baud/(PERCLK / div / 16), but the values cannot + * exceed 16 bits + */ + + num = priv->baud; + den = (BOARD_PERCLK1_FREQUENCY << 4) / div; + + if (num > den) + { + if (num > 0x00010000) + { + /* b16 is a scale such that b16*num = 0x10000 * 2**16 */ + + uint32_t b16 = 0x100000000LL / num; + num = 0x00010000; + den = (b16 * den) >> 16; + } + } + else + { + if (den > 0x0000ffff) + { + /* b16 is a scale such that b16*den = 0x10000 * 2**16 */ + + uint32_t b16 = 0x100000000LL / den; + num = (b16 * num) >> 16; + den = 0x00010000; + } + } + + /* The actual values are we write to the registers need to be + * decremented by 1. + */ + + if (num > 0) + { + num--; + } + + if (den > 0) + { + den--; + } + + /* The UBIR must be set before the UBMR register */ + + imx_serialout(priv, UART_UBIR_OFFSET, num); + imx_serialout(priv, UART_UBMR_OFFSET, den); + + /* Fixup the divisor, the value in the UFCR regiser is + * + * 000 = Divide input clock by 6 + * 001 = Divide input clock by 5 + * 010 = Divide input clock by 4 + * 011 = Divide input clock by 3 + * 100 = Divide input clock by 2 + * 101 = Divide input clock by 1 + * 110 = Divide input clock by 7 + */ + + if (div == 7) + { + div = 6; + } + else + { + div = 6 - div; + } + + regval = div << UART_UFCR_RFDIV_SHIFT; + + /* Set the TX trigger level to interrupt when the TxFIFO has 2 or fewer + * characters. Set the RX trigger level to interrupt when the RxFIFO has + * 1 character. + */ + + regval |= ((2 << UART_UFCR_TXTL_SHIFT) | (1 << UART_UFCR_RXTL_SHIFT)); + imx_serialout(priv, UART_UFCR_OFFSET, regval); + + /* Initialize the UCR1 shadow register */ + + priv->ucr1 = imx_serialin(priv, UART_UCR1_OFFSET); + + /* Enable the UART + * + * UART_UCR1_UARTCLEN = Enable UART clocking + */ + + ucr2 |= (UART_UCR2_TXEN | UART_UCR2_RXEN); + imx_serialout(priv, UART_UCR1_OFFSET, ucr2); + + priv->ucr1 |= UART_UCR1_UARTCLEN; + imx_serialout(priv, UART_UCR1_OFFSET, priv->ucr1); +#endif + + return OK; +} + +/**************************************************************************** + * Name: imx_shutdown + * + * Description: + * Disable the UART. This method is called when the serial + * port is closed + * + ****************************************************************************/ + +static void imx_shutdown(struct uart_dev_s *dev) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + + /* Disable the UART */ + + imx_serialout(priv, UART_UCR1_OFFSET, 0); + imx_serialout(priv, UART_UCR2_OFFSET, 0); + imx_serialout(priv, UART_UCR3_OFFSET, 0); + imx_serialout(priv, UART_UCR4_OFFSET, 0); +} + +/**************************************************************************** + * Name: imx_attach + * + * Description: + * Configure the UART to operation in interrupt driven mode. This method is + * called when the serial port is opened. Normally, this is just after the + * the setup() method is called, however, the serial console may operate in + * a non-interrupt driven mode during the boot phase. + * + * RX and TX interrupts are not enabled when by the attach method (unless the + * hardware supports multiple levels of interrupt enabling). The RX and TX + * interrupts are not enabled until the txint() and rxint() methods are called. + * + ****************************************************************************/ + +static int imx_attach(struct uart_dev_s *dev) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + int ret; + + /* Attach and enable the IRQ */ + + ret = irq_attach(priv->irq, priv->handler); + if (ret == OK) + { + /* Enable the interrupt (RX and TX interrupts are still disabled + * in the UART + */ + + up_enable_irq(priv->irq); + } + + return ret; +} + +/**************************************************************************** + * Name: imx_detach + * + * Description: + * Detach UART interrupts. This method is called when the serial port is + * closed normally just before the shutdown method is called. The exception is + * the serial console which is never shutdown. + * + ****************************************************************************/ + +static void imx_detach(struct uart_dev_s *dev) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + + up_disable_irq(priv->irq); + irq_detach(priv->irq); +} + +/**************************************************************************** + * Name: imx_interrupt (and front-ends) + * + * Description: + * This is the common UART interrupt handler. It should cal + * uart_transmitchars or uart_receivechar to perform the appropriate data + * transfers. + * + ****************************************************************************/ + +static int imx_interrupt(struct uart_dev_s *priv) +{ + struct uart_dev_s *dev; + uint32_t usr1; + int passes = 0; + + /* Loop until there are no characters to be transferred or, + * until we have been looping for a long time. + */ + + for (; ; ) + { + /* Get the current UART status and check for loop + * termination conditions + */ + + usr1 = imx_serialin(priv, UART_USR1_OFFSET); + usr1 &= (UART_USR1_RRDY | UART_USR1_TRDY); + + if (usr1 == 0 || passes > 256) + { + return OK; + } + + /* Handline incoming, receive bytes */ + + if (usr1 & UART_USR1_RRDY) + { + uart_recvchars(dev); + } + + /* Handle outgoing, transmit bytes */ + + if (usr1 & UART_USR1_TRDY && + (imx_serialin(priv, UART_UCR1_OFFSET) & UART_UCR1_TXEMPTYEN) != 0) + { + uart_xmitchars(dev); + } + + /* Keep track of how many times we do this in case there + * is some hardware failure condition. + */ + + passes++; + } +} + +/**************************************************************************** + * Name: imx_uart[n]_interrupt + * + * Description: + * UART-specific interrupt handlers just transfer control to the common + * UART interrupt handler, passing the relevant driver state structure. + * + ****************************************************************************/ + +#ifdef CONFIG_IMX6_UART1 +static int imx_uart1_interrupt(int irq, void *context) +{ + return imx_interrupt(&g_uart1port); +} +#endif +#ifdef CONFIG_IMX6_UART2 +static int imx_uart2_interrupt(int irq, void *context) +{ + return imx_interrupt(&g_uart2port); +} +#endif +#ifdef CONFIG_IMX6_UART3 +static int imx_uart3_interrupt(int irq, void *context) +{ + return imx_interrupt(&g_uart3port); +} +#endif +#ifdef CONFIG_IMX6_UART4 +static int imx_uart4_interrupt(int irq, void *context) +{ + return imx_interrupt(&g_uart4port); +} +#endif +#ifdef CONFIG_IMX6_UART5 +static int imx_uart5_interrupt(int irq, void *context) +{ + return imx_interrupt(&g_uart5port); +} +#endif + +/**************************************************************************** + * Name: imx_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + ****************************************************************************/ + +static int imx_ioctl(struct file *filep, int cmd, unsigned long arg) +{ +#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT + struct inode *inode = filep->f_inode; + struct uart_dev_s *dev = inode->i_private; +#endif + int ret = OK; + + switch (cmd) + { +#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT + case TIOCSERGSTRUCT: + { + struct imx_uart_s *user = (struct imx_uart_s *)arg; + if (!user) + { + ret = -EINVAL; + } + else + { + memcpy(user, dev, sizeof(struct imx_uart_s)); + } + } + break; +#endif + + case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */ + case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */ + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Name: imx_receive + * + * Description: + * Called (usually) from the interrupt level to receive one + * character from the UART. Error bits associated with the + * receipt are provided in the return 'status'. + * + ****************************************************************************/ + +static int imx_receive(struct uart_dev_s *dev, uint32_t *status) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + uint32_t rxd0; + + rxd0 = imx_serialin(priv, UART_RXD_OFFSET); + *status = rxd0; + return (rxd0 & UART_RXD_DATA_MASK) >> UART_RXD_DATA_SHIFT; +} + +/**************************************************************************** + * Name: imx_rxint + * + * Description: + * Call to enable or disable RX interrupts + * + ****************************************************************************/ + +static void imx_rxint(struct uart_dev_s *dev, bool enable) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + + /* Enable interrupts for data availab at Rx FIFO */ + + if (enable) + { +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + priv->ucr1 |= UART_UCR1_RRDYEN; +#endif + } + else + { + priv->ucr1 &= ~UART_UCR1_RRDYEN; + } + + imx_serialout(priv, UART_UCR1_OFFSET, priv->ucr1); +} + +/**************************************************************************** + * Name: imx_rxavailable + * + * Description: + * Return true if the receive fifo is not empty + * + ****************************************************************************/ + +static bool imx_rxavailable(struct uart_dev_s *dev) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + + /* Return true is data is ready in the Rx FIFO */ + + return ((imx_serialin(priv, UART_USR2_OFFSET) & UART_USR2_RDR) != 0); +} + +/**************************************************************************** + * Name: imx_send + * + * Description: + * This method will send one byte on the UART + * + ****************************************************************************/ + +static void imx_send(struct uart_dev_s *dev, int ch) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + imx_serialout(priv, UART_TXD_OFFSET, (uint32_t)ch); +} + +/**************************************************************************** + * Name: imx_txint + * + * Description: + * Call to enable or disable TX interrupts + * + ****************************************************************************/ + +static void imx_txint(struct uart_dev_s *dev, bool enable) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + + /* We won't take an interrupt until the FIFO is completely empty (although + * there may still be a transmission in progress). + */ + + if (enable) + { +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + priv->ucr1 |= UART_UCR1_TXEMPTYEN; +#endif + } + else + { + priv->ucr1 &= ~UART_UCR1_TXEMPTYEN; + } + + imx_serialout(priv, UART_UCR1_OFFSET, priv->ucr1); +} + +/**************************************************************************** + * Name: imx_txready + * + * Description: + * Return true if the tranmsit fifo is not full + * + ****************************************************************************/ + +static bool imx_txready(struct uart_dev_s *dev) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + + /* When TXFULL is set, there is no space in the Tx FIFO */ + + return ((imx_serialin(priv, UART_UTS_OFFSET) & UART_UTS_TXFULL) == 0); +} + +/**************************************************************************** + * Name: imx_txempty + * + * Description: + * Return true if the transmit fifo is empty + * + ****************************************************************************/ + +static bool imx_txempty(struct uart_dev_s *dev) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)dev->priv; + + /* When TXDC is set, the FIFO is empty and the transmission is complete */ + + return ((imx_serialin(priv, UART_USR2_OFFSET) & UART_USR2_TXDC) != 0); +} + +/**************************************************************************** + * Public Funtions + ****************************************************************************/ + +/**************************************************************************** + * Name: imx_serialinit + * + * Description: + * Performs the low level UART initialization early in + * debug so that the serial console will be available + * during bootup. This must be called before imx_serialinit. + * + ****************************************************************************/ + +void imx_earlyserialinit(void) +{ + /* Configure and disable the UART1 */ + +#ifdef CONFIG_IMX6_UART1 + imx_serialout(&g_uart1priv, UART_UCR1_OFFSET, 0); + imx_serialout(&g_uart1priv, UART_UCR2_OFFSET, 0); + + /* Configure UART1 pins: RXD, TXD, RTS, and CTS */ + + imxgpio_configpfoutput(GPIOC, 9); /* Port C, pin 9: CTS */ + imxgpio_configpfinput(GPIOC, 10); /* Port C, pin 10: RTS */ + imxgpio_configpfoutput(GPIOC, 11); /* Port C, pin 11: TXD */ + imxgpio_configpfinput(GPIOC, 12); /* Port C, pin 12: RXD */ +#endif + + /* Configure and disable the UART2 */ + +#ifdef CONFIG_IMX6_UART2 + imx_serialout(&g_uart2priv, UART_UCR1_OFFSET, 0); + imx_serialout(&g_uart2priv, UART_UCR2_OFFSET, 0); + + /* Configure UART2 pins: RXD, TXD, RTS, and CTS (only, also + * supports DTR, DCD, RI, and DSR -- not configured) + */ + + imxgpio_configpfoutput(GPIOB, 28); /* Port B, pin 28: CTS */ + imxgpio_configpfinput(GPIOB, 29); /* Port B, pin 29: RTS */ + imxgpio_configpfoutput(GPIOB, 30); /* Port B, pin 30: TXD */ + imxgpio_configpfinput(GPIOB, 31); /* Port B, pin 31: RXD */ +#endif + + /* Configure and disable the UART3 */ + +#ifdef CONFIG_IMX6_UART3 + imx_serialout(&g_uart3priv, UART_UCR1_OFFSET, 0); + imx_serialout(&g_uart3priv, UART_UCR2_OFFSET, 0); + + /* Configure UART2 pins: RXD, TXD, RTS, and CTS (only, also + * supports DTR, DCD, RI, and DSR -- not configured) + */ + + imxgpio_configpfoutput(GPIOC, 28); /* Port C, pin 18: CTS */ + imxgpio_configpfinput(GPIOC, 29); /* Port C, pin 29: RTS */ + imxgpio_configpfoutput(GPIOC, 30); /* Port C, pin 30: TXD */ + imxgpio_configpfinput(GPIOC, 31); /* Port C, pin 31: RXD */ +#endif + + /* Then enable the console UART. The others will be initialized + * if and when they are opened. + */ + +#ifdef CONSOLE_DEV + CONSOLE_DEV.isconsole = true; + imx_setup(&CONSOLE_DEV); +#endif +} + +/**************************************************************************** + * Name: imx_serialinit + * + * Description: + * Register serial console and serial ports. This assumes + * that imx_earlyserialinit was called previously. + * + ****************************************************************************/ + +void imx_serialinit(void) +{ +#ifdef CONSOLE_DEV + (void)uart_register("/dev/console", &CONSOLE_DEV); +#endif + +#ifdef TTYS0_DEV + (void)uart_register("/dev/ttyS0", &TTYS0_DEV); +# ifdef TTYS1_DEV + (void)uart_register("/dev/ttyS1", &TTYS1_DEV); +# ifdef TTYS2_DEV + (void)uart_register("/dev/ttyS2", &TTYS2_DEV); +# ifdef TTYS3_DEV + (void)uart_register("/dev/ttyS3", &TTYS2_DEV); +# ifdef TTYS4_DEV + (void)uart_register("/dev/ttyS4", &TTYS2_DEV); +# endif +# endif +# endif +# endif +#endif +} + +/**************************************************************************** + * Name: imx_putc + * + * Description: + * Provide priority, low-level access to support OS debug + * writes + * + ****************************************************************************/ + +int imx_putc(int ch) +{ + struct imx_uart_s *priv = (struct imx_uart_s *)CONSOLE_DEV.priv; + uint32_t ier; + + imx_disableuartint(priv, &ier); + imx_waittxready(priv); + + /* Check for LF */ + + if (ch == '\n') + { + /* Add CR */ + + imx_serialout(priv, UART_TXD_OFFSET, (uint32_t)'\r'); + imx_waittxready(priv); + } + + imx_serialout(priv, UART_TXD_OFFSET, (uint32_t)ch); + imx_waittxready(priv); + imx_restoreuartint(priv, ier); + return ch; +} + +#else /* USE_SERIALDRIVER */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +# undef IMX_REGISTER_BASE +# if defined(CONFIG_UART1_SERIAL_CONSOLE) +# define IMX_REGISTER_BASE IMX_UART1_VBASE +# elif defined(CONFIG_UART2_SERIAL_CONSOLE) +# define IMX_REGISTER_BASE IMX_UART2_VBASE +# elif defined(CONFIG_UART3_SERIAL_CONSOLE) +# define IMX_REGISTER_BASE IMX_UART3_VBASE +# elif defined(CONFIG_UART4_SERIAL_CONSOLE) +# define IMX_REGISTER_BASE IMX_UART4_VBASE +# elif defined(CONFIG_UART5_SERIAL_CONSOLE) +# define IMX_REGISTER_BASE IMX_UART5_VBASE +# endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef IMX_REGISTER_BASE +static inline void imx_waittxready(void) +{ + int tmp; + + for (tmp = 1000 ; tmp > 0 ; tmp--) + { + /* Loop until TXFULL is zero -- meaning that there is space available + * in the TX FIFO. + */ + + if ((getreg32(IMX_REGISTER_BASE + UART_UTS) & UART_UTS_TXFULL) == 0) + { + break; + } + } +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int imx_putc(int ch) +{ +#ifdef IMX_REGISTER_BASE + imx_waittxready(); + + /* Check for LF */ + + if (ch == '\n') + { + /* Add CR */ + + putreg32((uint16_t)'\r', IMX_REGISTER_BASE + UART_TXD_OFFSET); + imx_waittxready(); + } + + putreg32((uint16_t)ch, IMX_REGISTER_BASE + UART_TXD_OFFSET); +#endif + + return ch; +} + +#endif /* USE_SERIALDRIVER */