diff --git a/arch/arm/src/tms570/tms570_boot.c b/arch/arm/src/tms570/tms570_boot.c index e1a1e546b87..cd789c48a9e 100644 --- a/arch/arm/src/tms570/tms570_boot.c +++ b/arch/arm/src/tms570/tms570_boot.c @@ -331,12 +331,4 @@ void arm_boot(void) /* Perform common, low-level chip initialization (might do nothing) */ tms570_lowsetup(); - -#ifdef USE_EARLYSERIALINIT - /* Perform early serial initialization if we are going to use the serial - * driver. - */ - - up_earlyserialinit(); -#endif } diff --git a/arch/arm/src/tms570/tms570_boot.h b/arch/arm/src/tms570/tms570_boot.h index 8f8a2f3b5ea..55bc565626a 100644 --- a/arch/arm/src/tms570/tms570_boot.h +++ b/arch/arm/src/tms570/tms570_boot.h @@ -97,7 +97,7 @@ EXTERN const uintptr_t g_idle_topstack; * * Description: * Called at the very beginning of _start. Performs low level initialization - * including setup of the console UART. This UART done early so that the serial + * including setup of the console SCI. This SCI done early so that the serial * console is available for debugging very early in the boot sequence. * ************************************************************************************/ @@ -118,7 +118,7 @@ void tms570_lowsetup(void); * * - Initialization of board-specific memory resources (e.g., SDRAM) * - Configuration of board specific resources (GPIOs, LEDs, etc). - * - Setup of the console UART. This UART done early so that the serial console + * - Setup of the console SCI. This SCI done early so that the serial console * is available for debugging very early in the boot sequence. * * Special precautions must be taken if .data/.bss lie in SRAM. in that case, diff --git a/arch/arm/src/tms570/tms570_lowputc.c b/arch/arm/src/tms570/tms570_lowputc.c new file mode 100644 index 00000000000..2125eaf62da --- /dev/null +++ b/arch/arm/src/tms570/tms570_lowputc.c @@ -0,0 +1,291 @@ +/**************************************************************************** + * arch/arm/src/tms570/tms570_lowputc.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Includes some logic from TI sample which has a compatibile three-clause + * BSD license and: + * + * Copyright (c) 2012, Texas Instruments Incorporated + * All rights reserved. + * + * 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 "up_internal.h" +#include "up_arch.h" + +#include "tms570_config.h" +#include "tms570_lowputc.h" + +#include "chip/tms570_sci.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration **********************************************************/ + +#ifdef HAVE_SERIAL_CONSOLE + +/* BAUD definitions + * + * The source clock is selectable and could be one of: + * + * - The peripheral clock + * - A division of the peripheral clock, where the divider is product- + * dependent, but generally set to 8 + * - A processor/peripheral independent clock source fully programmable + * provided by PMC (PCK) + * - The external clock, available on the SCK pin + * + * Only the first two options are supported by this driver. The divided + * peripheral clock is only used for very low BAUD selections. + */ + +#define FAST_SCI_CLOCK BOARD_MCK_FREQUENCY +#define SLOW_SCI_CLOCK (BOARD_MCK_FREQUENCY >> 3) + +/* Select SCI parameters for the selected console */ + +# if defined(CONFIG_SCI1_SERIAL_CONSOLE) +# define TMS570_CONSOLE_BASE TMS570_SCI1_BASE +# define TMS570_CONSOLE_BAUD CONFIG_SCI1_BAUD +# define TMS570_CONSOLE_BITS CONFIG_SCI1_BITS +# define TMS570_CONSOLE_PARITY CONFIG_SCI1_PARITY +# define TMS570_CONSOLE_2STOP CONFIG_SCI1_2STOP +# elif defined(CONFIG_SCI2_SERIAL_CONSOLE) +# define TMS570_CONSOLE_BASE TMS570_SCI2_BASE +# define TMS570_CONSOLE_BAUD CONFIG_SCI2_BAUD +# define TMS570_CONSOLE_BITS CONFIG_SCI2_BITS +# define TMS570_CONSOLE_PARITY CONFIG_SCI2_PARITY +# define TMS570_CONSOLE_2STOP CONFIG_SCI2_2STOP +# else +# error "No CONFIG_SCIn_SERIAL_CONSOLE Setting" +# endif + +/* Select the settings for the mode register */ + +# if TMS570_CONSOLE_BITS == 5 +# define MR_CHRL_VALUE SCI_MR_CHRL_5BITS /* 5 bits */ +# elif TMS570_CONSOLE_BITS == 6 +# define MR_CHRL_VALUE SCI_MR_CHRL_6BITS /* 6 bits */ +# elif TMS570_CONSOLE_BITS == 7 +# define MR_CHRL_VALUE SCI_MR_CHRL_7BITS /* 7 bits */ +# elif TMS570_CONSOLE_BITS == 8 +# define MR_CHRL_VALUE SCI_MR_CHRL_8BITS /* 8 bits */ +# elif TMS570_CONSOLE_BITS == 9 && !defined(CONFIG_SCI1_SERIAL_CONSOLE) && \ + !defined(CONFIG_SCI2_SERIAL_CONSOLE) +# define MR_CHRL_VALUE SCI_MR_MODE9 +# else +# error "Invalid number of bits" +# endif + +# if TMS570_CONSOLE_PARITY == 1 +# define MR_PAR_VALUE SCI_MR_PAR_ODD +# elif TMS570_CONSOLE_PARITY == 2 +# define MR_PAR_VALUE SCI_MR_PAR_EVEN +# else +# define MR_PAR_VALUE SCI_MR_PAR_NONE +# endif + +# if TMS570_CONSOLE_2STOP != 0 +# define MR_NBSTOP_VALUE SCI_MR_NBSTOP_2 +# else +# define MR_NBSTOP_VALUE SCI_MR_NBSTOP_1 +# endif + +# define MR_VALUE (SCI_MR_MODE_NORMAL | SCI_MR_USCLKS_MCK | \ + MR_CHRL_VALUE | MR_PAR_VALUE | MR_NBSTOP_VALUE) + +#endif /* HAVE_SERIAL_CONSOLE */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_lowputc + * + * Description: + * Output one byte on the serial console + * + ****************************************************************************/ + +void up_lowputc(char ch) +{ +#ifdef HAVE_SERIAL_CONSOLE + irqstate_t flags; + + for (; ; ) + { + /* Wait for the transmitter to be available */ + + while ((getreg32(TMS570_CONSOLE_BASE + TMS570_SCI_SR_OFFSET) & + SCI_INT_TXEMPTY) == 0); + + /* Disable interrupts so that the test and the transmission are + * atomic. + */ + + flags = irqsave(); + if ((getreg32(TMS570_CONSOLE_BASE + TMS570_SCI_SR_OFFSET) & + SCI_INT_TXEMPTY) != 0) + { + /* Send the character */ + + putreg32((uint32_t)ch, TMS570_CONSOLE_BASE + TMS570_SCI_THR_OFFSET); + irqrestore(flags); + return; + } + + irqrestore(flags); + } +#endif +} + +/**************************************************************************** + * Name: up_putc + * + * Description: + * Provide priority, low-level access to support OS debug writes + * + ****************************************************************************/ + +int up_putc(int ch) +{ +#ifdef HAVE_SERIAL_CONSOLE + /* Check for LF */ + + if (ch == '\n') + { + /* Add CR */ + + up_lowputc('\r'); + } + + up_lowputc(ch); +#endif + return ch; +} + +/**************************************************************************** + * Name: tms570_lowsetup + * + * Description: + * This performs basic initialization of the SCI used for the serial + * console. Its purpose is to get the console output availabe as soon + * as possible. + * + ****************************************************************************/ + +void tms570_lowsetup(void) +{ +#if defined(HAVE_SERIAL_CONSOLE) && !defined(CONFIG_SUPPRESS_SCI_CONFIG) + uint64_t divb3; + uint32_t intpart; + uint32_t fracpart; + uint32_t regval; +#endif + + /* Configure the console (only) */ +#if defined(HAVE_SERIAL_CONSOLE) && !defined(CONFIG_SUPPRESS_SCI_CONFIG) + /* Reset and disable receiver and transmitter */ + + putreg32((SCI_CR_RSTRX | SCI_CR_RSTTX | SCI_CR_RXDIS | SCI_CR_TXDIS), + TMS570_CONSOLE_BASE + TMS570_SCI_CR_OFFSET); + + /* Disable all interrupts */ + + putreg32(0xffffffff, TMS570_CONSOLE_BASE + TMS570_SCI_IDR_OFFSET); + + /* Set up the mode register */ + + putreg32(MR_VALUE, TMS570_CONSOLE_BASE + TMS570_SCI_MR_OFFSET); + + /* Configure the console baud: + * + * Fbaud = SCI_CLOCK / (16 * divisor) + * divisor = SCI_CLOCK / (16 * Fbaud) + * + * NOTE: Oversampling by 8 is not supported. This may limit BAUD rates + * for lower SCI clocks. + */ + + divb3 = ((FAST_SCI_CLOCK + (TMS570_CONSOLE_BAUD << 3)) << 3) / + (TMS570_CONSOLE_BAUD << 4); + intpart = (divb3 >> 3); + fracpart = (divb3 & 7); + + /* Retain the fast MR peripheral clock UNLESS unless using that clock + * would result in an excessively large divider. + * + * REVISIT: The fractional divider is not used. + */ + + if ((intpart & ~SCI_BRGR_CD_MASK) != 0) + { + /* Use the divided SCI clock */ + + divb3 = ((SLOW_SCI_CLOCK + (TMS570_CONSOLE_BAUD << 3)) << 3) / + (TMS570_CONSOLE_BAUD << 4); + intpart = (divb3 >> 3); + fracpart = (divb3 & 7); + + /* Re-select the clock source */ + + regval = getreg32(TMS570_CONSOLE_BASE + TMS570_SCI_MR_OFFSET); + regval &= ~SCI_MR_USCLKS_MASK; + regval |= SCI_MR_USCLKS_MCKDIV; + putreg32(regval, TMS570_CONSOLE_BASE + TMS570_SCI_MR_OFFSET); + } + + /* Save the BAUD divider (the fractional part is not used for SCIs) */ + + regval = SCI_BRGR_CD(intpart) | SCI_BRGR_FP(fracpart); + putreg32(regval, TMS570_CONSOLE_BASE + TMS570_SCI_BRGR_OFFSET); + + /* Enable receiver & transmitter */ + + putreg32((SCI_CR_RXEN | SCI_CR_TXEN), + TMS570_CONSOLE_BASE + TMS570_SCI_CR_OFFSET); +#endif +} diff --git a/arch/arm/src/tms570/tms570_lowputc.h b/arch/arm/src/tms570/tms570_lowputc.h new file mode 100644 index 00000000000..addc2b16be2 --- /dev/null +++ b/arch/arm/src/tms570/tms570_lowputc.h @@ -0,0 +1,114 @@ +/************************************************************************************ + * arch/arm/src/tms570/tms570_lowputc.h + * + * Copyright (C) 2015 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. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_TMS570_TMS570_LOWPUTC_H +#define __ARCH_ARM_SRC_TMS570_TMS570_LOWPUTC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include + +#include +#include +#include + +#include "up_internal.h" +#include "chip.h" + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/************************************************************************************ + * Inline Functions + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +/************************************************************************************ + * Name: tms570_lowsetup + * + * Description: + * Called at the very beginning of _start. Performs low level initialization + * including setup of the console SCI. This SCI done early so that the serial + * console is available for debugging very early in the boot sequence. + * + ************************************************************************************/ + +void tms570_lowsetup(void); + +/************************************************************************************ + * Name: tms570_boardinitialize + * + * Description: + * All TMS570 architectures must provide the following entry point. This entry + * point is called early in the initialization -- after all memory has been + * configured and mapped but before any devices have been initialized. + * + ************************************************************************************/ + +void tms570_boardinitialize(void); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_TMS570_TMS570_LOWPUTC_H */ diff --git a/arch/arm/src/tms570/tms570_serial.c b/arch/arm/src/tms570/tms570_serial.c new file mode 100644 index 00000000000..bd75dfd8907 --- /dev/null +++ b/arch/arm/src/tms570/tms570_serial.c @@ -0,0 +1,1043 @@ +/**************************************************************************** + * arch/arm/src/tms570/tms570_serial.c + * + * Copyright (C) 2015 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 + +#ifdef CONFIG_SERIAL_TERMIOS +# include +#endif + +#include +#include +#include + +#include +#include + +#include "up_arch.h" +#include "up_internal.h" + +#include "tms570_config.h" +#include "chip/tms570_sci.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifdef USE_SERIALDRIVER + +/* Which SCI with be tty0/console and which tty1? */ + +/* First pick the console and ttys0. This could be any of SCI1-1 */ + +#if defined(CONFIG_SCI1_SERIAL_CONSOLE) +# define CONSOLE_DEV g_sci1port /* SCI1 is console */ +# define TTYS0_DEV g_sci1port /* SCI1 is ttyS0 */ +# define SCI1_ASSIGNED 1 +#elif defined(CONFIG_SCI2_SERIAL_CONSOLE) +# define CONSOLE_DEV g_sci2port /* SCI2 is console */ +# define TTYS0_DEV g_sci2port /* SCI2 is ttyS0 */ +# define SCI2_ASSIGNED 1 +#else +# undef CONSOLE_DEV /* No console */ +# if defined(CONFIG_SAMV7_SCI1) +# define TTYS0_DEV g_sci1port /* SCI1 is ttyS0 */ +# define SCI1_ASSIGNED 1 +# elif defined(CONFIG_SAMV7_SCI2) +# define TTYS0_DEV g_sci2port /* SCI2 is ttyS0 */ +# define SCI2_ASSIGNED 1 +# endif +#endif + +/* Pick ttys1. This could be any of SCI1-1, excluding the console + * SCI. + */ + +#if defined(CONFIG_SAMV7_SCI1) && !defined(SCI1_ASSIGNED) +# define TTYS1_DEV g_sci1port /* SCI1 is ttyS1 */ +# define SCI1_ASSIGNED 1 +#elif defined(CONFIG_SAMV7_SCI2) && !defined(SCI2_ASSIGNED) +# define TTYS1_DEV g_sci2port /* SCI2 is ttyS1 */ +# define SCI2_ASSIGNED 1 +#endif + +/* BAUD definitions + * + * The source clock is selectable and could be one of: + * + * - The peripheral clock + * - A division of the peripheral clock, where the divider is product- + * dependent, but generally set to 8 + * - A processor/peripheral independent clock source fully programmable + * provided by PMC (PCK) + * - The external clock, available on the SCK pin + * + * Only the first two options are supported by this driver. The divided + * peripheral clock is only used for very low BAUD selections. + */ + +#define FAST_SCI_CLOCK BOARD_MCK_FREQUENCY +#define SLOW_SCI_CLOCK (BOARD_MCK_FREQUENCY >> 3) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct tms570_dev_s +{ + const uint32_t usartbase; /* Base address of SCI registers */ + xcpt_t handler; /* Interrupt handler */ + uint32_t baud; /* Configured baud */ + uint32_t sr; /* Saved status bits */ + uint8_t irq; /* IRQ associated with this SCI */ + uint8_t parity; /* 0=none, 1=odd, 2=even */ + uint8_t bits; /* Number of bits (5-9) */ + bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */ +#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL) + bool flowc; /* input flow control (RTS) enabled */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int tms570_setup(struct sci_dev_s *dev); +static void tms570_shutdown(struct sci_dev_s *dev); +static int tms570_attach(struct sci_dev_s *dev); +static void tms570_detach(struct sci_dev_s *dev); +static int tms570_interrupt(struct sci_dev_s *dev); +#ifdef CONFIG_SAMV7_SCI1 +static int tms570_sci1_interrupt(int irq, void *context); +#endif +#ifdef CONFIG_SAMV7_SCI2 +static int tms570_sci2_interrupt(int irq, void *context); +#endif +static int tms570_ioctl(struct file *filep, int cmd, unsigned long arg); +static int tms570_receive(struct sci_dev_s *dev, uint32_t *status); +static void tms570_rxint(struct sci_dev_s *dev, bool enable); +static bool tms570_rxavailable(struct sci_dev_s *dev); +static void tms570_send(struct sci_dev_s *dev, int ch); +static void tms570_txint(struct sci_dev_s *dev, bool enable); +static bool tms570_txready(struct sci_dev_s *dev); +static bool tms570_txempty(struct sci_dev_s *dev); + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +static const struct sci_ops_s g_sci_ops = +{ + .setup = tms570_setup, + .shutdown = tms570_shutdown, + .attach = tms570_attach, + .detach = tms570_detach, + .ioctl = tms570_ioctl, + .receive = tms570_receive, + .rxint = tms570_rxint, + .rxavailable = tms570_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = NULL, +#endif + .send = tms570_send, + .txint = tms570_txint, + .txready = tms570_txready, + .txempty = tms570_txempty, +}; + +/* I/O buffers */ + +#ifdef CONFIG_SAMV7_SCI1 +static char g_sci1rxbuffer[CONFIG_SCI1_RXBUFSIZE]; +static char g_sci1txbuffer[CONFIG_SCI1_TXBUFSIZE]; +#endif +#ifdef CONFIG_SAMV7_SCI2 +static char g_sci2rxbuffer[CONFIG_SCI2_RXBUFSIZE]; +static char g_sci2txbuffer[CONFIG_SCI2_TXBUFSIZE]; +#endif + +/* This describes the state of the SCI1 port. */ + +#ifdef CONFIG_SAMV7_SCI1 +static struct tms570_dev_s g_sci1priv = +{ + .usartbase = TMS570_SCI1_BASE, + .handler = tms570_sci1_interrupt, + .baud = CONFIG_SCI1_BAUD, + .irq = TMS570_IRQ_SCI1, + .parity = CONFIG_SCI1_PARITY, + .bits = CONFIG_SCI1_BITS, + .stopbits2 = CONFIG_SCI1_2STOP, +}; + +static sci_dev_t g_sci1port = +{ + .recv = + { + .size = CONFIG_SCI1_RXBUFSIZE, + .buffer = g_sci1rxbuffer, + }, + .xmit = + { + .size = CONFIG_SCI1_TXBUFSIZE, + .buffer = g_sci1txbuffer, + }, + .ops = &g_sci_ops, + .priv = &g_sci1priv, +}; +#endif + +/* This describes the state of the SCI2 port. */ + +#ifdef CONFIG_SAMV7_SCI2 +static struct tms570_dev_s g_sci2priv = +{ + .usartbase = TMS570_SCI2_BASE, + .handler = tms570_sci2_interrupt, + .baud = CONFIG_SCI2_BAUD, + .irq = TMS570_IRQ_SCI2, + .parity = CONFIG_SCI2_PARITY, + .bits = CONFIG_SCI2_BITS, + .stopbits2 = CONFIG_SCI2_2STOP, +}; + +static sci_dev_t g_sci2port = +{ + .recv = + { + .size = CONFIG_SCI2_RXBUFSIZE, + .buffer = g_sci2rxbuffer, + }, + .xmit = + { + .size = CONFIG_SCI2_TXBUFSIZE, + .buffer = g_sci2txbuffer, + }, + .ops = &g_sci_ops, + .priv = &g_sci2priv, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tms570_serialin + ****************************************************************************/ + +static inline uint32_t tms570_serialin(struct tms570_dev_s *priv, int offset) +{ + return getreg32(priv->usartbase + offset); +} + +/**************************************************************************** + * Name: tms570_serialout + ****************************************************************************/ + +static inline void tms570_serialout(struct tms570_dev_s *priv, int offset, uint32_t value) +{ + putreg32(value, priv->usartbase + offset); +} + +/**************************************************************************** + * Name: tms570_restoreusartint + ****************************************************************************/ + +static inline void tms570_restoreusartint(struct tms570_dev_s *priv, uint32_t imr) +{ + /* Restore the previous interrupt state (assuming all interrupts disabled) */ + + tms570_serialout(priv, TMS570_SCI_IER_OFFSET, imr); +} + +/**************************************************************************** + * Name: tms570_disableallints + ****************************************************************************/ + +static void tms570_disableallints(struct tms570_dev_s *priv, uint32_t *imr) +{ + irqstate_t flags; + + /* The following must be atomic */ + + flags = irqsave(); + if (imr) + { + /* Return the current interrupt mask */ + + *imr = tms570_serialin(priv, TMS570_SCI_IMR_OFFSET); + } + + /* Disable all interrupts */ + + tms570_serialout(priv, TMS570_SCI_IDR_OFFSET, SCI_INT_ALLINTS); + irqrestore(flags); +} + +/**************************************************************************** + * Name: tms570_setup + * + * Description: + * Configure the SCI baud, bits, parity, etc. This method is called the + * first time that the serial port is opened. + * + ****************************************************************************/ + +static int tms570_setup(struct sci_dev_s *dev) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; +#ifndef CONFIG_SUPPRESS_SCI_CONFIG + uint32_t divb3; + uint32_t intpart; + uint32_t fracpart; + uint32_t regval; + + /* Note: The logic here depends on the fact that that the SCI module + * was enabled and the pins were configured in tms570_lowsetup(). + */ + + /* The shutdown method will put the SCI in a known, disabled state */ + + tms570_shutdown(dev); + + /* Set up the mode register. Start with normal SCI mode and the MCK + * as the timing source + */ + +#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL) + /* "Setting the SCI to operate with hardware handshaking is performed by + * writing the SCI_MODE field in the Mode Register (US_MR) to the value + * 0x2. ... Using this mode requires using the PDC or DMAC channel for + * reception. The transmitter can handle hardware handshaking in any case." + */ + + if (priv->flowc) + { + /* Enable hardware flow control and MCK as the timing source + * (the divided clock source may be reselected below). + */ + + regval = (SCI_MR_MODE_HWHS | SCI_MR_USCLKS_MCK); + } + else +#endif + { + /* Set up the mode register. Start with normal SCI mode and the MCK + * as the timing source (the divided clock source may be reselected + * below). + */ + + regval = (SCI_MR_MODE_NORMAL | SCI_MR_USCLKS_MCK); + } + + /* OR in settings for the selected number of bits */ + + if (priv->bits == 5) + { + regval |= SCI_MR_CHRL_5BITS; /* 5 bits */ + } + else if (priv->bits == 6) + { + regval |= SCI_MR_CHRL_6BITS; /* 6 bits */ + } + else if (priv->bits == 7) + { + regval |= SCI_MR_CHRL_7BITS; /* 7 bits */ + } +#ifdef HAVE_SCI_DEVICE + else if (priv->bits == 9 +#if defined(CONFIG_SAMV7_SCI1) + && priv->usartbase != TMS570_SCI1_BASE +#endif +#if defined(CONFIG_SAMV7_SCI2) + && priv->usartbase != TMS570_SCI2_BASE +#endif + ) + { + regval |= SCI_MR_MODE9; /* 9 bits */ + } +#endif + else /* if (priv->bits == 8) */ + { + regval |= SCI_MR_CHRL_8BITS; /* 8 bits (default) */ + } + + /* OR in settings for the selected parity */ + + if (priv->parity == 1) + { + regval |= SCI_MR_PAR_ODD; + } + else if (priv->parity == 2) + { + regval |= SCI_MR_PAR_EVEN; + } + else + { + regval |= SCI_MR_PAR_NONE; + } + + /* OR in settings for the number of stop bits */ + + if (priv->stopbits2) + { + regval |= SCI_MR_NBSTOP_2; + } + else + { + regval |= SCI_MR_NBSTOP_1; + } + + /* And save the new mode register value */ + + tms570_serialout(priv, TMS570_SCI_MR_OFFSET, regval); + + /* Configure the console baud: + * + * Fbaud = SCI_CLOCK / (16 * divisor) + * divisor = SCI_CLOCK / (16 * Fbaud) + * + * NOTE: Oversampling by 8 is not supported. This may limit BAUD rates + * for lower SCI clocks. + */ + + divb3 = ((FAST_SCI_CLOCK + (priv->baud << 3)) << 3) / + (priv->baud << 4); + intpart = divb3 >> 3; + fracpart = divb3 & 7; + + /* Retain the fast MR peripheral clock UNLESS unless using that clock + * would result in an excessively large divider. + * + * REVISIT: The fractional divider is not used. + */ + + if ((intpart & ~SCI_BRGR_CD_MASK) != 0) + { + /* Use the divided SCI clock */ + + divb3 = ((SLOW_SCI_CLOCK + (priv->baud << 3)) << 3) / + (priv->baud << 4); + intpart = divb3 >> 3; + fracpart = divb3 & 7; + + /* Re-select the clock source */ + + regval = tms570_serialin(priv, TMS570_SCI_MR_OFFSET); + regval &= ~SCI_MR_USCLKS_MASK; + regval |= SCI_MR_USCLKS_MCKDIV; + tms570_serialout(priv, TMS570_SCI_MR_OFFSET, regval); + } + + /* Save the BAUD divider (the fractional part is not used for SCIs) */ + + regval = SCI_BRGR_CD(intpart) | SCI_BRGR_FP(fracpart); + tms570_serialout(priv, TMS570_SCI_BRGR_OFFSET, regval); + + /* Enable receiver & transmitter */ + + tms570_serialout(priv, TMS570_SCI_CR_OFFSET, (SCI_CR_RXEN | SCI_CR_TXEN)); +#endif + + return OK; +} + +/**************************************************************************** + * Name: tms570_shutdown + * + * Description: + * Disable the SCI. This method is called when the serial + * port is closed + * + ****************************************************************************/ + +static void tms570_shutdown(struct sci_dev_s *dev) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + + /* Reset and disable receiver and transmitter */ + + tms570_serialout(priv, TMS570_SCI_CR_OFFSET, + (SCI_CR_RSTRX | SCI_CR_RSTTX | SCI_CR_RXDIS | + SCI_CR_TXDIS)); + + /* Disable all interrupts */ + + tms570_disableallints(priv, NULL); +} + +/**************************************************************************** + * Name: tms570_attach + * + * Description: + * Configure the SCI 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 tms570_attach(struct sci_dev_s *dev) +{ + struct tms570_dev_s *priv = (struct tms570_dev_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 SCI + */ + + up_enable_irq(priv->irq); + } + + return ret; +} + +/**************************************************************************** + * Name: tms570_detach + * + * Description: + * Detach SCI 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 tms570_detach(struct sci_dev_s *dev) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + up_disable_irq(priv->irq); + irq_detach(priv->irq); +} + +/**************************************************************************** + * Name: tms570_interrupt + * + * Description: + * This is the common SCI interrupt handler. It will be invoked + * when an interrupt received on the device. It should call + * sci_transmitchars or sci_receivechar to perform the appropriate data + * transfers. + * + ****************************************************************************/ + +static int tms570_interrupt(struct sci_dev_s *dev) +{ + struct tms570_dev_s *priv; + uint32_t pending; + uint32_t imr; + int passes; + bool handled; + + DEBUGASSERT(dev && dev->priv); + priv = (struct tms570_dev_s *)dev->priv; + + /* Loop until there are no characters to be transferred or, until we have + * been looping for a long time. + */ + + handled = true; + for (passes = 0; passes < 256 && handled; passes++) + { + handled = false; + + /* Get the SCI status (we are only interested in the unmasked interrupts). */ + + priv->sr = tms570_serialin(priv, TMS570_SCI_SR_OFFSET); + imr = tms570_serialin(priv, TMS570_SCI_IMR_OFFSET); + pending = priv->sr & imr; + + /* Handle an incoming, receive byte. RXRDY: At least one complete character + * has been received and US_RHR has not yet been read. + */ + + if ((pending & SCI_INT_RXRDY) != 0) + { + /* Received data ready... process incoming bytes */ + + sci_recvchars(dev); + handled = true; + } + + /* Handle outgoing, transmit bytes. TXRDY: There is no character in the + * US_THR. + */ + + if ((pending & SCI_INT_TXRDY) != 0) + { + /* Transmit data register empty ... process outgoing bytes */ + + sci_xmitchars(dev); + handled = true; + } + } + + return OK; +} + +/**************************************************************************** + * Name: tms570_sci[n]_interrupt + * + * Description: + * SCI interrupt handlers + * + ****************************************************************************/ + +#ifdef CONFIG_SAMV7_SCI1 +static int tms570_sci1_interrupt(int irq, void *context) +{ + return tms570_interrupt(&g_sci1port); +} +#endif +#ifdef CONFIG_SAMV7_SCI2 +static int tms570_sci2_interrupt(int irq, void *context) +{ + return tms570_interrupt(&g_sci2port); +} +#endif + +/**************************************************************************** + * Name: tms570_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + ****************************************************************************/ + +static int tms570_ioctl(struct file *filep, int cmd, unsigned long arg) +{ +#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT) + struct inode *inode = filep->f_inode; + struct sci_dev_s *dev = inode->i_private; +#endif + int ret = OK; + + switch (cmd) + { +#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT + case TIOCSERGSTRUCT: + { + struct tms570_dev_s *user = (struct tms570_dev_s *)arg; + if (!user) + { + ret = -EINVAL; + } + else + { + memcpy(user, dev, sizeof(struct tms570_dev_s)); + } + } + break; +#endif + +#ifdef CONFIG_SERIAL_TERMIOS + case TCGETS: + { + struct termios *termiosp = (struct termios *)arg; + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + /* Return baud */ + + cfsetispeed(termiosp, priv->baud); + + /* Return parity */ + + termiosp->c_cflag = ((priv->parity != 0) ? PARENB : 0) | + ((priv->parity == 1) ? PARODD : 0); + + /* Return stop bits */ + + termiosp->c_cflag |= (priv->stopbits2) ? CSTOPB : 0; + + /* Return flow control */ + +#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL) + termiosp->c_cflag |= (priv->flowc) ? (CCTS_OFLOW | CRTS_IFLOW): 0; +#endif + /* Return number of bits */ + + switch (priv->bits) + { + case 5: + termiosp->c_cflag |= CS5; + break; + + case 6: + termiosp->c_cflag |= CS6; + break; + + case 7: + termiosp->c_cflag |= CS7; + break; + + default: + case 8: + termiosp->c_cflag |= CS8; + break; + + case 9: + termiosp->c_cflag |= CS8 /* CS9 */; + break; + } + } + break; + + case TCSETS: + { + struct termios *termiosp = (struct termios *)arg; + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + uint32_t baud; + uint32_t imr; + uint8_t parity; + uint8_t nbits; + bool stop2; +#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL) + bool flowc; +#endif + + if (!termiosp) + { + ret = -EINVAL; + break; + } + + /* Decode baud. */ + + ret = OK; + baud = cfgetispeed(termiosp); + + /* Decode number of bits */ + + switch (termiosp->c_cflag & CSIZE) + { + case CS5: + nbits = 5; + break; + + case CS6: + nbits = 6; + break; + + case CS7: + nbits = 7; + break; + + case CS8: + nbits = 8; + break; +#if 0 + case CS9: + nbits = 9; + break; +#endif + default: + ret = -EINVAL; + break; + } + + /* Decode parity */ + + if ((termiosp->c_cflag & PARENB) != 0) + { + parity = (termiosp->c_cflag & PARODD) ? 1 : 2; + } + else + { + parity = 0; + } + + /* Decode stop bits */ + + stop2 = (termiosp->c_cflag & CSTOPB) != 0; + + /* Decode flow control */ + +#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL) + flowc = (termiosp->c_cflag & (CCTS_OFLOW | CRTS_IFLOW)) != 0; +#endif + /* Verify that all settings are valid before committing */ + + if (ret == OK) + { + /* Commit */ + + priv->baud = baud; + priv->parity = parity; + priv->bits = nbits; + priv->stopbits2 = stop2; +#if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL) + priv->flowc = flowc; +#endif + /* effect the changes immediately - note that we do not + * implement TCSADRAIN / TCSAFLUSH + */ + + tms570_disableallints(priv, &imr); + ret = tms570_setup(dev); + + /* Restore the interrupt state */ + + tms570_restoreusartint(priv, imr); + } + } + break; +#endif /* CONFIG_SERIAL_TERMIOS */ + + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +/**************************************************************************** + * Name: tms570_receive + * + * Description: + * Called (usually) from the interrupt level to receive one + * character from the SCI. Error bits associated with the + * receipt are provided in the return 'status'. + * + ****************************************************************************/ + +static int tms570_receive(struct sci_dev_s *dev, uint32_t *status) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + + /* Return the error information in the saved status */ + + *status = priv->sr; + priv->sr = 0; + + /* Then return the actual received byte */ + + return (int)(tms570_serialin(priv, TMS570_SCI_RHR_OFFSET) & 0xff); +} + +/**************************************************************************** + * Name: tms570_rxint + * + * Description: + * Call to enable or disable RXRDY interrupts + * + ****************************************************************************/ + +static void tms570_rxint(struct sci_dev_s *dev, bool enable) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + + if (enable) + { + /* Receive an interrupt when their is anything in the Rx data register (or an Rx + * timeout occurs). + */ + +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + tms570_serialout(priv, TMS570_SCI_IER_OFFSET, SCI_INT_RXRDY); +#endif + } + else + { + tms570_serialout(priv, TMS570_SCI_IDR_OFFSET, SCI_INT_RXRDY); + } +} + +/**************************************************************************** + * Name: tms570_rxavailable + * + * Description: + * Return true if the receive holding register is not empty + * + ****************************************************************************/ + +static bool tms570_rxavailable(struct sci_dev_s *dev) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + return ((tms570_serialin(priv, TMS570_SCI_SR_OFFSET) & SCI_INT_RXRDY) != 0); +} + +/**************************************************************************** + * Name: tms570_send + * + * Description: + * This method will send one byte on the SCI + *- + ****************************************************************************/ + +static void tms570_send(struct sci_dev_s *dev, int ch) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + tms570_serialout(priv, TMS570_SCI_THR_OFFSET, (uint32_t)ch); +} + +/**************************************************************************** + * Name: tms570_txint + * + * Description: + * Call to enable or disable TX interrupts + * + ****************************************************************************/ + +static void tms570_txint(struct sci_dev_s *dev, bool enable) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + irqstate_t flags; + + flags = irqsave(); + if (enable) + { + /* Set to receive an interrupt when the TX holding register register + * is empty + */ + +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + tms570_serialout(priv, TMS570_SCI_IER_OFFSET, SCI_INT_TXRDY); + + /* Fake a TX interrupt here by just calling sci_xmitchars() with + * interrupts disabled (note this may recurse). + */ + + sci_xmitchars(dev); + +#endif + } + else + { + /* Disable the TX interrupt */ + + tms570_serialout(priv, TMS570_SCI_IDR_OFFSET, SCI_INT_TXRDY); + } + + irqrestore(flags); +} + +/**************************************************************************** + * Name: tms570_txready + * + * Description: + * Return true if the transmit holding register is empty (TXRDY) + * + ****************************************************************************/ + +static bool tms570_txready(struct sci_dev_s *dev) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + return ((tms570_serialin(priv, TMS570_SCI_SR_OFFSET) & SCI_INT_TXRDY) != 0); +} + +/**************************************************************************** + * Name: tms570_txempty + * + * Description: + * Return true if the transmit holding and shift registers are empty + * + ****************************************************************************/ + +static bool tms570_txempty(struct sci_dev_s *dev) +{ + struct tms570_dev_s *priv = (struct tms570_dev_s *)dev->priv; + return ((tms570_serialin(priv, TMS570_SCI_SR_OFFSET) & SCI_INT_TXEMPTY) != 0); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: up_serialinit + * + * Description: + * Register serial console and serial ports. + * + ****************************************************************************/ + +void up_serialinit(void) +{ + /* Disable all SCIS */ + + tms570_disableallints(TTYS0_DEV.priv, NULL); +#ifdef TTYS1_DEV + tms570_disableallints(TTYS1_DEV.priv, NULL); +#endif + + /* Configuration whichever one is the console */ + +#ifdef HAVE_SERIAL_CONSOLE + CONSOLE_DEV.isconsole = true; + tms570_setup(&CONSOLE_DEV); + + /* Register the console */ + + (void)sci_register("/dev/console", &CONSOLE_DEV); +#endif + + /* Register all SCIs */ + + (void)sci_register("/dev/ttyS0", &TTYS0_DEV); +#ifdef TTYS1_DEV + (void)sci_register("/dev/ttyS1", &TTYS1_DEV); +#endif +} + +#endif /* USE_SERIALDRIVER */