[PATCH 2/2] tty: serial: add driver for the LRW UART
LiuQingtao
qtliu at mail.ustc.edu.cn
Fri Feb 13 01:22:52 PST 2026
From: Wenhong Liu <liu.wenhong35 at zte.com.cn>
This commit introduces a serial driver for the LRW UART controller
Key features implemented:
- Support for FIFO mode (16-byte depth)
- Baud rate configuration
- Standard asynchronous communication formats:
* Data bits: 5, 6, 7, 8, 9 bits
* Parity: odd, even, fixed, none
* Stop bits: 1 or 2 bits
- Hardware flow control (RTS/CTS)
- Multiple interrupt reporting mechanisms
Signed-off-by: Wenhong Liu <liu.wenhong35 at zte.com.cn>
Signed-off-by: Qingtao Liu <liu.qingtao2 at zte.com.cn>
Cc: linux-serial at vger.kernel.org
Cc: linux-riscv at lists.infradead.org
---
MAINTAINERS | 3 +
drivers/tty/serial/Kconfig | 33 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/lrw_uart.c | 2822 ++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 3 +
5 files changed, 2862 insertions(+)
create mode 100644 drivers/tty/serial/lrw_uart.c
diff --git a/MAINTAINERS b/MAINTAINERS
index ad6acbe24544..a97fbd205f75 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15041,6 +15041,9 @@ R: Qingtao Liu <liu.qingtao2 at zte.com.cn>
L: linux-serial at vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/serial/lrw,lrw-uart.yaml
+F: drivers/tty/serial/Kconfig
+F: drivers/tty/serial/Makefile
+F: drivers/tty/serial/lrw_uart.c
LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
M: Sathya Prakash <sathya.prakash at broadcom.com>
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f86775cfdcc9..a8f2d750c5b4 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1619,6 +1619,39 @@ config SERIAL_ESP32_ACM
snippet may be used:
earlycon=esp32s3acm,mmio32,0x60038000
+config SERIAL_LRW_UART
+ tristate "LRW UART support"
+ select SERIAL_CORE
+ help
+ This option enables support for the LRW Universal Asynchronous
+ Receiver/Transmitter (UART) serial controller.
+
+ Select this option if you are building a kernel for a device that
+ contains a LRW UART IP block.
+
+ This driver can be built as a module; if so, the module will be
+ called lrw_uart.
+
+ If you are using a system with an LRW UART controller, say Y or M here.
+ If unsure, say N.
+
+config SERIAL_LRW_UART_CONSOLE
+ bool "Console on LRW UART"
+ depends on SERIAL_LRW_UART=y
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Say Y here if you wish to use an LRW UART as the system
+ console (the system console is the device which receives all kernel
+ messages and warnings and which allows logins in single user mode).
+
+ Even if you say Y here, the currently visible framebuffer console
+ (/dev/tty0) will still be used as the system console by default, but
+ you can alter that using a kernel command line option such as
+ "console=ttyLRW0". (Try "man bootparam" or see the documentation of
+ your boot loader (lilo or loadlin) about how to pass options to the
+ kernel at boot time.)
+
endmenu
config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index a2ccbc508ec5..0f694c4a4e22 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -94,6 +94,7 @@ obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
obj-$(CONFIG_SERIAL_ZS) += zs.o
+obj-$(CONFIG_SERIAL_LRW_UART) += lrw_uart.o
# GPIOLIB helpers for modem control lines
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o
diff --git a/drivers/tty/serial/lrw_uart.c b/drivers/tty/serial/lrw_uart.c
new file mode 100644
index 000000000000..e2399bef203f
--- /dev/null
+++ b/drivers/tty/serial/lrw_uart.c
@@ -0,0 +1,2822 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Serial Port driver for LRW
+ *
+ * Copyright (c) 2025, LRW CORPORATION. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/platform_device.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/sizes.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+
+#define UART_NR 14
+
+#define ISR_PASS_LIMIT 256
+
+#define LRW_UART_NAME "lrw-uart"
+
+#define LRW_UART_TTY_PREFIX "ttyLRW"
+
+/* LRW_UART_TX_FIFO_DEPTH: depth of the TX FIFO (in bytes) */
+#define LRW_UART_TX_FIFO_DEPTH 16
+
+/* LRW_UART_RX_FIFO_DEPTH: depth of the RX FIFO (in bytes) */
+#define LRW_UART_RX_FIFO_DEPTH 16
+
+/* LRW UART register offsets */
+#define UARTDR 0x00 /* Data register */
+#define UARTRSR 0x04 /* Receive status register */
+#define UARTECR 0x04 /* Error clear register */
+#define UARTSC 0x08 /* Special character register */
+#define UARTMDR 0x0C /* RS485 Muti-drop register */
+#define UARTTAT 0x10 /* RS485 turn-around time register */
+#define UARTFCR 0x14 /* FIFO control register */
+#define UARTFR 0x18 /* Flag register */
+#define UARTIND 0x1C /* Integer baud rate register */
+#define UARTFD 0x20 /* Fractional baud rate register */
+#define UARTBSR 0x24 /* Baud sample rate register */
+#define UARTFRCR 0x28 /* Frame control register */
+#define UARTMCFG 0x2C /* config register */
+#define UARTMCR 0x30 /* Modem control register */
+#define UARTIRCR 0x34 /* IrDA mode control register */
+#define UARTIMSC 0x38 /* Interrupt mask set/clear register */
+#define UARTRIS 0x3C /* Raw interrupt status register */
+#define UARTMIS 0x40 /* Masked interrupt states register */
+#define UARTICR 0x44 /* Interrupt clear register */
+#define UARTFCCR 0x48 /* Flow control register */
+#define UARTRVS 0x58 /* Version register */
+
+#define UARTDR_OE BIT(11)
+#define UARTDR_BE BIT(10)
+#define UARTDR_PE BIT(9)
+#define UARTDR_FE BIT(8)
+
+#define UARTRSR_OE BIT(3)
+#define UARTRSR_BE BIT(2)
+#define UARTRSR_PE BIT(1)
+#define UARTRSR_FE BIT(0)
+
+#define UARTFCR_RXFTRS GENMASK(7, 5)
+#define UARTFCR_RXFTRS_RX1_8 FIELD_PREP_CONST(UARTFCR_RXFTRS, 0)
+#define UARTFCR_RXFTRS_RX2_8 FIELD_PREP_CONST(UARTFCR_RXFTRS, 1)
+#define UARTFCR_RXFTRS_RX4_8 FIELD_PREP_CONST(UARTFCR_RXFTRS, 2)
+#define UARTFCR_RXFTRS_RX6_8 FIELD_PREP_CONST(UARTFCR_RXFTRS, 3)
+#define UARTFCR_RXFTRS_RX7_8 FIELD_PREP_CONST(UARTFCR_RXFTRS, 4)
+#define UARTFCR_TXFTRS GENMASK(4, 2)
+#define UARTFCR_TXFTRS_TX1_8 FIELD_PREP_CONST(UARTFCR_TXFTRS, 0)
+#define UARTFCR_TXFTRS_TX2_8 FIELD_PREP_CONST(UARTFCR_TXFTRS, 1)
+#define UARTFCR_TXFTRS_TX4_8 FIELD_PREP_CONST(UARTFCR_TXFTRS, 2)
+#define UARTFCR_TXFTRS_TX6_8 FIELD_PREP_CONST(UARTFCR_TXFTRS, 3)
+#define UARTFCR_TXFTRS_TX7_8 FIELD_PREP_CONST(UARTFCR_TXFTRS, 4)
+#define UARTFCR_FEN BIT(0)
+
+#define UARTFR_RI BIT(8)
+#define UARTFR_TXFE BIT(7)
+#define UARTFR_RXFF BIT(6)
+#define UARTFR_TXFF (1 << 5) /* used in ASM */
+#define UARTFR_RXFE BIT(4)
+#define UARTFR_BUSY (1 << 3) /* used in ASM */
+#define UARTFR_DCD BIT(2)
+#define UARTFR_DSR BIT(1)
+#define UARTFR_CTS BIT(0)
+#define UARTFR_TMSK (UARTFR_TXFF + UARTFR_BUSY)
+
+#define UARTFRCR_STP2 BIT(5)
+#define UARTFRCR_SPS BIT(4)
+#define UARTFRCR_EOP BIT(3)
+#define UARTFRCR_PEN BIT(2)
+#define UARTFRCR_WLEN_8 0x3
+#define UARTFRCR_WLEN_7 0x2
+#define UARTFRCR_WLEN_6 0x1
+#define UARTFRCR_WLEN_5 0x0
+
+#define UARTMCFG_LBE BIT(7) /* loopback enable */
+#define UARTMCFG_RXE BIT(3) /* receive enable */
+#define UARTMCFG_TXE BIT(2) /* transmit enable */
+#define UARTMCFG_BRK BIT(1) /* send break */
+#define UARTMCFG_UARTEN BIT(0) /* UART enable */
+
+#define UARTMCR_OUT2 BIT(3) /* OUT2 */
+#define UARTMCR_OUT1 BIT(2) /* OUT1 */
+#define UARTMCR_RTS BIT(1) /* RTS */
+#define UARTMCR_DTR BIT(0) /* DTR */
+
+#define UARTIMSC_OEIM BIT(10) /* overrun error interrupt mask */
+#define UARTIMSC_BEIM BIT(9) /* break error interrupt mask */
+#define UARTIMSC_PEIM BIT(8) /* parity error interrupt mask */
+#define UARTIMSC_FEIM BIT(7) /* framing error interrupt mask */
+#define UARTIMSC_RTIM BIT(6) /* receive timeout interrupt mask */
+#define UARTIMSC_TXIM BIT(5) /* transmit interrupt mask */
+#define UARTIMSC_RXIM BIT(4) /* receive interrupt mask */
+#define UARTIMSC_DSRMIM BIT(3) /* DSR interrupt mask */
+#define UARTIMSC_DCDMIM BIT(2) /* DCD interrupt mask */
+#define UARTIMSC_CTSMIM BIT(1) /* CTS interrupt mask */
+#define UARTIMSC_RIMIM BIT(0) /* RI interrupt mask */
+
+#define UARTICR_OEIC BIT(10) /* overrun error interrupt clear */
+#define UARTICR_BEIC BIT(9) /* break error interrupt clear */
+#define UARTICR_PEIC BIT(8) /* parity error interrupt clear */
+#define UARTICR_FEIC BIT(7) /* framing error interrupt clear */
+#define UARTICR_RTIC BIT(6) /* receive timeout interrupt clear */
+#define UARTICR_TXIC BIT(5) /* transmit interrupt clear */
+#define UARTICR_RXIC BIT(4) /* receive interrupt clear */
+#define UARTICR_DSRMIC BIT(3) /* DSR interrupt clear */
+#define UARTICR_DCDMIC BIT(2) /* DCD interrupt clear */
+#define UARTICR_CTSMIC BIT(1) /* CTS interrupt clear */
+#define UARTICR_RIMIC BIT(0) /* RI interrupt clear */
+
+#define UARTFCCR_CTSEN BIT(5) /* CTS hardware flow control */
+#define UARTFCCR_RTSEN BIT(4) /* RTS hardware flow control */
+#define UARTFCCR_DMAONERR BIT(2) /* disable dma on error */
+#define UARTFCCR_TXDMAE BIT(1) /* enable transmit dma */
+#define UARTFCCR_RXDMAE BIT(0) /* enable receive dma */
+
+#define UARTRSR_ANY (UARTRSR_OE | UARTRSR_BE | UARTRSR_PE | UARTRSR_FE)
+#define UARTFR_MODEM_ANY (UARTFR_DCD | UARTFR_DSR | UARTFR_CTS)
+
+#define UART_DR_ERROR (UARTDR_OE | UARTDR_BE | UARTDR_PE | UARTDR_FE)
+#define UART_DUMMY_DR_RX BIT(16)
+
+enum {
+ REG_DR,
+ REG_FCR,
+ REG_FR,
+ REG_IND,
+ REG_FD,
+ REG_BSR,
+ REG_FRCR,
+ REG_MCFG,
+ REG_MCR,
+ REG_IMSC,
+ REG_RIS,
+ REG_MIS,
+ REG_ICR,
+ REG_FCCR,
+
+ /* The size of the array - must be last */
+ REG_ARRAY_SIZE,
+};
+
+static u16 lrw_uart_std_offsets[REG_ARRAY_SIZE] = {
+ [REG_DR] = UARTDR,
+ [REG_FCR] = UARTFCR,
+ [REG_FR] = UARTFR,
+ [REG_IND] = UARTIND,
+ [REG_FD] = UARTFD,
+ [REG_BSR] = UARTBSR,
+ [REG_FRCR] = UARTFRCR,
+ [REG_MCFG] = UARTMCFG,
+ [REG_MCR] = UARTMCR,
+ [REG_IMSC] = UARTIMSC,
+ [REG_RIS] = UARTRIS,
+ [REG_MIS] = UARTMIS,
+ [REG_ICR] = UARTICR,
+ [REG_FCCR] = UARTFCCR,
+};
+
+/* There is by now at least one vendor with differing details, so handle it */
+struct vendor_data {
+ const u16 *reg_offset;
+ unsigned int fcr;
+ unsigned int fr_busy;
+ unsigned int fr_dsr;
+ unsigned int fr_cts;
+ unsigned int fr_ri;
+ unsigned int inv_fr;
+ bool access_32b;
+ bool oversampling;
+ bool dma_threshold;
+ bool cts_event_workaround;
+ bool always_enabled;
+ bool fixed_options;
+};
+
+static struct vendor_data vendor_lrw = {
+ .reg_offset = lrw_uart_std_offsets,
+ .fcr = UARTFCR_RXFTRS_RX4_8 | UARTFCR_TXFTRS_TX4_8 | UARTFCR_FEN,
+ .fr_busy = UARTFR_BUSY,
+ .fr_dsr = UARTFR_DSR,
+ .fr_cts = UARTFR_CTS,
+ .fr_ri = UARTFR_RI,
+ .access_32b = true,
+ .oversampling = false,
+ .dma_threshold = false,
+ .cts_event_workaround = false,
+ .always_enabled = false,
+ .fixed_options = true,
+};
+
+/* Deals with DMA transactions */
+
+struct lrw_uart_dmabuf {
+ dma_addr_t dma;
+ size_t len;
+ char *buf;
+};
+
+struct lrw_uart_dmarx_data {
+ struct dma_chan *chan;
+ struct completion complete;
+ bool use_buf_b;
+ struct lrw_uart_dmabuf dbuf_a;
+ struct lrw_uart_dmabuf dbuf_b;
+ dma_cookie_t cookie;
+ bool running;
+ struct timer_list timer;
+ unsigned int last_residue;
+ unsigned long last_jiffies;
+ bool auto_poll_rate;
+ unsigned int poll_rate;
+ unsigned int poll_timeout;
+};
+
+struct lrw_uart_dmatx_data {
+ struct dma_chan *chan;
+ dma_addr_t dma;
+ size_t len;
+ char *buf;
+ bool queued;
+};
+
+struct lrw_uart_data {
+ bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
+ void *dma_rx_param;
+ void *dma_tx_param;
+ bool dma_rx_poll_enable;
+ unsigned int dma_rx_poll_rate;
+ unsigned int dma_rx_poll_timeout;
+ void (*init)(void);
+ void (*exit)(void);
+};
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct lrw_uart_port {
+ struct uart_port port;
+ const u16 *reg_offset;
+ struct clk *clk;
+ const struct vendor_data *vendor;
+ unsigned int im; /* interrupt mask */
+ unsigned int old_status;
+ unsigned int fifosize; /* vendor-specific */
+ unsigned int fixed_baud; /* vendor-set fixed baud rate */
+ char type[12];
+ bool rs485_tx_started;
+ unsigned int rs485_tx_drain_interval; /* usecs */
+#ifdef CONFIG_DMA_ENGINE
+ /* DMA stuff */
+ unsigned int dmacr; /* dma control reg */
+ bool using_tx_dma;
+ bool using_rx_dma;
+ struct lrw_uart_dmarx_data dmarx;
+ struct lrw_uart_dmatx_data dmatx;
+ bool dma_probed;
+#endif
+};
+
+static unsigned int lrw_uart_tx_empty(struct uart_port *port);
+
+static unsigned int lrw_uart_reg_to_offset(const struct lrw_uart_port *sup,
+ unsigned int reg)
+{
+ return sup->reg_offset[reg];
+}
+
+static unsigned int lrw_uart_read(const struct lrw_uart_port *sup,
+ unsigned int reg)
+{
+ void __iomem *addr = sup->port.membase + lrw_uart_reg_to_offset(sup, reg);
+
+ return (sup->port.iotype == UPIO_MEM32) ?
+ readl_relaxed(addr) : readw_relaxed(addr);
+}
+
+static void lrw_uart_write(unsigned int val, const struct lrw_uart_port *sup,
+ unsigned int reg)
+{
+ void __iomem *addr = sup->port.membase + lrw_uart_reg_to_offset(sup, reg);
+
+ if (sup->port.iotype == UPIO_MEM32)
+ writel_relaxed(val, addr);
+ else
+ writew_relaxed(val, addr);
+}
+
+/*
+ * Reads up to 256 characters from the FIFO or until it's empty and
+ * inserts them into the TTY layer. Returns the number of characters
+ * read from the FIFO.
+ */
+static int lrw_uart_fifo_to_tty(struct lrw_uart_port *sup)
+{
+ unsigned int ch, fifotaken;
+ int sysrq;
+ u16 status;
+ u8 flag;
+
+ for (fifotaken = 0; fifotaken != 256; fifotaken++) {
+ status = lrw_uart_read(sup, REG_FR);
+ if (status & UARTFR_RXFE)
+ break;
+
+ /* Take chars from the FIFO and update status */
+ ch = lrw_uart_read(sup, REG_DR) | UART_DUMMY_DR_RX;
+ flag = TTY_NORMAL;
+ sup->port.icount.rx++;
+
+ if (unlikely(ch & UART_DR_ERROR)) {
+ if (ch & UARTDR_BE) {
+ ch &= ~(UARTDR_FE | UARTDR_PE);
+ sup->port.icount.brk++;
+ if (uart_handle_break(&sup->port))
+ continue;
+ } else if (ch & UARTDR_PE) {
+ sup->port.icount.parity++;
+ } else if (ch & UARTDR_FE) {
+ sup->port.icount.frame++;
+ }
+ if (ch & UARTDR_OE)
+ sup->port.icount.overrun++;
+
+ ch &= sup->port.read_status_mask;
+
+ if (ch & UARTDR_BE)
+ flag = TTY_BREAK;
+ else if (ch & UARTDR_PE)
+ flag = TTY_PARITY;
+ else if (ch & UARTDR_FE)
+ flag = TTY_FRAME;
+ }
+
+ sysrq = uart_prepare_sysrq_char(&sup->port, ch & 255);
+ if (!sysrq)
+ uart_insert_char(&sup->port, ch, UARTDR_OE, ch, flag);
+ }
+
+ return fifotaken;
+}
+
+/*
+ * All the DMA operation mode stuff goes inside this ifdef.
+ * This assumes that you have a generic DMA device interface,
+ * no custom DMA interfaces are supported.
+ */
+#ifdef CONFIG_DMA_ENGINE
+
+#define LRW_UART_DMA_BUFFER_SIZE PAGE_SIZE
+
+static int lrw_uart_dmabuf_init(struct dma_chan *chan, struct lrw_uart_dmabuf *db,
+ enum dma_data_direction dir)
+{
+ db->buf = dma_alloc_coherent(chan->device->dev, LRW_UART_DMA_BUFFER_SIZE,
+ &db->dma, GFP_KERNEL);
+ if (!db->buf)
+ return -ENOMEM;
+ db->len = LRW_UART_DMA_BUFFER_SIZE;
+
+ return 0;
+}
+
+static void lrw_uart_dmabuf_free(struct dma_chan *chan, struct lrw_uart_dmabuf *db,
+ enum dma_data_direction dir)
+{
+ if (db->buf) {
+ dma_free_coherent(chan->device->dev,
+ LRW_UART_DMA_BUFFER_SIZE, db->buf, db->dma);
+ }
+}
+
+static void lrw_uart_dma_probe(struct lrw_uart_port *sup)
+{
+ /* DMA is the sole user of the platform data right now */
+ struct lrw_uart_data *plat = dev_get_platdata(sup->port.dev);
+ struct device *dev = sup->port.dev;
+ struct dma_slave_config tx_conf = {
+ .dst_addr = sup->port.mapbase +
+ lrw_uart_reg_to_offset(sup, REG_DR),
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .direction = DMA_MEM_TO_DEV,
+ .dst_maxburst = sup->fifosize >> 1,
+ .device_fc = false,
+ };
+ struct dma_chan *chan;
+ dma_cap_mask_t mask;
+
+ sup->dma_probed = true;
+ chan = dma_request_chan(dev, "tx");
+ if (IS_ERR(chan)) {
+ if (PTR_ERR(chan) == -EPROBE_DEFER) {
+ sup->dma_probed = false;
+ return;
+ }
+
+ /* We need platform data */
+ if (!plat || !plat->dma_filter) {
+ dev_dbg(sup->port.dev, "no DMA platform data\n");
+ return;
+ }
+
+ /* Try to acquire a generic DMA engine slave TX channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ chan = dma_request_channel(mask, plat->dma_filter,
+ plat->dma_tx_param);
+ if (!chan) {
+ dev_err(sup->port.dev, "no TX DMA channel!\n");
+ return;
+ }
+ }
+
+ dmaengine_slave_config(chan, &tx_conf);
+ sup->dmatx.chan = chan;
+
+ dev_info(sup->port.dev, "DMA channel TX %s\n",
+ dma_chan_name(sup->dmatx.chan));
+
+ /* Optionally make use of an RX channel as well */
+ chan = dma_request_chan(dev, "rx");
+
+ if (IS_ERR(chan) && plat && plat->dma_rx_param) {
+ chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param);
+
+ if (!chan) {
+ dev_err(sup->port.dev, "no RX DMA channel!\n");
+ return;
+ }
+ }
+
+ if (!IS_ERR(chan)) {
+ struct dma_slave_config rx_conf = {
+ .src_addr = sup->port.mapbase +
+ lrw_uart_reg_to_offset(sup, REG_DR),
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .direction = DMA_DEV_TO_MEM,
+ .src_maxburst = sup->fifosize >> 2,
+ .device_fc = false,
+ };
+ struct dma_slave_caps caps;
+
+ /*
+ * Some DMA controllers provide information on their capabilities.
+ * If the controller does, check for suitable residue processing
+ * otherwise assime all is well.
+ */
+ if (dma_get_slave_caps(chan, &caps) == 0) {
+ if (caps.residue_granularity ==
+ DMA_RESIDUE_GRANULARITY_DESCRIPTOR) {
+ dma_release_channel(chan);
+ dev_info(sup->port.dev,
+ "RX DMA disabled - no residue processing\n");
+ return;
+ }
+ }
+ dmaengine_slave_config(chan, &rx_conf);
+ sup->dmarx.chan = chan;
+
+ sup->dmarx.auto_poll_rate = false;
+ if (plat && plat->dma_rx_poll_enable) {
+ /* Set poll rate if specified. */
+ if (plat->dma_rx_poll_rate) {
+ sup->dmarx.auto_poll_rate = false;
+ sup->dmarx.poll_rate = plat->dma_rx_poll_rate;
+ } else {
+ /*
+ * 100 ms defaults to poll rate if not
+ * specified. This will be adjusted with
+ * the baud rate at set_termios.
+ */
+ sup->dmarx.auto_poll_rate = true;
+ sup->dmarx.poll_rate = 100;
+ }
+ /* 3 secs defaults poll_timeout if not specified. */
+ if (plat->dma_rx_poll_timeout)
+ sup->dmarx.poll_timeout =
+ plat->dma_rx_poll_timeout;
+ else
+ sup->dmarx.poll_timeout = 3000;
+ } else if (!plat && dev->of_node) {
+ sup->dmarx.auto_poll_rate =
+ of_property_read_bool(dev->of_node, "auto-poll");
+ if (sup->dmarx.auto_poll_rate) {
+ u32 x;
+
+ if (of_property_read_u32(dev->of_node, "poll-rate-ms", &x) == 0)
+ sup->dmarx.poll_rate = x;
+ else
+ sup->dmarx.poll_rate = 100;
+ if (of_property_read_u32(dev->of_node, "poll-timeout-ms", &x) == 0)
+ sup->dmarx.poll_timeout = x;
+ else
+ sup->dmarx.poll_timeout = 3000;
+ }
+ }
+ dev_info(sup->port.dev, "DMA channel RX %s\n",
+ dma_chan_name(sup->dmarx.chan));
+ }
+}
+
+static void lrw_uart_dma_remove(struct lrw_uart_port *sup)
+{
+ if (sup->dmatx.chan)
+ dma_release_channel(sup->dmatx.chan);
+ if (sup->dmarx.chan)
+ dma_release_channel(sup->dmarx.chan);
+}
+
+/* Forward declare these for the refill routine */
+static int lrw_uart_dma_tx_refill(struct lrw_uart_port *sup);
+static void lrw_uart_start_tx_pio(struct lrw_uart_port *sup);
+
+/*
+ * The current DMA TX buffer has been sent.
+ * Try to queue up another DMA buffer.
+ */
+static void lrw_uart_dma_tx_callback(void *data)
+{
+ struct lrw_uart_port *sup = data;
+ struct tty_port *tport = &sup->port.state->port;
+ struct lrw_uart_dmatx_data *dmatx = &sup->dmatx;
+ unsigned long flags;
+ u16 dmacr;
+
+ uart_port_lock_irqsave(&sup->port, &flags);
+ if (sup->dmatx.queued)
+ dma_unmap_single(dmatx->chan->device->dev, dmatx->dma,
+ dmatx->len, DMA_TO_DEVICE);
+
+ dmacr = sup->dmacr;
+ sup->dmacr = dmacr & ~UARTFCCR_TXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+
+ /*
+ * If TX DMA was disabled, it means that we've stopped the DMA for
+ * some reason (eg, XOFF received, or we want to send an X-char.)
+ *
+ * Note: we need to be careful here of a potential race between DMA
+ * and the rest of the driver - if the driver disables TX DMA while
+ * a TX buffer completing, we must update the tx queued status to
+ * get further refills (hence we check dmacr).
+ */
+ if (!(dmacr & UARTFCCR_TXDMAE) || uart_tx_stopped(&sup->port) ||
+ kfifo_is_empty(&tport->xmit_fifo)) {
+ sup->dmatx.queued = false;
+ uart_port_unlock_irqrestore(&sup->port, flags);
+ return;
+ }
+
+ if (lrw_uart_dma_tx_refill(sup) <= 0)
+ /*
+ * We didn't queue a DMA buffer for some reason, but we
+ * have data pending to be sent. Re-enable the TX IRQ.
+ */
+ lrw_uart_start_tx_pio(sup);
+
+ uart_port_unlock_irqrestore(&sup->port, flags);
+}
+
+/*
+ * Try to refill the TX DMA buffer.
+ * Locking: called with port lock held and IRQs disabled.
+ * Returns:
+ * 1 if we queued up a TX DMA buffer.
+ * 0 if we didn't want to handle this by DMA
+ * <0 on error
+ */
+static int lrw_uart_dma_tx_refill(struct lrw_uart_port *sup)
+{
+ struct lrw_uart_dmatx_data *dmatx = &sup->dmatx;
+ struct dma_chan *chan = dmatx->chan;
+ struct dma_device *dma_dev = chan->device;
+ struct dma_async_tx_descriptor *desc;
+ struct tty_port *tport = &sup->port.state->port;
+ unsigned int count;
+
+ /*
+ * Try to avoid the overhead involved in using DMA if the
+ * transaction fits in the first half of the FIFO, by using
+ * the standard interrupt handling. This ensures that we
+ * issue a uart_write_wakeup() at the appropriate time.
+ */
+ count = kfifo_len(&tport->xmit_fifo);
+ if (count < (sup->fifosize >> 1)) {
+ sup->dmatx.queued = false;
+ return 0;
+ }
+
+ /*
+ * Bodge: don't send the last character by DMA, as this
+ * will prevent XON from notifying us to restart DMA.
+ */
+ count -= 1;
+
+ /* Else proceed to copy the TX chars to the DMA buffer and fire DMA */
+ if (count > LRW_UART_DMA_BUFFER_SIZE)
+ count = LRW_UART_DMA_BUFFER_SIZE;
+
+ count = kfifo_out_peek(&tport->xmit_fifo, dmatx->buf, count);
+ dmatx->len = count;
+ dmatx->dma = dma_map_single(dma_dev->dev, dmatx->buf, count,
+ DMA_TO_DEVICE);
+ if (dmatx->dma == DMA_MAPPING_ERROR) {
+ sup->dmatx.queued = false;
+ dev_dbg(sup->port.dev, "unable to map TX DMA\n");
+ return -EBUSY;
+ }
+
+ desc = dmaengine_prep_slave_single(chan, dmatx->dma, dmatx->len, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dma_unmap_single(dma_dev->dev, dmatx->dma, dmatx->len, DMA_TO_DEVICE);
+ sup->dmatx.queued = false;
+ /*
+ * If DMA cannot be used right now, we complete this
+ * transaction via IRQ and let the TTY layer retry.
+ */
+ dev_dbg(sup->port.dev, "TX DMA busy\n");
+ return -EBUSY;
+ }
+
+ /* Some data to go along to the callback */
+ desc->callback = lrw_uart_dma_tx_callback;
+ desc->callback_param = sup;
+
+ /* All errors should happen at prepare time */
+ dmaengine_submit(desc);
+
+ /* Fire the DMA transaction */
+ dma_dev->device_issue_pending(chan);
+
+ sup->dmacr |= UARTFCCR_TXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+ sup->dmatx.queued = true;
+
+ /*
+ * Now we know that DMA will fire, so advance the ring buffer
+ * with the stuff we just dispatched.
+ */
+ uart_xmit_advance(&sup->port, count);
+
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
+ uart_write_wakeup(&sup->port);
+
+ return 1;
+}
+
+/*
+ * We received a transmit interrupt without a pending X-char but with
+ * pending characters.
+ * Locking: called with port lock held and IRQs disabled.
+ * Returns:
+ * false if we want to use PIO to transmit
+ * true if we queued a DMA buffer
+ */
+static bool lrw_uart_dma_tx_irq(struct lrw_uart_port *sup)
+{
+ if (!sup->using_tx_dma)
+ return false;
+
+ /*
+ * If we already have a TX buffer queued, but received a
+ * TX interrupt, it will be because we've just sent an X-char.
+ * Ensure the TX DMA is enabled and the TX IRQ is disabled.
+ */
+ if (sup->dmatx.queued) {
+ sup->dmacr |= UARTFCCR_TXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+ sup->im &= ~UARTIMSC_TXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ return true;
+ }
+
+ /*
+ * We don't have a TX buffer queued, so try to queue one.
+ * If we successfully queued a buffer, mask the TX IRQ.
+ */
+ if (lrw_uart_dma_tx_refill(sup) > 0) {
+ sup->im &= ~UARTIMSC_TXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Stop the DMA transmit (eg, due to received XOFF).
+ * Locking: called with port lock held and IRQs disabled.
+ */
+static inline void lrw_uart_dma_tx_stop(struct lrw_uart_port *sup)
+{
+ if (sup->dmatx.queued) {
+ sup->dmacr &= ~UARTFCCR_TXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+ }
+}
+
+/*
+ * Try to start a DMA transmit, or in the case of an XON/OFF
+ * character queued for send, try to get that character out ASAP.
+ * Locking: called with port lock held and IRQs disabled.
+ * Returns:
+ * false if we want the TX IRQ to be enabled
+ * true if we have a buffer queued
+ */
+static inline bool lrw_uart_dma_tx_start(struct lrw_uart_port *sup)
+{
+ u16 dmacr;
+
+ if (!sup->using_tx_dma)
+ return false;
+
+ if (!sup->port.x_char) {
+ /* no X-char, try to push chars out in DMA mode */
+ bool ret = true;
+
+ if (!sup->dmatx.queued) {
+ if (lrw_uart_dma_tx_refill(sup) > 0) {
+ sup->im &= ~UARTIMSC_TXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ } else {
+ ret = false;
+ }
+ } else if (!(sup->dmacr & UARTFCCR_TXDMAE)) {
+ sup->dmacr |= UARTFCCR_TXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+ }
+ return ret;
+ }
+
+ /*
+ * We have an X-char to send. Disable DMA to prevent it loading
+ * the TX fifo, and then see if we can stuff it into the FIFO.
+ */
+ dmacr = sup->dmacr;
+ sup->dmacr &= ~UARTFCCR_TXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+
+ if (lrw_uart_read(sup, REG_FR) & UARTFR_TXFF) {
+ /*
+ * No space in the FIFO, so enable the transmit interrupt
+ * so we know when there is space. Note that once we've
+ * loaded the character, we should just re-enable DMA.
+ */
+ return false;
+ }
+
+ lrw_uart_write(sup->port.x_char, sup, REG_DR);
+ sup->port.icount.tx++;
+ sup->port.x_char = 0;
+
+ /* Success - restore the DMA state */
+ sup->dmacr = dmacr;
+ lrw_uart_write(dmacr, sup, REG_FCCR);
+
+ return true;
+}
+
+/*
+ * Flush the transmit buffer.
+ * Locking: called with port lock held and IRQs disabled.
+ */
+static void lrw_uart_dma_flush_buffer(struct uart_port *port)
+__releases(&sup->port.lock)
+__acquires(&sup->port.lock)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ if (!sup->using_tx_dma)
+ return;
+
+ dmaengine_terminate_async(sup->dmatx.chan);
+
+ if (sup->dmatx.queued) {
+ dma_unmap_single(sup->dmatx.chan->device->dev, sup->dmatx.dma,
+ sup->dmatx.len, DMA_TO_DEVICE);
+ sup->dmatx.queued = false;
+ sup->dmacr &= ~UARTFCCR_TXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+ }
+}
+
+static void lrw_uart_dma_rx_callback(void *data);
+
+static int lrw_uart_dma_rx_trigger_dma(struct lrw_uart_port *sup)
+{
+ struct dma_chan *rxchan = sup->dmarx.chan;
+ struct lrw_uart_dmarx_data *dmarx = &sup->dmarx;
+ struct dma_async_tx_descriptor *desc;
+ struct lrw_uart_dmabuf *dbuf;
+
+ if (!rxchan)
+ return -EIO;
+
+ /* Start the RX DMA job */
+ dbuf = sup->dmarx.use_buf_b ?
+ &sup->dmarx.dbuf_b : &sup->dmarx.dbuf_a;
+ desc = dmaengine_prep_slave_single(rxchan, dbuf->dma, dbuf->len,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ /*
+ * If the DMA engine is busy and cannot prepare a
+ * channel, no big deal, the driver will fall back
+ * to interrupt mode as a result of this error code.
+ */
+ if (!desc) {
+ sup->dmarx.running = false;
+ dmaengine_terminate_all(rxchan);
+ return -EBUSY;
+ }
+
+ /* Some data to go along to the callback */
+ desc->callback = lrw_uart_dma_rx_callback;
+ desc->callback_param = sup;
+ dmarx->cookie = dmaengine_submit(desc);
+ dma_async_issue_pending(rxchan);
+
+ sup->dmacr |= UARTFCCR_RXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+ sup->dmarx.running = true;
+
+ sup->im &= ~UARTIMSC_RXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+
+ return 0;
+}
+
+/*
+ * This is called when either the DMA job is complete, or
+ * the FIFO timeout interrupt occurred. This must be called
+ * with the port spinlock sup->port.lock held.
+ */
+static void lrw_uart_dma_rx_chars(struct lrw_uart_port *sup,
+ u32 pending, bool use_buf_b,
+ bool readfifo)
+{
+ struct tty_port *port = &sup->port.state->port;
+ struct lrw_uart_dmabuf *dbuf = use_buf_b ?
+ &sup->dmarx.dbuf_b : &sup->dmarx.dbuf_a;
+ int dma_count = 0;
+ u32 fifotaken = 0; /* only used for vdbg() */
+
+ struct lrw_uart_dmarx_data *dmarx = &sup->dmarx;
+ int dmataken = 0;
+
+ if (sup->dmarx.poll_rate) {
+ /* The data can be taken by polling */
+ dmataken = dbuf->len - dmarx->last_residue;
+ /* Recalculate the pending size */
+ if (pending >= dmataken)
+ pending -= dmataken;
+ }
+
+ /* Pick the remain data from the DMA */
+ if (pending) {
+ /*
+ * First take all chars in the DMA pipe, then look in the FIFO.
+ * Note that tty_insert_flip_buf() tries to take as many chars
+ * as it can.
+ */
+ dma_count = tty_insert_flip_string(port, dbuf->buf + dmataken, pending);
+
+ sup->port.icount.rx += dma_count;
+ if (dma_count < pending)
+ dev_warn(sup->port.dev,
+ "couldn't insert all characters (TTY is full?)\n");
+ }
+
+ /* Reset the last_residue for Rx DMA poll */
+ if (sup->dmarx.poll_rate)
+ dmarx->last_residue = dbuf->len;
+
+ /*
+ * Only continue with trying to read the FIFO if all DMA chars have
+ * been taken first.
+ */
+ if (dma_count == pending && readfifo) {
+ /* Clear any error flags */
+ lrw_uart_write(UARTICR_OEIC | UARTICR_BEIC | UARTICR_PEIC |
+ UARTICR_FEIC, sup, REG_ICR);
+
+ /*
+ * If we read all the DMA'd characters, and we had an
+ * incomplete buffer, that could be due to an rx error, or
+ * maybe we just timed out. Read any pending chars and check
+ * the error status.
+ *
+ * Error conditions will only occur in the FIFO, these will
+ * trigger an immediate interrupt and stop the DMA job, so we
+ * will always find the error in the FIFO, never in the DMA
+ * buffer.
+ */
+ fifotaken = lrw_uart_fifo_to_tty(sup);
+ }
+
+ dev_vdbg(sup->port.dev,
+ "Took %d chars from DMA buffer and %d chars from the FIFO\n",
+ dma_count, fifotaken);
+ tty_flip_buffer_push(port);
+}
+
+static void lrw_uart_dma_rx_irq(struct lrw_uart_port *sup)
+{
+ struct lrw_uart_dmarx_data *dmarx = &sup->dmarx;
+ struct dma_chan *rxchan = dmarx->chan;
+ struct lrw_uart_dmabuf *dbuf = dmarx->use_buf_b ?
+ &dmarx->dbuf_b : &dmarx->dbuf_a;
+ size_t pending;
+ struct dma_tx_state state;
+ enum dma_status dmastat;
+
+ /*
+ * Pause the transfer so we can trust the current counter,
+ * do this before we pause the LRW UART block, else we may
+ * overflow the FIFO.
+ */
+ if (dmaengine_pause(rxchan))
+ dev_err(sup->port.dev, "unable to pause DMA transfer\n");
+ dmastat = rxchan->device->device_tx_status(rxchan,
+ dmarx->cookie, &state);
+ if (dmastat != DMA_PAUSED)
+ dev_err(sup->port.dev, "unable to pause DMA transfer\n");
+
+ /* Disable RX DMA - incoming data will wait in the FIFO */
+ sup->dmacr &= ~UARTFCCR_RXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+ sup->dmarx.running = false;
+
+ pending = dbuf->len - state.residue;
+ if (WARN_ONCE(pending > LRW_UART_DMA_BUFFER_SIZE,
+ "pending %zu exceeds DMA buffer size %zu\n",
+ pending, LRW_UART_DMA_BUFFER_SIZE))
+ pending = LRW_UART_DMA_BUFFER_SIZE;
+ /* Then we terminate the transfer - we now know our residue */
+ dmaengine_terminate_all(rxchan);
+
+ /*
+ * This will take the chars we have so far and insert
+ * into the framework.
+ */
+ lrw_uart_dma_rx_chars(sup, pending, dmarx->use_buf_b, true);
+
+ /* Switch buffer & re-trigger DMA job */
+ dmarx->use_buf_b = !dmarx->use_buf_b;
+ if (lrw_uart_dma_rx_trigger_dma(sup)) {
+ dev_dbg(sup->port.dev,
+ "could not retrigger RX DMA job fall back to interrupt mode\n");
+ sup->im |= UARTIMSC_RXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ }
+}
+
+static void lrw_uart_dma_rx_callback(void *data)
+{
+ struct lrw_uart_port *sup = data;
+ struct lrw_uart_dmarx_data *dmarx = &sup->dmarx;
+ struct dma_chan *rxchan = dmarx->chan;
+ bool lastbuf = dmarx->use_buf_b;
+ struct lrw_uart_dmabuf *dbuf = dmarx->use_buf_b ?
+ &dmarx->dbuf_b : &dmarx->dbuf_a;
+ size_t pending;
+ struct dma_tx_state state;
+ int ret;
+
+ /*
+ * This completion interrupt occurs typically when the
+ * RX buffer is totally stuffed but no timeout has yet
+ * occurred. When that happens, we just want the RX
+ * routine to flush out the secondary DMA buffer while
+ * we immediately trigger the next DMA job.
+ */
+ uart_port_lock_irq(&sup->port);
+ /*
+ * Rx data can be taken by the UART interrupts during
+ * the DMA irq handler. So we check the residue here.
+ */
+ rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state);
+ pending = dbuf->len - state.residue;
+ if (WARN_ONCE(pending > LRW_UART_DMA_BUFFER_SIZE,
+ "pending %zu exceeds DMA buffer size %zu\n",
+ pending, LRW_UART_DMA_BUFFER_SIZE))
+ pending = LRW_UART_DMA_BUFFER_SIZE;
+ /* Then we terminate the transfer - we now know our residue */
+ dmaengine_terminate_all(rxchan);
+
+ sup->dmarx.running = false;
+ dmarx->use_buf_b = !lastbuf;
+ ret = lrw_uart_dma_rx_trigger_dma(sup);
+
+ lrw_uart_dma_rx_chars(sup, pending, lastbuf, false);
+ uart_unlock_and_check_sysrq(&sup->port);
+ /*
+ * Do this check after we picked the DMA chars so we don't
+ * get some IRQ immediately from RX.
+ */
+ if (ret) {
+ dev_dbg(sup->port.dev,
+ "could not retrigger RX DMA job fall back to interrupt mode\n");
+ sup->im |= UARTIMSC_RXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ }
+}
+
+/*
+ * Stop accepting received characters, when we're shutting down or
+ * suspending this port.
+ * Locking: called with port lock held and IRQs disabled.
+ */
+static inline void lrw_uart_dma_rx_stop(struct lrw_uart_port *sup)
+{
+ if (!sup->using_rx_dma)
+ return;
+
+ /* FIXME. Just disable the DMA enable */
+ sup->dmacr &= ~UARTFCCR_RXDMAE;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+}
+
+/*
+ * Timer handler for Rx DMA polling.
+ * Every polling, It checks the residue in the dma buffer and transfer
+ * data to the tty. Also, last_residue is updated for the next polling.
+ */
+static void lrw_uart_dma_rx_poll(struct timer_list *t)
+{
+ struct lrw_uart_port *sup = timer_container_of(sup, t, dmarx.timer);
+ struct tty_port *port = &sup->port.state->port;
+ struct lrw_uart_dmarx_data *dmarx = &sup->dmarx;
+ struct dma_chan *rxchan = sup->dmarx.chan;
+ unsigned long flags;
+ unsigned int dmataken = 0;
+ unsigned int size = 0;
+ struct lrw_uart_dmabuf *dbuf;
+ int dma_count;
+ struct dma_tx_state state;
+
+ dbuf = dmarx->use_buf_b ? &sup->dmarx.dbuf_b : &sup->dmarx.dbuf_a;
+ rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state);
+ if (likely(state.residue < dmarx->last_residue)) {
+ dmataken = dbuf->len - dmarx->last_residue;
+ size = dmarx->last_residue - state.residue;
+ dma_count = tty_insert_flip_string(port, dbuf->buf + dmataken,
+ size);
+ if (dma_count == size)
+ dmarx->last_residue = state.residue;
+ dmarx->last_jiffies = jiffies;
+ }
+ tty_flip_buffer_push(port);
+
+ /*
+ * If no data is received in poll_timeout, the driver will fall back
+ * to interrupt mode. We will retrigger DMA at the first interrupt.
+ */
+ if (jiffies_to_msecs(jiffies - dmarx->last_jiffies)
+ > sup->dmarx.poll_timeout) {
+ uart_port_lock_irqsave(&sup->port, &flags);
+ lrw_uart_dma_rx_stop(sup);
+ sup->im |= UARTIMSC_RXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ uart_port_unlock_irqrestore(&sup->port, flags);
+
+ sup->dmarx.running = false;
+ dmaengine_terminate_all(rxchan);
+ timer_delete(&sup->dmarx.timer);
+ } else {
+ mod_timer(&sup->dmarx.timer,
+ jiffies + msecs_to_jiffies(sup->dmarx.poll_rate));
+ }
+}
+
+static void lrw_uart_dma_startup(struct lrw_uart_port *sup)
+{
+ int ret;
+
+ if (!sup->dma_probed)
+ lrw_uart_dma_probe(sup);
+
+ if (!sup->dmatx.chan)
+ return;
+
+ sup->dmatx.buf = kmalloc(LRW_UART_DMA_BUFFER_SIZE, GFP_KERNEL | __GFP_DMA);
+ if (!sup->dmatx.buf) {
+ sup->port.fifosize = sup->fifosize;
+ return;
+ }
+
+ sup->dmatx.len = LRW_UART_DMA_BUFFER_SIZE;
+
+ /* The DMA buffer is now the FIFO the TTY subsystem can use */
+ sup->port.fifosize = LRW_UART_DMA_BUFFER_SIZE;
+ sup->using_tx_dma = true;
+
+ if (!sup->dmarx.chan)
+ goto skip_rx;
+
+ /* Allocate and map DMA RX buffers */
+ ret = lrw_uart_dmabuf_init(sup->dmarx.chan, &sup->dmarx.dbuf_a,
+ DMA_FROM_DEVICE);
+ if (ret) {
+ dev_err(sup->port.dev, "failed to init DMA %s: %d\n",
+ "RX buffer A", ret);
+ goto skip_rx;
+ }
+
+ ret = lrw_uart_dmabuf_init(sup->dmarx.chan, &sup->dmarx.dbuf_b,
+ DMA_FROM_DEVICE);
+ if (ret) {
+ dev_err(sup->port.dev, "failed to init DMA %s: %d\n",
+ "RX buffer B", ret);
+ lrw_uart_dmabuf_free(sup->dmarx.chan, &sup->dmarx.dbuf_a,
+ DMA_FROM_DEVICE);
+ goto skip_rx;
+ }
+
+ sup->using_rx_dma = true;
+
+skip_rx:
+ /* Turn on DMA error (RX/TX will be enabled on demand) */
+ sup->dmacr |= UARTFCCR_DMAONERR;
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+
+ if (sup->using_rx_dma) {
+ if (lrw_uart_dma_rx_trigger_dma(sup))
+ dev_dbg(sup->port.dev,
+ "could not trigger initial RX DMA job, fall back to interrupt mode\n");
+ if (sup->dmarx.poll_rate) {
+ timer_setup(&sup->dmarx.timer, lrw_uart_dma_rx_poll, 0);
+ mod_timer(&sup->dmarx.timer,
+ jiffies + msecs_to_jiffies(sup->dmarx.poll_rate));
+ sup->dmarx.last_residue = LRW_UART_DMA_BUFFER_SIZE;
+ sup->dmarx.last_jiffies = jiffies;
+ }
+ }
+}
+
+static void lrw_uart_dma_shutdown(struct lrw_uart_port *sup)
+{
+ if (!(sup->using_tx_dma || sup->using_rx_dma))
+ return;
+
+ /* Disable RX and TX DMA */
+ while (lrw_uart_read(sup, REG_FR) & sup->vendor->fr_busy)
+ cpu_relax();
+
+ uart_port_lock_irq(&sup->port);
+ sup->dmacr &= ~(UARTFCCR_DMAONERR | UARTFCCR_RXDMAE | UARTFCCR_TXDMAE);
+ lrw_uart_write(sup->dmacr, sup, REG_FCCR);
+ uart_port_unlock_irq(&sup->port);
+
+ if (sup->using_tx_dma) {
+ /* In theory, this should already be done by lrw_uart_dma_flush_buffer */
+ dmaengine_terminate_all(sup->dmatx.chan);
+ if (sup->dmatx.queued) {
+ dma_unmap_single(sup->dmatx.chan->device->dev,
+ sup->dmatx.dma, sup->dmatx.len,
+ DMA_TO_DEVICE);
+ sup->dmatx.queued = false;
+ }
+
+ kfree(sup->dmatx.buf);
+ sup->using_tx_dma = false;
+ }
+
+ if (sup->using_rx_dma) {
+ dmaengine_terminate_all(sup->dmarx.chan);
+ /* Clean up the RX DMA */
+ lrw_uart_dmabuf_free(sup->dmarx.chan, &sup->dmarx.dbuf_a, DMA_FROM_DEVICE);
+ lrw_uart_dmabuf_free(sup->dmarx.chan, &sup->dmarx.dbuf_b, DMA_FROM_DEVICE);
+ if (sup->dmarx.poll_rate)
+ timer_delete_sync(&sup->dmarx.timer);
+ sup->using_rx_dma = false;
+ }
+}
+
+static inline bool lrw_uart_dma_rx_available(struct lrw_uart_port *sup)
+{
+ return sup->using_rx_dma;
+}
+
+static inline bool lrw_uart_dma_rx_running(struct lrw_uart_port *sup)
+{
+ return sup->using_rx_dma && sup->dmarx.running;
+}
+
+#else
+/* Blank functions if the DMA engine is not available */
+static inline void lrw_uart_dma_remove(struct lrw_uart_port *sup)
+{
+}
+
+static inline void lrw_uart_dma_startup(struct lrw_uart_port *sup)
+{
+}
+
+static inline void lrw_uart_dma_shutdown(struct lrw_uart_port *sup)
+{
+}
+
+static inline bool lrw_uart_dma_tx_irq(struct lrw_uart_port *sup)
+{
+ return false;
+}
+
+static inline void lrw_uart_dma_tx_stop(struct lrw_uart_port *sup)
+{
+}
+
+static inline bool lrw_uart_dma_tx_start(struct lrw_uart_port *sup)
+{
+ return false;
+}
+
+static inline void lrw_uart_dma_rx_irq(struct lrw_uart_port *sup)
+{
+}
+
+static inline void lrw_uart_dma_rx_stop(struct lrw_uart_port *sup)
+{
+}
+
+static inline int lrw_uart_dma_rx_trigger_dma(struct lrw_uart_port *sup)
+{
+ return -EIO;
+}
+
+static inline bool lrw_uart_dma_rx_available(struct lrw_uart_port *sup)
+{
+ return false;
+}
+
+static inline bool lrw_uart_dma_rx_running(struct lrw_uart_port *sup)
+{
+ return false;
+}
+
+#define lrw_uart_dma_flush_buffer NULL
+#endif
+
+static void lrw_uart_rs485_tx_stop(struct lrw_uart_port *sup)
+{
+ /*
+ * To be on the safe side only time out after twice as many iterations
+ * as fifo size.
+ */
+ const int MAX_TX_DRAIN_ITERS = sup->port.fifosize * 2;
+ struct uart_port *port = &sup->port;
+ int i = 0;
+ u32 mcr, mcfg;
+
+ /* Wait until hardware tx queue is empty */
+ while (!lrw_uart_tx_empty(port)) {
+ if (i > MAX_TX_DRAIN_ITERS) {
+ dev_warn(port->dev,
+ "timeout while draining hardware tx queue\n");
+ break;
+ }
+
+ udelay(sup->rs485_tx_drain_interval);
+ i++;
+ }
+
+ if (port->rs485.delay_rts_after_send)
+ mdelay(port->rs485.delay_rts_after_send);
+
+ mcr = lrw_uart_read(sup, REG_MCR);
+
+ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ mcr &= ~UARTMCR_RTS;
+ else
+ mcr |= UARTMCR_RTS;
+
+ lrw_uart_write(mcr, sup, REG_MCR);
+
+ /* Disable the transmitter and reenable the transceiver */
+ mcfg = lrw_uart_read(sup, REG_MCFG);
+ mcfg &= ~UARTMCFG_TXE;
+ mcfg |= UARTMCFG_RXE;
+ lrw_uart_write(mcfg, sup, REG_MCFG);
+
+ sup->rs485_tx_started = false;
+}
+
+static void lrw_uart_stop_tx(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ sup->im &= ~UARTIMSC_TXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ lrw_uart_dma_tx_stop(sup);
+
+ if ((port->rs485.flags & SER_RS485_ENABLED) && sup->rs485_tx_started)
+ lrw_uart_rs485_tx_stop(sup);
+}
+
+static bool lrw_uart_tx_chars(struct lrw_uart_port *sup, bool from_irq);
+
+/* Start TX with programmed I/O only (no DMA) */
+static void lrw_uart_start_tx_pio(struct lrw_uart_port *sup)
+{
+ if (lrw_uart_tx_chars(sup, false)) {
+ sup->im |= UARTIMSC_TXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ }
+}
+
+static void lrw_uart_rs485_tx_start(struct lrw_uart_port *sup)
+{
+ struct uart_port *port = &sup->port;
+ u32 mcr, mcfg;
+
+ /* Enable transmitter */
+ mcfg = lrw_uart_read(sup, REG_MCFG);
+ mcfg |= UARTMCFG_TXE;
+
+ /* Disable receiver if half-duplex */
+ if (!(port->rs485.flags & SER_RS485_RX_DURING_TX))
+ mcfg &= ~UARTMCFG_RXE;
+
+ lrw_uart_write(mcfg, sup, REG_MCFG);
+
+ mcr = lrw_uart_read(sup, REG_MCR);
+ if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
+ mcr &= ~UARTMCR_RTS;
+ else
+ mcr |= UARTMCR_RTS;
+
+ lrw_uart_write(mcr, sup, REG_MCR);
+
+ if (port->rs485.delay_rts_before_send)
+ mdelay(port->rs485.delay_rts_before_send);
+
+ sup->rs485_tx_started = true;
+}
+
+static void lrw_uart_start_tx(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ if ((sup->port.rs485.flags & SER_RS485_ENABLED) &&
+ !sup->rs485_tx_started)
+ lrw_uart_rs485_tx_start(sup);
+
+ if (!lrw_uart_dma_tx_start(sup))
+ lrw_uart_start_tx_pio(sup);
+}
+
+static void lrw_uart_stop_rx(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ sup->im &= ~(UARTIMSC_RXIM | UARTIMSC_RTIM | UARTIMSC_FEIM |
+ UARTIMSC_PEIM | UARTIMSC_BEIM | UARTIMSC_OEIM);
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+
+ lrw_uart_dma_rx_stop(sup);
+}
+
+static void lrw_uart_throttle_rx(struct uart_port *port)
+{
+ unsigned long flags;
+
+ uart_port_lock_irqsave(port, &flags);
+ lrw_uart_stop_rx(port);
+ uart_port_unlock_irqrestore(port, flags);
+}
+
+static void lrw_uart_enable_ms(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ sup->im |= UARTIMSC_RIMIM | UARTIMSC_CTSMIM | UARTIMSC_DCDMIM | UARTIMSC_DSRMIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+}
+
+static void lrw_uart_rx_chars(struct lrw_uart_port *sup)
+__releases(&sup->port.lock)
+__acquires(&sup->port.lock)
+{
+ lrw_uart_fifo_to_tty(sup);
+
+ uart_port_unlock(&sup->port);
+ tty_flip_buffer_push(&sup->port.state->port);
+ /*
+ * If we were temporarily out of DMA mode for a while,
+ * attempt to switch back to DMA mode again.
+ */
+ if (lrw_uart_dma_rx_available(sup)) {
+ if (lrw_uart_dma_rx_trigger_dma(sup)) {
+ dev_dbg(sup->port.dev,
+ "could not trigger RX DMA job fall back to interrupt mode again\n");
+ sup->im |= UARTIMSC_RXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ } else {
+#ifdef CONFIG_DMA_ENGINE
+ /* Start Rx DMA poll */
+ if (sup->dmarx.poll_rate) {
+ sup->dmarx.last_jiffies = jiffies;
+ sup->dmarx.last_residue = LRW_UART_DMA_BUFFER_SIZE;
+ mod_timer(&sup->dmarx.timer,
+ jiffies + msecs_to_jiffies(sup->dmarx.poll_rate));
+ }
+#endif
+ }
+ }
+ uart_port_lock(&sup->port);
+}
+
+static bool lrw_uart_tx_char(struct lrw_uart_port *sup, unsigned char c,
+ bool from_irq)
+{
+ if (unlikely(!from_irq) &&
+ lrw_uart_read(sup, REG_FR) & UARTFR_TXFF)
+ return false; /* unable to transmit character */
+
+ lrw_uart_write(c, sup, REG_DR);
+ sup->port.icount.tx++;
+
+ return true;
+}
+
+/* Returns true if tx interrupts have to be (kept) enabled */
+static bool lrw_uart_tx_chars(struct lrw_uart_port *sup, bool from_irq)
+{
+ struct tty_port *tport = &sup->port.state->port;
+ int count = sup->fifosize >> 1;
+
+ if (sup->port.x_char) {
+ if (!lrw_uart_tx_char(sup, sup->port.x_char, from_irq))
+ return true;
+ sup->port.x_char = 0;
+ --count;
+ }
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(&sup->port)) {
+ lrw_uart_stop_tx(&sup->port);
+ return false;
+ }
+
+ /* If we are using DMA mode, try to send some characters. */
+ if (lrw_uart_dma_tx_irq(sup))
+ return true;
+
+ while (1) {
+ unsigned char c;
+
+ if (likely(from_irq) && count-- == 0)
+ break;
+
+ if (!kfifo_peek(&tport->xmit_fifo, &c))
+ break;
+
+ if (!lrw_uart_tx_char(sup, c, from_irq))
+ break;
+
+ kfifo_skip(&tport->xmit_fifo);
+ }
+
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
+ uart_write_wakeup(&sup->port);
+
+ if (kfifo_is_empty(&tport->xmit_fifo)) {
+ lrw_uart_stop_tx(&sup->port);
+ return false;
+ }
+ return true;
+}
+
+static void lrw_uart_modem_status(struct lrw_uart_port *sup)
+{
+ unsigned int status, delta;
+
+ status = lrw_uart_read(sup, REG_FR) & UARTFR_MODEM_ANY;
+
+ delta = status ^ sup->old_status;
+ sup->old_status = status;
+
+ if (!delta)
+ return;
+
+ if (delta & UARTFR_DCD)
+ uart_handle_dcd_change(&sup->port, status & UARTFR_DCD);
+
+ if (delta & sup->vendor->fr_dsr)
+ sup->port.icount.dsr++;
+
+ if (delta & sup->vendor->fr_cts)
+ uart_handle_cts_change(&sup->port,
+ status & sup->vendor->fr_cts);
+
+ wake_up_interruptible(&sup->port.state->port.delta_msr_wait);
+}
+
+static void check_apply_cts_event_workaround(struct lrw_uart_port *sup)
+{
+ if (!sup->vendor->cts_event_workaround)
+ return;
+
+ /* workaround to make sure that all bits are unlocked.. */
+ lrw_uart_write(0x00, sup, REG_ICR);
+
+ /*
+ * WA: introduce 26ns(1 uart clk) delay before W1C;
+ * single apb access will incur 2 pclk(133.12Mhz) delay,
+ * so add 2 dummy reads
+ */
+ lrw_uart_read(sup, REG_ICR);
+ lrw_uart_read(sup, REG_ICR);
+}
+
+static irqreturn_t lrw_uart_int(int irq, void *dev_id)
+{
+ struct lrw_uart_port *sup = dev_id;
+ unsigned int status, pass_counter = ISR_PASS_LIMIT;
+ int handled = 0;
+
+ uart_port_lock(&sup->port);
+ status = lrw_uart_read(sup, REG_RIS) & sup->im;
+ if (status) {
+ do {
+ check_apply_cts_event_workaround(sup);
+
+ lrw_uart_write(status & ~(UARTICR_TXIC | UARTICR_RTIC | UARTICR_RXIC),
+ sup, REG_ICR);
+
+ if (status & (UARTICR_RTIC | UARTICR_RXIC)) {
+ if (lrw_uart_dma_rx_running(sup))
+ lrw_uart_dma_rx_irq(sup);
+ else
+ lrw_uart_rx_chars(sup);
+ }
+ if (status & (UARTICR_DSRMIC | UARTICR_DCDMIC |
+ UARTICR_CTSMIC | UARTICR_RIMIC))
+ lrw_uart_modem_status(sup);
+ if (status & UARTICR_TXIC)
+ lrw_uart_tx_chars(sup, true);
+
+ if (pass_counter-- == 0)
+ break;
+
+ status = lrw_uart_read(sup, REG_RIS) & sup->im;
+ } while (status != 0);
+ handled = 1;
+ }
+
+ uart_unlock_and_check_sysrq(&sup->port);
+
+ return IRQ_RETVAL(handled);
+}
+
+static unsigned int lrw_uart_tx_empty(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ /* Allow feature register bits to be inverted to work around errata */
+ unsigned int status = lrw_uart_read(sup, REG_FR) ^ sup->vendor->inv_fr;
+
+ return status & (sup->vendor->fr_busy | UARTFR_TXFF) ?
+ 0 : TIOCSER_TEMT;
+}
+
+static void lrw_uart_maybe_set_bit(bool cond, unsigned int *ptr, unsigned int mask)
+{
+ if (cond)
+ *ptr |= mask;
+}
+
+static unsigned int lrw_uart_get_mctrl(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+ unsigned int result = 0;
+ unsigned int status = lrw_uart_read(sup, REG_FR);
+
+ lrw_uart_maybe_set_bit(status & UARTFR_DCD, &result, TIOCM_CAR);
+ lrw_uart_maybe_set_bit(status & sup->vendor->fr_dsr, &result, TIOCM_DSR);
+ lrw_uart_maybe_set_bit(status & sup->vendor->fr_cts, &result, TIOCM_CTS);
+ lrw_uart_maybe_set_bit(status & sup->vendor->fr_ri, &result, TIOCM_RNG);
+
+ return result;
+}
+
+static void lrw_uart_assign_bit(bool cond, unsigned int *ptr, unsigned int mask)
+{
+ if (cond)
+ *ptr |= mask;
+ else
+ *ptr &= ~mask;
+}
+
+static void lrw_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+ unsigned int mcr;
+ unsigned int mcfg;
+ unsigned int fccr;
+
+ mcr = lrw_uart_read(sup, REG_MCR);
+ mcfg = lrw_uart_read(sup, REG_MCFG);
+ fccr = lrw_uart_read(sup, REG_FCCR);
+
+ lrw_uart_assign_bit(mctrl & TIOCM_RTS, &mcr, UARTMCR_RTS);
+ lrw_uart_assign_bit(mctrl & TIOCM_DTR, &mcr, UARTMCR_DTR);
+ lrw_uart_assign_bit(mctrl & TIOCM_OUT1, &mcr, UARTMCR_OUT1);
+ lrw_uart_assign_bit(mctrl & TIOCM_OUT2, &mcr, UARTMCR_OUT2);
+ lrw_uart_assign_bit(mctrl & TIOCM_LOOP, &mcfg, UARTMCFG_LBE);
+
+ if (port->status & UPSTAT_AUTORTS) {
+ /* We need to disable auto-RTS if we want to turn RTS off */
+ lrw_uart_assign_bit(mctrl & TIOCM_RTS, &fccr, UARTFCCR_RTSEN);
+ }
+
+ lrw_uart_write(mcr, sup, REG_MCR);
+ lrw_uart_write(mcfg, sup, REG_MCFG);
+ lrw_uart_write(fccr, sup, REG_FCCR);
+}
+
+static void lrw_uart_break_ctl(struct uart_port *port, int break_state)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+ unsigned long flags;
+ unsigned int mcfg;
+
+ uart_port_lock_irqsave(&sup->port, &flags);
+ mcfg = lrw_uart_read(sup, REG_MCFG);
+ if (break_state == -1)
+ mcfg |= UARTMCFG_BRK;
+ else
+ mcfg &= ~UARTMCFG_BRK;
+ lrw_uart_write(mcfg, sup, REG_MCFG);
+ uart_port_unlock_irqrestore(&sup->port, flags);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+
+static void lrw_uart_quiesce_irqs(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ lrw_uart_write(lrw_uart_read(sup, REG_MIS), sup, REG_ICR);
+ /*
+ * There is no way to clear TXIM as this is "ready to transmit IRQ", so
+ * we simply mask it. start_tx() will unmask it.
+ *
+ * Note we can race with start_tx(), and if the race happens, the
+ * polling user might get another interrupt just after we clear it.
+ * But it should be OK and can happen even w/o the race, e.g.
+ * controller immediately got some new data and raised the IRQ.
+ *
+ * And whoever uses polling routines assumes that it manages the device
+ * (including tx queue), so we're also fine with start_tx()'s caller
+ * side.
+ */
+ lrw_uart_write(lrw_uart_read(sup, REG_IMSC) & ~UARTIMSC_TXIM, sup,
+ REG_IMSC);
+}
+
+static int lrw_uart_get_poll_char(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+ unsigned int status;
+
+ /*
+ * The caller might need IRQs lowered, e.g. if used with KDB NMI
+ * debugger.
+ */
+ lrw_uart_quiesce_irqs(port);
+
+ status = lrw_uart_read(sup, REG_FR);
+ if (status & UARTFR_RXFE)
+ return NO_POLL_CHAR;
+
+ return lrw_uart_read(sup, REG_DR);
+}
+
+static void lrw_uart_put_poll_char(struct uart_port *port, unsigned char ch)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ while (lrw_uart_read(sup, REG_FR) & UARTFR_TXFF)
+ cpu_relax();
+
+ lrw_uart_write(ch, sup, REG_DR);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+static int lrw_uart_hwinit(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+ int retval;
+ unsigned int clk;
+
+ /* Optionaly enable pins to be muxed in and configured */
+ pinctrl_pm_select_default_state(port->dev);
+
+ /*
+ * Try to enable the clock producer.
+ */
+ retval = clk_prepare_enable(sup->clk);
+ if (retval)
+ return retval;
+
+ if (has_acpi_companion(sup->port.dev)) {
+ device_property_read_u32(sup->port.dev, "clock-frequency", &clk);
+ sup->port.uartclk = clk;
+ } else {
+ sup->port.uartclk = clk_get_rate(sup->clk);
+ }
+
+ /* Clear pending error and receive interrupts */
+ lrw_uart_write(UARTICR_OEIC | UARTICR_BEIC | UARTICR_PEIC |
+ UARTICR_FEIC | UARTICR_RTIC | UARTICR_RXIC,
+ sup, REG_ICR);
+
+ /*
+ * Save interrupts enable mask, and enable RX interrupts in case if
+ * the interrupt is used for NMI entry.
+ */
+ sup->im = lrw_uart_read(sup, REG_IMSC);
+ lrw_uart_write(UARTIMSC_RTIM | UARTIMSC_RXIM, sup, REG_IMSC);
+
+ if (dev_get_platdata(sup->port.dev)) {
+ struct lrw_uart_data *plat;
+
+ plat = dev_get_platdata(sup->port.dev);
+ if (plat->init)
+ plat->init();
+ }
+ return 0;
+}
+
+static int lrw_uart_allocate_irq(struct lrw_uart_port *sup)
+{
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+
+ return request_irq(sup->port.irq, lrw_uart_int, IRQF_SHARED, "lrw-uart", sup);
+}
+
+/*
+ * Enable interrupts, only timeouts when using DMA
+ * if initial RX DMA job failed, start in interrupt mode
+ * as well.
+ */
+static void lrw_uart_enable_interrupts(struct lrw_uart_port *sup)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ uart_port_lock_irqsave(&sup->port, &flags);
+
+ /* Clear out any spuriously appearing RX interrupts */
+ lrw_uart_write(UARTICR_RTIC | UARTICR_RXIC, sup, REG_ICR);
+
+ /*
+ * RXIS is asserted only when the RX FIFO transitions from below
+ * to above the trigger threshold. If the RX FIFO is already
+ * full to the threshold this can't happen and RXIS will now be
+ * stuck off. Drain the RX FIFO explicitly to fix this:
+ */
+ for (i = 0; i < sup->fifosize * 2; ++i) {
+ if (lrw_uart_read(sup, REG_FR) & UARTFR_RXFE)
+ break;
+
+ lrw_uart_read(sup, REG_DR);
+ }
+
+ sup->im = UARTIMSC_RTIM;
+ if (!lrw_uart_dma_rx_running(sup))
+ sup->im |= UARTIMSC_RXIM;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ uart_port_unlock_irqrestore(&sup->port, flags);
+}
+
+static void lrw_uart_unthrottle_rx(struct uart_port *port)
+{
+ struct lrw_uart_port *sup = container_of(port, struct lrw_uart_port, port);
+ unsigned long flags;
+
+ uart_port_lock_irqsave(&sup->port, &flags);
+
+ sup->im = UARTIMSC_RTIM;
+ if (!lrw_uart_dma_rx_running(sup))
+ sup->im |= UARTIMSC_RXIM;
+
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+
+ uart_port_unlock_irqrestore(&sup->port, flags);
+}
+
+static int lrw_uart_startup(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+ unsigned int mcr;
+ unsigned int mcfg;
+ int retval;
+
+ retval = lrw_uart_hwinit(port);
+ if (retval)
+ goto clk_dis;
+
+ retval = lrw_uart_allocate_irq(sup);
+ if (retval)
+ goto clk_dis;
+
+ lrw_uart_write(sup->vendor->fcr, sup, REG_FCR);
+
+ uart_port_lock_irq(&sup->port);
+
+ mcr = lrw_uart_read(sup, REG_MCR);
+ mcr &= UARTMCR_RTS | UARTMCR_DTR;
+
+ lrw_uart_write(mcr, sup, REG_MCR);
+
+ mcfg = lrw_uart_read(sup, REG_MCFG);
+
+ mcfg |= UARTMCFG_UARTEN | UARTMCFG_RXE;
+
+ if (!(port->rs485.flags & SER_RS485_ENABLED))
+ mcfg |= UARTMCFG_TXE;
+
+ lrw_uart_write(mcfg, sup, REG_MCFG);
+
+ uart_port_unlock_irq(&sup->port);
+
+ /*
+ * initialise the old status of the modem signals
+ */
+ sup->old_status = lrw_uart_read(sup, REG_FR) & UARTFR_MODEM_ANY;
+
+ /* Startup DMA */
+ lrw_uart_dma_startup(sup);
+
+ lrw_uart_enable_interrupts(sup);
+
+ return 0;
+
+ clk_dis:
+ clk_disable_unprepare(sup->clk);
+ return retval;
+}
+
+static void lrw_uart_shutdown_channel(struct lrw_uart_port *sup,
+ unsigned int mcfg, unsigned int fcr)
+{
+ unsigned long val;
+
+ val = lrw_uart_read(sup, mcfg);
+ val &= ~(UARTMCFG_BRK);
+ lrw_uart_write(val, sup, mcfg);
+
+ val = lrw_uart_read(sup, fcr);
+ val &= ~(UARTFCR_FEN);
+ lrw_uart_write(val, sup, fcr);
+}
+
+/*
+ * disable the port. It should not disable RTS and DTR.
+ * Also RTS and DTR state should be preserved to restore
+ * it during startup().
+ */
+static void lrw_uart_disable_uart(struct lrw_uart_port *sup)
+{
+ unsigned int mcr;
+ unsigned int mcfg;
+
+ sup->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
+ uart_port_lock_irq(&sup->port);
+ mcr = lrw_uart_read(sup, REG_MCR);
+ mcr &= UARTMCR_RTS | UARTMCR_DTR;
+ lrw_uart_write(mcr, sup, REG_MCR);
+
+ mcfg = lrw_uart_read(sup, REG_MCFG);
+ mcfg |= UARTMCFG_UARTEN | UARTMCFG_TXE;
+ lrw_uart_write(mcfg, sup, REG_MCFG);
+ uart_port_unlock_irq(&sup->port);
+
+ /*
+ * disable break condition and fifos
+ */
+ lrw_uart_shutdown_channel(sup, REG_MCFG, REG_FCR);
+}
+
+static void lrw_uart_disable_interrupts(struct lrw_uart_port *sup)
+{
+ uart_port_lock_irq(&sup->port);
+
+ /* mask all interrupts and clear all pending ones */
+ sup->im = 0;
+ lrw_uart_write(sup->im, sup, REG_IMSC);
+ lrw_uart_write(0xffff, sup, REG_ICR);
+
+ uart_port_unlock_irq(&sup->port);
+}
+
+static void lrw_uart_shutdown(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ lrw_uart_disable_interrupts(sup);
+
+ lrw_uart_dma_shutdown(sup);
+
+ if ((port->rs485.flags & SER_RS485_ENABLED) && sup->rs485_tx_started)
+ lrw_uart_rs485_tx_stop(sup);
+
+ free_irq(sup->port.irq, sup);
+
+ lrw_uart_disable_uart(sup);
+
+ /*
+ * Shut down the clock producer
+ */
+ clk_disable_unprepare(sup->clk);
+ /* Optionally let pins go into sleep states */
+ pinctrl_pm_select_sleep_state(port->dev);
+
+ if (dev_get_platdata(sup->port.dev)) {
+ struct lrw_uart_data *plat;
+
+ plat = dev_get_platdata(sup->port.dev);
+ if (plat->exit)
+ plat->exit();
+ }
+
+ if (sup->port.ops->flush_buffer)
+ sup->port.ops->flush_buffer(port);
+}
+
+static void
+lrw_uart_setup_status_masks(struct uart_port *port, struct ktermios *termios)
+{
+ port->read_status_mask = UARTDR_OE | 255;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= UARTDR_FE | UARTDR_PE;
+ if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
+ port->read_status_mask |= UARTDR_BE;
+
+ /*
+ * Characters to ignore
+ */
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= UARTDR_FE | UARTDR_PE;
+ if (termios->c_iflag & IGNBRK) {
+ port->ignore_status_mask |= UARTDR_BE;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= UARTDR_OE;
+ }
+
+ /*
+ * Ignore all characters if CREAD is not set.
+ */
+ if ((termios->c_cflag & CREAD) == 0)
+ port->ignore_status_mask |= UART_DUMMY_DR_RX;
+}
+
+static void
+lrw_uart_set_termios(struct uart_port *port, struct ktermios *termios,
+ const struct ktermios *old)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+ unsigned int frcr;
+ unsigned int mcr, fccr;
+ unsigned int mcfg;
+ unsigned long flags;
+ unsigned int baud, quot, clkdiv;
+ unsigned int bits;
+ unsigned int clk;
+
+ if (sup->vendor->oversampling)
+ clkdiv = 8;
+ else
+ clkdiv = 16;
+
+ if (has_acpi_companion(sup->port.dev)) {
+ device_property_read_u32(sup->port.dev, "clock-frequency", &clk);
+ sup->port.uartclk = clk;
+ }
+
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
+ baud = uart_get_baud_rate(port, termios, old, 0,
+ port->uartclk / clkdiv);
+
+#ifdef CONFIG_DMA_ENGINE
+ /*
+ * Adjust RX DMA polling rate with baud rate if not specified.
+ */
+ if (sup->dmarx.auto_poll_rate)
+ sup->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud);
+#endif
+
+ if (baud > port->uartclk / 16)
+ quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud);
+ else
+ quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud);
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ frcr = UARTFRCR_WLEN_5;
+ break;
+ case CS6:
+ frcr = UARTFRCR_WLEN_6;
+ break;
+ case CS7:
+ frcr = UARTFRCR_WLEN_7;
+ break;
+ default: // CS8
+ frcr = UARTFRCR_WLEN_8;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ frcr |= UARTFRCR_STP2;
+ if (termios->c_cflag & PARENB) {
+ frcr |= UARTFRCR_PEN;
+ if (!(termios->c_cflag & PARODD))
+ frcr |= UARTFRCR_EOP;
+ if (termios->c_cflag & CMSPAR)
+ frcr |= UARTFRCR_SPS;
+ }
+
+
+ bits = tty_get_frame_size(termios->c_cflag);
+
+ uart_port_lock_irqsave(port, &flags);
+
+ /*
+ * Update the per-port timeout.
+ */
+ uart_update_timeout(port, termios->c_cflag, baud);
+
+ /*
+ * Calculate the approximated time it takes to transmit one character
+ * with the given baud rate. We use this as the poll interval when we
+ * wait for the tx queue to empty.
+ */
+ sup->rs485_tx_drain_interval = DIV_ROUND_UP(bits * 1000 * 1000, baud);
+
+ lrw_uart_setup_status_masks(port, termios);
+
+ if (UART_ENABLE_MS(port, termios->c_cflag))
+ lrw_uart_enable_ms(port);
+
+ if (port->rs485.flags & SER_RS485_ENABLED)
+ termios->c_cflag &= ~CRTSCTS;
+
+ mcr = lrw_uart_read(sup, REG_MCR);
+ mcfg = lrw_uart_read(sup, REG_MCFG);
+ fccr = lrw_uart_read(sup, REG_FCCR);
+
+ if (termios->c_cflag & CRTSCTS) {
+ if (mcr & UARTMCR_RTS)
+ fccr |= UARTFCCR_RTSEN;
+
+ fccr |= UARTFCCR_CTSEN;
+ port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
+ } else {
+ fccr &= ~(UARTFCCR_CTSEN | UARTFCCR_RTSEN);
+ port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
+ }
+
+ /* Set baud rate */
+ lrw_uart_write(quot & 0x3f, sup, REG_FD);
+ lrw_uart_write(quot >> 6, sup, REG_IND);
+
+ /*
+ * ----------v----------v----------v----------v-----
+ * NOTE: REG_FRCR MUST BE WRITTEN AFTER REG_FD & REG_IND.
+ * ----------^----------^----------^----------^-----
+ */
+ lrw_uart_write(frcr, sup, REG_FRCR);
+
+ lrw_uart_write(fccr, sup, REG_FCCR);
+
+ /*
+ * Receive was disabled by lrw_uart_disable_uart during shutdown.
+ * Need to reenable receive if you need to use a tty_driver
+ * returns from tty_find_polling_driver() after a port shutdown.
+ */
+ mcfg |= UARTMCFG_RXE;
+ lrw_uart_write(mcfg, sup, REG_MCFG);
+
+ uart_port_unlock_irqrestore(port, flags);
+}
+
+static const char *lrw_uart_type(struct uart_port *port)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+ return sup->port.type == PORT_LRW ? sup->type : NULL;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void lrw_uart_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_LRW;
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int lrw_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+ int ret = 0;
+
+ if (ser->type != PORT_UNKNOWN && ser->type != PORT_LRW)
+ ret = -EINVAL;
+ if (ser->irq < 0 || ser->irq >= irq_get_nr_irqs())
+ ret = -EINVAL;
+ if (ser->baud_base < 9600)
+ ret = -EINVAL;
+ if (port->mapbase != (unsigned long)ser->iomem_base)
+ ret = -EINVAL;
+ return ret;
+}
+
+static int lrw_uart_rs485_config(struct uart_port *port, struct ktermios *termios,
+ struct serial_rs485 *rs485)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ if (port->rs485.flags & SER_RS485_ENABLED)
+ lrw_uart_rs485_tx_stop(sup);
+
+ /* Make sure auto RTS is disabled */
+ if (rs485->flags & SER_RS485_ENABLED) {
+ u32 fccr = lrw_uart_read(sup, REG_FCCR);
+
+ fccr &= ~UARTFCCR_RTSEN;
+ lrw_uart_write(fccr, sup, REG_FCCR);
+ port->status &= ~UPSTAT_AUTORTS;
+ }
+
+ return 0;
+}
+
+static const struct uart_ops lrw_uart_pops = {
+ .tx_empty = lrw_uart_tx_empty,
+ .set_mctrl = lrw_uart_set_mctrl,
+ .get_mctrl = lrw_uart_get_mctrl,
+ .stop_tx = lrw_uart_stop_tx,
+ .start_tx = lrw_uart_start_tx,
+ .stop_rx = lrw_uart_stop_rx,
+ .throttle = lrw_uart_throttle_rx,
+ .unthrottle = lrw_uart_unthrottle_rx,
+ .enable_ms = lrw_uart_enable_ms,
+ .break_ctl = lrw_uart_break_ctl,
+ .startup = lrw_uart_startup,
+ .shutdown = lrw_uart_shutdown,
+ .flush_buffer = lrw_uart_dma_flush_buffer,
+ .set_termios = lrw_uart_set_termios,
+ .type = lrw_uart_type,
+ .config_port = lrw_uart_config_port,
+ .verify_port = lrw_uart_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_init = lrw_uart_hwinit,
+ .poll_get_char = lrw_uart_get_poll_char,
+ .poll_put_char = lrw_uart_put_poll_char,
+#endif
+};
+
+static struct lrw_uart_port *lrw_uart_console_ports[UART_NR];
+
+#ifdef CONFIG_SERIAL_LRW_UART_CONSOLE
+
+static void lrw_uart_console_putchar(struct uart_port *port, unsigned char ch)
+{
+ struct lrw_uart_port *sup =
+ container_of(port, struct lrw_uart_port, port);
+
+ while (lrw_uart_read(sup, REG_FR) & UARTFR_TXFF)
+ cpu_relax();
+ lrw_uart_write(ch, sup, REG_DR);
+}
+
+static void
+lrw_uart_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct lrw_uart_port *sup = lrw_uart_console_ports[co->index];
+ unsigned int old_fccr = 0, new_fccr;
+ unsigned int old_mcfg = 0, new_mcfg;
+ unsigned long flags;
+ int locked = 1;
+
+ clk_enable(sup->clk);
+
+ if (oops_in_progress)
+ locked = uart_port_trylock_irqsave(&sup->port, &flags);
+ else
+ uart_port_lock_irqsave(&sup->port, &flags);
+
+ /*
+ * First save the FCCR then disable the interrupts
+ */
+ if (!sup->vendor->always_enabled) {
+ old_fccr = lrw_uart_read(sup, REG_FCCR);
+ new_fccr = old_fccr & ~UARTFCCR_CTSEN;
+ lrw_uart_write(new_fccr, sup, REG_FCCR);
+
+ old_mcfg = lrw_uart_read(sup, REG_MCFG);
+ new_mcfg |= UARTMCFG_UARTEN | UARTMCFG_TXE;
+ lrw_uart_write(new_mcfg, sup, REG_MCFG);
+ }
+
+ uart_console_write(&sup->port, s, count, lrw_uart_console_putchar);
+
+ /*
+ * Finally, wait for transmitter to become empty and restore the
+ * TCR. Allow feature register bits to be inverted to work around
+ * errata.
+ */
+ while ((lrw_uart_read(sup, REG_FR) ^ sup->vendor->inv_fr)
+ & sup->vendor->fr_busy)
+ cpu_relax();
+ if (!sup->vendor->always_enabled) {
+ lrw_uart_write(old_fccr, sup, REG_FCCR);
+ lrw_uart_write(old_mcfg, sup, REG_MCFG);
+ }
+
+ if (locked)
+ uart_port_unlock_irqrestore(&sup->port, flags);
+
+ clk_disable(sup->clk);
+}
+
+static void lrw_uart_console_get_options(struct lrw_uart_port *sup, int *baud,
+ int *parity, int *bits)
+{
+ unsigned int frcr, ind, fd;
+
+ if (!(lrw_uart_read(sup, REG_MCFG) & UARTMCFG_UARTEN))
+ return;
+
+ frcr = lrw_uart_read(sup, REG_FRCR);
+
+ *parity = 'n';
+ if (frcr & UARTFRCR_PEN) {
+ if (frcr & UARTFRCR_EOP)
+ *parity = 'e';
+ else
+ *parity = 'o';
+ }
+
+ if ((frcr & 0x3) == UARTFRCR_WLEN_7)
+ *bits = 7;
+ else
+ *bits = 8;
+
+ ind = lrw_uart_read(sup, REG_IND);
+ fd = lrw_uart_read(sup, REG_FD);
+
+ *baud = sup->port.uartclk * 4 / (64 * ind + fd);
+}
+
+static int lrw_uart_console_setup(struct console *co, char *options)
+{
+ struct lrw_uart_port *sup;
+ int baud = 38400;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ int ret;
+ unsigned int clk;
+
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index >= UART_NR)
+ co->index = 0;
+ sup = lrw_uart_console_ports[co->index];
+ if (!sup)
+ return -ENODEV;
+
+ /* Allow pins to be muxed in and configured */
+ pinctrl_pm_select_default_state(sup->port.dev);
+
+ ret = clk_prepare(sup->clk);
+ if (ret)
+ return ret;
+
+ if (dev_get_platdata(sup->port.dev)) {
+ struct lrw_uart_data *plat;
+
+ plat = dev_get_platdata(sup->port.dev);
+ if (plat->init)
+ plat->init();
+ }
+
+ if (has_acpi_companion(sup->port.dev)) {
+ device_property_read_u32(sup->port.dev, "clock-frequency", &clk);
+ sup->port.uartclk = clk;
+ } else {
+ sup->port.uartclk = clk_get_rate(sup->clk);
+ }
+
+ if (sup->vendor->fixed_options) {
+ baud = sup->fixed_baud;
+ } else {
+ if (options)
+ uart_parse_options(options,
+ &baud, &parity, &bits, &flow);
+ else
+ lrw_uart_console_get_options(sup, &baud, &parity, &bits);
+ }
+
+ return uart_set_options(&sup->port, co, baud, parity, bits, flow);
+}
+
+/**
+ * lrw_uart_console_match - non-standard console matching
+ * @co: registering console
+ * @name: name from console command line
+ * @idx: index from console command line
+ * @options: ptr to option string from console command line
+ *
+ * Only attempts to match console command lines of the form:
+ * console=lrw_uart,mmio|mmio32,<addr>[,<options>]
+ * console=lrw_uart,0x<addr>[,<options>]
+ * This form is used to register an initial earlycon boot console and
+ * replace it with the lrw_uart_console at lrw_uart driver init.
+ *
+ * Performs console setup for a match (as required by interface)
+ * If no <options> are specified, then assume the h/w is already setup.
+ *
+ * Returns 0 if console matches; otherwise non-zero to use default matching
+ */
+static int lrw_uart_console_match(struct console *co, char *name, int idx,
+ char *options)
+{
+ enum uart_iotype iotype;
+ resource_size_t addr;
+ int i;
+
+ if (strcmp(name, "lrw_uart") != 0)
+ return -ENODEV;
+
+ if (uart_parse_earlycon(options, &iotype, &addr, &options))
+ return -ENODEV;
+
+ if (iotype != UPIO_MEM && iotype != UPIO_MEM32)
+ return -ENODEV;
+
+ /* try to match the port specified on the command line */
+ for (i = 0; i < ARRAY_SIZE(lrw_uart_console_ports); i++) {
+ struct uart_port *port;
+
+ if (!lrw_uart_console_ports[i])
+ continue;
+
+ port = &lrw_uart_console_ports[i]->port;
+
+ if (port->mapbase != addr)
+ continue;
+
+ co->index = i;
+ uart_port_set_cons(port, co);
+ return lrw_uart_console_setup(co, options);
+ }
+
+ return -ENODEV;
+}
+
+static struct uart_driver lrw_uart_driver;
+
+static struct console lrw_uart_console = {
+ .name = LRW_UART_TTY_PREFIX,
+ .write = lrw_uart_console_write,
+ .device = uart_console_device,
+ .setup = lrw_uart_console_setup,
+ .match = lrw_uart_console_match,
+ .flags = CON_PRINTBUFFER | CON_ANYTIME,
+ .index = -1,
+ .data = &lrw_uart_driver,
+};
+
+#define LRW_UART_CONSOLE (&lrw_uart_console)
+
+static void lrw_uart_putc(struct uart_port *port, unsigned char c)
+{
+ while (readl(port->membase + UARTFR) & UARTFR_TXFF)
+ cpu_relax();
+ if (port->iotype == UPIO_MEM32)
+ writel(c, port->membase + UARTDR);
+ else
+ writeb(c, port->membase + UARTDR);
+ while (readl(port->membase + UARTFR) & UARTFR_BUSY)
+ cpu_relax();
+}
+
+static void lrw_uart_early_write(struct console *con, const char *s, unsigned int n)
+{
+ struct earlycon_device *dev = con->data;
+
+ uart_console_write(&dev->port, s, n, lrw_uart_putc);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int lrw_uart_getc(struct uart_port *port)
+{
+ if (readl(port->membase + UARTFR) & UARTFR_RXFE)
+ return NO_POLL_CHAR;
+
+ if (port->iotype == UPIO_MEM32)
+ return readl(port->membase + UARTDR);
+ else
+ return readb(port->membase + UARTDR);
+}
+
+static int lrw_uart_early_read(struct console *con, char *s, unsigned int n)
+{
+ struct earlycon_device *dev = con->data;
+ int ch, num_read = 0;
+
+ while (num_read < n) {
+ ch = lrw_uart_getc(&dev->port);
+ if (ch == NO_POLL_CHAR)
+ break;
+
+ s[num_read++] = ch;
+ }
+
+ return num_read;
+}
+#else
+#define lrw_uart_early_read NULL
+#endif
+
+/*
+ * On non-ACPI systems, earlycon is enabled by specifying
+ * "earlycon=lrw_uart,<address>" on the kernel command line.
+ *
+ * On ACPI ARM64 systems, an "early" console is enabled via the SPCR table,
+ * by specifying only "earlycon" on the command line. Because it requires
+ * SPCR, the console starts after ACPI is parsed, which is later than a
+ * traditional early console.
+ *
+ * To get the traditional early console that starts before ACPI is parsed,
+ * specify the full "earlycon=lrw_uart,<address>" option.
+ */
+static int __init lrw_uart_early_console_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ device->con->write = lrw_uart_early_write;
+ device->con->read = lrw_uart_early_read;
+
+ return 0;
+}
+
+OF_EARLYCON_DECLARE(lrw_uart, "lrw-uart", lrw_uart_early_console_setup);
+
+#else
+#define LRW_UART_CONSOLE NULL
+#endif
+
+static struct uart_driver lrw_uart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = LRW_UART_NAME,
+ .dev_name = LRW_UART_TTY_PREFIX,
+ .nr = UART_NR,
+ .cons = LRW_UART_CONSOLE,
+};
+
+static int lrw_uart_probe_dt_alias(int index, struct device *dev)
+{
+ struct device_node *np;
+ static bool seen_dev_with_alias;
+ static bool seen_dev_without_alias;
+ int ret = index;
+
+ if (!IS_ENABLED(CONFIG_OF))
+ return ret;
+
+ np = dev->of_node;
+ if (!np)
+ return ret;
+
+ ret = of_alias_get_id(np, "serial");
+ if (ret < 0) {
+ seen_dev_without_alias = true;
+ ret = index;
+ } else {
+ seen_dev_with_alias = true;
+ if (ret >= ARRAY_SIZE(lrw_uart_console_ports) || lrw_uart_console_ports[ret]) {
+ dev_warn(dev, "requested serial port %d not available.\n", ret);
+ ret = index;
+ }
+ }
+
+ if (seen_dev_with_alias && seen_dev_without_alias)
+ dev_warn(dev, "aliased and non-aliased serial devices found in device tree. Serial port enumeration may be unpredictable.\n");
+
+ return ret;
+}
+
+/* unregisters the driver also if no more ports are left */
+static void lrw_uart_unregister_port(struct lrw_uart_port *sup)
+{
+ int i;
+ bool busy = false;
+
+ for (i = 0; i < ARRAY_SIZE(lrw_uart_console_ports); i++) {
+ if (lrw_uart_console_ports[i] == sup)
+ lrw_uart_console_ports[i] = NULL;
+ else if (lrw_uart_console_ports[i])
+ busy = true;
+ }
+ lrw_uart_dma_remove(sup);
+ if (!busy)
+ uart_unregister_driver(&lrw_uart_driver);
+}
+
+static int lrw_uart_find_free_port(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lrw_uart_console_ports); i++)
+ if (!lrw_uart_console_ports[i])
+ return i;
+
+ return -EBUSY;
+}
+
+static int lrw_uart_setup_port(struct device *dev, struct lrw_uart_port *sup,
+ struct resource *mmiobase, int index)
+{
+ void __iomem *base;
+ int ret;
+
+ base = devm_ioremap_resource(dev, mmiobase);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ index = lrw_uart_probe_dt_alias(index, dev);
+
+ sup->port.dev = dev;
+ sup->port.mapbase = mmiobase->start;
+ sup->port.membase = base;
+ sup->port.fifosize = sup->fifosize;
+ sup->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_LRW_UART_CONSOLE);
+ sup->port.flags = UPF_BOOT_AUTOCONF;
+ sup->port.line = index;
+
+ ret = uart_get_rs485_mode(&sup->port);
+ if (ret)
+ return ret;
+
+ lrw_uart_console_ports[index] = sup;
+
+ return 0;
+}
+
+static int lrw_uart_register_port(struct lrw_uart_port *sup)
+{
+ int ret, i;
+
+ /* Ensure interrupts from this UART are masked and cleared */
+ lrw_uart_write(0, sup, REG_IMSC);
+ lrw_uart_write(0xffff, sup, REG_ICR);
+
+ if (!lrw_uart_driver.state) {
+ ret = uart_register_driver(&lrw_uart_driver);
+ if (ret < 0) {
+ dev_err(sup->port.dev,
+ "Failed to register LRW UART driver\n");
+ for (i = 0; i < ARRAY_SIZE(lrw_uart_console_ports); i++)
+ if (lrw_uart_console_ports[i] == sup)
+ lrw_uart_console_ports[i] = NULL;
+ return ret;
+ }
+ }
+
+ ret = uart_add_one_port(&lrw_uart_driver, &sup->port);
+ if (ret)
+ lrw_uart_unregister_port(sup);
+
+ return ret;
+}
+
+static const struct serial_rs485 lrw_uart_rs485_supported = {
+ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND |
+ SER_RS485_RX_DURING_TX,
+ .delay_rts_before_send = 1,
+ .delay_rts_after_send = 1,
+};
+
+static int lrw_uart_probe(struct platform_device *pdev)
+{
+ struct lrw_uart_port *sup;
+ struct resource *r;
+ int portnr, ret;
+ unsigned int clk;
+ unsigned int baudrate;
+
+ /*
+ * Check the mandatory baud rate parameter in the DT node early
+ * so that we can easily exit with the error.
+ */
+ if (pdev->dev.of_node) {
+ struct device_node *np = pdev->dev.of_node;
+
+ ret = of_property_read_u32(np, "current-speed", &baudrate);
+ if (ret)
+ return ret;
+ } else if (has_acpi_companion(&pdev->dev)) {
+ ret = device_property_read_u32(&pdev->dev, "current-speed", &baudrate);
+ if (ret)
+ return ret;
+ } else {
+ baudrate = 115200;
+ }
+
+ portnr = lrw_uart_find_free_port();
+ if (portnr < 0)
+ return portnr;
+
+ sup = devm_kzalloc(&pdev->dev, sizeof(struct lrw_uart_port),
+ GFP_KERNEL);
+ if (!sup)
+ return -ENOMEM;
+
+ if (has_acpi_companion(&pdev->dev)) {
+ device_property_read_u32(&pdev->dev, "clock-frequency", &clk);
+ sup->port.uartclk = clk;
+ } else {
+ sup->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sup->clk))
+ return PTR_ERR(sup->clk);
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+ sup->port.irq = ret;
+
+ sup->vendor = &vendor_lrw;
+
+ sup->reg_offset = sup->vendor->reg_offset;
+ sup->fifosize = LRW_UART_TX_FIFO_DEPTH;
+ sup->port.iotype = sup->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
+ sup->port.ops = &lrw_uart_pops;
+ sup->port.rs485_config = lrw_uart_rs485_config;
+ sup->port.rs485_supported = lrw_uart_rs485_supported;
+ sup->fixed_baud = baudrate;
+
+ snprintf(sup->type, sizeof(sup->type), "LRW UART");
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ ret = lrw_uart_setup_port(&pdev->dev, sup, r, portnr);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, sup);
+
+ return lrw_uart_register_port(sup);
+}
+
+static void lrw_uart_remove(struct platform_device *dev)
+{
+ struct lrw_uart_port *sup = platform_get_drvdata(dev);
+
+ uart_remove_one_port(&lrw_uart_driver, &sup->port);
+ lrw_uart_unregister_port(sup);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int lrw_uart_suspend(struct device *dev)
+{
+ struct lrw_uart_port *sup = dev_get_drvdata(dev);
+
+ if (!sup)
+ return -EINVAL;
+
+ return uart_suspend_port(&lrw_uart_driver, &sup->port);
+}
+
+static int lrw_uart_resume(struct device *dev)
+{
+ struct lrw_uart_port *sup = dev_get_drvdata(dev);
+
+ if (!sup)
+ return -EINVAL;
+
+ return uart_resume_port(&lrw_uart_driver, &sup->port);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(lrw_uart_pm_ops, lrw_uart_suspend, lrw_uart_resume);
+
+static const struct of_device_id lrw_uart_of_match[] = {
+ { .compatible = "lrw,lrw-uart" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lrw_uart_of_match);
+
+static const struct acpi_device_id __maybe_unused lrw_uart_acpi_match[] = {
+ { "LRWX0000", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, lrw_uart_acpi_match);
+
+static struct platform_driver lrw_uart_platform_driver = {
+ .probe = lrw_uart_probe,
+ .remove = lrw_uart_remove,
+ .driver = {
+ .name = LRW_UART_NAME,
+ .pm = pm_sleep_ptr(&lrw_uart_pm_ops),
+ .of_match_table = of_match_ptr(lrw_uart_of_match),
+ .acpi_match_table = ACPI_PTR(lrw_uart_acpi_match),
+ .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_LRW_UART),
+ },
+};
+
+static int __init lrw_uart_init(void)
+{
+ pr_info("Serial: LRW UART driver\n");
+
+ int ret;
+
+ ret = uart_register_driver(&lrw_uart_driver);
+ if (ret < 0) {
+ pr_err("Could not register %s driver\n",
+ lrw_uart_driver.driver_name);
+ return ret;
+ }
+
+ ret = platform_driver_register(&lrw_uart_platform_driver);
+ if (ret < 0) {
+ pr_err("LRW UART platform driver register failed, e = %d\n", ret);
+ uart_unregister_driver(&lrw_uart_driver);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit lrw_uart_exit(void)
+{
+ platform_driver_unregister(&lrw_uart_platform_driver);
+ uart_unregister_driver(&lrw_uart_driver);
+}
+
+/*
+ * While this can be a module, if builtin it's most likely the console
+ * So let's leave module_exit but move module_init to an earlier place
+ */
+arch_initcall(lrw_uart_init);
+module_exit(lrw_uart_exit);
+
+MODULE_AUTHOR("Wenhong Liu/Qingtao Liu");
+MODULE_DESCRIPTION("LRW UART serial driver");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 9c007a106330..8e7322067e1f 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -231,6 +231,9 @@
/* Sunplus UART */
#define PORT_SUNPLUS 123
+/* LRW UART */
+#define PORT_LRW 124
+
/* Generic type identifier for ports which type is not important to userspace. */
#define PORT_GENERIC (-1)
--
2.27.0
More information about the linux-riscv
mailing list