| /*************************************************************************** |
| * drivers/serial/uart_pl011.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/bits.h> |
| #include <nuttx/spinlock.h> |
| #include <nuttx/init.h> |
| #include <nuttx/fs/ioctl.h> |
| #include <nuttx/semaphore.h> |
| #include <nuttx/serial/serial.h> |
| #include <nuttx/serial/uart_pl011.h> |
| |
| #ifdef CONFIG_UART_PL011 |
| |
| /*************************************************************************** |
| * Pre-processor Definitions |
| ***************************************************************************/ |
| |
| /* Which UART with be tty0/console and which tty1-4? The console will |
| * always be ttyS0. If there is no console then will use the lowest |
| * numbered UART. |
| */ |
| |
| /* First pick the console and ttys0. This could be any of UART1-5 */ |
| |
| #if defined(CONFIG_UART0_SERIAL_CONSOLE) && defined(CONFIG_UART0_PL011) |
| # define HAVE_PL011_CONSOLE 1 |
| #elif defined(CONFIG_UART1_SERIAL_CONSOLE) && defined(CONFIG_UART1_PL011) |
| # define HAVE_PL011_CONSOLE 1 |
| #elif defined(CONFIG_UART2_SERIAL_CONSOLE) && defined(CONFIG_UART2_PL011) |
| # define HAVE_PL011_CONSOLE 1 |
| #elif defined(CONFIG_UART3_SERIAL_CONSOLE) && defined(CONFIG_UART3_PL011) |
| # define HAVE_PL011_CONSOLE 1 |
| #else |
| # undef HAVE_PL011_CONSOLE 1 |
| #endif |
| |
| #define PL011_BIT_MASK(x, y) (((2 << (x)) - 1) << (y)) |
| |
| /* PL011 Uart Flags Register */ |
| #define PL011_FR_CTS BIT(0) /* clear to send - inverted */ |
| #define PL011_FR_DSR BIT(1) /* data set ready - inverted |
| */ |
| #define PL011_FR_DCD BIT(2) /* data carrier detect - |
| * inverted */ |
| #define PL011_FR_BUSY BIT(3) /* busy transmitting data */ |
| #define PL011_FR_RXFE BIT(4) /* receive FIFO empty */ |
| #define PL011_FR_TXFF BIT(5) /* transmit FIFO full */ |
| #define PL011_FR_RXFF BIT(6) /* receive FIFO full */ |
| #define PL011_FR_TXFE BIT(7) /* transmit FIFO empty */ |
| #define PL011_FR_RI BIT(8) /* ring indicator - inverted */ |
| |
| /* PL011 Integer baud rate register */ |
| #define PL011_IBRD_BAUD_DIVINT_MASK 0xff /* 16 bits of divider */ |
| |
| /* PL011 Fractional baud rate register */ |
| #define PL011_FBRD_BAUD_DIVFRAC 0x3f |
| #define PL011_FBRD_WIDTH 6u |
| |
| /* PL011 Receive status register / error clear register */ |
| #define PL011_RSR_ECR_FE BIT(0) /* framing error */ |
| #define PL011_RSR_ECR_PE BIT(1) /* parity error */ |
| #define PL011_RSR_ECR_BE BIT(2) /* break error */ |
| #define PL011_RSR_ECR_OE BIT(3) /* overrun error */ |
| |
| #define PL011_RSR_ERROR_MASK (PL011_RSR_ECR_FE | PL011_RSR_ECR_PE | \ |
| PL011_RSR_ECR_BE | PL011_RSR_ECR_OE) |
| |
| /* PL011 Line Control Register */ |
| #define PL011_LCRH_BRK BIT(0) /* send break */ |
| #define PL011_LCRH_PEN BIT(1) /* enable parity */ |
| #define PL011_LCRH_EPS BIT(2) /* select even parity */ |
| #define PL011_LCRH_STP2 BIT(3) /* select two stop bits */ |
| #define PL011_LCRH_FEN BIT(4) /* enable FIFOs */ |
| #define PL011_LCRH_WLEN_SHIFT 5 /* word length */ |
| #define PL011_LCRH_WLEN_WIDTH 2 |
| #define PL011_LCRH_SPS BIT(7) /* stick parity bit */ |
| |
| #define PL011_LCRH_WLEN_SIZE(x) ((x) - 5) |
| |
| #define PL011_LCRH_FORMAT_MASK (PL011_LCRH_PEN | PL011_LCRH_EPS | \ |
| PL011_LCRH_SPS | \ |
| PL011_BIT_MASK(PL011_LCRH_WLEN_WIDTH, \ |
| PL011_LCRH_WLEN_SHIFT)) |
| |
| #define PL011_LCRH_PARTIY_EVEN (PL011_LCRH_PEN | PL011_LCRH_EPS) |
| #define PL011_LCRH_PARITY_ODD (PL011_LCRH_PEN) |
| #define PL011_LCRH_PARITY_NONE (0) |
| |
| /* PL011 Control Register */ |
| #define PL011_CR_UARTEN BIT(0) /* enable uart operations */ |
| #define PL011_CR_SIREN BIT(1) /* enable IrDA SIR */ |
| #define PL011_CR_SIRLP BIT(2) /* IrDA SIR low power mode */ |
| #define PL011_CR_LBE BIT(7) /* loop back enable */ |
| #define PL011_CR_TXE BIT(8) /* transmit enable */ |
| #define PL011_CR_RXE BIT(9) /* receive enable */ |
| #define PL011_CR_DTR BIT(10) /* data transmit ready */ |
| #define PL011_CR_RTS BIT(11) /* request to send */ |
| #define PL011_CR_Out1 BIT(12) |
| #define PL011_CR_Out2 BIT(13) |
| #define PL011_CR_RTSEn BIT(14) /* RTS hw flow control enable |
| */ |
| #define PL011_CR_CTSEn BIT(15) /* CTS hw flow control enable |
| */ |
| |
| /* PL011 Interrupt Fifo Level Select Register */ |
| #define PL011_IFLS_TXIFLSEL_SHIFT 0 /* bits 2:0 */ |
| #define PL011_IFLS_TXIFLSEL_WIDTH 3 |
| #define PL011_IFLS_RXIFLSEL_SHIFT 3 /* bits 5:3 */ |
| #define PL011_IFLS_RXIFLSEL_WIDTH 3 |
| |
| /* PL011 Interrupt Mask Set/Clear Register */ |
| #define PL011_IMSC_RIMIM BIT(0) /* RTR modem interrupt mask */ |
| #define PL011_IMSC_CTSMIM BIT(1) /* CTS modem interrupt mask */ |
| #define PL011_IMSC_DCDMIM BIT(2) /* DCD modem interrupt mask */ |
| #define PL011_IMSC_DSRMIM BIT(3) /* DSR modem interrupt mask */ |
| #define PL011_IMSC_RXIM BIT(4) /* receive interrupt mask */ |
| #define PL011_IMSC_TXIM BIT(5) /* transmit interrupt mask */ |
| #define PL011_IMSC_RTIM BIT(6) /* receive timeout interrupt |
| * mask */ |
| #define PL011_IMSC_FEIM BIT(7) /* framing error interrupt |
| * mask */ |
| #define PL011_IMSC_PEIM BIT(8) /* parity error interrupt mask |
| */ |
| #define PL011_IMSC_BEIM BIT(9) /* break error interrupt mask |
| */ |
| #define PL011_IMSC_OEIM BIT(10) /* overrun error interrupt |
| * mask */ |
| |
| #define PL011_IMSC_ERROR_MASK (PL011_IMSC_FEIM | \ |
| PL011_IMSC_PEIM | PL011_IMSC_BEIM | \ |
| PL011_IMSC_OEIM) |
| |
| #define PL011_IMSC_MASK_ALL (PL011_IMSC_OEIM | PL011_IMSC_BEIM | \ |
| PL011_IMSC_PEIM | PL011_IMSC_FEIM | \ |
| PL011_IMSC_RIMIM | \ |
| PL011_IMSC_CTSMIM | \ |
| PL011_IMSC_DCDMIM | \ |
| PL011_IMSC_DSRMIM | \ |
| PL011_IMSC_RXIM | PL011_IMSC_TXIM | \ |
| PL011_IMSC_RTIM) |
| |
| /*************************************************************************** |
| * Private Types |
| ***************************************************************************/ |
| |
| /* UART PL011 register map structure */ |
| |
| struct pl011_regs |
| { |
| uint32_t dr; /* data register */ |
| union |
| { |
| uint32_t rsr; |
| uint32_t ecr; |
| }; |
| |
| uint32_t reserved_0[4]; |
| uint32_t fr; /* flags register */ |
| uint32_t reserved_1; |
| uint32_t ilpr; |
| uint32_t ibrd; |
| uint32_t fbrd; |
| uint32_t lcr_h; |
| uint32_t cr; |
| uint32_t ifls; |
| uint32_t imsc; |
| uint32_t ris; |
| uint32_t mis; |
| uint32_t icr; |
| uint32_t dmacr; |
| }; |
| |
| struct pl011_config |
| { |
| FAR volatile struct pl011_regs *uart; |
| uint32_t sys_clk_freq; |
| }; |
| |
| /* Device data structure */ |
| |
| struct pl011_data |
| { |
| uint32_t baud_rate; |
| bool sbsa; |
| }; |
| |
| struct pl011_uart_port_s |
| { |
| struct pl011_data data; |
| struct pl011_config config; |
| unsigned int irq_num; |
| }; |
| |
| static int pl011_setup(FAR struct uart_dev_s *dev); |
| static void pl011_shutdown(FAR struct uart_dev_s *dev); |
| static int pl011_attach(FAR struct uart_dev_s *dev); |
| static void pl011_detach(FAR struct uart_dev_s *dev); |
| static int pl011_ioctl(FAR struct file *filep, int cmd, unsigned long arg); |
| static int pl011_receive(FAR struct uart_dev_s *dev, |
| FAR unsigned int *status); |
| static void pl011_rxint(FAR struct uart_dev_s *dev, bool enable); |
| static bool pl011_rxavailable(FAR struct uart_dev_s *dev); |
| static void pl011_send(FAR struct uart_dev_s *dev, int ch); |
| static void pl011_txint(FAR struct uart_dev_s *dev, bool enable); |
| static bool pl011_txready(FAR struct uart_dev_s *dev); |
| static bool pl011_txempty(FAR struct uart_dev_s *dev); |
| |
| /*************************************************************************** |
| * Private Data |
| ***************************************************************************/ |
| |
| /* Serial driver UART operations */ |
| |
| static const struct uart_ops_s g_uart_ops = |
| { |
| .setup = pl011_setup, |
| .shutdown = pl011_shutdown, |
| .attach = pl011_attach, |
| .detach = pl011_detach, |
| .ioctl = pl011_ioctl, |
| .receive = pl011_receive, |
| .rxint = pl011_rxint, |
| .rxavailable = pl011_rxavailable, |
| #ifdef CONFIG_SERIAL_IFLOWCONTROL |
| .rxflowcontrol = NULL, |
| #endif |
| .send = pl011_send, |
| .txint = pl011_txint, |
| .txready = pl011_txready, |
| .txempty = pl011_txempty, |
| }; |
| |
| /* I/O buffers */ |
| |
| #ifdef CONFIG_UART0_PL011 |
| static char g_uart0rxbuffer[CONFIG_UART0_RXBUFSIZE]; |
| static char g_uart0txbuffer[CONFIG_UART0_TXBUFSIZE]; |
| #endif |
| #ifdef CONFIG_UART1_PL011 |
| static char g_uart1rxbuffer[CONFIG_UART1_RXBUFSIZE]; |
| static char g_uart1txbuffer[CONFIG_UART1_TXBUFSIZE]; |
| #endif |
| #ifdef CONFIG_UART2_PL011 |
| static char g_uart2rxbuffer[CONFIG_UART2_RXBUFSIZE]; |
| static char g_uart2txbuffer[CONFIG_UART2_TXBUFSIZE]; |
| #endif |
| #ifdef CONFIG_UART3_PL011 |
| static char g_uart3rxbuffer[CONFIG_UART3_RXBUFSIZE]; |
| static char g_uart3txbuffer[CONFIG_UART3_TXBUFSIZE]; |
| #endif |
| |
| /* This describes the state of the uart0 port. */ |
| |
| #ifdef CONFIG_UART0_PL011 |
| |
| static struct pl011_uart_port_s g_uart0priv = |
| { |
| .data = |
| { |
| .baud_rate = CONFIG_UART0_BAUD, |
| .sbsa = false, |
| }, |
| |
| .config = |
| { |
| .uart = (FAR volatile struct pl011_regs *)CONFIG_UART0_BASE, |
| .sys_clk_freq = CONFIG_UART0_CLK_FREQ, |
| }, |
| |
| .irq_num = CONFIG_UART0_IRQ, |
| }; |
| |
| /* I/O buffers */ |
| |
| static struct uart_dev_s 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 /* CONFIG_UART0_PL011 */ |
| |
| /* This describes the state of the uart1 port. */ |
| |
| #ifdef CONFIG_UART1_PL011 |
| |
| static struct pl011_uart_port_s g_uart1priv = |
| { |
| .data = |
| { |
| .baud_rate = CONFIG_UART1_BAUD, |
| .sbsa = false, |
| }, |
| |
| .config = |
| { |
| .uart = (FAR volatile struct pl011_regs *)CONFIG_UART1_BASE, |
| .sys_clk_freq = CONFIG_UART1_CLK_FREQ, |
| }, |
| |
| .irq_num = CONFIG_UART1_IRQ, |
| }; |
| |
| /* I/O buffers */ |
| |
| static struct uart_dev_s g_uart1port = |
| { |
| .recv = |
| { |
| .size = CONFIG_UART1_RXBUFSIZE, |
| .buffer = g_uart1rxbuffer, |
| }, |
| |
| .xmit = |
| { |
| .size = CONFIG_UART1_TXBUFSIZE, |
| .buffer = g_uart1txbuffer, |
| }, |
| |
| .ops = &g_uart_ops, |
| .priv = &g_uart1priv, |
| }; |
| |
| #endif /* CONFIG_UART1_PL011 */ |
| |
| /* This describes the state of the uart2 port. */ |
| |
| #ifdef CONFIG_UART2_PL011 |
| |
| static struct pl011_uart_port_s g_uart2priv = |
| { |
| .data = |
| { |
| .baud_rate = CONFIG_UART2_BAUD, |
| .sbsa = false, |
| }, |
| |
| .config = |
| { |
| .uart = (FAR volatile struct pl011_regs *)CONFIG_UART2_BASE, |
| .sys_clk_freq = CONFIG_UART2_CLK_FREQ, |
| }, |
| |
| .irq_num = CONFIG_UART2_IRQ, |
| }; |
| |
| /* I/O buffers */ |
| |
| static struct uart_dev_s g_uart2port = |
| { |
| .recv = |
| { |
| .size = CONFIG_UART2_RXBUFSIZE, |
| .buffer = g_uart2rxbuffer, |
| }, |
| |
| .xmit = |
| { |
| .size = CONFIG_UART2_TXBUFSIZE, |
| .buffer = g_uart2txbuffer, |
| }, |
| |
| .ops = &g_uart_ops, |
| .priv = &g_uart2priv, |
| }; |
| |
| #endif /* CONFIG_UART2_PL011 */ |
| |
| /* This describes the state of the uart3 port. */ |
| |
| #ifdef CONFIG_UART3_PL011 |
| |
| static struct pl011_uart_port_s g_uart3priv = |
| { |
| .data = |
| { |
| .baud_rate = CONFIG_UART3_BAUD, |
| .sbsa = false, |
| }, |
| |
| .config = |
| { |
| .uart = (FAR volatile struct pl011_regs *)CONFIG_UART3_BASE, |
| .sys_clk_freq = CONFIG_UART3_CLK_FREQ, |
| }, |
| |
| .irq_num = CONFIG_UART3_IRQ, |
| }; |
| |
| /* I/O buffers */ |
| |
| static struct uart_dev_s g_uart3port = |
| { |
| .recv = |
| { |
| .size = CONFIG_UART3_RXBUFSIZE, |
| .buffer = g_uart3rxbuffer, |
| }, |
| |
| .xmit = |
| { |
| .size = CONFIG_UART3_TXBUFSIZE, |
| .buffer = g_uart3txbuffer, |
| }, |
| |
| .ops = &g_uart_ops, |
| .priv = &g_uart3priv, |
| }; |
| |
| #endif /* CONFIG_UART3_PL011 */ |
| |
| #if defined(CONFIG_UART0_SERIAL_CONSOLE) |
| # define CONSOLE_DEV g_uart0port /* UART0 is console */ |
| #elif defined(CONFIG_UART1_SERIAL_CONSOLE) |
| # define CONSOLE_DEV g_uart1port /* UART1 is console */ |
| #elif defined(CONFIG_UART2_SERIAL_CONSOLE) |
| # define CONSOLE_DEV g_uart2port /* UART2 is console */ |
| #elif defined(CONFIG_UART3_SERIAL_CONSOLE) |
| # define CONSOLE_DEV g_uart3port /* UART3 is console */ |
| #endif |
| |
| #ifdef CONFIG_UART0_PL011 |
| # define TTYS0_DEV g_uart0port |
| #endif |
| |
| #ifdef CONFIG_UART1_PL011 |
| # define TTYS1_DEV g_uart1port |
| #endif |
| |
| #ifdef CONFIG_UART2_PL011 |
| # define TTYS2_DEV g_uart2port |
| #endif |
| |
| #ifdef CONFIG_UART3_PL011 |
| # define TTYS3_DEV g_uart3port |
| #endif |
| |
| /*************************************************************************** |
| * Private Functions |
| ***************************************************************************/ |
| |
| static void pl011_enable(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| config->uart->cr |= PL011_CR_UARTEN; |
| } |
| |
| static void pl011_disable(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| config->uart->cr &= ~PL011_CR_UARTEN; |
| } |
| |
| static void pl011_enable_fifo(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| config->uart->lcr_h |= PL011_LCRH_FEN; |
| } |
| |
| static void pl011_disable_fifo(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| config->uart->lcr_h &= ~PL011_LCRH_FEN; |
| } |
| |
| static int pl011_set_baudrate(FAR const struct pl011_uart_port_s *sport, |
| uint32_t clk, uint32_t baudrate) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| /* Avoiding float calculations, bauddiv is left shifted by 6 */ |
| |
| uint64_t bauddiv = |
| (((uint64_t)clk) << PL011_FBRD_WIDTH) / (baudrate * 16U); |
| |
| /* Valid bauddiv value |
| * uart_clk (min) >= 16 x baud_rate (max) |
| * uart_clk (max) <= 16 x 65535 x baud_rate (min) |
| */ |
| |
| if ((bauddiv < (1U << PL011_FBRD_WIDTH)) || |
| (bauddiv > (65535U << PL011_FBRD_WIDTH))) |
| { |
| return -EINVAL; |
| } |
| |
| config->uart->ibrd = bauddiv >> PL011_FBRD_WIDTH; |
| config->uart->fbrd = bauddiv & ((1U << PL011_FBRD_WIDTH) - 1U); |
| |
| /* In order to internally update the contents of ibrd or fbrd, a |
| * lcr_h write must always be performed at the end |
| * ARM DDI 0183F, Pg 3-13 |
| */ |
| |
| config->uart->lcr_h = config->uart->lcr_h; |
| |
| return 0; |
| } |
| |
| static void pl011_irq_tx_enable(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| config->uart->imsc |= PL011_IMSC_TXIM; |
| } |
| |
| static void pl011_irq_tx_disable(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| config->uart->imsc &= ~PL011_IMSC_TXIM; |
| } |
| |
| static void pl011_irq_rx_enable(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| config->uart->imsc |= PL011_IMSC_RXIM | PL011_IMSC_RTIM; |
| } |
| |
| static void pl011_irq_rx_disable(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| config->uart->imsc &= ~(PL011_IMSC_RXIM | PL011_IMSC_RTIM); |
| } |
| |
| static int pl011_irq_tx_complete(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| |
| /* check for TX FIFO empty */ |
| |
| return config->uart->fr & PL011_FR_TXFE; |
| } |
| |
| static int pl011_irq_tx_ready(const struct pl011_uart_port_s *sport) |
| { |
| const struct pl011_config *config = &sport->config; |
| |
| /* check for TX FIFO not full */ |
| |
| return ((config->uart->fr & PL011_FR_TXFF) == 0); |
| } |
| |
| static int pl011_irq_rx_ready(FAR const struct pl011_uart_port_s *sport) |
| { |
| FAR const struct pl011_config *config = &sport->config; |
| FAR const struct pl011_data *data = &sport->data; |
| |
| if (!data->sbsa && !(config->uart->cr & PL011_CR_RXE)) |
| { |
| return false; |
| } |
| |
| return (config->uart->imsc & PL011_IMSC_RXIM) && |
| (!(config->uart->fr & PL011_FR_RXFE)); |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_txready |
| * |
| * Description: |
| * Return true if the tranmsit fifo is not full |
| * |
| ***************************************************************************/ |
| |
| static bool pl011_txready(FAR struct uart_dev_s *dev) |
| { |
| FAR struct pl011_uart_port_s *sport = dev->priv; |
| FAR const struct pl011_config *config = &sport->config; |
| FAR struct pl011_data *data = &sport->data; |
| |
| if (!data->sbsa && !(config->uart->cr & PL011_CR_TXE)) |
| { |
| return false; |
| } |
| |
| return (config->uart->imsc & PL011_IMSC_TXIM) && |
| pl011_irq_tx_ready(sport); |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_txempty |
| * |
| * Description: |
| * Return true if the transmit fifo is empty |
| * |
| ***************************************************************************/ |
| |
| static bool pl011_txempty(FAR struct uart_dev_s *dev) |
| { |
| FAR struct pl011_uart_port_s *sport = dev->priv; |
| |
| return pl011_irq_tx_complete(sport); |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_send |
| * |
| * Description: |
| * This method will send one byte on the UART |
| * |
| ***************************************************************************/ |
| |
| static void pl011_send(FAR struct uart_dev_s *dev, int ch) |
| { |
| FAR struct pl011_uart_port_s *sport = dev->priv; |
| FAR const struct pl011_config *config = &sport->config; |
| |
| config->uart->dr = ch; |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_rxavailable |
| * |
| * Description: |
| * Return true if the receive fifo is not empty |
| * |
| ***************************************************************************/ |
| |
| static bool pl011_rxavailable(FAR struct uart_dev_s *dev) |
| { |
| FAR struct pl011_uart_port_s *sport = dev->priv; |
| FAR const struct pl011_config *config = &sport->config; |
| FAR struct pl011_data *data = &sport->data; |
| |
| if (!data->sbsa && |
| (!(config->uart->cr & PL011_CR_UARTEN) || |
| !(config->uart->cr & PL011_CR_RXE))) |
| { |
| return false; |
| } |
| |
| return (config->uart->fr & PL011_FR_RXFE) == 0U; |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_rxint |
| * |
| * Description: |
| * Call to enable or disable RX interrupts |
| * |
| ***************************************************************************/ |
| |
| static void pl011_rxint(FAR struct uart_dev_s *dev, bool enable) |
| { |
| FAR struct pl011_uart_port_s *sport = dev->priv; |
| |
| if (enable) |
| { |
| pl011_irq_rx_enable(sport); |
| } |
| else |
| { |
| pl011_irq_rx_disable(sport); |
| } |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_txint |
| * |
| * Description: |
| * Call to enable or disable TX interrupts |
| * |
| ***************************************************************************/ |
| |
| static void pl011_txint(FAR struct uart_dev_s *dev, bool enable) |
| { |
| FAR struct pl011_uart_port_s *sport = dev->priv; |
| irqstate_t flags; |
| |
| flags = enter_critical_section(); |
| |
| if (enable) |
| { |
| pl011_irq_tx_enable(sport); |
| |
| /* Fake a TX interrupt here by just calling uart_xmitchars() with |
| * interrupts disabled (note this may recurse). |
| */ |
| |
| uart_xmitchars(dev); |
| } |
| else |
| { |
| pl011_irq_tx_disable(sport); |
| } |
| |
| leave_critical_section(flags); |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_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 pl011_receive(FAR struct uart_dev_s *dev, |
| FAR unsigned int *status) |
| { |
| FAR struct pl011_uart_port_s *sport = dev->priv; |
| FAR const struct pl011_config *config = &sport->config; |
| unsigned int rx; |
| |
| rx = config->uart->dr; |
| |
| *status = rx & 0xf00; |
| |
| return rx & 0xff; |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_ioctl |
| * |
| * Description: |
| * All ioctl calls will be routed through this method |
| * for current qemu configure, |
| * |
| ***************************************************************************/ |
| |
| static int pl011_ioctl(FAR struct file *filep, int cmd, unsigned long arg) |
| { |
| int ret = OK; |
| UNUSED(filep); |
| UNUSED(arg); |
| |
| switch (cmd) |
| { |
| case TIOCSBRK: /* BSD compatibility: Turn break on, unconditionally */ |
| case TIOCCBRK: /* BSD compatibility: Turn break off, unconditionally */ |
| default: |
| { |
| ret = -ENOTTY; |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_irq_handler (and front-ends) |
| * |
| * Description: |
| * This is the common UART interrupt handler. It should cal |
| * uart_transmitchars or uart_receivechar to perform the appropriate data |
| * transfers. |
| * |
| ***************************************************************************/ |
| |
| static int pl011_irq_handler(int irq, FAR void *context, FAR void *arg) |
| { |
| FAR struct uart_dev_s *dev = arg; |
| FAR struct pl011_uart_port_s *sport; |
| UNUSED(irq); |
| UNUSED(context); |
| |
| DEBUGASSERT(dev != NULL && dev->priv != NULL); |
| sport = dev->priv; |
| |
| if (pl011_irq_rx_ready(sport)) |
| { |
| uart_recvchars(dev); |
| } |
| |
| if (pl011_txready(dev)) |
| { |
| uart_xmitchars(dev); |
| } |
| |
| return OK; |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_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 pl011_detach(FAR struct uart_dev_s *dev) |
| { |
| FAR struct pl011_uart_port_s *sport = dev->priv; |
| |
| up_disable_irq(sport->irq_num); |
| irq_detach(sport->irq_num); |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_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 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 pl011_attach(FAR struct uart_dev_s *dev) |
| { |
| FAR struct pl011_uart_port_s *sport; |
| FAR struct pl011_data *data; |
| int ret; |
| |
| sport = dev->priv; |
| data = &sport->data; |
| |
| ret = irq_attach(sport->irq_num, pl011_irq_handler, dev); |
| |
| if (ret == OK) |
| { |
| up_enable_irq(sport->irq_num); |
| } |
| else |
| { |
| sinfo("error ret=%d\n", ret); |
| } |
| |
| if (!data->sbsa) |
| { |
| pl011_enable(sport); |
| } |
| |
| return ret; |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_shutdown |
| * |
| * Description: |
| * Disable the UART. This method is called when the serial |
| * port is closed |
| * |
| ***************************************************************************/ |
| |
| static void pl011_shutdown(FAR struct uart_dev_s *dev) |
| { |
| #ifdef CONFIG_UART_PL011_PLATFORMIF |
| struct pl011_uart_port_s *sport = (struct pl011_uart_port_s *)dev->priv; |
| const struct pl011_config *config = &sport->config; |
| |
| /* If needed, implement platform specific process such as disabling pl011 |
| * to reduce power consumption. |
| */ |
| |
| pl011_platform_shutdown((uint32_t)config->uart); |
| #else |
| UNUSED(dev); |
| sinfo("%s: call unexpected\n", __func__); |
| #endif |
| } |
| |
| static int pl011_setup(FAR struct uart_dev_s *dev) |
| { |
| FAR struct pl011_uart_port_s *sport = dev->priv; |
| FAR const struct pl011_config *config = &sport->config; |
| FAR struct pl011_data *data = &sport->data; |
| int ret; |
| uint32_t lcrh; |
| irqstate_t i_flags; |
| |
| #ifdef CONFIG_UART_PL011_PLATFORMIF |
| /* If needed, implement platform specific process such as enabling pl011 |
| * to reduce power consumption. |
| */ |
| |
| pl011_platform_setup((uint32_t)config->uart); |
| #endif |
| |
| i_flags = up_irq_save(); |
| |
| /* If working in SBSA mode, we assume that UART is already configured, |
| * or does not require configuration at all (if UART is emulated by |
| * virtualization software). |
| */ |
| |
| if (!data->sbsa) |
| { |
| /* disable the uart */ |
| |
| pl011_disable(sport); |
| pl011_disable_fifo(sport); |
| |
| /* Set baud rate */ |
| |
| ret = pl011_set_baudrate(sport, config->sys_clk_freq, |
| data->baud_rate); |
| if (ret != 0) |
| { |
| up_irq_restore(i_flags); |
| return ret; |
| } |
| |
| /* Setting the default character format */ |
| |
| lcrh = config->uart->lcr_h & ~(PL011_LCRH_FORMAT_MASK); |
| lcrh &= ~(BIT(0) | BIT(7)); |
| lcrh |= PL011_LCRH_WLEN_SIZE(8) << PL011_LCRH_WLEN_SHIFT; |
| config->uart->lcr_h = lcrh; |
| |
| /* Enabling the FIFOs */ |
| |
| pl011_enable_fifo(sport); |
| } |
| |
| /* initialize all IRQs as masked */ |
| |
| config->uart->imsc = 0U; |
| config->uart->icr = PL011_IMSC_MASK_ALL; |
| |
| if (!data->sbsa) |
| { |
| config->uart->dmacr = 0U; |
| config->uart->cr &= ~(BIT(14) | BIT(15) | BIT(1)); |
| config->uart->cr |= PL011_CR_RXE | PL011_CR_TXE; |
| } |
| |
| up_irq_restore(i_flags); |
| |
| return 0; |
| } |
| |
| /*************************************************************************** |
| * Public Functions |
| ***************************************************************************/ |
| |
| /*************************************************************************** |
| * Name: pl011_earlyserialinit |
| * |
| * Description: |
| * see nuttx/serial/uart_pl011.h |
| * |
| ***************************************************************************/ |
| |
| void pl011_earlyserialinit(void) |
| { |
| /* Enable the console UART. The other UARTs will be initialized if and |
| * when they are first opened. |
| */ |
| #ifdef CONSOLE_DEV |
| CONSOLE_DEV.isconsole = true; |
| pl011_setup(&CONSOLE_DEV); |
| #endif |
| } |
| |
| /*************************************************************************** |
| * Name: pl011_serialinit |
| * |
| * Description: |
| * Register serial console and serial ports. This assumes that |
| * pl011_earlyserialinit was called previously. |
| * |
| ***************************************************************************/ |
| |
| void pl011_serialinit(void) |
| { |
| #ifdef CONSOLE_DEV |
| uart_register("/dev/console", &CONSOLE_DEV); |
| #endif |
| #ifdef TTYS0_DEV |
| uart_register("/dev/ttyS0", &TTYS0_DEV); |
| #endif |
| #ifdef TTYS1_DEV |
| uart_register("/dev/ttyS1", &TTYS1_DEV); |
| #endif |
| #ifdef TTYS2_DEV |
| uart_register("/dev/ttyS2", &TTYS2_DEV); |
| #endif |
| #ifdef TTYS3_DEV |
| uart_register("/dev/ttyS3", &TTYS3_DEV); |
| #endif |
| } |
| |
| /*************************************************************************** |
| * Name: up_putc |
| * |
| * Description: |
| * Provide priority, low-level access to support OS debug writes |
| * |
| ***************************************************************************/ |
| |
| #ifdef HAVE_PL011_CONSOLE |
| int up_putc(int ch) |
| { |
| FAR struct uart_dev_s *dev = &CONSOLE_DEV; |
| |
| /* Check for LF */ |
| |
| if (ch == '\n') |
| { |
| /* Add CR */ |
| |
| pl011_send(dev, '\r'); |
| } |
| |
| pl011_send(dev, ch); |
| |
| return ch; |
| } |
| #endif |
| |
| #endif /* CONFIG_UART_PL011 */ |