Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: zjzhao@edatec.cn
Cc: robh@kernel.org, linux-serial@vger.kernel.org,
	devicetree@vger.kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v4 2/2] serial: wk2xxx: Add WK2xxx SPI UART driver
Date: Tue, 08 Sep 2026 10:44:13 +0000	[thread overview]
Message-ID: <20260908104417.7EF0D1F00A3A@smtp.kernel.org> (raw)
In-Reply-To: <20260908103129.58085-3-zjzhao@edatec.cn>

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] UART transmit FIFO data corruption and stats race due to missing port lock in wk2xxx_handle_tx().
- [High] TTY buffer flood due to persistent overrun flag in batch RX loop.
- [High] The tx_empty callback performs a synchronous SPI read which sleeps while the serial core holds the port spinlock.
- [High] Early return on SPI error in wk2xxx_shutdown skips essential software state cleanup, causing open_ports to permanently leak.
- [Medium] OF node reference leak when parsing DT nodes for non-RS485 variants.
- [Medium] Commit message claims to apply serial properties from DT subnodes, but they are not applied.
- [Low] The driver intentionally breaks the build on architectures with L1_CACHE_BYTES < 31 (e.g., m68k).
--

commit 8ec8660308853d2cff61b90024208e8b3e1bafdb
Author: Zi Jie Zhao <zjzhao@edatec.cn>
Subject: serial: wk2xxx: Add WK2xxx SPI UART driver

This commit introduces a new driver for the WK2xxx SPI to UART bridge ICs.
It models the driver after the NXP sc16is7xx driver, utilizing a kthread
worker for SPI register accesses and supporting polled or interrupt-driven
operation.

> diff --git a/drivers/tty/serial/wk2xxx.c b/drivers/tty/serial/wk2xxx.c
> index 0000000000000..15869fbdeab07
> --- /dev/null
> +++ b/drivers/tty/serial/wk2xxx.c
[ ... ]
> +	/*
> +	 * 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);

[Severity: Low]
Does this intentionally break the build for architectures with a cache line 
size smaller than 31 bytes (such as m68k where L1_CACHE_BYTES is 16)? 

Might it be better to size the array to something like 
max(L1_CACHE_BYTES, 32) instead of failing the compilation?

[ ... ]
> +static void wk2xxx_handle_tx(struct uart_port *port)
> +{
[ ... ]
> +	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);

[Severity: High]
Is it safe to manipulate the xmit_fifo pointers and call uart_xmit_advance() 
here without holding the port lock?

If a concurrent operation like a tcflush() ioctl resets the FIFO via 
uart_flush_buffer() (which zeroes the in and out indices), could this 
lockless uart_xmit_advance() call advance the out index beyond the in index, 
causing a substantial underflow and subsequent data corruption?

[ ... ]
> +static void wk2xxx_handle_rx(struct uart_port *port)
> +{
[ ... ]
> +	/* 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;
> +		}
[ ... ]
> +	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);
> +	}

[Severity: High]
Could this loop insert an excessive number of dummy overrun characters into 
the TTY buffer?

Since lsr is modified once outside the loop when the WK2XXX_FSR_RFOE_BIT is 
set, the WK2XXX_LSR_OE_BIT remains active for the entire iteration. When 
uart_insert_char() evaluates this bit, it will insert a dummy overrun byte 
for every single character in this chunk (up to 256 times) for a single 
overrun event.

[ ... ]
> +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;

[Severity: High]
Can this sequence sleep while executing in an atomic context?

The serial core routinely invokes the ops->tx_empty() callback while holding 
the port spinlock with interrupts disabled (for instance, during the 
TIOCSERGETLSR ioctl). However, wk2xxx_port_reg_read() performs a synchronous 
spi_sync() transfer which can sleep.

[ ... ]
> +static void wk2xxx_shutdown(struct uart_port *port)
> +{
> +	struct wk2xxx_port *s = dev_get_drvdata(port->dev);
> +	unsigned int portno = port->iobase;
> +	u8 reg;
> +
> +	scoped_guard(mutex, &s->reg_lock) {
> +		/* Disable the sub-UART interrupt in the global mask. */
> +		if (wk2xxx_raw_read(s, WK2XXX_GIER_REG, &reg))
> +			return;

[Severity: High]
If this SPI read fails, the function returns early. Does this leave the 
software state permanently out of sync?

By returning here, the function skips the atomic_dec_return(&s->open_ports) 
and kthread_flush_worker() cleanup steps at the end of wk2xxx_shutdown(). 
This leaves the open_ports counter permanently elevated and strands the 
polling kthread so it can never be stopped.

[ ... ]
> +static int wk2xxx_probe(struct spi_device *spi)
> +{
[ ... ]
> +		/* Locate the matching "serial@i" DT subnode, if any. */
> +		for_each_available_child_of_node(dev->of_node, child) {
> +			u32 reg;
> +
> +			if (!of_node_name_eq(child, "serial"))
> +				continue;
> +			if (of_property_read_u32(child, "reg", &reg))
> +				continue;
> +			if (reg == i) {
> +				port_np = child;
> +				break;
> +			}
> +		}
[ ... ]
> +		/*
> +		 * 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;
> +		}

[Severity: Medium]
Is there a device tree node reference leak here for non-RS485 variants?

The port_np reference is acquired via the for_each_available_child_of_node() 
loop earlier. However, if devtype->has_rs485 evaluates to false (such as for 
the WK2132 or WK2202 variants), this block is entirely bypassed and 
of_node_put(port_np) is never called.

[Severity: Medium]
The commit message states the driver will "apply serial and RS-485 properties 
from each serial@N DT subnode to the corresponding channel."

While uart_get_rs485_mode() is called in this block, should there also be a 
call to uart_read_port_properties() to ensure the generic serial properties 
(like tx-trigger or rx-trigger) are actually applied as described?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260908103129.58085-1-zjzhao@edatec.cn?part=2

  reply	other threads:[~2026-09-08 10:44 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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:38   ` sashiko-bot
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 10:44   ` sashiko-bot [this message]
2026-09-08 17:51   ` Hugo Villeneuve

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260908104417.7EF0D1F00A3A@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=zjzhao@edatec.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox