From nobody Mon Feb 9 02:15:29 2026 Received: from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 950613A35D2; Thu, 15 Jan 2026 15:05:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768489518; cv=none; b=D9zODTbHs00krsYX492MI+1QnBNgKJCtOAOOm9ih/+jfAbLv6u+Onhp0h1ib6oz+YMOrUxHUwDIev5rLjlbRA0a4DZHjfekSvenHH3fzBal7DrWnmkQIdflLr4alQ63NO26GC9HyCwgmDB9xKCxCDLn/qMKmPBOHGYg0zI0+7Ds= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768489518; c=relaxed/simple; bh=TS5GD8M9+z4MQzf2q38esRrfp1ClFZVNZh/XQpDpB9Y=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VQQufv35f0454WXFonjGx6AfKfHqCrgNHXQZ/talQhiZRWD8gKsZwI7MSg53J9VVWD/5cKU03wZpeIc3Gsh8YDFtzt1Ct+AGw0dgvMV00RrJfrtUKIr/u8tL/z2giP2+qTCgT7k/t+ywH9e9QXsbGeZLkEUH8YJvaI8TGt4B+hw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Thu, 15 Jan 2026 23:04:55 +0800 Received: from aspeedtech.com (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Thu, 15 Jan 2026 23:04:55 +0800 From: Chin-Ting Kuo To: , , , , , , , , , , Subject: [PATCH 1/2] spi: spi-mem: Protect dirmap_create() with spi_mem_access_start/end Date: Thu, 15 Jan 2026 23:04:53 +0800 Message-ID: <20260115150454.1575970-2-chin-ting_kuo@aspeedtech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260115150454.1575970-1-chin-ting_kuo@aspeedtech.com> References: <20260115150454.1575970-1-chin-ting_kuo@aspeedtech.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" spi_mem_dirmap_create() may reconfigure controller-wide settings, which can interfere with concurrent transfers to other devices sharing the same SPI controller but using different chip selects. Wrap the ->dirmap_create() callback with spi_mem_access_start() and spi_mem_access_end() to serialize access and prevent cross-CS interference during dirmap creation. Signed-off-by: Chin-Ting Kuo --- drivers/spi/spi-mem.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index c8b2add2640e..85702a77b3c8 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -708,9 +708,18 @@ spi_mem_dirmap_create(struct spi_mem *mem, =20 desc->mem =3D mem; desc->info =3D *info; - if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create) + if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create) { + ret =3D spi_mem_access_start(mem); + if (ret) { + kfree(desc); + return ERR_PTR(ret); + } + ret =3D ctlr->mem_ops->dirmap_create(desc); =20 + spi_mem_access_end(mem); + } + if (ret) { desc->nodirmap =3D true; if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl)) --=20 2.34.1 From nobody Mon Feb 9 02:15:29 2026 Received: from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C9DF3A9DA6; Thu, 15 Jan 2026 15:05:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768489523; cv=none; b=c5bpAHX8VL9Q9h97FC+6p2f2eGZRN6LozNkJtoOu40O5MRbSpMiOXaP0sVnxLXUJdFvvW1PpxMXXHdI0zWtm2W3jEx3uSFOstrP1jMfXaBpVW7sq8zeE505t4Nozxy47A1EsACHCp3gvPmgPbN1qtiYC6Ka5u07qZK9qQewcvXw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768489523; c=relaxed/simple; bh=bU9AjUDMRK1u35dzDbIQpITdo2h0on9FC592OFWlblo=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=KUOEZwO2go++8SszTLx+3bruWH0R9qaqFwUFHLSbOk0A0UEQPjDPE3Z4oxRrLUCr6YOKEIas18FQQnSCdK3Fbi+0HpwZ8bZCDgtzWUW1fx9bLc/b4lWDD8f27ACdpch7V8m+BCDwWijkQpOQezLnx0kV8U0BJB2deHsW52IoaX4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Thu, 15 Jan 2026 23:04:55 +0800 Received: from aspeedtech.com (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Thu, 15 Jan 2026 23:04:55 +0800 From: Chin-Ting Kuo To: , , , , , , , , , , Subject: [PATCH 2/2] spi: aspeed: Add support for non-spi-mem devices Date: Thu, 15 Jan 2026 23:04:54 +0800 Message-ID: <20260115150454.1575970-3-chin-ting_kuo@aspeedtech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260115150454.1575970-1-chin-ting_kuo@aspeedtech.com> References: <20260115150454.1575970-1-chin-ting_kuo@aspeedtech.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The ASPEED FMC/SPI controller may be shared by spi-mem devices and other SPI peripherals that do not use the spi-mem framework. The driver currently assumes spi-mem semantics for all devices, while the controller also supports direct user mode access commonly used by non-spi-mem devices. This mismatch can result in incorrect behavior when different types of devices share the same controller. Update the driver to properly handle non-spi-mem devices, allowing them to operate correctly in pure user mode alongside spi-mem devices on a shared SPI controller. Signed-off-by: Chin-Ting Kuo --- drivers/spi/spi-aspeed-smc.c | 121 +++++++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index db3e096f2eb0..93ad72634e42 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -48,6 +48,8 @@ /* CEx Address Decoding Range Register */ #define CE0_SEGMENT_ADDR_REG 0x30 =20 +#define FULL_DUPLEX_RX_DATA 0x1e4 + /* CEx Read timing compensation register */ #define CE0_TIMING_COMPENSATION_REG 0x94 =20 @@ -81,6 +83,7 @@ struct aspeed_spi_data { u32 hclk_mask; u32 hdiv_max; u32 min_window_size; + bool full_duplex; =20 phys_addr_t (*segment_start)(struct aspeed_spi *aspi, u32 reg); phys_addr_t (*segment_end)(struct aspeed_spi *aspi, u32 reg); @@ -105,6 +108,7 @@ struct aspeed_spi { =20 struct clk *clk; u32 clk_freq; + u8 cs_change; =20 struct aspeed_spi_chip chips[ASPEED_SPI_MAX_NUM_CS]; }; @@ -280,7 +284,8 @@ static ssize_t aspeed_spi_write_user(struct aspeed_spi_= chip *chip, } =20 /* support for 1-1-1, 1-1-2 or 1-1-4 */ -static bool aspeed_spi_supports_op(struct spi_mem *mem, const struct spi_m= em_op *op) +static bool aspeed_spi_supports_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) { if (op->cmd.buswidth > 1) return false; @@ -305,7 +310,8 @@ static bool aspeed_spi_supports_op(struct spi_mem *mem,= const struct spi_mem_op =20 static const struct aspeed_spi_data ast2400_spi_data; =20 -static int do_aspeed_spi_exec_op(struct spi_mem *mem, const struct spi_mem= _op *op) +static int do_aspeed_spi_exec_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) { struct aspeed_spi *aspi =3D spi_controller_get_devdata(mem->spi->controll= er); struct aspeed_spi_chip *chip =3D &aspi->chips[spi_get_chipselect(mem->spi= , 0)]; @@ -367,11 +373,12 @@ static int do_aspeed_spi_exec_op(struct spi_mem *mem,= const struct spi_mem_op *o return ret; } =20 -static int aspeed_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op= *op) +static int aspeed_spi_exec_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) { int ret; =20 - ret =3D do_aspeed_spi_exec_op(mem, op); + ret =3D do_aspeed_spi_exec_mem_op(mem, op); if (ret) dev_err(&mem->spi->dev, "operation failed: %d\n", ret); return ret; @@ -773,8 +780,8 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_di= rmap_desc *desc, } =20 static const struct spi_controller_mem_ops aspeed_spi_mem_ops =3D { - .supports_op =3D aspeed_spi_supports_op, - .exec_op =3D aspeed_spi_exec_op, + .supports_op =3D aspeed_spi_supports_mem_op, + .exec_op =3D aspeed_spi_exec_mem_op, .get_name =3D aspeed_spi_get_name, .dirmap_create =3D aspeed_spi_dirmap_create, .dirmap_read =3D aspeed_spi_dirmap_read, @@ -843,6 +850,99 @@ static void aspeed_spi_enable(struct aspeed_spi *aspi,= bool enable) aspeed_spi_chip_enable(aspi, cs, enable); } =20 +static void aspeed_spi_user_transfer_tx(struct aspeed_spi *aspi, + struct spi_device *spidev, + const u8 *tx_buf, u8 *rx_buf, + void *dst, u32 len) +{ + const struct aspeed_spi_data *data =3D aspi->data; + bool full_duplex_transfer =3D data->full_duplex && tx_buf =3D=3D rx_buf; + u32 i; + + if (full_duplex_transfer && + !!(spidev->mode & (SPI_TX_DUAL | SPI_TX_QUAD | + SPI_RX_DUAL | SPI_RX_QUAD))) { + dev_err(aspi->dev, + "full duplex is only supported for single IO mode\n"); + return; + } + + for (i =3D 0; i < len; i++) { + writeb(tx_buf[i], dst); + if (full_duplex_transfer) + rx_buf[i] =3D readb(aspi->regs + FULL_DUPLEX_RX_DATA); + } +} + +static int aspeed_spi_user_transfer(struct spi_controller *ctlr, + struct spi_message *msg) +{ + struct aspeed_spi *aspi =3D + (struct aspeed_spi *)spi_controller_get_devdata(ctlr); + const struct aspeed_spi_data *data =3D aspi->data; + struct spi_device *spidev =3D msg->spi; + u32 cs =3D spi_get_chipselect(spidev, 0); + struct aspeed_spi_chip *chip =3D &aspi->chips[cs]; + void __iomem *ahb_base =3D aspi->chips[cs].ahb_base; + u32 ctrl_val; + u32 clk_div =3D data->get_clk_div(chip, spidev->max_speed_hz); + struct spi_transfer *xfer; + const u8 *tx_buf; + u8 *rx_buf; + u32 i =3D 0; + + ctrl_val =3D chip->ctl_val[ASPEED_SPI_BASE]; + ctrl_val &=3D ~CTRL_IO_MODE_MASK & data->hclk_mask; + ctrl_val |=3D clk_div; + chip->ctl_val[ASPEED_SPI_BASE] =3D ctrl_val; + + if (aspi->cs_change =3D=3D 0) + aspeed_spi_start_user(chip); + + dev_dbg(aspi->dev, "cs: %d\n", cs); + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + dev_dbg(aspi->dev, + "xfer[%d]: width %d, len %u, tx %p, rx %p\n", + i, + xfer->bits_per_word, xfer->len, + xfer->tx_buf, xfer->rx_buf); + + tx_buf =3D xfer->tx_buf; + rx_buf =3D xfer->rx_buf; + + if (tx_buf) { + if (spidev->mode & SPI_TX_DUAL) + aspeed_spi_set_io_mode(chip, CTRL_IO_DUAL_DATA); + else if (spidev->mode & SPI_TX_QUAD) + aspeed_spi_set_io_mode(chip, CTRL_IO_QUAD_DATA); + + aspeed_spi_user_transfer_tx(aspi, spidev, tx_buf, rx_buf, + (void *)ahb_base, xfer->len); + } + + if (rx_buf && rx_buf !=3D tx_buf) { + if (spidev->mode & SPI_RX_DUAL) + aspeed_spi_set_io_mode(chip, CTRL_IO_DUAL_DATA); + else if (spidev->mode & SPI_RX_QUAD) + aspeed_spi_set_io_mode(chip, CTRL_IO_QUAD_DATA); + + ioread8_rep(ahb_base, rx_buf, xfer->len); + } + + msg->actual_length +=3D xfer->len; + aspi->cs_change =3D xfer->cs_change; + i++; + } + + if (aspi->cs_change =3D=3D 0) + aspeed_spi_stop_user(chip); + + msg->status =3D 0; + spi_finalize_current_message(ctlr); + + return 0; +} + static int aspeed_spi_probe(struct platform_device *pdev) { struct device *dev =3D &pdev->dev; @@ -899,6 +999,7 @@ static int aspeed_spi_probe(struct platform_device *pde= v) ctlr->cleanup =3D aspeed_spi_cleanup; ctlr->num_chipselect =3D of_get_available_child_count(dev->of_node); ctlr->dev.of_node =3D dev->of_node; + ctlr->transfer_one_message =3D aspeed_spi_user_transfer; =20 aspi->num_cs =3D ctlr->num_chipselect; =20 @@ -1455,6 +1556,7 @@ static const struct aspeed_spi_data ast2400_fmc_data = =3D { .hclk_mask =3D 0xfffff0ff, .hdiv_max =3D 1, .min_window_size =3D 0x800000, + .full_duplex =3D false, .calibrate =3D aspeed_spi_calibrate, .get_clk_div =3D aspeed_get_clk_div_ast2400, .segment_start =3D aspeed_spi_segment_start, @@ -1471,6 +1573,7 @@ static const struct aspeed_spi_data ast2400_spi_data = =3D { .timing =3D 0x14, .hclk_mask =3D 0xfffff0ff, .hdiv_max =3D 1, + .full_duplex =3D false, .get_clk_div =3D aspeed_get_clk_div_ast2400, .calibrate =3D aspeed_spi_calibrate, /* No segment registers */ @@ -1485,6 +1588,7 @@ static const struct aspeed_spi_data ast2500_fmc_data = =3D { .hclk_mask =3D 0xffffd0ff, .hdiv_max =3D 1, .min_window_size =3D 0x800000, + .full_duplex =3D false, .get_clk_div =3D aspeed_get_clk_div_ast2500, .calibrate =3D aspeed_spi_calibrate, .segment_start =3D aspeed_spi_segment_start, @@ -1502,6 +1606,7 @@ static const struct aspeed_spi_data ast2500_spi_data = =3D { .hclk_mask =3D 0xffffd0ff, .hdiv_max =3D 1, .min_window_size =3D 0x800000, + .full_duplex =3D false, .get_clk_div =3D aspeed_get_clk_div_ast2500, .calibrate =3D aspeed_spi_calibrate, .segment_start =3D aspeed_spi_segment_start, @@ -1520,6 +1625,7 @@ static const struct aspeed_spi_data ast2600_fmc_data = =3D { .hclk_mask =3D 0xf0fff0ff, .hdiv_max =3D 2, .min_window_size =3D 0x200000, + .full_duplex =3D false, .get_clk_div =3D aspeed_get_clk_div_ast2600, .calibrate =3D aspeed_spi_ast2600_calibrate, .segment_start =3D aspeed_spi_segment_ast2600_start, @@ -1538,6 +1644,7 @@ static const struct aspeed_spi_data ast2600_spi_data = =3D { .hclk_mask =3D 0xf0fff0ff, .hdiv_max =3D 2, .min_window_size =3D 0x200000, + .full_duplex =3D false, .get_clk_div =3D aspeed_get_clk_div_ast2600, .calibrate =3D aspeed_spi_ast2600_calibrate, .segment_start =3D aspeed_spi_segment_ast2600_start, @@ -1556,6 +1663,7 @@ static const struct aspeed_spi_data ast2700_fmc_data = =3D { .hclk_mask =3D 0xf0fff0ff, .hdiv_max =3D 2, .min_window_size =3D 0x10000, + .full_duplex =3D true, .get_clk_div =3D aspeed_get_clk_div_ast2600, .calibrate =3D aspeed_spi_ast2600_calibrate, .segment_start =3D aspeed_spi_segment_ast2700_start, @@ -1573,6 +1681,7 @@ static const struct aspeed_spi_data ast2700_spi_data = =3D { .hclk_mask =3D 0xf0fff0ff, .hdiv_max =3D 2, .min_window_size =3D 0x10000, + .full_duplex =3D true, .get_clk_div =3D aspeed_get_clk_div_ast2600, .calibrate =3D aspeed_spi_ast2600_calibrate, .segment_start =3D aspeed_spi_segment_ast2700_start, --=20 2.34.1