[PATCH 4/5] spi: ma35d1-qspi: Add DTR support

Miquel Raynal miquel.raynal at bootlin.com
Thu Aug 13 01:00:24 PDT 2026


The controller has DTR support, a bit must be set for it. The behaviour
is interesting though, as the speed won't improve when enabled. This is
because there seems to be an internal divisor (/2) which keeps the rate
equal when DTR is enabled. As a result, this commit also doubles the
target bus speed, which in practice does not happen. This way, there is
a real gain:

Before:
$ flash_speed /dev/mtd0 -dc10
eraseblock write speed is 1000 KiB/s
[...]
eraseblock read speed is 1199 KiB/s
[...]

After:
$ flash_speed /dev/mtd0 -dc10
eraseblock write speed is 985 KiB/s
[...]
eraseblock read speed is 1540 KiB/s
[...]

Signed-off-by: Miquel Raynal <miquel.raynal at bootlin.com>
---
 drivers/spi/spi-ma35d1-qspi.c | 27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/drivers/spi/spi-ma35d1-qspi.c b/drivers/spi/spi-ma35d1-qspi.c
index 027a9433f2b1..9965a11573d8 100644
--- a/drivers/spi/spi-ma35d1-qspi.c
+++ b/drivers/spi/spi-ma35d1-qspi.c
@@ -31,6 +31,7 @@
 #define NUVOTON_QSPI_RX_OFFSET		0x30 /* Data Receive Register, RO */
 
 /* QSPI Control Register bit masks */
+#define NUVOTON_QSPI_CTL_DTREN_MASK	BIT(23) /* DTR I/O Mode Enable */
 #define NUVOTON_QSPI_CTL_QUADIOEN_MASK	BIT(22) /* Quad I/O Mode Enable */
 #define NUVOTON_QSPI_CTL_DUALIOEN_MASK	BIT(21) /* Dual I/O Mode Enable */
 #define NUVOTON_QSPI_CTL_DATDIR_MASK	BIT(20) /* Data Port Direction Control */
@@ -131,7 +132,7 @@ static int nuvoton_qspi_reset_fifo(struct nuvoton_qspi *qspi)
 					 1, NUVOTON_QSPI_TIMEOUT_US);
 }
 
-static int nuvoton_qspi_set_speed(struct spi_device *spi, u32 speed_hz)
+static int nuvoton_qspi_set_speed(struct spi_device *spi, u32 speed_hz, bool dtr)
 {
 	struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
 	unsigned long clk_rate;
@@ -143,6 +144,10 @@ static int nuvoton_qspi_set_speed(struct spi_device *spi, u32 speed_hz)
 	if (!speed_hz)
 		return -EINVAL;
 
+	/* Experimentally, when enabling DTR the frequency is cut in half */
+	if (dtr)
+		speed_hz *= 2;
+
 	if (qspi->speed_hz == speed_hz)
 		return 0;
 
@@ -215,16 +220,19 @@ static int nuvoton_qspi_setup_transfer(struct spi_device *spi, u8 bpw)
 static int nuvoton_qspi_configure_bus(struct spi_device *spi,
 				      unsigned int buswidth,
 				      enum spi_mem_data_dir dir,
-				      u32 speed_hz)
+				      u32 speed_hz, bool dtr)
 {
 	struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
 	u32 ctl = 0;
 	int ret;
 
-	ret = nuvoton_qspi_set_speed(spi, speed_hz);
+	ret = nuvoton_qspi_set_speed(spi, speed_hz, dtr);
 	if (ret)
 		return ret;
 
+	if (dtr)
+		ctl |= NUVOTON_QSPI_CTL_DTREN_MASK;
+
 	if (buswidth == 4)
 		ctl |= NUVOTON_QSPI_CTL_QUADIOEN_MASK;
 	else if (buswidth == 2)
@@ -234,6 +242,7 @@ static int nuvoton_qspi_configure_bus(struct spi_device *spi,
 		ctl |= NUVOTON_QSPI_CTL_DATDIR_MASK;
 
 	nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+				 NUVOTON_QSPI_CTL_DTREN_MASK |
 				 NUVOTON_QSPI_CTL_QUADIOEN_MASK |
 				 NUVOTON_QSPI_CTL_DUALIOEN_MASK |
 				 NUVOTON_QSPI_CTL_DATDIR_MASK, ctl);
@@ -470,7 +479,7 @@ static int nuvoton_qspi_mem_exec_op(struct spi_mem *mem,
 		cmd[i] = op->cmd.opcode >> (8 * (op->cmd.nbytes - i - 1));
 
 	ret = nuvoton_qspi_configure_bus(spi, op->cmd.buswidth, SPI_MEM_DATA_OUT,
-					 op->max_freq);
+					 op->max_freq, op->cmd.dtr);
 	if (ret)
 		goto out_deassert_cs;
 
@@ -483,7 +492,7 @@ static int nuvoton_qspi_mem_exec_op(struct spi_mem *mem,
 			addr[i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
 
 		ret = nuvoton_qspi_configure_bus(spi, op->addr.buswidth, SPI_MEM_DATA_OUT,
-						 op->max_freq);
+						 op->max_freq, op->addr.dtr);
 		if (ret)
 			goto out_deassert_cs;
 
@@ -494,7 +503,7 @@ static int nuvoton_qspi_mem_exec_op(struct spi_mem *mem,
 
 	if (op->dummy.nbytes) {
 		ret = nuvoton_qspi_configure_bus(spi, op->dummy.buswidth, SPI_MEM_DATA_OUT,
-						 op->max_freq);
+						 op->max_freq, op->dummy.dtr);
 		if (ret)
 			goto out_deassert_cs;
 
@@ -505,7 +514,7 @@ static int nuvoton_qspi_mem_exec_op(struct spi_mem *mem,
 
 	if (op->data.nbytes) {
 		ret = nuvoton_qspi_configure_bus(spi, op->data.buswidth, op->data.dir,
-						 op->max_freq);
+						 op->max_freq, op->data.dtr);
 		if (ret)
 			goto out_deassert_cs;
 
@@ -531,6 +540,7 @@ static const struct spi_controller_mem_ops nuvoton_qspi_mem_ops = {
 
 static const struct spi_controller_mem_caps nuvoton_qspi_mem_caps = {
 	.per_op_freq = true,
+	.dtr = true,
 };
 
 static int nuvoton_qspi_transfer_one(struct spi_controller *ctlr,
@@ -567,7 +577,8 @@ static int nuvoton_qspi_transfer_one(struct spi_controller *ctlr,
 			buswidth = 2;
 	}
 
-	ret = nuvoton_qspi_configure_bus(spi, buswidth, dir, xfer->speed_hz);
+	ret = nuvoton_qspi_configure_bus(spi, buswidth, dir, xfer->speed_hz,
+					 xfer->dtr_mode);
 	if (ret)
 		return ret;
 

-- 
2.54.0




More information about the linux-arm-kernel mailing list