[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