* [PATCH v4 0/2] WK2xxx SPI to UART bridge driver @ 2026-09-08 10:31 zjzhao 2026-09-08 10:31 ` [PATCH v4 1/2] dt-bindings: serial: Document WK2xxx SPI UART zjzhao 2026-09-08 10:31 ` [PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver zjzhao 0 siblings, 2 replies; 5+ messages in thread From: zjzhao @ 2026-09-08 10:31 UTC (permalink / raw) To: gregkh Cc: jirislaby, robh, krzk+dt, conor+dt, linux-kernel, linux-serial, devicetree, Zi Jie Zhao From: Zi Jie Zhao <zjzhao@edatec.cn> Add support for the WK2xxx family of SPI to UART bridge ICs from Chengdu Weikai Microelectronics (WKmic): WK2124, WK2132, WK2168, WK2202 and WK2204. Base the driver on the vendor's open-source implementation: https://github.com/britus/wk2xxx/blob/master/spi-wk2xxx.c Use the company's website domain, http://www.wkmic.com/, for the vendor prefix. Describe the external reference clock with the standard "clocks" property and model each UART as a "serial@N" child node. Changes in v4: - Rename the binding file to "wkmic,wk2124.yaml" so its filename matches a compatible string; - Replace "clock-frequency" with a required standard "clocks" input; - Document why individual compatible strings are retained: the two-channel WK2132 and WK2202 do not have the hardware flow-control and RS-485 registers present on the four-channel variants; - Identify the maintainer as Zi Jie Zhao and document the vendor website and source attribution; - Remove the unnecessary "linux/mod_devicetable.h" include; - Describe hardware flow control and RS-485 support per chip variant; - Retain the v3 fixes for IRQ setup and teardown, polling lifetime, SPI error handling, cache-safe shared buffers, and bounded IRQ processing. Testing: - Run dt_binding_check with the WK2xxx binding; - Build the driver for an ARM64 target; - IPC12 with WK2132 on SPI0: reboot, load the driver successfully, and register ttyWK0 and ttyWK1 with IRQ 184 and base_baud 691200; - SBC2300 with WK2204: run four-port TX stress and repeated module unload/load with closed ports. Zi Jie Zhao (2): dt-bindings: serial: Document WK2xxx SPI UART serial: wk2xxx: Add WK2xxx SPI UART driver .../bindings/serial/wkmic,wk2124.yaml | 119 ++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + drivers/tty/serial/Kconfig | 17 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/wk2xxx.c | 1433 +++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 6 files changed, 1575 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/wkmic,wk2124.yaml create mode 100644 drivers/tty/serial/wk2xxx.c -- 2.43.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v4 1/2] dt-bindings: serial: Document WK2xxx SPI UART 2026-09-08 10:31 [PATCH v4 0/2] WK2xxx SPI to UART bridge driver zjzhao @ 2026-09-08 10:31 ` zjzhao 2026-09-08 17:49 ` Conor Dooley 2026-09-08 10:31 ` [PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver zjzhao 1 sibling, 1 reply; 5+ messages in thread From: zjzhao @ 2026-09-08 10:31 UTC (permalink / raw) To: gregkh Cc: jirislaby, robh, krzk+dt, conor+dt, linux-kernel, linux-serial, devicetree, Zi Jie Zhao From: Zi Jie Zhao <zjzhao@edatec.cn> Add a DT binding for the WK2xxx SPI to UART bridge ICs (WK2124, WK2132, WK2168, WK2202 and WK2204) from Chengdu Weikai Microelectronics (WKmic). Describe each UART channel with a serial@N child node carrying its serial and RS-485 properties. Register the wkmic vendor prefix based on the vendor website http://www.wkmic.com/. Differentiate compatible strings by channel count and register availability. Account for missing hardware flow-control and RS-485 registers on WK2132 and WK2202. Reject serial@2 and serial@3 on the two-channel members. Require the standard clocks property for the single external reference clock used by the chips. Signed-off-by: Zi Jie Zhao <zjzhao@edatec.cn> --- .../bindings/serial/wkmic,wk2124.yaml | 119 ++++++++++++++++++ .../devicetree/bindings/vendor-prefixes.yaml | 2 + 2 files changed, 121 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/wkmic,wk2124.yaml diff --git a/Documentation/devicetree/bindings/serial/wkmic,wk2124.yaml b/Documentation/devicetree/bindings/serial/wkmic,wk2124.yaml new file mode 100644 index 000000000000..0f6ba8755e1c --- /dev/null +++ b/Documentation/devicetree/bindings/serial/wkmic,wk2124.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serial/wkmic,wk2124.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: WKmic WK2xxx SPI to UART bridge + +maintainers: + - Zi Jie Zhao <zjzhao@edatec.cn> + +description: + The WK2xxx family (WK2124, WK2132, WK2168, WK2202 and WK2204) are SPI to + UART bridge ICs from WKmic (Chengdu Weikai Microelectronics). 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, and is + clocked from a single external reference clock. Each channel is + described by a "serial@N" child node that carries its own serial and + RS-485 properties. + +properties: + compatible: + enum: + - wkmic,wk2124 + - wkmic,wk2132 + - wkmic,wk2168 + - wkmic,wk2202 + - wkmic,wk2204 + + reg: + maxItems: 1 + + interrupts: + description: + When missing, the device driver uses polling instead. + maxItems: 1 + + clocks: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^serial@[0-3]$": + type: object + description: A single UART channel of the chip. + allOf: + - $ref: /schemas/serial/serial.yaml# + - $ref: /schemas/serial/rs485.yaml# + properties: + reg: + description: UART channel number on the chip. + maximum: 3 + required: + - reg + unevaluatedProperties: false + +required: + - compatible + - reg + - clocks + - "#address-cells" + - "#size-cells" + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + + - if: + properties: + compatible: + contains: + enum: + - wkmic,wk2132 + - wkmic,wk2202 + then: + patternProperties: + "^serial@[23]$": false + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + + xtal: clock-11059200 { + compatible = "fixed-clock"; + clock-frequency = <11059200>; + #clock-cells = <0>; + }; + + spi { + #address-cells = <1>; + #size-cells = <0>; + + serial@0 { + compatible = "wkmic,wk2132"; + reg = <0>; + spi-max-frequency = <10000000>; + clocks = <&xtal>; + interrupt-parent = <&gpio>; + interrupts = <24 IRQ_TYPE_LEVEL_LOW>; + #address-cells = <1>; + #size-cells = <0>; + + serial@0 { + reg = <0>; + }; + + serial@1 { + reg = <1>; + rs485-rts-active-low; + linux,rs485-enabled-at-boot-time; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index ba2002969373..cdc04229d642 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1907,6 +1907,8 @@ patternProperties: description: Wireless Tag (qiming yunduan) "^wits,.*": description: Shenzhen Merrii Technology Co., Ltd. (WITS) + "^wkmic,.*": + description: Chengdu Weikai Microelectronics Co., Ltd. (http://www.wkmic.com/) "^wlf,.*": description: Wolfson Microelectronics "^wm,.*": -- 2.43.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v4 1/2] dt-bindings: serial: Document WK2xxx SPI UART 2026-09-08 10:31 ` [PATCH v4 1/2] dt-bindings: serial: Document WK2xxx SPI UART zjzhao @ 2026-09-08 17:49 ` Conor Dooley 0 siblings, 0 replies; 5+ messages in thread From: Conor Dooley @ 2026-09-08 17:49 UTC (permalink / raw) To: zjzhao Cc: gregkh, jirislaby, robh, krzk+dt, conor+dt, linux-kernel, linux-serial, devicetree [-- Attachment #1: Type: text/plain, Size: 928 bytes --] On Tue, Sep 08, 2026 at 06:31:28PM +0800, zjzhao@edatec.cn wrote: > From: Zi Jie Zhao <zjzhao@edatec.cn> > > Add a DT binding for the WK2xxx SPI to UART bridge ICs (WK2124, WK2132, > WK2168, WK2202 and WK2204) from Chengdu Weikai Microelectronics (WKmic). > Describe each UART channel with a serial@N child node carrying its serial > and RS-485 properties. Register the wkmic vendor prefix based on the vendor > website http://www.wkmic.com/. > > Differentiate compatible strings by channel count and > register availability. Account for missing hardware flow-control and > RS-485 registers on WK2132 and WK2202. Reject serial@2 and serial@3 on the > two-channel members. > > Require the standard clocks property for the single external reference > clock used by the chips. > > Signed-off-by: Zi Jie Zhao <zjzhao@edatec.cn> Reviewed-by: Conor Dooley <conor.dooley@microchip.com> pw-bot: not-applicable [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --] ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver 2026-09-08 10:31 [PATCH v4 0/2] WK2xxx SPI to UART bridge driver zjzhao 2026-09-08 10:31 ` [PATCH v4 1/2] dt-bindings: serial: Document WK2xxx SPI UART zjzhao @ 2026-09-08 10:31 ` zjzhao 2026-09-08 17:51 ` Hugo Villeneuve 1 sibling, 1 reply; 5+ messages in thread From: zjzhao @ 2026-09-08 10:31 UTC (permalink / raw) To: gregkh Cc: jirislaby, robh, krzk+dt, conor+dt, linux-kernel, linux-serial, devicetree, Zi Jie Zhao From: Zi Jie Zhao <zjzhao@edatec.cn> 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@edatec.cn> --- 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@edatec.cn> + */ + +#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, ®)) + 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, ®)) + 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 |= 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, ®)) + 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 |= BIT(portno); + wk2xxx_raw_write(s, WK2XXX_GRST_REG, reg); + + /* Disable the sub-UART. */ + if (wk2xxx_raw_read(s, WK2XXX_GENA_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", ®)) + 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@edatec.cn>"); +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 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver 2026-09-08 10:31 ` [PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver zjzhao @ 2026-09-08 17:51 ` Hugo Villeneuve 0 siblings, 0 replies; 5+ messages in thread From: Hugo Villeneuve @ 2026-09-08 17:51 UTC (permalink / raw) To: zjzhao Cc: gregkh, jirislaby, robh, krzk+dt, conor+dt, linux-kernel, linux-serial, devicetree On Tue, 8 Sep 2026 18:31:29 +0800 zjzhao@edatec.cn wrote: > From: Zi Jie Zhao <zjzhao@edatec.cn> > > 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@edatec.cn> > --- > 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@edatec.cn> > + */ > + > +#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. */ This is the number of devices/ICs, not lines. You could remove the comment as this is self-explanatory. > +#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. */ I do not understand this comment in relation with the #define name... > +#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. */ A little bit unclear, so bit 6 is direction R/W, and bit 7 is access type, register or FIFO? > +#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 */ Your previous comments have a trailing dot ".", and these have not. Choose one format and use it everywhere... > +#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); > +} sc16is7xx and max310x use regmap for I2C/SPI access. You do not, can you explain why? Using regmap, you can take advantage of the cache. And you can (maybe) simplify paged accesses using regmap ranges? > + > +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; Add empty line for clarity? > + 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; Same as above? > + 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; Empty line > + 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); Put on the same line to take advantage of 100 columns limit, like you already did previously... > +} > + > +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); Put on same line, as above... > +} > + > +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; Empty line > + 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; > + } Empty line > + 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; Empty line... > + 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; Where does this 200 limit come from? > + > + 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)); Same line (100 columns)... > + > + /* 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)); Same line (100 columns)... > + > + /* 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; Empty line > + 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; Empty line > + 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, ®)) > + 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, ®)) > + 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 |= 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, ®)) > + 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 |= BIT(portno); > + wk2xxx_raw_write(s, WK2XXX_GRST_REG, reg); > + > + /* Disable the sub-UART. */ > + if (wk2xxx_raw_read(s, WK2XXX_GENA_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; Use reverse-xmax ordering... > + > + /* 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", ®)) > + 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); Why not using devm_ to drop irq_requested variable and irq cleanup in remove()? > + if (ret) Why not issue, like sc16is7xx: if (!ret) return 0; ? > + 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; > + } Similar here? > + 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@edatec.cn>"); > +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 > > -- Hugo Villeneuve ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-08 18:35 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-08 10:31 [PATCH v4 0/2] WK2xxx SPI to UART bridge driver zjzhao 2026-09-08 10:31 ` [PATCH v4 1/2] dt-bindings: serial: Document WK2xxx SPI UART zjzhao 2026-09-08 17:49 ` Conor Dooley 2026-09-08 10:31 ` [PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver zjzhao 2026-09-08 17:51 ` Hugo Villeneuve
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®