[PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver

From: zjzhao

Date: Tue Sep 08 2026 - 06:54:32 EST


From: Zi Jie Zhao <zjzhao@xxxxxxxxx>

Add a driver for the WK2xxx SPI to UART bridge ICs (WK2124, WK2132, WK2168,
WK2202 and WK2204) from Chengdu Weikai Microelectronics. Support two or
four full-duplex UART channels with 256-byte RX/TX FIFOs through a single
SPI slave interface and one interrupt line.

Rework the WKmic open-source driver:
https://github.com/britus/wk2xxx/blob/master/spi-wk2xxx.c
Model the new driver after the NXP sc16is7xx driver. Register ttyWK0..N
lines, use a threaded IRQ with a kthread worker for register access, fall
back to polling when the interrupt line is not described, and apply serial
and RS-485 properties from each serial@N DT subnode to the corresponding
channel.

Limit hardware flow-control and RS-485 reporting and configuration to the
four-channel variants that implement these features.

Reset the chip and disable every sub-UART at probe time. Request the IRQ
only after registering all ports. Unregister the ports before stopping the
IRQ and worker during removal.

Allocate PORT_WK2XXX (124) and add the SERIAL_WK2XXX Kconfig option.

Signed-off-by: Zi Jie Zhao <zjzhao@xxxxxxxxx>
---
drivers/tty/serial/Kconfig | 17 +
drivers/tty/serial/Makefile | 1 +
drivers/tty/serial/wk2xxx.c | 1433 ++++++++++++++++++++++++++++++
include/uapi/linux/serial_core.h | 3 +
4 files changed, 1454 insertions(+)
create mode 100644 drivers/tty/serial/wk2xxx.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index cf7dba473b20..5f7a71f377ac 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1207,6 +1207,23 @@ config SERIAL_MXS_AUART_CONSOLE
help
Enable a MXS AUART port to be the system console.

+config SERIAL_WK2XXX
+ tristate "WK2xxx SPI UART support"
+ depends on SPI_MASTER
+ select SERIAL_CORE
+ help
+ This selects the WK2xxx SPI to UART bridge driver.
+ Supported ICs are:
+
+ WK2124
+ WK2132
+ WK2168
+ WK2202
+ WK2204
+
+ To compile this driver as a module, choose M here: the module
+ will be called wk2xxx.
+
config SERIAL_XILINX_PS_UART
tristate "Cadence (Xilinx Zynq) UART support"
depends on OF
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index bba7b21a4a1d..fdd13f3dd058 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -90,6 +90,7 @@ obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o
obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
+obj-$(CONFIG_SERIAL_WK2XXX) += wk2xxx.o
obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
obj-$(CONFIG_SERIAL_ZS) += zs.o

diff --git a/drivers/tty/serial/wk2xxx.c b/drivers/tty/serial/wk2xxx.c
new file mode 100644
index 000000000000..15869fbdeab0
--- /dev/null
+++ b/drivers/tty/serial/wk2xxx.c
@@ -0,0 +1,1433 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * WK2xxx SPI to UART bridge tty serial driver
+ *
+ * SPI-to-UART bridge ICs from WKmic (Chengdu Weikai Microelectronics):
+ * WK2124, WK2132, WK2168, WK2202 and WK2204. Each IC exposes two or four
+ * full-duplex UART channels with 256-byte RX/TX FIFOs through a single SPI
+ * slave interface and one interrupt line. The slave register set is split
+ * into two banks (page 0 / page 1) selected by the SPAGE register.
+ *
+ * This driver is a rework of the WKmic open-source driver:
+ * https://github.com/britus/wk2xxx/blob/master/spi-wk2xxx.c
+ * It is modeled after the NXP sc16is7xx driver.
+ *
+ * (C) Copyright 2022 WKIC Ltd. by Xu XunWei Tech, Xuxunwei
+ * (C) Copyright 2024 EoF Software Labs, B. Eschrich
+ * Copyright (C) 2026 Zi Jie Zhao, EDATEC Technology Co., Ltd. <zjzhao@xxxxxxxxx>
+ */
+
+#include <linux/atomic.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/cache.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/kthread.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/overflow.h>
+#include <linux/property.h>
+#include <linux/sched.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/units.h>
+#include <linux/workqueue.h>
+
+#define WK2XXX_NAME "wk2xxx"
+#define WK2XXX_MAX_DEVS 8 /* Total number of lines. */
+#define WK2XXX_MAX_PORTS 4 /* Max number of ports per IC. */
+#define WK2XXX_FIFO_SIZE 256
+#define WK2XXX_MAX_SPI_LEN 30 /* Max bytes per SPI FIFO burst. */
+#define WK2XXX_MAX_TX_CHARS 200 /* Leave headroom in the TX FIFO. */
+#define WK2XXX_RXFIFO_LEVEL 0x40 /* RX FIFO trigger level. */
+#define WK2XXX_TXFIFO_LEVEL 0x01 /* TX FIFO trigger level. */
+#define WK2XXX_POLL_PERIOD_MS 10
+#define WK2XXX_IRQ_MAX_PASSES 8 /* Bound the IRQ drain loop. */
+
+/* SPI command byte: bit 6 = read, bit 7 = FIFO access. */
+#define WK2XXX_SPI_READ BIT(6)
+#define WK2XXX_SPI_FIFO_WRITE BIT(7)
+#define WK2XXX_SPI_FIFO_READ (BIT(7) | BIT(6))
+
+/* Marker used to address registers located in page 1. */
+#define WK2XXX_PAGE1 BIT(7)
+
+/* Global registers. */
+#define WK2XXX_GENA_REG 0x00 /* Global UART enable */
+#define WK2XXX_GRST_REG 0x01 /* Global reset */
+#define WK2XXX_GMUT_REG 0x02 /* Master UART control */
+#define WK2XXX_GIER_REG 0x10 /* Global interrupt enable */
+#define WK2XXX_GIFR_REG 0x11 /* Global interrupt flag */
+
+/* Port (sub-UART) registers, page 0. */
+#define WK2XXX_SPAGE_REG 0x03 /* Register page select */
+#define WK2XXX_SCR_REG 0x04 /* Slave control */
+#define WK2XXX_LCR_REG 0x05 /* Line control */
+#define WK2XXX_FCR_REG 0x06 /* FIFO control */
+#define WK2XXX_SIER_REG 0x07 /* Slave interrupt enable */
+#define WK2XXX_SIFR_REG 0x08 /* Slave interrupt flag */
+#define WK2XXX_TFCNT_REG 0x09 /* TX FIFO count */
+#define WK2XXX_RFCNT_REG 0x0a /* RX FIFO count */
+#define WK2XXX_FSR_REG 0x0b /* FIFO status */
+#define WK2XXX_LSR_REG 0x0c /* Line status */
+#define WK2XXX_FDAT_REG 0x0d /* FIFO data */
+#define WK2XXX_FWCR_REG 0x0e /* Flow control */
+#define WK2XXX_RS485_REG 0x0f /* RS485 control */
+
+/* Port (sub-UART) registers, page 1. */
+#define WK2XXX_BAUD1_REG (0x04 | WK2XXX_PAGE1) /* Divisor Latch High */
+#define WK2XXX_BAUD0_REG (0x05 | WK2XXX_PAGE1) /* Divisor Latch Low */
+#define WK2XXX_PRES_REG (0x06 | WK2XXX_PAGE1) /* Fractional divisor */
+#define WK2XXX_RFTL_REG (0x07 | WK2XXX_PAGE1) /* RX FIFO trigger level */
+#define WK2XXX_TFTL_REG (0x08 | WK2XXX_PAGE1) /* TX FIFO trigger level */
+#define WK2XXX_FWTH_REG (0x09 | WK2XXX_PAGE1) /* Flow control high level */
+#define WK2XXX_FWTL_REG (0x0a | WK2XXX_PAGE1) /* Flow control low level */
+#define WK2XXX_XON1_REG (0x0b | WK2XXX_PAGE1) /* Xon word */
+#define WK2XXX_XOFF1_REG (0x0c | WK2XXX_PAGE1) /* Xoff word */
+#define WK2XXX_SADR_REG (0x0d | WK2XXX_PAGE1) /* RS485 auto address */
+#define WK2XXX_SAEN_REG (0x0e | WK2XXX_PAGE1) /* RS485 address mask */
+#define WK2XXX_RRSDLY_REG (0x0f | WK2XXX_PAGE1) /* RS485 RTS delay */
+
+/* SCR register bits. */
+#define WK2XXX_SCR_RXEN_BIT BIT(0)
+#define WK2XXX_SCR_TXEN_BIT BIT(1)
+
+/* LCR register bits. */
+#define WK2XXX_LCR_STPL_BIT BIT(0) /* Two stop bits */
+#define WK2XXX_LCR_PAM0_BIT BIT(1) /* Parity mode bit 0 */
+#define WK2XXX_LCR_PAM1_BIT BIT(2) /* Parity mode bit 1 */
+#define WK2XXX_LCR_PAEN_BIT BIT(3) /* Parity enable */
+#define WK2XXX_LCR_BREAK_BIT BIT(5) /* TX break */
+
+/* SIER register bits. */
+#define WK2XXX_SIER_RFTRIG_IEN_BIT BIT(0) /* RX FIFO trigger */
+#define WK2XXX_SIER_RXOUT_IEN_BIT BIT(1) /* RX time-out */
+#define WK2XXX_SIER_TFTRIG_IEN_BIT BIT(2) /* TX FIFO trigger */
+
+/* SIFR register bits. */
+#define WK2XXX_SIFR_RFTRIG_INT_BIT BIT(0)
+#define WK2XXX_SIFR_RXOVT_INT_BIT BIT(1)
+#define WK2XXX_SIFR_TFTRIG_INT_BIT BIT(2)
+
+/* FSR register bits. */
+#define WK2XXX_FSR_TBUSY_BIT BIT(0)
+#define WK2XXX_FSR_TFULL_BIT BIT(1)
+#define WK2XXX_FSR_TDAT_BIT BIT(2)
+#define WK2XXX_FSR_RDAT_BIT BIT(3)
+#define WK2XXX_FSR_RFPE_BIT BIT(4) /* RX FIFO parity error */
+#define WK2XXX_FSR_RFFE_BIT BIT(5) /* RX FIFO frame error */
+#define WK2XXX_FSR_RFBI_BIT BIT(6) /* RX FIFO break */
+#define WK2XXX_FSR_RFOE_BIT BIT(7) /* RX FIFO overrun */
+#define WK2XXX_FSR_ERR_MASK GENMASK(7, 4)
+
+/* LSR error bits, for use with uart_insert_char(). */
+#define WK2XXX_LSR_PE_BIT BIT(0)
+#define WK2XXX_LSR_FE_BIT BIT(1)
+#define WK2XXX_LSR_BI_BIT BIT(2)
+#define WK2XXX_LSR_OE_BIT BIT(3)
+#define WK2XXX_LSR_BRK_ERROR_MASK (WK2XXX_LSR_OE_BIT | WK2XXX_LSR_PE_BIT | \
+ WK2XXX_LSR_FE_BIT | WK2XXX_LSR_BI_BIT)
+/* Internal marker: drop all received data (termios CREAD is clear). */
+#define WK2XXX_LSR_IGNORE_DATA BIT(7)
+
+/*
+ * FWCR register bits. The flow-control mode is selected by the FWM2-0
+ * field in bits 6-4 (the two-channel variants have no FWCR register).
+ */
+#define WK2XXX_FWCR_FWM_MASK GENMASK(6, 4)
+#define WK2XXX_FWCR_FWM_RTS_CTS FIELD_PREP(WK2XXX_FWCR_FWM_MASK, 0x3)
+
+/* RS485 register bits. */
+#define WK2XXX_RS485_RTSINV_BIT BIT(0)
+#define WK2XXX_RS485_RTSEN_BIT BIT(1)
+#define WK2XXX_RS485_RSRS485_BIT BIT(6)
+
+struct wk2xxx_devtype {
+ const char *name;
+ int nr_uart;
+ bool has_hw_flow_control;
+ bool has_rs485;
+};
+
+#define WK2XXX_RECONF_IER BIT(0)
+#define WK2XXX_RECONF_RS485 BIT(1)
+
+struct wk2xxx_one_config {
+ unsigned int flags;
+ u8 ier_mask;
+ u8 ier_val;
+};
+
+struct wk2xxx_one {
+ struct uart_port port;
+ struct mutex tx_lock; /* Serializes the TX path. */
+ struct kthread_work tx_work;
+ struct kthread_work reg_work;
+ struct wk2xxx_one_config config;
+ unsigned char buf[WK2XXX_FIFO_SIZE]; /* RX buffer. */
+};
+
+struct wk2xxx_port {
+ const struct wk2xxx_devtype *devtype;
+ struct spi_device *spi;
+ struct clk *clk;
+ struct mutex reg_lock; /* SPI register access. */
+ struct mutex poll_lock; /* Serializes polling start/stop. */
+ struct kthread_worker kworker;
+ struct task_struct *kworker_task;
+ struct kthread_delayed_work poll_work;
+ bool polling;
+ bool irq_requested;
+ atomic_t open_ports;
+ /*
+ * Shared SPI transfer buffers. All SPI accesses are serialized by
+ * s->reg_lock, so these are never used concurrently. Each buffer is
+ * cache-line aligned and sized to a full cache line, so DMA cache
+ * maintenance on one of them can never invalidate a line shared
+ * with any other field (notably the p[] array below).
+ */
+ u8 spi_tx[L1_CACHE_BYTES] ____cacheline_aligned;
+ u8 spi_rx[L1_CACHE_BYTES] ____cacheline_aligned;
+ struct wk2xxx_one p[];
+};
+
+static_assert(WK2XXX_MAX_SPI_LEN + 1 <= L1_CACHE_BYTES);
+
+static DEFINE_IDA(wk2xxx_lines);
+
+static struct uart_driver wk2xxx_uart = {
+ .owner = THIS_MODULE,
+ .driver_name = WK2XXX_NAME,
+ .dev_name = "ttyWK",
+ .nr = WK2XXX_MAX_DEVS,
+};
+
+#define to_wk2xxx_one(p, e) ((container_of((p), struct wk2xxx_one, e)))
+
+static const struct wk2xxx_devtype wk2124_devtype = {
+ .name = "WK2124",
+ .nr_uart = 4,
+ .has_hw_flow_control = true,
+ .has_rs485 = true,
+};
+
+static const struct wk2xxx_devtype wk2132_devtype = {
+ .name = "WK2132",
+ .nr_uart = 2,
+};
+
+static const struct wk2xxx_devtype wk2168_devtype = {
+ .name = "WK2168",
+ .nr_uart = 4,
+ .has_hw_flow_control = true,
+ .has_rs485 = true,
+};
+
+static const struct wk2xxx_devtype wk2202_devtype = {
+ .name = "WK2202",
+ .nr_uart = 2,
+};
+
+static const struct wk2xxx_devtype wk2204_devtype = {
+ .name = "WK2204",
+ .nr_uart = 4,
+ .has_hw_flow_control = true,
+ .has_rs485 = true,
+};
+
+/*
+ * The following functions are the low-level SPI accessors. The caller must
+ * hold s->reg_lock, so that multi-byte accesses and page switches are
+ * performed atomically on the SPI bus.
+ */
+static int wk2xxx_spi_transfer(struct wk2xxx_port *s, const u8 *tx, u8 *rx,
+ unsigned int len)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = tx,
+ .rx_buf = rx,
+ .len = len,
+ };
+ struct spi_message msg;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ return spi_sync(s->spi, &msg);
+}
+
+static int wk2xxx_raw_read(struct wk2xxx_port *s, u8 addr, u8 *val)
+{
+ u8 *tx = s->spi_tx;
+ u8 *rx = s->spi_rx;
+ int ret;
+
+ tx[0] = WK2XXX_SPI_READ | addr;
+ tx[1] = 0;
+ ret = wk2xxx_spi_transfer(s, tx, rx, 2);
+ if (ret) {
+ *val = 0;
+ return ret;
+ }
+
+ *val = rx[1];
+ return 0;
+}
+
+static int wk2xxx_raw_write(struct wk2xxx_port *s, u8 addr, u8 val)
+{
+ u8 *tx = s->spi_tx;
+ u8 *rx = s->spi_rx;
+
+ tx[0] = addr;
+ tx[1] = val;
+
+ return wk2xxx_spi_transfer(s, tx, rx, 2);
+}
+
+static unsigned int wk2xxx_port_addr(unsigned int portno, u8 reg)
+{
+ /* The sub-UART number is encoded in the upper nibble of the cmd byte. */
+ return (portno << 4) | reg;
+}
+
+static int wk2xxx_raw_port_read(struct wk2xxx_port *s, unsigned int portno,
+ u8 reg, u8 *val)
+{
+ int ret;
+
+ if (reg & WK2XXX_PAGE1) {
+ ret = wk2xxx_raw_write(s, wk2xxx_port_addr(portno, WK2XXX_SPAGE_REG), 1);
+ if (ret)
+ return ret;
+ ret = wk2xxx_raw_read(s, wk2xxx_port_addr(portno, reg & 0x0f), val);
+ wk2xxx_raw_write(s, wk2xxx_port_addr(portno, WK2XXX_SPAGE_REG), 0);
+ return ret;
+ }
+
+ return wk2xxx_raw_read(s, wk2xxx_port_addr(portno, reg & 0x0f), val);
+}
+
+static int wk2xxx_raw_port_write(struct wk2xxx_port *s, unsigned int portno,
+ u8 reg, u8 val)
+{
+ int ret;
+
+ if (reg & WK2XXX_PAGE1) {
+ ret = wk2xxx_raw_write(s, wk2xxx_port_addr(portno, WK2XXX_SPAGE_REG), 1);
+ if (ret)
+ return ret;
+ ret = wk2xxx_raw_write(s, wk2xxx_port_addr(portno, reg & 0x0f), val);
+ wk2xxx_raw_write(s, wk2xxx_port_addr(portno, WK2XXX_SPAGE_REG), 0);
+ return ret;
+ }
+
+ return wk2xxx_raw_write(s, wk2xxx_port_addr(portno, reg & 0x0f), val);
+}
+
+/*
+ * Locked wrappers used outside the register sequences that already hold
+ * s->reg_lock.
+ */
+static int wk2xxx_reg_read(struct wk2xxx_port *s, u8 reg, u8 *val)
+{
+ guard(mutex)(&s->reg_lock);
+ return wk2xxx_raw_read(s, reg, val);
+}
+
+static int wk2xxx_reg_write(struct wk2xxx_port *s, u8 addr, u8 val)
+{
+ guard(mutex)(&s->reg_lock);
+ return wk2xxx_raw_write(s, addr, val);
+}
+
+static int wk2xxx_port_reg_read(struct wk2xxx_port *s, unsigned int portno,
+ u8 reg, u8 *val)
+{
+ guard(mutex)(&s->reg_lock);
+ return wk2xxx_raw_port_read(s, portno, reg, val);
+}
+
+static int wk2xxx_port_reg_write(struct wk2xxx_port *s, unsigned int portno,
+ u8 reg, u8 val)
+{
+ guard(mutex)(&s->reg_lock);
+ return wk2xxx_raw_port_write(s, portno, reg, val);
+}
+
+static void wk2xxx_port_reg_update(struct wk2xxx_port *s, unsigned int portno,
+ u8 reg, u8 mask, u8 val)
+{
+ u8 r = 0;
+
+ guard(mutex)(&s->reg_lock);
+ if (wk2xxx_raw_port_read(s, portno, reg, &r))
+ return;
+ wk2xxx_raw_port_write(s, portno, reg, (r & ~mask) | val);
+}
+
+static int wk2xxx_fifo_read(struct wk2xxx_port *s, unsigned int portno,
+ u8 *buf, unsigned int len)
+{
+ u8 *tx = s->spi_tx;
+ u8 *rx = s->spi_rx;
+ int ret;
+
+ if (len == 0 || len > WK2XXX_MAX_SPI_LEN)
+ return -EINVAL;
+
+ /*
+ * Take the register lock before touching the shared SPI buffers so a
+ * concurrent transfer cannot observe (or be corrupted by) a partially
+ * constructed command.
+ */
+ guard(mutex)(&s->reg_lock);
+
+ memset(tx, 0, WK2XXX_MAX_SPI_LEN + 1);
+ tx[0] = wk2xxx_port_addr(portno, WK2XXX_SPI_FIFO_READ);
+
+ ret = wk2xxx_spi_transfer(s, tx, rx, len + 1);
+ if (ret)
+ return ret;
+
+ memcpy(buf, rx + 1, len);
+ return 0;
+}
+
+static int wk2xxx_fifo_write(struct wk2xxx_port *s, unsigned int portno,
+ const u8 *buf, unsigned int len)
+{
+ u8 *tx = s->spi_tx;
+ u8 *rx = s->spi_rx;
+
+ if (len == 0 || len > WK2XXX_MAX_SPI_LEN)
+ return -EINVAL;
+
+ /*
+ * Take the register lock before touching the shared SPI buffers so a
+ * concurrent transfer cannot observe (or be corrupted by) a partially
+ * constructed command.
+ */
+ guard(mutex)(&s->reg_lock);
+
+ tx[0] = wk2xxx_port_addr(portno, WK2XXX_SPI_FIFO_WRITE);
+ memcpy(tx + 1, buf, len);
+
+ return wk2xxx_spi_transfer(s, tx, rx, len + 1);
+}
+
+static void wk2xxx_ier_set(struct uart_port *port, u8 bit)
+{
+ struct wk2xxx_one *one = to_wk2xxx_one(port, port);
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+
+ lockdep_assert_held_once(&port->lock);
+
+ one->config.flags |= WK2XXX_RECONF_IER;
+ one->config.ier_mask |= bit;
+ one->config.ier_val |= bit;
+ kthread_queue_work(&s->kworker, &one->reg_work);
+}
+
+static void wk2xxx_ier_clear(struct uart_port *port, u8 bit)
+{
+ struct wk2xxx_one *one = to_wk2xxx_one(port, port);
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+
+ lockdep_assert_held_once(&port->lock);
+
+ one->config.flags |= WK2XXX_RECONF_IER;
+ one->config.ier_mask |= bit;
+ one->config.ier_val &= ~bit;
+ kthread_queue_work(&s->kworker, &one->reg_work);
+}
+
+static void wk2xxx_stop_tx(struct uart_port *port)
+{
+ wk2xxx_ier_clear(port, WK2XXX_SIER_TFTRIG_IEN_BIT);
+}
+
+static void wk2xxx_stop_rx(struct uart_port *port)
+{
+ wk2xxx_ier_clear(port, WK2XXX_SIER_RFTRIG_IEN_BIT |
+ WK2XXX_SIER_RXOUT_IEN_BIT);
+}
+
+static void wk2xxx_throttle(struct uart_port *port)
+{
+ /*
+ * Stop draining the RX FIFO to apply back-pressure. The RX time-out
+ * interrupt must be disabled too, otherwise remaining FIFO data would
+ * still be pushed out and defeat the flow control request.
+ */
+ guard(uart_port_lock_irqsave)(port);
+ wk2xxx_ier_clear(port, WK2XXX_SIER_RFTRIG_IEN_BIT |
+ WK2XXX_SIER_RXOUT_IEN_BIT);
+}
+
+static void wk2xxx_unthrottle(struct uart_port *port)
+{
+ guard(uart_port_lock_irqsave)(port);
+ wk2xxx_ier_set(port, WK2XXX_SIER_RFTRIG_IEN_BIT |
+ WK2XXX_SIER_RXOUT_IEN_BIT);
+}
+
+static void wk2xxx_handle_tx(struct uart_port *port)
+{
+ struct wk2xxx_one *one = to_wk2xxx_one(port, port);
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+ struct tty_port *tport = &port->state->port;
+ unsigned int portno = port->iobase;
+ unsigned int txlen, to_send, sent;
+ const unsigned char *tail;
+ u8 x_char, fsr, tfcnt;
+
+ guard(mutex)(&one->tx_lock);
+
+ /*
+ * The serial core sets x_char and then calls start_tx() under the
+ * port lock, so snapshot it under the same lock.
+ */
+ scoped_guard(uart_port_lock_irqsave, port) {
+ x_char = port->x_char;
+ port->x_char = 0;
+
+ /* Nothing queued and no x_char: nothing to do. */
+ if (!x_char &&
+ (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))) {
+ wk2xxx_stop_tx(port);
+ return;
+ }
+ }
+
+ if (x_char) {
+ /*
+ * A high-priority x_char goes out first, but do not return
+ * here: pending xmit FIFO data still has to be drained (and
+ * the TX trigger re-armed) below, otherwise it would be
+ * stranded until the next start_tx().
+ */
+ wk2xxx_port_reg_write(s, portno, WK2XXX_FDAT_REG, x_char);
+ scoped_guard(uart_port_lock_irqsave, port)
+ port->icount.tx++;
+ }
+
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
+ scoped_guard(uart_port_lock_irqsave, port) {
+ wk2xxx_stop_tx(port);
+ }
+ return;
+ }
+
+ /* Limit to the free space available in the TX FIFO. */
+ if (wk2xxx_port_reg_read(s, portno, WK2XXX_TFCNT_REG, &tfcnt))
+ return;
+ if (tfcnt == 0) {
+ if (wk2xxx_port_reg_read(s, portno, WK2XXX_FSR_REG, &fsr))
+ return;
+ txlen = (fsr & WK2XXX_FSR_TFULL_BIT) ? 0 : WK2XXX_FIFO_SIZE;
+ } else {
+ txlen = WK2XXX_FIFO_SIZE - tfcnt;
+ }
+ if (txlen > WK2XXX_MAX_TX_CHARS)
+ txlen = WK2XXX_MAX_TX_CHARS;
+
+ to_send = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
+ sent = 0;
+ while (to_send) {
+ unsigned int chunk = min_t(unsigned int, to_send,
+ WK2XXX_MAX_SPI_LEN);
+
+ if (wk2xxx_fifo_write(s, portno, tail, chunk))
+ break;
+ tail += chunk;
+ to_send -= chunk;
+ sent += chunk;
+ }
+ uart_xmit_advance(port, sent);
+
+ scoped_guard(uart_port_lock_irqsave, port) {
+ if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
+ uart_write_wakeup(port);
+
+ if (kfifo_is_empty(&tport->xmit_fifo))
+ wk2xxx_stop_tx(port);
+ else
+ wk2xxx_ier_set(port, WK2XXX_SIER_TFTRIG_IEN_BIT);
+ }
+}
+
+static void wk2xxx_handle_rx(struct uart_port *port)
+{
+ struct wk2xxx_one *one = to_wk2xxx_one(port, port);
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+ unsigned int portno = port->iobase;
+ unsigned int i, rxlen, len_p, chunk;
+ u8 fsr = 0, rfcnt = 0, lsr = 0, flag = TTY_NORMAL;
+
+ if (wk2xxx_port_reg_read(s, portno, WK2XXX_FSR_REG, &fsr))
+ return;
+
+ if (!(fsr & WK2XXX_FSR_RDAT_BIT))
+ return;
+
+ /* Get the number of bytes available in the RX FIFO. */
+ if (wk2xxx_port_reg_read(s, portno, WK2XXX_RFCNT_REG, &rfcnt))
+ return;
+ if (rfcnt == 0) {
+ /* The count may race with the FIFO status bit; retry once. */
+ if (wk2xxx_port_reg_read(s, portno, WK2XXX_RFCNT_REG, &rfcnt))
+ return;
+ rxlen = rfcnt ? rfcnt : WK2XXX_FIFO_SIZE;
+ } else {
+ rxlen = rfcnt;
+ }
+
+ /* Read the FIFO contents in chunks. */
+ len_p = 0;
+ while (rxlen) {
+ chunk = min_t(unsigned int, rxlen, WK2XXX_MAX_SPI_LEN);
+ if (wk2xxx_fifo_read(s, portno, one->buf + len_p, chunk))
+ break;
+ len_p += chunk;
+ rxlen -= chunk;
+ }
+ rxlen = len_p;
+
+ /* Map the FIFO status register error flags to line status. */
+ if (fsr & WK2XXX_FSR_ERR_MASK) {
+ if (fsr & WK2XXX_FSR_RFPE_BIT) {
+ port->icount.parity++;
+ lsr |= WK2XXX_LSR_PE_BIT;
+ flag = TTY_PARITY;
+ }
+ if (fsr & WK2XXX_FSR_RFFE_BIT) {
+ port->icount.frame++;
+ lsr |= WK2XXX_LSR_FE_BIT;
+ flag = TTY_FRAME;
+ }
+ if (fsr & WK2XXX_FSR_RFOE_BIT) {
+ port->icount.overrun++;
+ lsr |= WK2XXX_LSR_OE_BIT;
+ flag = TTY_OVERRUN;
+ }
+ if (fsr & WK2XXX_FSR_RFBI_BIT) {
+ port->icount.brk++;
+ lsr |= WK2XXX_LSR_BI_BIT;
+ flag = TTY_BREAK;
+ }
+ }
+
+ port->icount.rx += rxlen;
+
+ /* CREAD is clear: drain the FIFO and drop all received data. */
+ if (port->ignore_status_mask & WK2XXX_LSR_IGNORE_DATA)
+ return;
+
+ for (i = 0; i < rxlen; ++i) {
+ u8 ch = one->buf[i];
+
+ if (uart_handle_sysrq_char(port, ch))
+ continue;
+
+ if (lsr & port->ignore_status_mask)
+ continue;
+
+ uart_insert_char(port, lsr, WK2XXX_LSR_OE_BIT, ch, flag);
+ }
+
+ tty_flip_buffer_push(&port->state->port);
+}
+
+static bool wk2xxx_port_irq(struct wk2xxx_port *s, unsigned int portno)
+{
+ struct uart_port *port = &s->p[portno].port;
+ u8 sifr = 0, sier = 0;
+ bool rc = false;
+
+ if (wk2xxx_port_reg_read(s, portno, WK2XXX_SIFR_REG, &sifr) ||
+ wk2xxx_port_reg_read(s, portno, WK2XXX_SIER_REG, &sier))
+ return false;
+
+ if (sifr & (WK2XXX_SIFR_RFTRIG_INT_BIT | WK2XXX_SIFR_RXOVT_INT_BIT)) {
+ wk2xxx_handle_rx(port);
+ rc = true;
+ }
+
+ if ((sifr & WK2XXX_SIFR_TFTRIG_INT_BIT) &&
+ (sier & WK2XXX_SIER_TFTRIG_IEN_BIT)) {
+ wk2xxx_handle_tx(port);
+ rc = true;
+ }
+
+ return rc;
+}
+
+static irqreturn_t wk2xxx_irq(int irq, void *dev_id)
+{
+ struct wk2xxx_port *s = dev_id;
+ bool handled = false;
+ bool keep_polling;
+ int passes = WK2XXX_IRQ_MAX_PASSES;
+
+ do {
+ u8 gifr;
+ int i;
+
+ keep_polling = false;
+
+ if (wk2xxx_reg_read(s, WK2XXX_GIFR_REG, &gifr))
+ return IRQ_NONE; /* Bus error; spurious handling applies. */
+
+ if (!gifr)
+ break;
+
+ handled = true;
+
+ for (i = 0; i < s->devtype->nr_uart; ++i)
+ if (gifr & BIT(i))
+ keep_polling |= wk2xxx_port_irq(s, i);
+ } while (keep_polling && !s->polling && --passes);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void wk2xxx_poll_proc(struct kthread_work *ws)
+{
+ struct wk2xxx_port *s = container_of(ws, struct wk2xxx_port,
+ poll_work.work);
+
+ /* Reuse the IRQ handler; the interrupt ID is unused here. */
+ wk2xxx_irq(0, s);
+
+ /*
+ * Only keep polling while at least one port is open. The last
+ * shutdown cancels the pending instance (see wk2xxx_shutdown); a
+ * poll that is already running when that happens must not re-queue
+ * itself, otherwise it would outlive the cancelled one.
+ */
+ if (atomic_read(&s->open_ports) > 0)
+ kthread_queue_delayed_work(&s->kworker, &s->poll_work,
+ msecs_to_jiffies(WK2XXX_POLL_PERIOD_MS));
+}
+
+static void wk2xxx_tx_proc(struct kthread_work *ws)
+{
+ struct uart_port *port = &(to_wk2xxx_one(ws, tx_work)->port);
+
+ wk2xxx_handle_tx(port);
+}
+
+static void wk2xxx_start_tx(struct uart_port *port)
+{
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+ struct wk2xxx_one *one = to_wk2xxx_one(port, port);
+
+ kthread_queue_work(&s->kworker, &one->tx_work);
+}
+
+static void wk2xxx_reconf_rs485(struct uart_port *port)
+{
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+ unsigned long irqflags;
+ u8 rs485 = 0;
+
+ /*
+ * TIOCSRS485 updates port->rs485 under the port lock, so read the
+ * flags under the same lock before programming the chip.
+ */
+ uart_port_lock_irqsave(port, &irqflags);
+ if (port->rs485.flags & SER_RS485_ENABLED) {
+ rs485 = WK2XXX_RS485_RSRS485_BIT | WK2XXX_RS485_RTSEN_BIT;
+ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ rs485 |= WK2XXX_RS485_RTSINV_BIT;
+ }
+ uart_port_unlock_irqrestore(port, irqflags);
+
+ wk2xxx_port_reg_write(s, port->iobase, WK2XXX_RS485_REG, rs485);
+}
+
+static int wk2xxx_config_rs485(struct uart_port *port, struct ktermios *termios,
+ struct serial_rs485 *rs485)
+{
+ struct wk2xxx_one *one = to_wk2xxx_one(port, port);
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+
+ if (!s->devtype->has_rs485)
+ return -EOPNOTSUPP;
+
+ /*
+ * RTS is driven by hardware and its timing cannot be influenced
+ * from the driver. Non-zero RTS delays are rejected (sanitized to
+ * zero) by the serial core.
+ */
+ one->config.flags |= WK2XXX_RECONF_RS485;
+ kthread_queue_work(&s->kworker, &one->reg_work);
+
+ return 0;
+}
+
+static void wk2xxx_reg_proc(struct kthread_work *ws)
+{
+ struct wk2xxx_one *one = to_wk2xxx_one(ws, reg_work);
+ struct wk2xxx_port *s = dev_get_drvdata(one->port.dev);
+ struct wk2xxx_one_config config;
+ unsigned long irqflags;
+
+ uart_port_lock_irqsave(&one->port, &irqflags);
+ config = one->config;
+ memset(&one->config, 0, sizeof(one->config));
+ uart_port_unlock_irqrestore(&one->port, irqflags);
+
+ if (config.flags & WK2XXX_RECONF_IER)
+ wk2xxx_port_reg_update(s, one->port.iobase, WK2XXX_SIER_REG,
+ config.ier_mask, config.ier_val);
+
+ if (config.flags & WK2XXX_RECONF_RS485)
+ wk2xxx_reconf_rs485(&one->port);
+}
+
+static unsigned int wk2xxx_tx_empty(struct uart_port *port)
+{
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+ u8 fsr = 0;
+
+ if (wk2xxx_port_reg_read(s, port->iobase, WK2XXX_FSR_REG, &fsr))
+ return TIOCSER_TEMT;
+
+ return (fsr & (WK2XXX_FSR_TDAT_BIT | WK2XXX_FSR_TBUSY_BIT)) ? 0 :
+ TIOCSER_TEMT;
+}
+
+static unsigned int wk2xxx_get_mctrl(struct uart_port *port)
+{
+ /* The WK2xxx does not expose modem control lines. */
+ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void wk2xxx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+ /* The WK2xxx does not support modem control lines. */
+}
+
+static void wk2xxx_enable_ms(struct uart_port *port)
+{
+ /* The WK2xxx does not have modem status registers. */
+}
+
+static void wk2xxx_break_ctl(struct uart_port *port, int break_state)
+{
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+
+ wk2xxx_port_reg_update(s, port->iobase, WK2XXX_LCR_REG,
+ WK2XXX_LCR_BREAK_BIT,
+ break_state ? WK2XXX_LCR_BREAK_BIT : 0);
+}
+
+/*
+ * Configure a sub-UART: disable interrupts and TX/RX, program the line
+ * control and baud rate registers and restore the previous state.
+ */
+static void wk2xxx_conf_port(struct uart_port *port, u8 lcr, u8 fwcr,
+ u8 baud0, u8 baud1, u8 pres)
+{
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+ unsigned int portno = port->iobase;
+ u8 sier, scr, fsr;
+ int count = 200;
+
+ guard(mutex)(&s->reg_lock);
+
+ /* Disable all sub-UART interrupts. */
+ wk2xxx_raw_port_read(s, portno, WK2XXX_SIER_REG, &sier);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SIER_REG, 0);
+
+ /* Wait for the transmitter to become idle. */
+ do {
+ wk2xxx_raw_port_read(s, portno, WK2XXX_FSR_REG, &fsr);
+ } while ((fsr & WK2XXX_FSR_TBUSY_BIT) && count--);
+
+ /* Disable the transmitter and receiver. */
+ wk2xxx_raw_port_read(s, portno, WK2XXX_SCR_REG, &scr);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SCR_REG,
+ scr & ~(WK2XXX_SCR_TXEN_BIT |
+ WK2XXX_SCR_RXEN_BIT));
+
+ /* Program the line control register. */
+ wk2xxx_raw_port_write(s, portno, WK2XXX_LCR_REG, lcr);
+
+ /* Configure hardware flow control levels. */
+ if (fwcr) {
+ wk2xxx_raw_port_write(s, portno, WK2XXX_FWCR_REG, fwcr);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SPAGE_REG, 1);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_FWTH_REG, 0xf0);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_FWTL_REG, 0x80);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SPAGE_REG, 0);
+ }
+
+ /* Program the baud rate generator (page 1 registers). */
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SPAGE_REG, 1);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_BAUD0_REG, baud0);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_BAUD1_REG, baud1);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_PRES_REG, pres);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SPAGE_REG, 0);
+
+ /* Re-enable the transmitter and receiver. */
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SCR_REG,
+ scr | (WK2XXX_SCR_TXEN_BIT |
+ WK2XXX_SCR_RXEN_BIT));
+
+ /* Restore the interrupt enable register. */
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SIER_REG, sier);
+}
+
+static void wk2xxx_calc_divisor(unsigned long clk, unsigned int baud,
+ u8 *baud0, u8 *baud1, u8 *pres)
+{
+ unsigned int div, rem;
+
+ /* Never divide by zero; the serial core normally prevents this. */
+ if (baud == 0)
+ baud = 9600;
+
+ div = clk / (baud * 16);
+ if (div == 0)
+ div = 1;
+ div--;
+ *baud0 = div & 0xff;
+ *baud1 = (div >> 8) & 0xff;
+
+ rem = clk % (baud * 16);
+ *pres = (u32)div_u64((u64)rem * 100, baud);
+ *pres = (*pres + 50) / 100;
+}
+
+static void wk2xxx_set_termios(struct uart_port *port, struct ktermios *termios,
+ const struct ktermios *old)
+{
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+ unsigned int baud, read_mask, ignore_mask;
+ u8 lcr = 0, fwcr = 0;
+ u8 baud0, baud1, pres;
+
+ /* The WK2xxx supports 8 data bits only. */
+ termios->c_cflag &= ~CSIZE;
+ termios->c_cflag |= CS8;
+
+ /* Parity. */
+ if (termios->c_cflag & PARENB) {
+ lcr |= WK2XXX_LCR_PAEN_BIT;
+ switch (termios->c_cflag & (PARODD | CMSPAR)) {
+ case 0:
+ lcr |= WK2XXX_LCR_PAM1_BIT; /* even */
+ break;
+ case PARODD:
+ lcr |= WK2XXX_LCR_PAM0_BIT; /* odd */
+ break;
+ case CMSPAR:
+ break; /* space */
+ case PARODD | CMSPAR:
+ lcr |= WK2XXX_LCR_PAM1_BIT |
+ WK2XXX_LCR_PAM0_BIT; /* mark */
+ break;
+ }
+ }
+
+ /* Stop bits. */
+ if (termios->c_cflag & CSTOPB)
+ lcr |= WK2XXX_LCR_STPL_BIT;
+
+ /* Determine the status masks to publish. */
+ read_mask = WK2XXX_LSR_OE_BIT;
+ if (termios->c_iflag & INPCK)
+ read_mask |= WK2XXX_LSR_PE_BIT | WK2XXX_LSR_FE_BIT;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ read_mask |= WK2XXX_LSR_BI_BIT;
+
+ ignore_mask = 0;
+ if (termios->c_iflag & IGNBRK)
+ ignore_mask |= WK2XXX_LSR_BI_BIT;
+ if (!(termios->c_cflag & CREAD))
+ ignore_mask |= WK2XXX_LSR_BRK_ERROR_MASK |
+ WK2XXX_LSR_IGNORE_DATA;
+
+ /* The two-channel variants do not implement hardware flow control. */
+ if (!s->devtype->has_hw_flow_control)
+ termios->c_cflag &= ~CRTSCTS;
+
+ /* Hardware flow control is configured in the chip below. */
+ if (s->devtype->has_hw_flow_control && (termios->c_cflag & CRTSCTS))
+ fwcr = WK2XXX_FWCR_FWM_RTS_CTS;
+
+ /* Get the baud rate generator configuration. */
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+ port->uartclk / 16);
+
+ wk2xxx_calc_divisor(port->uartclk, baud, &baud0, &baud1, &pres);
+ wk2xxx_conf_port(port, lcr, fwcr, baud0, baud1, pres);
+
+ /*
+ * Publish the masks and flow-control status under the port lock; the
+ * RX/TX paths read them from their kthread context.
+ */
+ guard(uart_port_lock_irqsave)(port);
+ port->read_status_mask = read_mask;
+ port->ignore_status_mask = ignore_mask;
+ port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
+ if (s->devtype->has_hw_flow_control && (termios->c_cflag & CRTSCTS))
+ port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
+ uart_update_timeout(port, termios->c_cflag, baud);
+}
+
+static int wk2xxx_startup(struct uart_port *port)
+{
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+ unsigned int portno = port->iobase;
+ u8 reg;
+
+ scoped_guard(mutex, &s->reg_lock) {
+ /*
+ * GENA and GIER are shared by all ports on the chip: a failed
+ * read must not be turned into a write of only this port's
+ * bit, which would clear the sibling ports' bits.
+ */
+ if (wk2xxx_raw_read(s, WK2XXX_GENA_REG, &reg))
+ return -EIO;
+ reg |= BIT(portno);
+ wk2xxx_raw_write(s, WK2XXX_GENA_REG, reg);
+
+ /* Reset the sub-UART. */
+ wk2xxx_raw_write(s, WK2XXX_GRST_REG, BIT(portno));
+
+ /* Enable the sub-UART interrupt in the global mask. */
+ if (wk2xxx_raw_read(s, WK2XXX_GIER_REG, &reg))
+ return -EIO;
+ reg |= BIT(portno);
+ wk2xxx_raw_write(s, WK2XXX_GIER_REG, reg);
+
+ /* Enable RX FIFO trigger and RX time-out interrupts. */
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SIER_REG,
+ WK2XXX_SIER_RFTRIG_IEN_BIT |
+ WK2XXX_SIER_RXOUT_IEN_BIT);
+
+ /* Enable the transmitter and receiver. */
+ wk2xxx_raw_port_read(s, portno, WK2XXX_SCR_REG, &reg);
+ reg |= WK2XXX_SCR_TXEN_BIT | WK2XXX_SCR_RXEN_BIT;
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SCR_REG, reg);
+
+ /* Reset and configure the FIFOs. */
+ wk2xxx_raw_port_write(s, portno, WK2XXX_FCR_REG, 0xff);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_FCR_REG, 0xfc);
+
+ /* Set the RX/TX FIFO trigger levels. */
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SPAGE_REG, 1);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_RFTL_REG,
+ WK2XXX_RXFIFO_LEVEL);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_TFTL_REG,
+ WK2XXX_TXFIFO_LEVEL);
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SPAGE_REG, 0);
+ }
+
+ kfifo_reset(&port->state->port.xmit_fifo);
+
+ /* Start the shared polling loop when the first port is opened. */
+ if (s->polling) {
+ guard(mutex)(&s->poll_lock);
+ if (atomic_inc_return(&s->open_ports) == 1)
+ kthread_queue_delayed_work(&s->kworker, &s->poll_work,
+ msecs_to_jiffies(WK2XXX_POLL_PERIOD_MS));
+ }
+
+ return 0;
+}
+
+static void wk2xxx_shutdown(struct uart_port *port)
+{
+ struct wk2xxx_port *s = dev_get_drvdata(port->dev);
+ unsigned int portno = port->iobase;
+ u8 reg;
+
+ scoped_guard(mutex, &s->reg_lock) {
+ /* Disable the sub-UART interrupt in the global mask. */
+ if (wk2xxx_raw_read(s, WK2XXX_GIER_REG, &reg))
+ return;
+ reg &= ~BIT(portno);
+ wk2xxx_raw_write(s, WK2XXX_GIER_REG, reg);
+
+ /* Disable all sub-UART interrupts. */
+ wk2xxx_raw_port_write(s, portno, WK2XXX_SIER_REG, 0);
+
+ /* Reset the sub-UART. */
+ wk2xxx_raw_read(s, WK2XXX_GRST_REG, &reg);
+ reg |= BIT(portno);
+ wk2xxx_raw_write(s, WK2XXX_GRST_REG, reg);
+
+ /* Disable the sub-UART. */
+ if (wk2xxx_raw_read(s, WK2XXX_GENA_REG, &reg))
+ return;
+ reg &= ~BIT(portno);
+ wk2xxx_raw_write(s, WK2XXX_GENA_REG, reg);
+ }
+
+ /*
+ * Stop the shared polling loop once the last port is closed. The
+ * check and the cancel are serialized by poll_lock against a
+ * concurrent open of another port, so the two cannot tear the
+ * open_ports 0/1 boundary in a way that leaves the loop cancelled
+ * while a port is still open.
+ */
+ if (s->polling) {
+ guard(mutex)(&s->poll_lock);
+ if (atomic_dec_return(&s->open_ports) == 0)
+ kthread_cancel_delayed_work_sync(&s->poll_work);
+ }
+
+ kthread_flush_worker(&s->kworker);
+}
+
+static const char *wk2xxx_type(struct uart_port *port)
+{
+ return (port->type == PORT_WK2XXX) ? WK2XXX_NAME : NULL;
+}
+
+static void wk2xxx_config_port(struct uart_port *port, int flags)
+{
+ if (flags & UART_CONFIG_TYPE)
+ port->type = PORT_WK2XXX;
+}
+
+static int wk2xxx_verify_port(struct uart_port *port, struct serial_struct *s)
+{
+ if (s->type != PORT_UNKNOWN && s->type != PORT_WK2XXX)
+ return -EINVAL;
+ if (s->irq != port->irq)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct uart_ops wk2xxx_ops = {
+ .tx_empty = wk2xxx_tx_empty,
+ .set_mctrl = wk2xxx_set_mctrl,
+ .get_mctrl = wk2xxx_get_mctrl,
+ .stop_tx = wk2xxx_stop_tx,
+ .start_tx = wk2xxx_start_tx,
+ .throttle = wk2xxx_throttle,
+ .unthrottle = wk2xxx_unthrottle,
+ .stop_rx = wk2xxx_stop_rx,
+ .enable_ms = wk2xxx_enable_ms,
+ .break_ctl = wk2xxx_break_ctl,
+ .startup = wk2xxx_startup,
+ .shutdown = wk2xxx_shutdown,
+ .set_termios = wk2xxx_set_termios,
+ .type = wk2xxx_type,
+ .config_port = wk2xxx_config_port,
+ .verify_port = wk2xxx_verify_port,
+};
+
+static const struct serial_rs485 wk2xxx_rs485_supported = {
+ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
+ SER_RS485_RTS_AFTER_SEND,
+ /* RTS timing is hardware-driven; RTS delays are not supported. */
+};
+
+static int wk2xxx_probe(struct spi_device *spi)
+{
+ const struct wk2xxx_devtype *devtype;
+ struct device *dev = &spi->dev;
+ struct wk2xxx_port *s;
+ unsigned long uartclk;
+ bool port_registered[WK2XXX_MAX_PORTS];
+ u8 val;
+ int i, ret;
+
+ /* Setup SPI bus. The SPI mode follows the device tree (spi-cpha,
+ * spi-cpol); it defaults to SPI mode 0 when unspecified.
+ */
+ spi->bits_per_word = 8;
+ spi->max_speed_hz = spi->max_speed_hz ? : 10 * HZ_PER_MHZ;
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
+
+ devtype = spi_get_device_match_data(spi);
+ if (!devtype)
+ return dev_err_probe(dev, -ENODEV, "Failed to match device\n");
+
+ /* Allocate port structure. */
+ s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL);
+ if (!s)
+ return dev_err_probe(dev, -ENOMEM,
+ "Error allocating port structure\n");
+
+ s->devtype = devtype;
+ s->spi = spi;
+ mutex_init(&s->reg_lock);
+ mutex_init(&s->poll_lock);
+ atomic_set(&s->open_ports, 0);
+ dev_set_drvdata(dev, s);
+
+ /*
+ * The WK2xxx has no identification register, so the best we can do
+ * is to check that communication is at all possible.
+ */
+ ret = wk2xxx_reg_read(s, WK2XXX_GENA_REG, &val);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read GENA register\n");
+
+ /* The reference clock (crystal or external clock input) is mandatory. */
+ s->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(s->clk))
+ return dev_err_probe(dev, PTR_ERR(s->clk),
+ "Failed to get the reference clock\n");
+
+ uartclk = clk_get_rate(s->clk);
+ if (!uartclk)
+ return dev_err_probe(dev, -EINVAL,
+ "Clock rate must not be zero\n");
+
+ /* Mark each port line and status as uninitialized. */
+ for (i = 0; i < devtype->nr_uart; ++i) {
+ s->p[i].port.line = WK2XXX_MAX_DEVS;
+ port_registered[i] = false;
+ }
+
+ kthread_init_worker(&s->kworker);
+ s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker,
+ "wk2xxx");
+ if (IS_ERR(s->kworker_task)) {
+ ret = PTR_ERR(s->kworker_task);
+ goto out_ports;
+ }
+ sched_set_fifo(s->kworker_task);
+
+ /*
+ * Reset the chip and disable every sub-UART and its interrupt before
+ * the ports are registered (and, in interrupt mode, before the IRQ is
+ * requested). The sub-UARTs stay disabled until a port is opened in
+ * wk2xxx_startup(), so no stale pending condition can raise the IRQ
+ * line while the ports are being set up.
+ */
+ wk2xxx_reg_write(s, WK2XXX_GRST_REG, (1 << devtype->nr_uart) - 1);
+ wk2xxx_reg_write(s, WK2XXX_GENA_REG, 0);
+ wk2xxx_reg_write(s, WK2XXX_GIER_REG, 0);
+
+ if (spi->irq <= 0) {
+ /* Poll the device instead of using interrupts. */
+ s->polling = true;
+ kthread_init_delayed_work(&s->poll_work, wk2xxx_poll_proc);
+ }
+
+ for (i = 0; i < devtype->nr_uart; ++i) {
+ struct fwnode_handle *saved_fwnode = dev_fwnode(dev);
+ struct device_node *port_np = NULL;
+ struct device_node *child;
+
+ ret = ida_alloc_max(&wk2xxx_lines, WK2XXX_MAX_DEVS - 1,
+ GFP_KERNEL);
+ if (ret < 0)
+ goto out_ports;
+
+ s->p[i].port.line = ret;
+
+ /* Locate the matching "serial@i" DT subnode, if any. */
+ for_each_available_child_of_node(dev->of_node, child) {
+ u32 reg;
+
+ if (!of_node_name_eq(child, "serial"))
+ continue;
+ if (of_property_read_u32(child, "reg", &reg))
+ continue;
+ if (reg == i) {
+ port_np = child;
+ break;
+ }
+ }
+
+ /* Initialize port data. */
+ s->p[i].port.dev = dev;
+ s->p[i].port.irq = spi->irq;
+ s->p[i].port.type = PORT_WK2XXX;
+ s->p[i].port.fifosize = WK2XXX_FIFO_SIZE;
+ s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY;
+ s->p[i].port.iobase = i;
+ s->p[i].port.iotype = UPIO_BUS;
+ s->p[i].port.uartclk = uartclk;
+ if (devtype->has_rs485) {
+ s->p[i].port.rs485_config = wk2xxx_config_rs485;
+ s->p[i].port.rs485_supported = wk2xxx_rs485_supported;
+ }
+ s->p[i].port.ops = &wk2xxx_ops;
+
+ mutex_init(&s->p[i].tx_lock);
+
+ kthread_init_work(&s->p[i].tx_work, wk2xxx_tx_proc);
+ kthread_init_work(&s->p[i].reg_work, wk2xxx_reg_proc);
+
+ /*
+ * Temporarily retarget dev's fwnode to the per-port subnode
+ * so uart_get_rs485_mode() picks up the per-port properties.
+ */
+ if (port_np && devtype->has_rs485) {
+ device_set_node(dev, of_fwnode_handle(port_np));
+ ret = uart_get_rs485_mode(&s->p[i].port);
+ device_set_node(dev, saved_fwnode);
+ of_node_put(port_np);
+ if (ret)
+ goto out_ports;
+ }
+
+ /* Register port. */
+ ret = uart_add_one_port(&wk2xxx_uart, &s->p[i].port);
+ if (ret)
+ goto out_ports;
+
+ port_registered[i] = true;
+ }
+
+ /*
+ * Request the IRQ only after every port is registered so that an early
+ * interrupt can never reach a port whose port->state is not ready yet.
+ * We first try to acquire the IRQ line as a level IRQ; if that
+ * succeeds, we can allow sharing the interrupt as well. In case the
+ * interrupt controller doesn't support that, we fall back to a
+ * non-shared falling-edge trigger.
+ */
+ if (!s->polling) {
+ ret = request_threaded_irq(spi->irq, NULL, wk2xxx_irq,
+ IRQF_TRIGGER_LOW | IRQF_SHARED |
+ IRQF_ONESHOT, dev_name(dev), s);
+ if (ret)
+ ret = request_threaded_irq(spi->irq, NULL, wk2xxx_irq,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ dev_name(dev), s);
+ if (ret) {
+ dev_err(dev, "Unable to request IRQ %i\n", spi->irq);
+ goto out_ports;
+ }
+ s->irq_requested = true;
+ }
+
+ return 0;
+
+out_ports:
+ if (s->irq_requested)
+ free_irq(spi->irq, s);
+
+ for (i = 0; i < devtype->nr_uart; i++) {
+ if (port_registered[i])
+ uart_remove_one_port(&wk2xxx_uart, &s->p[i].port);
+ if (s->p[i].port.line < WK2XXX_MAX_DEVS)
+ ida_free(&wk2xxx_lines, s->p[i].port.line);
+ }
+
+ if (!IS_ERR(s->kworker_task))
+ kthread_stop(s->kworker_task);
+
+ return ret;
+}
+
+static void wk2xxx_remove(struct spi_device *spi)
+{
+ struct wk2xxx_port *s = dev_get_drvdata(&spi->dev);
+ int i;
+
+ /*
+ * Unregister the ports first. Removing a port that is still open
+ * hangs up its tty and runs wk2xxx_shutdown(), which disables the
+ * sub-UART in the chip and drains the shared worker, so afterwards no
+ * enabled interrupt source (and thus no queued TX/register work) can
+ * target a port that is being torn down.
+ */
+ for (i = 0; i < s->devtype->nr_uart; i++) {
+ uart_remove_one_port(&wk2xxx_uart, &s->p[i].port);
+ ida_free(&wk2xxx_lines, s->p[i].port.line);
+ }
+
+ /*
+ * Every sub-UART is disabled now, so the chip can no longer raise the
+ * IRQ line. Free the IRQ; a handler already in flight only observes
+ * disabled ports and returns IRQ_NONE.
+ */
+ if (s->irq_requested)
+ free_irq(spi->irq, s);
+
+ if (s->polling)
+ kthread_cancel_delayed_work_sync(&s->poll_work);
+
+ kthread_flush_worker(&s->kworker);
+ kthread_stop(s->kworker_task);
+}
+
+static const struct of_device_id wk2xxx_dt_ids[] = {
+ { .compatible = "wkmic,wk2124", .data = &wk2124_devtype },
+ { .compatible = "wkmic,wk2132", .data = &wk2132_devtype },
+ { .compatible = "wkmic,wk2168", .data = &wk2168_devtype },
+ { .compatible = "wkmic,wk2202", .data = &wk2202_devtype },
+ { .compatible = "wkmic,wk2204", .data = &wk2204_devtype },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wk2xxx_dt_ids);
+
+static const struct spi_device_id wk2xxx_id_table[] = {
+ { "wk2124", (kernel_ulong_t)&wk2124_devtype },
+ { "wk2132", (kernel_ulong_t)&wk2132_devtype },
+ { "wk2168", (kernel_ulong_t)&wk2168_devtype },
+ { "wk2202", (kernel_ulong_t)&wk2202_devtype },
+ { "wk2204", (kernel_ulong_t)&wk2204_devtype },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, wk2xxx_id_table);
+
+static struct spi_driver wk2xxx_spi_driver = {
+ .driver = {
+ .name = WK2XXX_NAME,
+ .of_match_table = wk2xxx_dt_ids,
+ },
+ .probe = wk2xxx_probe,
+ .remove = wk2xxx_remove,
+ .id_table = wk2xxx_id_table,
+};
+
+static int __init wk2xxx_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&wk2xxx_uart);
+ if (ret)
+ return ret;
+
+ ret = spi_register_driver(&wk2xxx_spi_driver);
+ if (ret)
+ uart_unregister_driver(&wk2xxx_uart);
+
+ return ret;
+}
+module_init(wk2xxx_init);
+
+static void __exit wk2xxx_exit(void)
+{
+ spi_unregister_driver(&wk2xxx_spi_driver);
+ uart_unregister_driver(&wk2xxx_uart);
+}
+module_exit(wk2xxx_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Xuxunwei");
+MODULE_AUTHOR("B. Eschrich");
+MODULE_AUTHOR("Zi Jie Zhao <zjzhao@xxxxxxxxx>");
+MODULE_DESCRIPTION("WK2xxx SPI UART driver");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 377884e3856a..05c75d3ef479 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -234,6 +234,9 @@
/* Sunplus UART */
#define PORT_SUNPLUS 123

+/* WK2xxx SPI to UART bridge */
+#define PORT_WK2XXX 124
+
/* Generic type identifier for ports which type is not important to userspace. */
#define PORT_GENERIC (-1)

--
2.43.0