| /**************************************************************************** |
| * arch/xtensa/src/esp32/esp32_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 <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 <nuttx/irq.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/fs/ioctl.h> |
| #include <nuttx/serial/serial.h> |
| #include <nuttx/spinlock.h> |
| |
| #include <arch/board/board.h> |
| |
| #include "xtensa.h" |
| #include "hardware/esp32_soc.h" |
| #include "hardware/esp32_iomux.h" |
| #include "hardware/esp32_gpio_sigmap.h" |
| #include "hardware/esp32_uart.h" |
| #include "hardware/esp32_uhci.h" |
| #include "hardware/esp32_dma.h" |
| #include "esp32_config.h" |
| #include "esp32_gpio.h" |
| #include "esp32_irq.h" |
| #include "esp32_dma.h" |
| #include "hardware/esp32_dport.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #ifdef USE_SERIALDRIVER |
| |
| /* Which UART with be tty0/console and which tty1-2? */ |
| |
| /* First pick the console and ttys0. This could be any of UART0-5 */ |
| |
| #if defined(CONFIG_UART0_SERIAL_CONSOLE) |
| # define CONSOLE_DEV g_uart0port /* UART0 is console */ |
| # define TTYS0_DEV g_uart0port /* UART0 is ttyS0 */ |
| # define UART0_ASSIGNED 1 |
| #elif 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 |
| #else |
| # undef CONSOLE_DEV /* No console */ |
| # if defined(CONFIG_ESP32_UART0) |
| # define TTYS0_DEV g_uart0port /* UART0 is ttyS0 */ |
| # define UART0_ASSIGNED 1 |
| # elif defined(CONFIG_ESP32_UART1) |
| # define TTYS0_DEV g_uart1port /* UART1 is ttyS0 */ |
| # define UART1_ASSIGNED 1 |
| # elif defined(CONFIG_ESP32_UART2) |
| # define TTYS0_DEV g_uart2port /* UART2 is ttyS0 */ |
| # define UART2_ASSIGNED 1 |
| # endif |
| #endif |
| |
| /* Pick ttys1. This could be any of UART0-2 excluding the console |
| * UART. |
| */ |
| |
| #if defined(CONFIG_ESP32_UART0) && !defined(UART0_ASSIGNED) |
| # define TTYS1_DEV g_uart0port /* UART0 is ttyS1 */ |
| # define UART0_ASSIGNED 1 |
| #elif defined(CONFIG_ESP32_UART1) && !defined(UART1_ASSIGNED) |
| # define TTYS1_DEV g_uart1port /* UART1 is ttyS1 */ |
| # define UART1_ASSIGNED 1 |
| #elif defined(CONFIG_ESP32_UART2) && !defined(UART2_ASSIGNED) |
| # define TTYS1_DEV g_uart2port /* UART2 is ttyS1 */ |
| # define UART2_ASSIGNED 1 |
| #endif |
| |
| /* Pick ttys2. This could be one of UART1-2. It can't be UART0 |
| * because that was either assigned as ttyS0 or ttys1. One of these |
| * could also be the console. |
| */ |
| |
| #if defined(CONFIG_ESP32_UART1) && !defined(UART1_ASSIGNED) |
| # define TTYS2_DEV g_uart1port /* UART1 is ttyS2 */ |
| # define UART1_ASSIGNED 1 |
| #elif defined(CONFIG_ESP32_UART2) && !defined(UART2_ASSIGNED) |
| # define TTYS2_DEV g_uart2port /* UART2 is ttyS2 */ |
| # define UART2_ASSIGNED 1 |
| #endif |
| |
| /* UART source clock for BAUD generation */ |
| |
| #define UART_CLK_FREQ APB_CLK_FREQ |
| |
| /* DMA related */ |
| |
| #ifdef CONFIG_SERIAL_RXDMA |
| # error "SERIAL_RXDMA is not supported in ESP32 due to a hardware issue" |
| #endif |
| |
| #ifdef CONFIG_SERIAL_TXDMA |
| |
| #define UART_SELECT_SHIFT UHCI_UART0_CE_S |
| |
| /* UART DMA RX/TX number of descriptors */ |
| |
| #define UART_DMADESC_NUM (CONFIG_UART_DMADESC_NUM) |
| |
| /* In case the three UARTs select TX DMA support, |
| * let DMA 0 exclusive to one and DMA 1 |
| * shared with others. |
| */ |
| |
| #if defined(CONFIG_ESP32_UART0_TXDMA) && defined(CONFIG_ESP32_UART1_TXDMA) && defined(CONFIG_ESP32_UART2_TXDMA) |
| # ifdef CONFIG_ESP32_UART0_EXC |
| # define UART0_DMA 0 |
| # define UART1_DMA 1 |
| # define UART2_DMA 1 |
| # elif defined(CONFIG_ESP32_UART1_EXC) |
| # define UART0_DMA 1 |
| # define UART1_DMA 0 |
| # define UART2_DMA 1 |
| # elif defined(CONFIG_ESP32_UART2_EXC) |
| # define UART0_DMA 1 |
| # define UART1_DMA 1 |
| # define UART2_DMA 0 |
| # endif |
| # define USE_DMA0 1 |
| # define USE_DMA1 1 |
| #else |
| # ifdef CONFIG_ESP32_UART0_TXDMA |
| # define UART0_DMA 0 |
| # define USE_DMA0 1 |
| # endif |
| # ifdef CONFIG_ESP32_UART1_TXDMA |
| # ifndef USE_DMA0 |
| # define UART1_DMA 0 |
| # define USE_DMA0 1 |
| # else |
| # define UART1_DMA 1 |
| # define USE_DMA1 1 |
| # endif |
| # endif |
| # ifdef CONFIG_ESP32_UART2_TXDMA |
| # ifndef USE_DMA0 |
| # define UART2_DMA 0 |
| # define USE_DMA0 1 |
| # else |
| # define UART2_DMA 1 |
| # define USE_DMA1 1 |
| # endif |
| # endif |
| #endif |
| |
| /* UART DMA controllers */ |
| |
| #if defined(USE_DMA0) && defined(USE_DMA1) |
| # define UART_DMA_CONTROLLERS_NUM 2 |
| #else |
| # define UART_DMA_CONTROLLERS_NUM 1 |
| #endif |
| |
| /* Semaphores to control access to each DMA. |
| * These semaphores ensure a new transfer is |
| * triggered only after the previous one is completed, |
| * and it also avoids competing issues with multiple UART |
| * instances requesting to the same DMA. |
| */ |
| |
| #ifdef USE_DMA0 |
| static sem_t g_dma0_sem = SEM_INITIALIZER(1); |
| #endif |
| #ifdef USE_DMA1 |
| static sem_t g_dma1_sem = SEM_INITIALIZER(1); |
| #endif |
| |
| /* UART DMA RX/TX descriptors */ |
| |
| struct esp32_dmadesc_s s_dma_rxdesc[UART_DMA_CONTROLLERS_NUM] |
| [UART_DMADESC_NUM]; |
| struct esp32_dmadesc_s s_dma_txdesc[UART_DMA_CONTROLLERS_NUM] |
| [UART_DMADESC_NUM]; |
| |
| #endif |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* Constant properties of the UART. Other configuration setting may be |
| * changeable via Termios IOCTL calls. |
| */ |
| |
| struct esp32_config_s |
| { |
| const uint8_t id; /* UART id */ |
| uint8_t periph; /* UART peripheral ID */ |
| uint8_t irq; /* IRQ number assigned to the peripheral */ |
| uint8_t txpin; /* Tx pin number (0-39) */ |
| uint8_t rxpin; /* Rx pin number (0-39) */ |
| uint8_t txsig; /* Tx signal */ |
| uint8_t rxsig; /* Rx signal */ |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| uint8_t rtspin; /* RTS pin number (0-39) */ |
| uint8_t rtssig; /* RTS signal */ |
| #endif |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| uint8_t ctspin; /* CTS pin number (0-39) */ |
| uint8_t ctssig; /* CTS signal */ |
| #endif |
| #ifdef CONFIG_SERIAL_TXDMA |
| uint8_t dma_chan; /* DMA instance 0-1 */ |
| sem_t *dma_sem; /* DMA semaphore */ |
| #endif |
| #ifdef HAVE_RS485 |
| uint8_t rs485_dir_gpio; /* UART RS-485 DIR GPIO pin cfg */ |
| bool rs485_dir_polarity; /* UART RS-485 DIR TXEN polarity */ |
| #endif |
| }; |
| |
| /* Current state of the UART */ |
| |
| struct esp32_dev_s |
| { |
| const struct esp32_config_s *config; /* Constant configuration */ |
| uint32_t baud; /* Configured baud */ |
| uint32_t status; /* Saved status bits */ |
| int cpuint; /* CPU interrupt assigned to this UART */ |
| uint8_t cpu; /* CPU ID */ |
| 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 */ |
| #ifdef CONFIG_SERIAL_TXDMA |
| bool txdma; /* TX DMA enabled for this UART */ |
| #endif |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| bool iflow; /* Input flow control (RTS) enabled */ |
| #endif |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| bool oflow; /* Output flow control (CTS) enabled */ |
| #endif |
| spinlock_t lock; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static int esp32_setup(struct uart_dev_s *dev); |
| static void esp32_shutdown(struct uart_dev_s *dev); |
| static int esp32_attach(struct uart_dev_s *dev); |
| static void esp32_detach(struct uart_dev_s *dev); |
| static int esp32_interrupt(int cpuint, void *context, void *arg); |
| static int esp32_ioctl(struct file *filep, int cmd, unsigned long arg); |
| static int esp32_receive(struct uart_dev_s *dev, unsigned int *status); |
| static void esp32_rxint(struct uart_dev_s *dev, bool enable); |
| static bool esp32_rxavailable(struct uart_dev_s *dev); |
| static void esp32_send(struct uart_dev_s *dev, int ch); |
| static void esp32_txint(struct uart_dev_s *dev, bool enable); |
| static bool esp32_txready(struct uart_dev_s *dev); |
| static bool esp32_txempty(struct uart_dev_s *dev); |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| static bool esp32_rxflowcontrol(struct uart_dev_s *dev, |
| unsigned int nbuffered, bool upper); |
| #endif |
| #ifdef CONFIG_SERIAL_TXDMA |
| static void esp32_dmasend(struct uart_dev_s *dev); |
| static void esp32_dmatxavail(struct uart_dev_s *dev); |
| static void dma_config(uint8_t dma_chan); |
| static void dma_attach(uint8_t dma_chan); |
| static inline void dma_enable_int(uint8_t dma_chan); |
| static inline void dma_disable_int(uint8_t dma_chan); |
| static int esp32_interrupt_dma(int cpuint, void *context, void *arg); |
| #endif |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #define UART_TX_FIFO_SIZE 128 |
| #define UART_RX_FIFO_FULL_THRHD 112 |
| #define UART_RX_TOUT_THRHD_VALUE 0x02 |
| #define UART_RX_FLOW_THRHD_VALUE 64 /* Almost half RX FIFO size */ |
| |
| static const struct uart_ops_s g_uart_ops = |
| { |
| .setup = esp32_setup, |
| .shutdown = esp32_shutdown, |
| .attach = esp32_attach, |
| .detach = esp32_detach, |
| .ioctl = esp32_ioctl, |
| .receive = esp32_receive, |
| .rxint = esp32_rxint, |
| .rxavailable = esp32_rxavailable, |
| .send = esp32_send, |
| .txint = esp32_txint, |
| .txready = esp32_txready, |
| .txempty = esp32_txempty, |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| .rxflowcontrol = esp32_rxflowcontrol, |
| #endif |
| #ifdef CONFIG_SERIAL_TXDMA |
| .dmasend = esp32_dmasend, |
| .dmatxavail = esp32_dmatxavail, |
| #endif |
| }; |
| |
| /* I/O buffers */ |
| |
| #ifdef CONFIG_ESP32_UART0 |
| static char g_uart0rxbuffer[CONFIG_UART0_RXBUFSIZE]; |
| static char g_uart0txbuffer[CONFIG_UART0_TXBUFSIZE]; |
| #endif |
| #ifdef CONFIG_ESP32_UART1 |
| static char g_uart1rxbuffer[CONFIG_UART1_RXBUFSIZE]; |
| static char g_uart1txbuffer[CONFIG_UART1_TXBUFSIZE]; |
| #endif |
| #ifdef CONFIG_ESP32_UART2 |
| static char g_uart2rxbuffer[CONFIG_UART2_RXBUFSIZE]; |
| static char g_uart2txbuffer[CONFIG_UART2_TXBUFSIZE]; |
| #endif |
| |
| /* This describes the state of the UART0 port. */ |
| |
| #ifdef CONFIG_ESP32_UART0 |
| static const struct esp32_config_s g_uart0config = |
| { |
| .id = 0, |
| .periph = ESP32_PERIPH_UART, |
| .irq = ESP32_IRQ_UART, |
| .txpin = CONFIG_ESP32_UART0_TXPIN, |
| .rxpin = CONFIG_ESP32_UART0_RXPIN, |
| .txsig = U0TXD_OUT_IDX, |
| .rxsig = U0RXD_IN_IDX, |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| .rtspin = CONFIG_ESP32_UART0_RTSPIN, |
| .rtssig = U0RTS_OUT_IDX, |
| #endif |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| .ctspin = CONFIG_ESP32_UART0_CTSPIN, |
| .ctssig = U0CTS_IN_IDX, |
| #endif |
| #ifdef CONFIG_SERIAL_TXDMA |
| #ifdef CONFIG_ESP32_UART0_TXDMA |
| .dma_chan = UART0_DMA, |
| #if UART0_DMA == 0 |
| .dma_sem = &g_dma0_sem, |
| #else |
| .dma_sem = &g_dma1_sem, |
| #endif |
| #endif |
| #endif |
| #ifdef CONFIG_ESP32_UART0_RS485 |
| .rs485_dir_gpio = CONFIG_ESP32_UART0_RS485_DIR_PIN, |
| #if (CONFIG_ESP32_UART0_RS485_DIR_POLARITY == 0) |
| .rs485_dir_polarity = false, |
| #else |
| .rs485_dir_polarity = true, |
| #endif |
| #endif |
| }; |
| |
| static struct esp32_dev_s g_uart0priv = |
| { |
| .config = &g_uart0config, |
| .baud = CONFIG_UART0_BAUD, |
| .parity = CONFIG_UART0_PARITY, |
| .bits = CONFIG_UART0_BITS, |
| .stopbits2 = CONFIG_UART0_2STOP, |
| #ifdef CONFIG_SERIAL_TXDMA |
| # ifdef CONFIG_ESP32_UART0_TXDMA |
| .txdma = true, /* TX DMA enabled for this UART */ |
| # else |
| .txdma = false, /* TX DMA disabled for this UART */ |
| # endif |
| #endif |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| # ifdef CONFIG_UART0_IFLOWCONTROL |
| .iflow = true, /* Input flow control (RTS) enabled */ |
| # else |
| .iflow = false, /* Input flow control (RTS) disabled */ |
| # endif |
| #endif |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| # ifdef CONFIG_UART0_OFLOWCONTROL |
| .oflow = true, /* Output flow control (CTS) enabled */ |
| # else |
| .oflow = false, /* Output flow control (CTS) disabled */ |
| # endif |
| #endif |
| }; |
| |
| static uart_dev_t g_uart0port = |
| { |
| .recv = |
| { |
| .size = CONFIG_UART0_RXBUFSIZE, |
| .buffer = g_uart0rxbuffer, |
| }, |
| .xmit = |
| { |
| .size = CONFIG_UART0_TXBUFSIZE, |
| .buffer = g_uart0txbuffer, |
| }, |
| .ops = &g_uart_ops, |
| .priv = &g_uart0priv, |
| }; |
| #endif |
| |
| /* This describes the state of the UART1 port. */ |
| |
| #ifdef CONFIG_ESP32_UART1 |
| static const struct esp32_config_s g_uart1config = |
| { |
| .id = 1, |
| .periph = ESP32_PERIPH_UART1, |
| .irq = ESP32_IRQ_UART1, |
| .txpin = CONFIG_ESP32_UART1_TXPIN, |
| .rxpin = CONFIG_ESP32_UART1_RXPIN, |
| .txsig = U1TXD_OUT_IDX, |
| .rxsig = U1RXD_IN_IDX, |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| .rtspin = CONFIG_ESP32_UART1_RTSPIN, |
| .rtssig = U1RTS_OUT_IDX, |
| #endif |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| .ctspin = CONFIG_ESP32_UART1_CTSPIN, |
| .ctssig = U1CTS_IN_IDX, |
| #endif |
| #ifdef CONFIG_SERIAL_TXDMA |
| # ifdef CONFIG_ESP32_UART1_TXDMA |
| .dma_chan = UART1_DMA, |
| # if UART1_DMA == 0 |
| .dma_sem = &g_dma0_sem, |
| # else |
| .dma_sem = &g_dma1_sem, |
| # endif |
| # endif |
| #endif |
| #ifdef CONFIG_ESP32_UART1_RS485 |
| .rs485_dir_gpio = CONFIG_ESP32_UART1_RS485_DIR_PIN, |
| # if (CONFIG_ESP32_UART1_RS485_DIR_POLARITY == 0) |
| .rs485_dir_polarity = false, |
| # else |
| .rs485_dir_polarity = true, |
| # endif |
| #endif |
| }; |
| |
| static struct esp32_dev_s g_uart1priv = |
| { |
| .config = &g_uart1config, |
| .baud = CONFIG_UART1_BAUD, |
| .parity = CONFIG_UART1_PARITY, |
| .bits = CONFIG_UART1_BITS, |
| .stopbits2 = CONFIG_UART1_2STOP, |
| #ifdef CONFIG_SERIAL_TXDMA |
| # ifdef CONFIG_ESP32_UART1_TXDMA |
| .txdma = true, /* TX DMA enabled for this UART */ |
| # else |
| .txdma = false, /* TX DMA disabled for this UART */ |
| # endif |
| #endif |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| # ifdef CONFIG_UART1_IFLOWCONTROL |
| .iflow = true, /* input flow control (RTS) enabled */ |
| # else |
| .iflow = false, /* input flow control (RTS) disabled */ |
| # endif |
| #endif |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| # ifdef CONFIG_UART1_OFLOWCONTROL |
| .oflow = true, /* output flow control (CTS) enabled */ |
| # else |
| .oflow = false, /* output flow control (CTS) disabled */ |
| # endif |
| #endif |
| }; |
| |
| static uart_dev_t 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 UART2 port. */ |
| |
| #ifdef CONFIG_ESP32_UART2 |
| static const struct esp32_config_s g_uart2config = |
| { |
| .id = 2, |
| .periph = ESP32_PERIPH_UART2, |
| .irq = ESP32_IRQ_UART2, |
| .txpin = CONFIG_ESP32_UART2_TXPIN, |
| .rxpin = CONFIG_ESP32_UART2_RXPIN, |
| .txsig = U2TXD_OUT_IDX, |
| .rxsig = U2RXD_IN_IDX, |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| .rtspin = CONFIG_ESP32_UART2_RTSPIN, |
| .rtssig = U2RTS_OUT_IDX, |
| #endif |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| .ctspin = CONFIG_ESP32_UART2_CTSPIN, |
| .ctssig = U2CTS_IN_IDX, |
| #endif |
| #ifdef CONFIG_SERIAL_TXDMA |
| # ifdef CONFIG_ESP32_UART2_TXDMA |
| .dma_chan = UART2_DMA, |
| # if UART2_DMA == 0 |
| .dma_sem = &g_dma0_sem, |
| # else |
| .dma_sem = &g_dma1_sem, |
| # endif |
| # endif |
| #endif |
| #ifdef CONFIG_ESP32_UART2_RS485 |
| .rs485_dir_gpio = CONFIG_ESP32_UART2_RS485_DIR_PIN, |
| # if (CONFIG_ESP32_UART2_RS485_DIR_POLARITY == 0) |
| .rs485_dir_polarity = false, |
| # else |
| .rs485_dir_polarity = true, |
| # endif |
| #endif |
| }; |
| |
| static struct esp32_dev_s g_uart2priv = |
| { |
| .config = &g_uart2config, |
| .baud = CONFIG_UART2_BAUD, |
| .parity = CONFIG_UART2_PARITY, |
| .bits = CONFIG_UART2_BITS, |
| .stopbits2 = CONFIG_UART2_2STOP, |
| #ifdef CONFIG_SERIAL_TXDMA |
| # ifdef CONFIG_ESP32_UART2_TXDMA |
| .txdma = true, /* TX DMA enabled for this UART */ |
| # else |
| .txdma = false, /* TX DMA disabled for this UART */ |
| # endif |
| #endif |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| # ifdef CONFIG_UART2_IFLOWCONTROL |
| .iflow = true, /* input flow control (RTS) enabled */ |
| # else |
| .iflow = false, /* input flow control (RTS) disabled */ |
| # endif |
| #endif |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| # ifdef CONFIG_UART2_OFLOWCONTROL |
| .oflow = true, /* output flow control (CTS) enabled */ |
| # else |
| .oflow = false, /* output flow control (CTS) disabled */ |
| # endif |
| #endif |
| }; |
| |
| static uart_dev_t 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 |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_SERIAL_TXDMA |
| /**************************************************************************** |
| * Name: esp32_dmasend |
| * |
| * Description: |
| * Prepare the descriptor linked-list and initialize a transfer. |
| * |
| * Parameters: |
| * priv - Pointer to the serial driver struct. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_dmasend(struct uart_dev_s *dev) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| |
| if (priv->txdma) |
| { |
| struct esp32_dmadesc_s *dmadesc; |
| uint8_t *tp; |
| #ifdef CONFIG_ESP32_SPIRAM |
| uint8_t *alloctp = NULL; |
| #endif |
| |
| /* If the buffer comes from PSRAM, allocate a new one from |
| * Internal SRAM. |
| */ |
| |
| #ifdef CONFIG_ESP32_SPIRAM |
| if (esp32_ptr_extram(dev->dmatx.buffer)) |
| { |
| # ifdef CONFIG_MM_KERNEL_HEAP |
| alloctp = kmm_malloc(dev->dmatx.length); |
| # elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) |
| alloctp = xtensa_imm_malloc(dev->dmatx.length); |
| # endif |
| |
| DEBUGASSERT(alloctp != NULL); |
| memcpy(alloctp, dev->dmatx.buffer, dev->dmatx.length); |
| tp = alloctp; |
| } |
| else |
| #endif |
| { |
| tp = (uint8_t *)dev->dmatx.buffer; |
| } |
| |
| /* Initialize descriptor linked-list. |
| * esp32_dma_init divides the buffer into the list, perform |
| * the required alignment and fill all descriptor words. |
| */ |
| |
| dmadesc = s_dma_txdesc[priv->config->dma_chan]; |
| |
| esp32_dma_init(dmadesc, UART_DMADESC_NUM, tp, |
| (uint32_t) dev->dmatx.length); |
| |
| /* Set the 1st descriptor address */ |
| |
| modifyreg32(UHCI_DMA_OUT_LINK_REG(priv->config->dma_chan), |
| UHCI_OUTLINK_ADDR_M, |
| (((uintptr_t) dmadesc) & UHCI_OUTLINK_ADDR_M)); |
| |
| /* Trigger DMA transfer */ |
| |
| modifyreg32(UHCI_DMA_OUT_LINK_REG(priv->config->dma_chan), |
| UHCI_OUTLINK_STOP_M, UHCI_OUTLINK_START_M); |
| |
| #ifdef CONFIG_ESP32_SPIRAM |
| if (alloctp != NULL) |
| { |
| # ifdef CONFIG_MM_KERNEL_HEAP |
| kmm_free(alloctp); |
| # elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) |
| xtensa_imm_free(alloctp); |
| # endif |
| } |
| #endif |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_dmatxavail |
| * |
| * Description: |
| * Verifies if the DMA is available for a transfer. If so, trigger a |
| * transfer. |
| * |
| * Parameters: |
| * priv - Pointer to the serial driver struct. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_dmatxavail(struct uart_dev_s *dev) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| |
| /* Check if this UART instance has DMA TX enabled */ |
| |
| if (priv->txdma) |
| { |
| /* Try to acquire the semaphore. |
| * This semaphore is always unlocked at 1st time. |
| * Then, the next times, it is released when a DMA transfer |
| * is completed. |
| */ |
| |
| if (nxsem_trywait(priv->config->dma_sem) == OK) |
| { |
| if (priv->config->dma_chan == 0) |
| { |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_UHCI0_CLK_EN); |
| } |
| else |
| { |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_UHCI1_CLK_EN); |
| } |
| |
| modifyreg32(UHCI_CONF0_REG(priv->config->dma_chan), |
| UHCI_UART0_CE | UHCI_UART1_CE | UHCI_UART2_CE, |
| 1 << (UART_SELECT_SHIFT + priv->config->id)); |
| dma_enable_int(priv->config->dma_chan); |
| uart_xmitchars_dma(dev); |
| } |
| } |
| } |
| #endif |
| |
| #ifndef CONFIG_SUPPRESS_UART_CONFIG |
| /**************************************************************************** |
| * Name: esp32_reset_rx_fifo |
| * |
| * Description: |
| * Resets the RX FIFO. |
| * NOTE: We can not use rxfifo_rst to reset the hardware RX FIFO. |
| * |
| * Parameters: |
| * priv - Pointer to the serial driver struct. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_reset_rx_fifo(struct esp32_dev_s *priv) |
| { |
| uint32_t rx_status_reg = getreg32(UART_STATUS_REG(priv->config->id)); |
| uint32_t fifo_cnt = REG_MASK(rx_status_reg, UART_RXFIFO_CNT); |
| uint32_t mem_rx_status_reg = getreg32(UART_MEM_RX_STATUS_REG |
| (priv->config->id)); |
| uint32_t rd_address = REG_MASK(mem_rx_status_reg, UART_RD_ADDRESS); |
| uint32_t wr_address = REG_MASK(mem_rx_status_reg, UART_WR_ADDRESS); |
| |
| while ((fifo_cnt != 0) || (rd_address != wr_address)) |
| { |
| getreg32(DR_UART_FIFO_REG(priv->config->id)); |
| |
| rx_status_reg = getreg32(UART_STATUS_REG(priv->config->id)); |
| fifo_cnt = REG_MASK(rx_status_reg, UART_RXFIFO_CNT); |
| mem_rx_status_reg = getreg32(UART_MEM_RX_STATUS_REG(priv->config->id)); |
| rd_address = REG_MASK(mem_rx_status_reg, UART_RD_ADDRESS); |
| wr_address = REG_MASK(mem_rx_status_reg, UART_WR_ADDRESS); |
| } |
| } |
| |
| #endif |
| |
| /**************************************************************************** |
| * Name: esp32_get_rx_fifo_len |
| * |
| * Description: |
| * Get the real value on rx fixo. |
| * RX_FIFO_CNT shouldn't be used alone accordingly to: |
| * https://www.espressif.com/sites/default/files/documentation/eco_ |
| * and_workarounds_for_bugs_in_esp32_en.pdf. |
| * So, some arithmetic with the read and write RX FIFO pointers are |
| * necessary. |
| * |
| * Parameters: |
| * priv - Pointer to the serial driver struct. |
| * |
| * Return: |
| * The number of bytes in RX fifo. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t esp32_get_rx_fifo_len(struct esp32_dev_s *priv) |
| { |
| uint32_t rd_address; |
| uint32_t wr_address; |
| uint32_t fifo_cnt; |
| uint32_t mem_rx_status_reg; |
| uint32_t rx_status_reg; |
| uint32_t len; |
| |
| mem_rx_status_reg = getreg32(UART_MEM_RX_STATUS_REG(priv->config->id)); |
| rd_address = REG_MASK(mem_rx_status_reg, UART_RD_ADDRESS); |
| wr_address = REG_MASK(mem_rx_status_reg, UART_WR_ADDRESS); |
| rx_status_reg = getreg32(UART_STATUS_REG(priv->config->id)); |
| fifo_cnt = REG_MASK(rx_status_reg, UART_RXFIFO_CNT); |
| |
| if (wr_address > rd_address) |
| { |
| len = wr_address - rd_address; |
| } |
| else if (wr_address < rd_address) |
| { |
| len = (wr_address + 128) - rd_address; |
| } |
| else |
| { |
| len = fifo_cnt > 0 ? 128 : 0; |
| } |
| |
| return len; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_restoreuartint |
| ****************************************************************************/ |
| |
| static inline void esp32_restoreuartint(struct esp32_dev_s *priv, |
| uint32_t intena) |
| { |
| /* Restore the previous interrupt state |
| * (assuming all interrupts disabled) |
| */ |
| |
| putreg32(intena, UART_INT_ENA_REG(priv->config->id)); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_disableallints |
| ****************************************************************************/ |
| |
| static void esp32_disableallints(struct esp32_dev_s *priv, uint32_t *intena) |
| { |
| irqstate_t flags; |
| |
| /* The following must be atomic */ |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| |
| if (intena) |
| { |
| /* Return the current interrupt mask */ |
| |
| *intena = getreg32(UART_INT_ENA_REG(priv->config->id)); |
| } |
| |
| /* Disable all interrupts */ |
| |
| putreg32(0, UART_INT_ENA_REG(priv->config->id)); |
| spin_unlock_irqrestore(&priv->lock, flags); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_setup |
| * |
| * Description: |
| * Configure the UART baud, bits, parity, etc. This method is called the |
| * first time that the serial port is opened. |
| * |
| ****************************************************************************/ |
| |
| static int esp32_setup(struct uart_dev_s *dev) |
| { |
| #ifndef CONFIG_SUPPRESS_UART_CONFIG |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| uint32_t clkdiv; |
| uint32_t regval; |
| uint32_t conf0; |
| |
| /* Note: The logic here depends on the fact that that the UART module |
| * was enabled and the pins were configured in esp32_lowsetup(). |
| */ |
| |
| /* The shutdown method will put the UART in a known, disabled state */ |
| |
| esp32_shutdown(dev); |
| |
| /* Set up the CONF0 register. */ |
| |
| conf0 = UART_TICK_REF_ALWAYS_ON | UART_ERR_WR_MASK_M; |
| |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| /* Check if output flow control is enabled for this UART controller. */ |
| |
| if (priv->oflow) |
| { |
| /* Enable output hardware flow control */ |
| |
| conf0 |= UART_TX_FLOW_EN; |
| } |
| |
| #endif |
| |
| /* OR in settings for the selected number of bits */ |
| |
| if (priv->bits == 5) |
| { |
| /* 0=5 bits */ |
| } |
| else if (priv->bits == 6) |
| { |
| conf0 |= (1 << UART_BIT_NUM_S); /* 1=6 bits */ |
| } |
| else if (priv->bits == 7) |
| { |
| conf0 |= (2 << UART_BIT_NUM_S); /* 2=7 bits */ |
| } |
| else /* if (priv->bits == 8) */ |
| { |
| conf0 |= (3 << UART_BIT_NUM_S); /* 3=8 bits */ |
| } |
| |
| /* OR in settings for the selected parity */ |
| |
| if (priv->parity == 1) |
| { |
| conf0 |= UART_PARITY_EN; |
| } |
| else if (priv->parity == 2) |
| { |
| conf0 |= UART_PARITY_EN | UART_PARITY; |
| } |
| |
| /* OR in settings for the number of stop bits */ |
| |
| if (priv->stopbits2) |
| { |
| conf0 |= 2 << UART_STOP_BIT_NUM_S; |
| } |
| else |
| { |
| conf0 |= 1 << UART_STOP_BIT_NUM_S; |
| } |
| |
| /* Configure the UART BAUD */ |
| |
| clkdiv = (UART_CLK_FREQ << 4) / priv->baud; |
| |
| regval = (clkdiv >> 4) << UART_CLKDIV_S; |
| regval |= (clkdiv & 15) << UART_CLKDIV_FRAG_S; |
| putreg32(regval, UART_CLKDIV_REG(priv->config->id)); |
| |
| /* Reset the RX and TX FIFO */ |
| |
| esp32_reset_rx_fifo(priv); |
| |
| /* Configure and enable the UART */ |
| |
| putreg32(conf0, UART_CONF0_REG(priv->config->id)); |
| regval = VALUE_TO_FIELD(UART_RX_FIFO_FULL_THRHD, UART_RXFIFO_FULL_THRHD) | |
| VALUE_TO_FIELD(UART_RX_TOUT_THRHD_VALUE, UART_RX_TOUT_THRHD) | |
| UART_RX_TOUT_EN; |
| putreg32(regval, UART_CONF1_REG(priv->config->id)); |
| |
| /* Enable RX and error interrupts. Clear and pending interrtupt */ |
| |
| regval = UART_RXFIFO_FULL_INT_ENA | UART_FRM_ERR_INT_ENA | |
| UART_RXFIFO_TOUT_INT_ENA; |
| putreg32(regval, UART_INT_ENA_REG(priv->config->id)); |
| |
| putreg32(UINT32_MAX, UART_INT_CLR_REG(priv->config->id)); |
| |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| /* Check if input flow control is enabled for this UART controller */ |
| |
| if (priv->iflow) |
| { |
| /* Enable input hardware flow control */ |
| |
| regval |= VALUE_TO_FIELD(UART_RX_FLOW_THRHD_VALUE, UART_RX_FLOW_THRHD) |
| | UART_RX_FLOW_EN; |
| modifyreg32(UART_CONF1_REG(priv->config->id), 0, regval); |
| } |
| |
| #endif |
| |
| #endif |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_shutdown |
| * |
| * Description: |
| * Disable the UART. This method is called when the serial port is |
| * closed. It is assumed that esp32_detach was called earlier in the |
| * shutdown sequence. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_shutdown(struct uart_dev_s *dev) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| uint32_t status; |
| |
| /* Wait for outgoing FIFO to clear. The ROM bootloader does not flush |
| * the FIFO before handing over to user code, so some of this output is |
| * not currently seen when the UART is reconfigured in early stages of |
| * startup. |
| */ |
| |
| do |
| { |
| status = getreg32(UART_STATUS_REG(priv->config->id)); |
| } |
| while ((status & UART_TXFIFO_CNT_M) != 0); |
| |
| /* Disable all UART interrupts */ |
| |
| esp32_disableallints(priv, NULL); |
| putreg32(UINT32_MAX, UART_INT_CLR_REG(priv->config->id)); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_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 esp32_attach(struct uart_dev_s *dev) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| int ret = OK; |
| |
| /* Set up to receive peripheral interrupts on the current CPU */ |
| |
| priv->cpu = this_cpu(); |
| priv->cpuint = esp32_setup_irq(priv->cpu, priv->config->periph, |
| 1, ESP32_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->config->irq, esp32_interrupt, dev); |
| if (ret == OK) |
| { |
| /* Enable the CPU interrupt (RX and TX interrupts are still disabled |
| * in the UART |
| */ |
| |
| up_enable_irq(priv->config->irq); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_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 esp32_detach(struct uart_dev_s *dev) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| |
| /* Disable and detach the CPU interrupt */ |
| |
| up_disable_irq(priv->config->irq); |
| irq_detach(priv->config->irq); |
| |
| /* Disassociate the peripheral interrupt from the CPU interrupt */ |
| |
| esp32_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint); |
| priv->cpuint = -1; |
| } |
| |
| #ifdef CONFIG_SERIAL_TXDMA |
| |
| /**************************************************************************** |
| * Name: dma_enable_int |
| * |
| * Description: |
| * Enable UHCI interrupt. |
| * |
| * Parameters: |
| * dma_chan - DMA instance. |
| * |
| ****************************************************************************/ |
| |
| static inline void dma_enable_int(uint8_t dma_chan) |
| { |
| /* Interrupt will be triggered when all descriptors were transferred or |
| * in case of error with output descriptor. |
| */ |
| |
| uint32_t int_mask = UHCI_OUT_TOTAL_EOF_INT_ENA_M | |
| UHCI_OUT_DSCR_ERR_INT_ENA_M; |
| putreg32(int_mask, UHCI_INT_ENA_REG(dma_chan)); |
| } |
| |
| /**************************************************************************** |
| * Name: dma_disable_int. |
| * |
| * Description: |
| * Disable UHCI interrupt. |
| * |
| * Parameters: |
| * dma_chan - DMA instance. |
| * |
| ****************************************************************************/ |
| |
| static inline void dma_disable_int(uint8_t dma_chan) |
| { |
| putreg32(0, UHCI_INT_ENA_REG(dma_chan)); |
| } |
| |
| /**************************************************************************** |
| * Name: dma_attach |
| * |
| * Description: |
| * Configure an DMA interrupt, attach to a CPU interrupt and enable it. |
| * |
| * Parameters: |
| * dma_chan - DMA instance. |
| * |
| ****************************************************************************/ |
| |
| static void dma_attach(uint8_t dma_chan) |
| { |
| int dma_cpuint; |
| int cpu; |
| int ret; |
| int periph; |
| int irq; |
| |
| /* Clear the interrupts */ |
| |
| putreg32(UINT32_MAX, UHCI_INT_CLR_REG(dma_chan)); |
| |
| /* Attach the UHCI interrupt to the allocated CPU interrupt |
| * and attach and enable the IRQ. |
| */ |
| |
| if (dma_chan == 0) |
| { |
| periph = ESP32_PERIPH_UHCI0; |
| irq = ESP32_IRQ_UHCI0; |
| } |
| else |
| { |
| periph = ESP32_PERIPH_UHCI1; |
| irq = ESP32_IRQ_UHCI1; |
| } |
| |
| /* Set up to receive peripheral interrupts on the current CPU */ |
| |
| cpu = this_cpu(); |
| dma_cpuint = esp32_setup_irq(cpu, periph, 1, ESP32_CPUINT_LEVEL); |
| if (dma_cpuint < 0) |
| { |
| /* Failed to allocate a CPU interrupt of this type */ |
| |
| dmaerr("Failed to allocate a CPU interrupt.\n"); |
| return; |
| } |
| |
| ret = irq_attach(irq, esp32_interrupt_dma, NULL); |
| if (ret == OK) |
| { |
| /* Enable the CPU interrupt */ |
| |
| up_enable_irq(irq); |
| } |
| else |
| { |
| dmaerr("Couldn't attach IRQ to handler.\n"); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_interrupt_dma |
| * |
| * Description: |
| * DMA interrupt. |
| * |
| ****************************************************************************/ |
| |
| static int esp32_interrupt_dma(int irq, void *context, void *arg) |
| { |
| uint32_t value; |
| uint32_t status; |
| uint8_t uhci = irq - ESP32_IRQ_UHCI0; |
| struct uart_dev_s *dev = NULL; |
| |
| /* Disable interrupt, stop UHCI, save interrupt status |
| * clear interrupts. |
| */ |
| |
| status = getreg32(UHCI_INT_ST_REG(uhci)); |
| dma_disable_int(uhci); |
| modifyreg32(UHCI_DMA_OUT_LINK_REG(uhci), |
| UHCI_OUTLINK_START_M, UHCI_OUTLINK_STOP_M); |
| putreg32(UINT32_MAX, UHCI_INT_CLR_REG(uhci)); |
| |
| /* Check which UART is using DMA now and calls |
| * uart_xmitchars_done to adjust TX software buffer. |
| */ |
| |
| value = getreg32(UHCI_CONF0_REG(uhci)); |
| value = value & (UHCI_UART2_CE_M | UHCI_UART1_CE_M | UHCI_UART0_CE_M); |
| |
| switch (value) |
| { |
| # ifdef CONFIG_ESP32_UART0_TXDMA |
| case UHCI_UART0_CE_M: |
| dev = &g_uart0port; |
| break; |
| # endif |
| |
| # ifdef CONFIG_ESP32_UART1_TXDMA |
| case UHCI_UART1_CE_M: |
| dev = &g_uart1port; |
| break; |
| # endif |
| |
| # ifdef CONFIG_ESP32_UART2_TXDMA |
| case UHCI_UART2_CE_M: |
| dev = &g_uart2port; |
| break; |
| # endif |
| |
| default: |
| dmaerr("No UART selected\n"); |
| } |
| |
| if (dev != NULL) |
| { |
| dev->dmatx.nbytes = dev->dmatx.length; |
| uart_xmitchars_done(dev); |
| } |
| |
| /* Post on semaphore to allow new transfers and share the resource. |
| * Disable clk gate for UHCI, so RX can work properly. |
| */ |
| |
| if (status & UHCI_OUT_TOTAL_EOF_INT_ENA_M) |
| { |
| if (uhci == 0) |
| { |
| nxsem_post(&g_dma0_sem); |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, DPORT_UHCI0_CLK_EN, 0); |
| } |
| # ifdef USE_DMA1 |
| else |
| { |
| nxsem_post(&g_dma1_sem); |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, DPORT_UHCI1_CLK_EN, 0); |
| } |
| # endif |
| } |
| else |
| { |
| dmaerr("Error with the output descriptor in DMA 0\n"); |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: dma_config |
| * |
| * Description: |
| * Configure the UHCI peripheral. |
| * |
| * Parameters: |
| * dma_chan - DMA instance. |
| * |
| ****************************************************************************/ |
| |
| static void dma_config(uint8_t dma_chan) |
| { |
| /* Enable peripheral CLK and RST module */ |
| |
| if (dma_chan == 0) |
| { |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_UHCI0_CLK_EN); |
| modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_UHCI0_RST, DPORT_UHCI0_RST); |
| modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_UHCI0_RST, 0); |
| } |
| else |
| { |
| modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_UHCI1_CLK_EN); |
| modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_UHCI1_RST, DPORT_UHCI1_RST); |
| modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_UHCI1_RST, 0); |
| } |
| |
| /* Configure registers */ |
| |
| putreg32(UHCI_CLK_EN_M, UHCI_CONF0_REG(dma_chan)); |
| |
| putreg32(0, UHCI_CONF1_REG(dma_chan)); |
| putreg32(UHCI_CHECK_OWNER | (100 << UHCI_DMA_INFIFO_FULL_THRS_S), |
| UHCI_CONF1_REG(dma_chan)); |
| |
| putreg32(0, UHCI_HUNG_CONF_REG(dma_chan)); |
| |
| modifyreg32(UHCI_CONF0_REG(dma_chan), UHCI_IN_RST, UHCI_IN_RST); |
| modifyreg32(UHCI_CONF0_REG(dma_chan), UHCI_IN_RST, 0); |
| modifyreg32(UHCI_CONF0_REG(dma_chan), UHCI_OUT_RST, UHCI_OUT_RST); |
| modifyreg32(UHCI_CONF0_REG(dma_chan), UHCI_OUT_RST, 0); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: esp32_interrupt |
| * |
| * Description: |
| * This is the common UART interrupt handler. It will be invoked when an |
| * interrupt is received on the 'irq'. It should call uart_xmitchars or |
| * uart_recvchars to perform the appropriate data transfers. The |
| * interrupt handling logic must be able to map the 'arg' to the |
| * appropriate uart_dev_s structure in order to call these functions. |
| * |
| ****************************************************************************/ |
| |
| static int esp32_interrupt(int cpuint, void *context, void *arg) |
| { |
| struct uart_dev_s *dev = (struct uart_dev_s *)arg; |
| struct esp32_dev_s *priv; |
| uint32_t regval; |
| uint32_t status; |
| uint32_t enabled; |
| unsigned int nfifo; |
| int passes; |
| bool handled; |
| |
| DEBUGASSERT(dev != NULL && dev->priv != NULL); |
| priv = (struct esp32_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; |
| priv->status = getreg32(UART_INT_RAW_REG(priv->config->id)); |
| status = getreg32(UART_STATUS_REG(priv->config->id)); |
| enabled = getreg32(UART_INT_ENA_REG(priv->config->id)); |
| |
| /* Clear pending interrupts */ |
| |
| regval = (UART_RXFIFO_FULL_INT_CLR | UART_FRM_ERR_INT_CLR | |
| UART_RXFIFO_TOUT_INT_CLR | UART_TX_DONE_INT_CLR | |
| UART_TXFIFO_EMPTY_INT_CLR | UART_TX_BRK_IDLE_DONE_INT_CLR); |
| putreg32(regval, UART_INT_CLR_REG(priv->config->id)); |
| |
| #ifdef HAVE_RS485 |
| if ((enabled & UART_TX_BRK_IDLE_DONE_INT_ENA) != 0 && |
| (status & UART_TX_DONE_INT_ST) != 0) |
| { |
| /* If all bytes were transmitted, then we can disable the RS485 |
| * transmit (TX/nTX) pin. |
| */ |
| |
| nfifo = REG_MASK(status, UART_TXFIFO_CNT); |
| if (nfifo == 0) |
| { |
| esp32_gpiowrite(priv->config->rs485_dir_gpio, |
| !priv->config->rs485_dir_polarity); |
| } |
| } |
| #endif |
| |
| /* Are Rx interrupts enabled? The upper layer may hold off Rx input |
| * by disabling the Rx interrupts if there is no place to saved the |
| * data, possibly resulting in an overrun error. |
| */ |
| |
| if ((enabled & (UART_RXFIFO_FULL_INT_ENA | |
| UART_RXFIFO_TOUT_INT_ENA)) != 0) |
| { |
| /* Is there any data waiting in the Rx FIFO? */ |
| |
| nfifo = esp32_get_rx_fifo_len(priv); |
| if (nfifo > 0) |
| { |
| /* Received data in the RXFIFO! ... Process incoming bytes */ |
| |
| uart_recvchars(dev); |
| handled = true; |
| } |
| } |
| |
| /* Are Tx interrupts enabled? The upper layer will disable Tx |
| * interrupts when it has nothing to send. |
| */ |
| |
| if ((enabled & (UART_TX_DONE_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA)) |
| != 0) |
| { |
| nfifo = REG_MASK(status, UART_TXFIFO_CNT); |
| if (nfifo < 0x7f) |
| { |
| /* The TXFIFO is not full ... process outgoing bytes */ |
| |
| uart_xmitchars(dev); |
| handled = true; |
| } |
| } |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_ioctl |
| * |
| * Description: |
| * All ioctl calls will be routed through this method |
| * |
| ****************************************************************************/ |
| |
| static int esp32_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 uart_dev_s *dev = inode->i_private; |
| #endif |
| int ret = OK; |
| |
| switch (cmd) |
| { |
| #ifdef CONFIG_SERIAL_TIOCSERGSTRUCT |
| case TIOCSERGSTRUCT: |
| { |
| struct esp32_dev_s *user = (struct esp32_dev_s *)arg; |
| if (!user) |
| { |
| ret = -EINVAL; |
| } |
| else |
| { |
| memcpy(user, dev, sizeof(struct esp32_dev_s)); |
| } |
| } |
| break; |
| #endif |
| |
| #ifdef CONFIG_SERIAL_TERMIOS |
| case TCGETS: |
| { |
| struct termios *termiosp = (struct termios *)arg; |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| |
| if (!termiosp) |
| { |
| ret = -EINVAL; |
| break; |
| } |
| |
| /* 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 */ |
| |
| # ifdef CONFIG_SERIAL_OFLOWCONTROL |
| termiosp->c_cflag |= (priv->oflow) ? CCTS_OFLOW : 0; |
| # endif |
| # ifdef CONFIG_SERIAL_IFLOWCONTROL |
| termiosp->c_cflag |= (priv->iflow) ? CRTS_IFLOW : 0; |
| # endif |
| /* Return baud */ |
| |
| cfsetispeed(termiosp, priv->baud); |
| |
| /* 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 esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| uint32_t baud; |
| uint32_t intena; |
| uint8_t parity; |
| uint8_t nbits; |
| bool stop2; |
| # ifdef CONFIG_SERIAL_IFLOWCONTROL |
| bool iflow; |
| # endif |
| # ifdef CONFIG_SERIAL_OFLOWCONTROL |
| bool oflow; |
| # 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 */ |
| |
| # ifdef CONFIG_SERIAL_IFLOWCONTROL |
| iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0; |
| # endif |
| # ifdef CONFIG_SERIAL_OFLOWCONTROL |
| oflow = (termiosp->c_cflag & CCTS_OFLOW) != 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; |
| # ifdef CONFIG_SERIAL_IFLOWCONTROL |
| priv->iflow = iflow; |
| # endif |
| # ifdef CONFIG_SERIAL_OFLOWCONTROL |
| priv->oflow = oflow; |
| # endif |
| /* effect the changes immediately - note that we do not |
| * implement TCSADRAIN / TCSAFLUSH |
| */ |
| |
| esp32_disableallints(priv, &intena); |
| ret = esp32_setup(dev); |
| |
| /* Restore the interrupt state */ |
| |
| esp32_restoreuartint(priv, intena); |
| } |
| } |
| break; |
| #endif /* CONFIG_SERIAL_TERMIOS */ |
| |
| default: |
| ret = -ENOTTY; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_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 esp32_receive(struct uart_dev_s *dev, unsigned int *status) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| uint32_t rx_fifo; |
| |
| /* Return the error information in the saved status */ |
| |
| *status = (unsigned int)priv->status; |
| priv->status = 0; |
| |
| /* Then return the actual received byte */ |
| |
| rx_fifo = getreg32(DR_UART_FIFO_REG(priv->config->id)); |
| |
| return (int)REG_MASK(rx_fifo, UART_RXFIFO_RD_BYTE); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_rxint |
| * |
| * Description: |
| * Call to enable or disable RXRDY interrupts |
| * |
| ****************************************************************************/ |
| |
| static void esp32_rxint(struct uart_dev_s *dev, bool enable) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| irqstate_t flags; |
| int regval; |
| |
| flags = spin_lock_irqsave(&priv->lock); |
| |
| if (enable) |
| { |
| /* Receive an interrupt when their is anything in the Rx data register |
| * (or an Rx timeout occurs). |
| */ |
| |
| #ifndef CONFIG_SUPPRESS_SERIAL_INTS |
| regval = getreg32(UART_INT_ENA_REG(priv->config->id)); |
| regval |= (UART_RXFIFO_FULL_INT_ENA | UART_FRM_ERR_INT_ENA | |
| UART_RXFIFO_TOUT_INT_ENA); |
| putreg32(regval, UART_INT_ENA_REG(priv->config->id)); |
| #endif |
| } |
| else |
| { |
| /* Disable the RX interrupts */ |
| |
| regval = getreg32(UART_INT_ENA_REG(priv->config->id)); |
| regval &= ~(UART_RXFIFO_FULL_INT_ENA | UART_FRM_ERR_INT_ENA | |
| UART_RXFIFO_TOUT_INT_ENA); |
| putreg32(regval, UART_INT_ENA_REG(priv->config->id)); |
| } |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_rxavailable |
| * |
| * Description: |
| * Return true if the receive holding register is not empty |
| * |
| ****************************************************************************/ |
| |
| static bool esp32_rxavailable(struct uart_dev_s *dev) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| return esp32_get_rx_fifo_len(priv) > 0; |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_send |
| * |
| * Description: |
| * This method will send one byte on the UART. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_send(struct uart_dev_s *dev, int ch) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| |
| #ifdef HAVE_RS485 |
| if (priv->config->rs485_dir_gpio != 0) |
| { |
| esp32_gpiowrite(priv->config->rs485_dir_gpio, |
| priv->config->rs485_dir_polarity); |
| } |
| #endif |
| |
| putreg32((uint32_t)ch, AHB_UART_FIFO_REG(priv->config->id)); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_txint |
| * |
| * Description: |
| * Call to enable or disable TX interrupts |
| * |
| ****************************************************************************/ |
| |
| static void esp32_txint(struct uart_dev_s *dev, bool enable) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| irqstate_t flags; |
| #ifdef CONFIG_SERIAL_TXDMA |
| if (priv->txdma == false) |
| { |
| #endif |
| flags = spin_lock_irqsave(&priv->lock); |
| |
| if (enable) |
| { |
| /* After all bytes physically transmitted in the RS485 bus |
| * the TX_BRK_IDLE will indicate we can disable the TX pin. |
| */ |
| |
| #ifdef HAVE_RS485 |
| if (priv->config->rs485_dir_gpio != 0) |
| { |
| modifyreg32(UART_INT_ENA_REG(priv->config->id), |
| 0, UART_TX_BRK_IDLE_DONE_INT_ENA); |
| } |
| #endif |
| |
| /* Set to receive an interrupt when the TX holding register |
| * is empty. |
| */ |
| |
| #ifndef CONFIG_SUPPRESS_SERIAL_INTS |
| modifyreg32(UART_INT_ENA_REG(priv->config->id), |
| 0, (UART_TX_DONE_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA)); |
| |
| #else |
| /* Fake a TX interrupt here by just calling uart_xmitchars() with |
| * interrupts disabled (note this may recurse). |
| */ |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| uart_xmitchars(dev); |
| #endif |
| } |
| else |
| { |
| /* Disable the TX interrupt */ |
| |
| modifyreg32(UART_INT_ENA_REG(priv->config->id), |
| (UART_TX_DONE_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA), 0); |
| } |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| #ifdef CONFIG_SERIAL_TXDMA |
| } |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_txready |
| * |
| * Description: |
| * Return true if the transmit holding register is empty (TXRDY) |
| * |
| ****************************************************************************/ |
| |
| static bool esp32_txready(struct uart_dev_s *dev) |
| { |
| uint32_t txcnt; |
| uint32_t reg; |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| |
| reg = getreg32(UART_STATUS_REG(priv->config->id)); |
| txcnt = REG_MASK(reg, UART_TXFIFO_CNT); |
| |
| return (txcnt < (UART_TX_FIFO_SIZE - 1)); |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_txempty |
| * |
| * Description: |
| * Return true if the transmit holding and shift registers are empty |
| * |
| ****************************************************************************/ |
| |
| static bool esp32_txempty(struct uart_dev_s *dev) |
| { |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| |
| return ((getreg32(UART_STATUS_REG(priv->config->id)) |
| & UART_TXFIFO_CNT_M) == 0); |
| } |
| |
| #ifndef CONFIG_SUPPRESS_UART_CONFIG |
| /**************************************************************************** |
| * Name: esp32_config_pins |
| * |
| * Description: |
| * Performs the pin configuration. |
| * |
| * Parameters: |
| * priv - Pointer to the serial driver struct. |
| * |
| ****************************************************************************/ |
| |
| static void esp32_config_pins(struct esp32_dev_s *priv) |
| { |
| /* Configure UART pins |
| * |
| * Internal signals can be output to multiple GPIO pads. |
| * But only one GPIO pad can connect with input signal |
| */ |
| |
| /* Keep TX pin in high level to avoid "?" trash character |
| * This "?" is the Unicode replacement character (U+FFFD) |
| */ |
| |
| esp32_gpiowrite(priv->config->txpin, true); |
| esp32_configgpio(priv->config->txpin, OUTPUT_FUNCTION_3); |
| esp32_gpio_matrix_out(priv->config->txpin, priv->config->txsig, 0, 0); |
| |
| esp32_configgpio(priv->config->rxpin, INPUT_FUNCTION_3); |
| esp32_gpio_matrix_in(priv->config->rxpin, priv->config->rxsig, 0); |
| |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| if (priv->iflow) |
| { |
| esp32_configgpio(priv->config->rtspin, OUTPUT_FUNCTION_3); |
| esp32_gpio_matrix_out(priv->config->rtspin, priv->config->rtssig, |
| 0, 0); |
| } |
| |
| #endif |
| #ifdef CONFIG_SERIAL_OFLOWCONTROL |
| if (priv->oflow) |
| { |
| esp32_configgpio(priv->config->ctspin, INPUT_FUNCTION_3); |
| esp32_gpio_matrix_in(priv->config->ctspin, priv->config->ctssig, 0); |
| } |
| #endif |
| |
| #ifdef HAVE_RS485 |
| if (priv->config->rs485_dir_gpio != 0) |
| { |
| esp32_configgpio(priv->config->rs485_dir_gpio, OUTPUT_FUNCTION_3); |
| esp32_gpio_matrix_out(priv->config->rs485_dir_gpio, |
| SIG_GPIO_OUT_IDX, 0, 0); |
| esp32_gpiowrite(priv->config->rs485_dir_gpio, |
| !priv->config->rs485_dir_polarity); |
| } |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: esp32_rxflowcontrol |
| * |
| * Description: |
| * Called when upper half RX buffer is full (or exceeds configured |
| * watermark levels if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined). |
| * Return true if UART activated RX flow control to block more incoming |
| * data. |
| * NOTE: ESP32 has a hardware RX FIFO threshold mechanism to control RTS |
| * line and to stop receiving data. This is very similar to the concept |
| * behind upper watermark level. The hardware threshold is used here |
| * to control the RTS line. When setting the threshold to zero, RTS will |
| * immediately be asserted. If nbuffered = 0 or the lower watermark is |
| * crossed and the serial driver decides to disable RX flow control, the |
| * threshold will be changed to UART_RX_FLOW_THRHD_VALUE, which is almost |
| * half the HW RX FIFO capacity. It keeps some space to keep the data |
| * received after the RTS is asserted, but before the sender stops. |
| * |
| * Input Parameters: |
| * dev - UART device instance |
| * nbuffered - the number of characters currently buffered |
| * (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is |
| * not defined the value will be 0 for an empty buffer or the |
| * defined buffer size for a full buffer) |
| * upper - true indicates the upper watermark was crossed where |
| * false indicates the lower watermark has been crossed |
| * |
| * Returned Value: |
| * true if RX flow control activated. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| static bool esp32_rxflowcontrol(struct uart_dev_s *dev, |
| unsigned int nbuffered, bool upper) |
| { |
| bool ret = false; |
| struct esp32_dev_s *priv = (struct esp32_dev_s *)dev->priv; |
| |
| if (priv->iflow) |
| { |
| if (nbuffered == 0 || upper == false) |
| { |
| uint32_t regval; |
| |
| /* Empty buffer, RTS should be de-asserted and logic in above |
| * layers should re-enable RX interrupt. |
| */ |
| |
| regval = VALUE_TO_FIELD(UART_RX_FLOW_THRHD_VALUE, |
| UART_RX_FLOW_THRHD); |
| modifyreg32(UART_CONF1_REG(priv->config->id), 0, regval); |
| esp32_rxint(dev, true); |
| ret = false; |
| } |
| else |
| { |
| /* If the RX buffer is not zero and watermarks are not enabled, |
| * then this function is called to announce RX buffer is full. |
| * The first thing it should do is to immediately assert RTS. |
| */ |
| |
| modifyreg32(UART_CONF1_REG(priv->config->id), UART_RX_FLOW_THRHD_M, |
| 0); |
| |
| /* Software RX FIFO is full, so besides asserting RTS, it's |
| * necessary to disable RX interrupts to prevent remaining bytes |
| * (that arrive after asserting RTS) to be pushed to the |
| * SW RX FIFO. |
| */ |
| |
| esp32_rxint(dev, false); |
| ret = true; |
| } |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: esp32_lowsetup |
| * |
| * Description: |
| * Performs the pin configuration for all UARTs. |
| * This functions is intended to be called in the __start function. |
| * |
| ****************************************************************************/ |
| |
| void esp32_lowsetup(void) |
| { |
| esp32_config_pins(TTYS0_DEV.priv); |
| #ifdef TTYS1_DEV |
| esp32_config_pins(TTYS1_DEV.priv); |
| #endif |
| #ifdef TTYS2_DEV |
| esp32_config_pins(TTYS2_DEV.priv); |
| #endif |
| } |
| |
| #endif /* CONFIG_SUPPRESS_UART_CONFIG */ |
| /**************************************************************************** |
| * Name: xtensa_early_serial_initialize |
| * |
| * Description: |
| * Performs the low level UART initialization early in debug so that the |
| * serial console will be available during boot up. This must be called |
| * before xtensa_serialinit. |
| * |
| ****************************************************************************/ |
| |
| #ifdef USE_EARLYSERIALINIT |
| void xtensa_earlyserialinit(void) |
| { |
| /* NOTE: All GPIO configuration for the UARTs was performed in |
| * esp32_lowsetup |
| */ |
| |
| /* Disable all UARTS */ |
| |
| esp32_disableallints(TTYS0_DEV.priv, NULL); |
| #ifdef TTYS1_DEV |
| esp32_disableallints(TTYS1_DEV.priv, NULL); |
| #endif |
| #ifdef TTYS2_DEV |
| esp32_disableallints(TTYS2_DEV.priv, NULL); |
| #endif |
| |
| /* Configuration whichever one is the console */ |
| |
| #ifdef HAVE_SERIAL_CONSOLE |
| CONSOLE_DEV.isconsole = true; |
| esp32_setup(&CONSOLE_DEV); |
| #endif |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: xtensa_serial_initialize |
| * |
| * Description: |
| * Register serial console and serial ports. This assumes |
| * that xtensa_earlyserialinit was called previously. |
| * |
| ****************************************************************************/ |
| |
| void xtensa_serialinit(void) |
| { |
| /* Register the console */ |
| |
| #ifdef HAVE_SERIAL_CONSOLE |
| uart_register("/dev/console", &CONSOLE_DEV); |
| #endif |
| |
| /* Register all UARTs */ |
| |
| uart_register("/dev/ttyS0", &TTYS0_DEV); |
| #ifdef TTYS1_DEV |
| uart_register("/dev/ttyS1", &TTYS1_DEV); |
| #endif |
| #ifdef TTYS2_DEV |
| uart_register("/dev/ttyS2", &TTYS2_DEV); |
| #endif |
| |
| /* DMA related */ |
| |
| #ifdef CONFIG_SERIAL_TXDMA |
| #ifdef USE_DMA0 |
| dma_config(0); |
| dma_attach(0); |
| #endif |
| #ifdef USE_DMA1 |
| dma_config(1); |
| dma_attach(1); |
| #endif |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: up_putc |
| * |
| * Description: |
| * Provide priority, low-level access to support OS debug writes |
| * |
| ****************************************************************************/ |
| |
| void up_putc(int ch) |
| { |
| #ifdef HAVE_SERIAL_CONSOLE |
| uint32_t intena; |
| |
| esp32_disableallints(CONSOLE_DEV.priv, &intena); |
| |
| while (!esp32_txready(&CONSOLE_DEV)); |
| esp32_send(&CONSOLE_DEV, ch); |
| |
| esp32_restoreuartint(CONSOLE_DEV.priv, intena); |
| #endif |
| } |
| #endif /* USE_SERIALDRIVER */ |