From nobody Sun Feb 8 21:29:10 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 2BBF44266B8; Tue, 20 Jan 2026 12:30:10 +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=1768912212; cv=none; b=fOQm2I8bbfvLo2B89Y15dNKRFqQNCUXzGYO/lj1/0/tGxa9pU/p1MjhI4yyxeD7uaAtiXHRidufe/AE4uOXUJ9kOb3rI2rq91IrVVHMXc0huYGy8b7KMoGNhCdJW+IzC4jmLvg4lEZB6iJasdeMsv9Gwb/dm5JeQnfSUHQsNq+k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768912212; c=relaxed/simple; bh=kazIy+1QIMcxqpyxoVHvN9xkLhu67bFVz+excxkCPYs=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=MjFQ+nvgTvwbWyKn81ffLJnd7OlTKLF9LRoadUuuSGl1RBjoRPt/D1T72rsV77tfm5iaW4ugFcmp0EmF6Wexqqg1N4AxlxqRxQlNTA2volz08ywSoJGVErUbNpM98vVfo81KwnctqQTkNHqXwPcnmpn4ghdIKO0m0+MH+fHMSa8= 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; Tue, 20 Jan 2026 20:30:05 +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; Tue, 20 Jan 2026 20:30:05 +0800 From: Chin-Ting Kuo To: , , , , , , , , , , , Subject: [PATCH v3 1/2] spi: spi-mem: Protect dirmap_create() with spi_mem_access_start/end Date: Tue, 20 Jan 2026 20:30:04 +0800 Message-ID: <20260120123005.1392071-2-chin-ting_kuo@aspeedtech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260120123005.1392071-1-chin-ting_kuo@aspeedtech.com> References: <20260120123005.1392071-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. This patch has been verified on a setup where a SPI TPM is connected to CS0 of a SPI controller, while a SPI NOR flash is connected to CS1 of the same controller. Without this patch, spi_mem_dirmap_create() for the SPI NOR flash interferes with ongoing SPI TPM data transfers, resulting in failure to create the TPM device. This was tested on an ASPEED AST2700 EVB. Signed-off-by: Chin-Ting Kuo Reviewed-by: Paul Menzel --- 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 Sun Feb 8 21:29:10 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 44BEB4279FF; Tue, 20 Jan 2026 12:30: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=1768912214; cv=none; b=nWByksK4O+WaubSQ54+wfr2cXQNOAnTwG/hwSrHN0o80XPEw0MOyGAnaW4GKh+7FZ5pthWJS30LAaxZiwMqIApTywNp4+UAccNCGFi5pi4deQD4kS8yaVGzue5bJWUqhfWXOUr7YBEIE3NBNSbUJsr+bu6A7FfJdudzIZh0bNtc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768912214; c=relaxed/simple; bh=YLWe2i7T8k93TurPz6zzATy6tFyHIa6NcGhCk4LhGXk=; h=From:To:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=eC1JjJBBi7tdwyPaM36l5F1HZzmpG8oL526jAO0kZZTDGhgb/au87AZYYudbH07KlebPh0lyjCJkW8L2GgWlhdjN96LkoHnFZCNi0MdBwBNvfUYPGD6jZBvI28DPtws23CzHYU1zBf9q3asF3fNpB0S92NVBHvhyDJfwYlWh+XU= 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; Tue, 20 Jan 2026 20:30:05 +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; Tue, 20 Jan 2026 20:30:05 +0800 From: Chin-Ting Kuo To: , , , , , , , , , , , Subject: [PATCH v3 2/2] spi: aspeed: Add support for non-spi-mem devices Date: Tue, 20 Jan 2026 20:30:05 +0800 Message-ID: <20260120123005.1392071-3-chin-ting_kuo@aspeedtech.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260120123005.1392071-1-chin-ting_kuo@aspeedtech.com> References: <20260120123005.1392071-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. Therefore, a user mode based path for non-spi-mem devices is added by implementing the transfer_one() callback and wiring up prepare_message() and unprepare_message() so controller state is initialized and restored for user mode transfers. This allows non-spi-mem devices to operate correctly alongside spi-mem devices on a shared controller. This patch has been tested on: - AST2700 EVB + Infineon and ST SPI TPM device. - AST2x00 EVB + spidev_test utility and the output waveforms are verified with logic analyzer. - AST2x00 EVB + SPI NOR flash read/write regression. Signed-off-by: Chin-Ting Kuo --- drivers/spi/spi-aspeed-smc.c | 134 +++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index db3e096f2eb0..3949f94b6667 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,110 @@ static void aspeed_spi_enable(struct aspeed_spi *aspi= , bool enable) aspeed_spi_chip_enable(aspi, cs, enable); } =20 +static int aspeed_spi_user_prepare_msg(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 *spi =3D msg->spi; + u32 cs =3D spi_get_chipselect(spi, 0); + struct aspeed_spi_chip *chip =3D &aspi->chips[cs]; + u32 ctrl_val; + u32 clk_div =3D data->get_clk_div(chip, spi->max_speed_hz); + + 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); + + return 0; +} + +static int aspeed_spi_user_unprepare_msg(struct spi_controller *ctlr, + struct spi_message *msg) +{ + struct aspeed_spi *aspi =3D + (struct aspeed_spi *)spi_controller_get_devdata(ctlr); + struct spi_device *spi =3D msg->spi; + u32 cs =3D spi_get_chipselect(spi, 0); + struct aspeed_spi_chip *chip =3D &aspi->chips[cs]; + + if (aspi->cs_change =3D=3D 0) + aspeed_spi_stop_user(chip); + + return 0; +} + +static void aspeed_spi_user_transfer_tx(struct aspeed_spi *aspi, + struct spi_device *spi, + 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 && + !!(spi->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_device *spi, + struct spi_transfer *xfer) +{ + struct aspeed_spi *aspi =3D + (struct aspeed_spi *)spi_controller_get_devdata(ctlr); + u32 cs =3D spi_get_chipselect(spi, 0); + struct aspeed_spi_chip *chip =3D &aspi->chips[cs]; + void __iomem *ahb_base =3D aspi->chips[cs].ahb_base; + const u8 *tx_buf =3D xfer->tx_buf; + u8 *rx_buf =3D xfer->rx_buf; + + dev_dbg(aspi->dev, + "[cs%d] xfer: width %d, len %u, tx %p, rx %p\n", + cs, xfer->bits_per_word, xfer->len, + tx_buf, rx_buf); + + if (tx_buf) { + if (spi->mode & SPI_TX_DUAL) + aspeed_spi_set_io_mode(chip, CTRL_IO_DUAL_DATA); + else if (spi->mode & SPI_TX_QUAD) + aspeed_spi_set_io_mode(chip, CTRL_IO_QUAD_DATA); + + aspeed_spi_user_transfer_tx(aspi, spi, tx_buf, rx_buf, + (void *)ahb_base, xfer->len); + } + + if (rx_buf && rx_buf !=3D tx_buf) { + if (spi->mode & SPI_RX_DUAL) + aspeed_spi_set_io_mode(chip, CTRL_IO_DUAL_DATA); + else if (spi->mode & SPI_RX_QUAD) + aspeed_spi_set_io_mode(chip, CTRL_IO_QUAD_DATA); + + ioread8_rep(ahb_base, rx_buf, xfer->len); + } + + xfer->error =3D 0; + aspi->cs_change =3D xfer->cs_change; + + return 0; +} + static int aspeed_spi_probe(struct platform_device *pdev) { struct device *dev =3D &pdev->dev; @@ -899,6 +1010,9 @@ static int aspeed_spi_probe(struct platform_device *pd= ev) 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->prepare_message =3D aspeed_spi_user_prepare_msg; + ctlr->unprepare_message =3D aspeed_spi_user_unprepare_msg; + ctlr->transfer_one =3D aspeed_spi_user_transfer; =20 aspi->num_cs =3D ctlr->num_chipselect; =20 @@ -1455,6 +1569,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 +1586,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 +1601,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 +1619,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 +1638,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 +1657,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 +1676,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 +1694,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