gustavonihei commented on a change in pull request #5352: URL: https://github.com/apache/incubator-nuttx/pull/5352#discussion_r793626150
########## File path: arch/xtensa/src/esp32s3/esp32s3_serial.c ########## @@ -0,0 +1,1166 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_serial.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/arch.h> +#include <nuttx/irq.h> +#include <nuttx/serial/serial.h> +#include <nuttx/fs/ioctl.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <debug.h> + +#ifdef CONFIG_SERIAL_TERMIOS +# include <termios.h> +#endif + +#include "xtensa.h" +#include "chip.h" + +#include "hardware/esp32s3_uart.h" +#include "hardware/esp32s3_system.h" + +#include "esp32s3_config.h" +#include "esp32s3_irq.h" +#include "esp32s3_lowputc.h" + +#ifdef CONFIG_ESP32S3_USBSERIAL +# include "esp32s3_usbserial.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* The console is enabled, and it's not the syslog device, + * so, it should be a serial device. + */ + +#ifdef USE_SERIALDRIVER + +/* Which UART with be tty0/console and which tty1? */ + +/* First pick the console and ttys0. + * Console can be UART0 or UART1, but will always be ttys0. + */ + +/* In case a UART was assigned to be + * the console and the corresponding peripheral was also selected. + */ + +#ifdef CONSOLE_UART +# if defined(CONFIG_UART0_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart0_dev /* UART0 is console */ +# define TTYS0_DEV g_uart0_dev /* UART0 is ttyS0 */ +# define UART0_ASSIGNED 1 +# elif defined(CONFIG_UART1_SERIAL_CONSOLE) +# define CONSOLE_DEV g_uart1_dev /* UART1 is console */ +# define TTYS0_DEV g_uart1_dev /* UART1 is ttyS0 */ +# define UART1_ASSIGNED 1 +# endif /* CONFIG_UART0_SERIAL_CONSOLE */ +#else /* No UART console */ +# undef CONSOLE_DEV +# if defined(CONFIG_ESP32S3_UART0) +# define TTYS0_DEV g_uart0_dev /* UART0 is ttyS0 */ +# define UART0_ASSIGNED 1 +# elif defined(CONFIG_ESP32S3_UART1) +# define TTYS0_DEV g_uart1_dev /* UART1 is ttyS0 */ +# define UART1_ASSIGNED 1 +# endif +#endif /* CONSOLE_UART */ + +#ifdef CONFIG_ESP32S3_USBSERIAL +# define CONSOLE_DEV g_uart_usbserial +# define TTYACM0_DEV g_uart_usbserial +#endif + +/* Pick ttys1 */ + +#if defined(CONFIG_ESP32S3_UART0) && !defined(UART0_ASSIGNED) +# define TTYS1_DEV g_uart0_dev /* UART0 is ttyS1 */ +# define UART0_ASSIGNED 1 +#elif defined(CONFIG_ESP32S3_UART1) && !defined(UART1_ASSIGNED) +# define TTYS1_DEV g_uart1_dev /* UART1 is ttyS1 */ +# define UART1_ASSIGNED 1 +#endif + +#ifdef HAVE_UART_DEVICE + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_ESP32S3_UART + +/* Serial driver methods */ + +static int esp32s3_setup(struct uart_dev_s *dev); +static void esp32s3_shutdown(struct uart_dev_s *dev); +static int esp32s3_attach(struct uart_dev_s *dev); +static void esp32s3_detach(struct uart_dev_s *dev); +static void esp32s3_txint(struct uart_dev_s *dev, bool enable); +static void esp32s3_rxint(struct uart_dev_s *dev, bool enable); +static bool esp32s3_rxavailable(struct uart_dev_s *dev); +static bool esp32s3_txready(struct uart_dev_s *dev); +static bool esp32s3_txempty(struct uart_dev_s *dev); +static void esp32s3_send(struct uart_dev_s *dev, int ch); +static int esp32s3_receive(struct uart_dev_s *dev, unsigned int *status); +static int esp32s3_ioctl(struct file *filep, int cmd, unsigned long arg); +#ifdef CONFIG_SERIAL_IFLOWCONTROL +static bool esp32s3_rxflowcontrol(struct uart_dev_s *dev, + unsigned int nbuffered, bool upper); +#endif +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_ESP32S3_UART + +/* Operations */ + +static struct uart_ops_s g_uart_ops = +{ + .setup = esp32s3_setup, + .shutdown = esp32s3_shutdown, + .attach = esp32s3_attach, + .detach = esp32s3_detach, + .txint = esp32s3_txint, + .rxint = esp32s3_rxint, + .rxavailable = esp32s3_rxavailable, + .txready = esp32s3_txready, + .txempty = esp32s3_txempty, + .send = esp32s3_send, + .receive = esp32s3_receive, + .ioctl = esp32s3_ioctl, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = esp32s3_rxflowcontrol, +#endif +}; + +/* UART 0 */ + +#ifdef CONFIG_ESP32S3_UART0 + +static char g_uart0_rxbuffer[CONFIG_UART0_RXBUFSIZE]; +static char g_uart0_txbuffer[CONFIG_UART0_TXBUFSIZE]; + +/* Fill only the requested fields */ + +static uart_dev_t g_uart0_dev = +{ +#ifdef CONFIG_UART0_SERIAL_CONSOLE + .isconsole = true, +#else + .isconsole = false, +#endif + .xmit = + { + .size = CONFIG_UART0_TXBUFSIZE, + .buffer = g_uart0_txbuffer, + }, + .recv = + { + .size = CONFIG_UART0_RXBUFSIZE, + .buffer = g_uart0_rxbuffer, + }, + + .ops = &g_uart_ops, + .priv = &g_uart0_config +}; + +#endif + +/* UART 1 */ + +#ifdef CONFIG_ESP32S3_UART1 + +static char g_uart1_rxbuffer[CONFIG_UART1_RXBUFSIZE]; +static char g_uart1_txbuffer[CONFIG_UART1_TXBUFSIZE]; + +/* Fill only the requested fields */ + +static uart_dev_t g_uart1_dev = +{ +#ifdef CONFIG_UART1_SERIAL_CONSOLE + .isconsole = true, +#else + .isconsole = false, +#endif + .xmit = + { + .size = CONFIG_UART1_TXBUFSIZE, + .buffer = g_uart1_txbuffer, + }, + .recv = + { + .size = CONFIG_UART1_RXBUFSIZE, + .buffer = g_uart1_rxbuffer, + }, + + .ops = &g_uart_ops, + .priv = &g_uart1_config +}; + +#endif + +#endif /* CONFIG_ESP32S3_UART */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef CONFIG_ESP32S3_UART + +/**************************************************************************** + * Name: uart_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 'irq' number into the + * appropriate uart_dev_s structure in order to call these functions. + * + ****************************************************************************/ + +static int uart_handler(int irq, void *context, void *arg) +{ + struct uart_dev_s *dev = (struct uart_dev_s *)arg; + struct esp32s3_uart_s *priv = dev->priv; + uint32_t tx_mask = UART_TXFIFO_EMPTY_INT_ST_M | UART_TX_DONE_INT_ST_M; + uint32_t rx_mask = UART_RXFIFO_TOUT_INT_ST_M | UART_RXFIFO_FULL_INT_ST_M; + uint32_t int_status; + + int_status = getreg32(UART_INT_ST_REG(priv->id)); + + /* Tx fifo empty interrupt or UART tx done int */ + + if ((int_status & tx_mask) != 0) + { + uart_xmitchars(dev); + modifyreg32(UART_INT_CLR_REG(priv->id), tx_mask, tx_mask); + } + + /* Rx fifo timeout interrupt or rx fifo full interrupt */ + + if ((int_status & rx_mask) != 0) + { + uart_recvchars(dev); + modifyreg32(UART_INT_CLR_REG(priv->id), rx_mask, rx_mask); + } + + return OK; +} + +/**************************************************************************** + * Name: esp32s3_setup + * + * Description: + * Configure the UART baud, bits, parity, fifos, etc. This method is + * called the first time that the serial port is opened. + * For the serial console, this will occur very early in initialization, + * for other serial ports this will occur when the port is first opened. + * This setup does not include attaching or enabling interrupts. + * That portion of the UART setup is performed when the attach() method + * is called. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * + * Returned Values: + * Zero (OK) is returned. + * + ****************************************************************************/ + +static int esp32s3_setup(struct uart_dev_s *dev) +{ + struct esp32s3_uart_s *priv = dev->priv; + + /* Initialize UART module */ + + /* Discard corrupt RX data and + * disable UART memory clock gate enable signal. + */ + + modifyreg32(UART_CONF0_REG(priv->id), UART_ERR_WR_MASK_M | + UART_MEM_CLK_EN_M, UART_ERR_WR_MASK_M); + + /* Define 0 as the threshold that means TX FIFO buffer is empty. */ + + modifyreg32(UART_CONF1_REG(priv->id), UART_TXFIFO_EMPTY_THRHD_M, 0); + + /* Define a threshold to trigger an RX FIFO FULL interrupt. + * Define just one byte to read data immediately. + */ + + modifyreg32(UART_CONF1_REG(priv->id), UART_RXFIFO_FULL_THRHD_M, + 1 << UART_RXFIFO_FULL_THRHD_S); + + /* Define the maximum FIFO size for RX and TX FIFO. + * That means, 1 block = 128 bytes. + * As a consequence, software serial FIFO can unload the bytes and + * not wait too much on polling activity. + */ + + modifyreg32(UART_MEM_CONF_REG(priv->id), UART_TX_SIZE_M | UART_RX_SIZE_M, + (1 << UART_TX_SIZE_S) | (1 << UART_RX_SIZE_S)); + + /* Configure the UART Baud Rate */ + + esp32s3_lowputc_baud(priv); + + /* Set a mode */ + + esp32s3_lowputc_normal_mode(priv); + + /* Parity */ + + esp32s3_lowputc_parity(priv); + + /* Data Frame size */ + + esp32s3_lowputc_data_length(priv); + + /* Stop bit */ + + esp32s3_lowputc_stop_length(priv); + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + /* Configure the input flow control */ + + if (priv->iflow) + { + /* Enable input flow control and set the RX FIFO threshold + * to assert the RTS line to half the RX FIFO buffer. + * It will then save some space on the hardware fifo to + * remaining bytes that may arrive after RTS be asserted + * and before the transmitter stops sending data. + */ + + esp32s3_lowputc_set_iflow(priv, (uint8_t)(UART_RX_FIFO_SIZE / 2), + true); + } + else + { + /* Just disable input flow control, threshold parameter + * will be discarded. + */ + + esp32s3_lowputc_set_iflow(priv, 0 , false); + } + +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + /* Configure the ouput flow control */ + + if (priv->oflow) + { + esp32s3_lowputc_set_oflow(priv, true); + } + else + { + esp32s3_lowputc_set_oflow(priv, false); + } +#endif + + /* No Tx idle interval */ + + esp32s3_lowputc_set_tx_idle_time(priv, 0); + + /* Enable cores */ + + esp32s3_lowputc_enable_sclk(priv); + + /* Clear FIFOs */ + + esp32s3_lowputc_rst_txfifo(priv); + esp32s3_lowputc_rst_rxfifo(priv); + + return OK; +} + +/**************************************************************************** + * Name: esp32s3_shutdown + * + * Description: + * Disable the UART. This method is called when the serial port is closed. + * This method reverses the operation the setup method. NOTE that the serial + * console is never shutdown. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * + ****************************************************************************/ + +static void esp32s3_shutdown(struct uart_dev_s *dev) +{ + struct esp32s3_uart_s *priv = dev->priv; + + /* Disable ints */ + + esp32s3_lowputc_disable_all_uart_int(priv, NULL); +} + +/**************************************************************************** + * Name: esp32s3_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. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * + * Returned Values: + * Zero (OK) is returned on success; A negated errno value is returned + * to indicate the nature of any failure. + * + ****************************************************************************/ + +static int esp32s3_attach(struct uart_dev_s *dev) +{ + struct esp32s3_uart_s *priv = dev->priv; + int ret; + + DEBUGASSERT(priv->cpuint == -ENOMEM); + + /* Set up to receive peripheral interrupts on the current CPU */ + + priv->cpu = up_cpu_index(); + priv->cpuint = esp32s3_setup_irq(0, priv->periph, priv->int_pri, + ESP32S3_CPUINT_LEVEL); + if (priv->cpuint < 0) + { + /* Failed to allocate a CPU interrupt of this type */ + + return priv->cpuint; + } + + /* Attach and enable the IRQ */ + + ret = irq_attach(priv->irq, uart_handler, dev); + if (ret == OK) + { + /* Enable the CPU interrupt (RX and TX interrupts are still disabled + * in the UART + */ + + up_enable_irq(priv->irq); + } + + return ret; +} + +/**************************************************************************** + * Name: esp32s3_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. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * + ****************************************************************************/ + +static void esp32s3_detach(struct uart_dev_s *dev) +{ + struct esp32s3_uart_s *priv = dev->priv; + + DEBUGASSERT(priv->cpuint != -ENOMEM); + + /* Disable and detach the CPU interrupt */ + + up_disable_irq(priv->irq); + irq_detach(priv->irq); + + /* Disassociate the peripheral interrupt from the CPU interrupt */ + + esp32s3_teardown_irq(priv->cpu, priv->periph, priv->cpuint); + priv->cpuint = -1; +} + +/**************************************************************************** + * Name: esp32s3_txint + * + * Description: + * Enable or disable TX interrupts. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * enable - If true enables the TX interrupt, if false disables it. + * + ****************************************************************************/ + +static void esp32s3_txint(struct uart_dev_s *dev, bool enable) +{ + struct esp32s3_uart_s *priv = dev->priv; + uint32_t ints_mask = UART_TXFIFO_EMPTY_INT_ENA_M | UART_TX_DONE_INT_ENA_M; + + if (enable) + { + /* Set to receive an interrupt when the TX holding register register + * is empty + */ + +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + modifyreg32(UART_INT_ENA_REG(priv->id), ints_mask, ints_mask); +#endif + } + else + { + /* Disable the TX interrupt */ + + modifyreg32(UART_INT_ENA_REG(priv->id), ints_mask, 0); + } +} + +/**************************************************************************** + * Name: esp32s3_rxint + * + * Description: + * Enable or disable RX interrupts. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * enable - If true enables the RX interrupt, if false disables it. + * + ****************************************************************************/ + +static void esp32s3_rxint(struct uart_dev_s *dev, bool enable) +{ + struct esp32s3_uart_s *priv = dev->priv; + uint32_t ints_mask = UART_RXFIFO_TOUT_INT_ENA_M | + UART_RXFIFO_FULL_INT_ENA_M; + + if (enable) + { + /* Receive an interrupt when there is anything in the RX data register + * (or an RX timeout occurs). + * NOTE: RX timeout feature needs to be enabled. + */ +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + modifyreg32(UART_CONF1_REG(priv->id), UART_RX_TOUT_EN_M, + UART_RX_TOUT_EN_M); + modifyreg32(UART_INT_ENA_REG(priv->id), ints_mask, ints_mask); +#endif + } + else + { + modifyreg32(UART_CONF1_REG(priv->id), UART_RX_TOUT_EN_M, 0); + + /* Disable the RX interrupts */ + + modifyreg32(UART_INT_ENA_REG(priv->id), ints_mask, 0); + } +} + +/**************************************************************************** + * Name: esp32s3_rxavailable + * + * Description: + * Check if there is any data available to be read. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * + * Returned Values: + * Return true if the RX FIFO is not empty and false if RX FIFO is empty. + * + ****************************************************************************/ + +static bool esp32s3_rxavailable(struct uart_dev_s *dev) +{ + struct esp32s3_uart_s *priv = dev->priv; + uint32_t status_reg; + uint32_t bytes; + + status_reg = getreg32(UART_STATUS_REG(priv->id)); + bytes = status_reg & UART_RXFIFO_CNT_M; + + return (bytes > 0); +} + +/**************************************************************************** + * Name: esp32s3_txready + * + * Description: + * Check if the transmit hardware is ready to send another byte. + * This is used to determine if send() method can be called. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * + * Returned Values: + * Return true if the transmit hardware is ready to send another byte, + * false otherwise. + * + ****************************************************************************/ + +static bool esp32s3_txready(struct uart_dev_s *dev) +{ + return !esp32s3_lowputc_is_tx_fifo_full(dev->priv); +} + +/**************************************************************************** + * Name: esp32s3_txempty + * + * Description: + * Verify if all characters have been sent. If for example, the UART + * hardware implements FIFOs, then this would mean the transmit FIFO is + * empty. This method is called when the driver needs to make sure that + * all characters are "drained" from the TX hardware. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * + * Returned Values: + * Return true if the TX FIFO is empty, false if it is not. + * + ****************************************************************************/ + +static bool esp32s3_txempty(struct uart_dev_s *dev) +{ + uint32_t reg; + struct esp32s3_uart_s *priv = dev->priv; + + reg = getreg32(UART_INT_RAW_REG(priv->id)); + reg = reg & UART_TXFIFO_EMPTY_INT_RAW_M; + + return (reg > 0); +} + +/**************************************************************************** + * Name: esp32s3_send + * + * Description: + * Send a unique character + * + * Parameters: + * dev - Pointer to the serial driver struct. + * ch - Byte to be sent. + * + ****************************************************************************/ + +static void esp32s3_send(struct uart_dev_s *dev, int ch) +{ + esp32s3_lowputc_send_byte(dev->priv, ch); +} + +/**************************************************************************** + * Name: esp32s3_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'. + * + * Parameters: + * dev - Pointer to the serial driver struct. + * status - Pointer to a variable to store eventual error bits. + * + * Returned Values: + * Return the byte read from the RX FIFO. + * + ****************************************************************************/ + +static int esp32s3_receive(struct uart_dev_s *dev, unsigned int *status) +{ + uint32_t rx_fifo; + struct esp32s3_uart_s *priv = dev->priv; + + rx_fifo = getreg32(UART_FIFO_REG(priv->id)); + rx_fifo = rx_fifo & UART_RXFIFO_RD_BYTE_M; + + /* Since we don't have error bits associated with receipt, we set zero */ + + *status = 0; + + return (int)rx_fifo; +} + +/**************************************************************************** + * Name: esp32s3_ioctl + * + * Description: + * All ioctl calls will be routed through this method. + * Here it's employed to implement the TERMIOS ioctls and TIOCSERGSTRUCT. + * + * Parameters: + * filep Pointer to a file structure instance. + * cmd The ioctl command. + * arg The argument of the ioctl cmd. + * + * Returned Value: + * Returns a non-negative number on success; A negated errno value is + * returned on any failure (see comments ioctl() for a list of appropriate + * errno values). + * + ****************************************************************************/ + +static int esp32s3_ioctl(struct file *filep, int cmd, unsigned long arg) +{ + /* Get access to the internal instance of the driver through the file + * pointer. + */ + +#if defined(CONFIG_SERIAL_TERMIOS) || defined(CONFIG_SERIAL_TIOCSERGSTRUCT) + struct inode *inode = filep->f_inode; + struct uart_dev_s *dev = inode->i_private; +#endif + int ret = OK; + + /* Run the requested ioctl command. */ + + switch (cmd) + { +#ifdef CONFIG_SERIAL_TIOCSERGSTRUCT + + /* Get the internal driver data structure for debug purposes. */ + + case TIOCSERGSTRUCT: + { + struct esp32s3_uart_s *user = (struct esp32s3_uart_s *)arg; + if (!user) Review comment: Actually the correct would be `user == NULL`. Thanks! -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
