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

Miquel Raynal <[email protected]>
Newsgroups org.kernel.vger.linux-spi,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel
Message-ID <20260813-perso-ma35d1-upstream-qspi-v1-4-b217b9870eb1@bootlin.com>
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 <[email protected]>
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.