* [PATCH v2 0/5] Add DDR (Dual Data-Rate) support for DW SPI controller
@ 2026-10-28 12:10 Changhuang Liang
2026-10-28 12:10 ` [PATCH v2 1/5] spi: dw: Detect DDR support in enhanced SPI mode Changhuang Liang
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Changhuang Liang @ 2026-10-28 12:10 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Mark Brown,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra
Cc: linux-spi, linux-kernel, linux-mtd, Changhuang Liang
This series implements DDR support for the DW SPI controller and also
implements 1-4-4-DTR read support for the MX66L1G45G.
This series has been tested on the JHB100 SoC.
Changes since v1:
- Add a new patch 1
Patch 2:
- Remove DDR instruction support; currently only supports 1S-nD-nD.
- Add dw_spi_supports_enh_ddr_op() to reject invalid DTR operations.
- Use the new DW_SPI_CAP_DDR flag to indicate DTR support.
- Use addr_data_ddr naming instead of spi_ddr.
Patch 3:
- Rework spi_nor_spimem_setup_op() so the command extension, 2-byte
opcode and SNOR_F_SWAP16 handling apply only to the 8D-8D-8D DTR
protocol. For 1S-xD-xD protocols the opcode remains a single byte
sent in STR, and only the address, dummy and data phases are
switched to DTR.
Patch 4:
- Add Miquel's Reviewed-by tag.
Patch 5:
- Add Miquel's Reviewed-by tag.
- Update commit message.
v1: https://lore.kernel.org/all/20261022102614.75696-1-changhuang.liang@starfivetech.com/
Changhuang Liang (5):
spi: dw: Detect DDR support in enhanced SPI mode
spi: dw: Add DDR (Dual Data-Rate) support for enhanced SPI mode
mtd: spi-nor: Support mixed 1S-xD-xD DTR protocols in
spi_nor_spimem_setup_op()
mtd: spi-nor: debugfs: Correct DTR protocol names for 1S-xD-xD modes
mtd: spi-nor: macronix: Add 1-4-4 DTR read support for MX66L1G45G
drivers/mtd/spi-nor/core.c | 23 ++++----
drivers/mtd/spi-nor/debugfs.c | 8 +--
drivers/mtd/spi-nor/macronix.c | 28 +++++++++-
drivers/spi/spi-dw-core.c | 96 ++++++++++++++++++++++++++++++----
drivers/spi/spi-dw.h | 6 +++
5 files changed, 133 insertions(+), 28 deletions(-)
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/5] spi: dw: Detect DDR support in enhanced SPI mode
2026-10-28 12:10 [PATCH v2 0/5] Add DDR (Dual Data-Rate) support for DW SPI controller Changhuang Liang
@ 2026-10-28 12:10 ` Changhuang Liang
2026-10-28 12:10 ` [PATCH v2 2/5] spi: dw: Add DDR (Dual Data-Rate) support for " Changhuang Liang
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Changhuang Liang @ 2026-10-28 12:10 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Mark Brown,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra
Cc: linux-spi, linux-kernel, linux-mtd, Changhuang Liang
DDR (Dual Data-Rate) is a synthesis option of the DW SSI IP, separate
from the enhanced SPI mode. When it is not implemented, the
SPI_CTRLR0.SPI_DDR_EN bit reads back as zero.
Check whether SPI_DDR_EN is writable in detect_enh_mode(), the same
way clock stretching is checked, and record the result in a new
DW_SPI_CAP_DDR capability. A follow-up patch uses it to decide
whether DTR operations can be advertised to the spi-mem layer.
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
drivers/spi/spi-dw-core.c | 12 +++++++++---
drivers/spi/spi-dw.h | 2 ++
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 47434fa78e4b..e8f70808352a 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -1269,9 +1269,11 @@ static u16 detect_enh_mode(struct dw_spi *dws)
goto disable_enh;
/* test clock stretching */
- dw_writel(dws, DW_SPI_SPI_CTRLR0, DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN);
- if ((DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN & dw_readl(dws, DW_SPI_SPI_CTRLR0)) !=
- DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN) {
+ dw_writel(dws, DW_SPI_SPI_CTRLR0,
+ DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN | DW_SPI_ENH_CTRLR0_SPI_DDR_EN);
+
+ tmp_val = dw_readl(dws, DW_SPI_SPI_CTRLR0);
+ if (!(tmp_val & DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN)) {
/*
* If clock stretching is not enabled then do not use
* enhanced mode.
@@ -1282,6 +1284,10 @@ static u16 detect_enh_mode(struct dw_spi *dws)
dws->caps |= DW_SPI_CAP_EMODE;
+ /* test DDR support, SPI_DDR_EN is read-only zero if not implemented */
+ if (tmp_val & DW_SPI_ENH_CTRLR0_SPI_DDR_EN)
+ dws->caps |= DW_SPI_CAP_DDR;
+
disable_enh:
dw_writel(dws, DW_SPI_CTRLR0, tmp_ctrlr0);
dw_writel(dws, DW_SPI_SPI_CTRLR0, tmp_spi_ctrlr0);
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index c16225c5c887..f922e000e7a0 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -36,6 +36,7 @@
#define DW_SPI_CAP_CS_OVERRIDE BIT(0)
#define DW_SPI_CAP_DFS32 BIT(1)
#define DW_SPI_CAP_EMODE BIT(2)
+#define DW_SPI_CAP_DDR BIT(3)
/* Register offsets (Generic for both DWC APB SSI and DWC SSI IP-cores) */
#define DW_SPI_CTRLR0 0x00
@@ -139,6 +140,7 @@
/* Bit fields in SPI_CTRLR0 */
#define DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN BIT(30)
+#define DW_SPI_ENH_CTRLR0_SPI_DDR_EN BIT(16)
#define DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK GENMASK(15, 11)
#define DW_SPI_ENH_CTRLR0_INST_L_MASK GENMASK(9, 8)
#define DW_SPI_ENH_CTRLR0_INST_L_INST_L0 0x0
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/5] spi: dw: Add DDR (Dual Data-Rate) support for enhanced SPI mode
2026-10-28 12:10 [PATCH v2 0/5] Add DDR (Dual Data-Rate) support for DW SPI controller Changhuang Liang
2026-10-28 12:10 ` [PATCH v2 1/5] spi: dw: Detect DDR support in enhanced SPI mode Changhuang Liang
@ 2026-10-28 12:10 ` Changhuang Liang
2026-10-28 16:39 ` Miquel Raynal
2026-10-28 12:10 ` [PATCH v2 3/5] mtd: spi-nor: Support mixed 1S-xD-xD DTR protocols in spi_nor_spimem_setup_op() Changhuang Liang
` (2 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Changhuang Liang @ 2026-10-28 12:10 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Mark Brown,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra
Cc: linux-spi, linux-kernel, linux-mtd, Changhuang Liang
Add support for 1S-xD-xD DDR transfers in the enhanced SPI mode of the
DW SPI controller, on controllers that have DW_SPI_CAP_DDR.
SPI_CTRLR0.SPI_DDR_EN switches the address, dummy and data phases to
DDR all together, and it only applies to the Dual/Quad/Octal frame
formats. So in supports_op() only accept DTR operations where the
command is sent in STR on one line, the data phase uses two lines or
more, and the address and dummy phases are DTR on the data lines.
DDR instruction phase (8D-8D-8D) is not supported for now.
For DDR transfers the dummy phase takes two bits per clock on each
line, so the wait cycles must be halved. Factor the wait cycle
calculation into dw_spi_enh_wait_cycles(), so that supports_op() and
exec_op() use the same value, and refuse dummy phases that are not a
whole number of clock cycles.
When DDR is enabled, program the DDR_DRIVE_EDGE register. The
recommended TDE value is SCKDV / 4, clamped to SCKDV / 2 - 1 and to
the field width. The register is only written when the controller
supports DDR.
Finally, advertise DTR to the spi-mem layer through a dedicated
dw_spi_ddr_mem_caps when the controller has DW_SPI_CAP_DDR.
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
drivers/spi/spi-dw-core.c | 84 +++++++++++++++++++++++++++++++++++----
drivers/spi/spi-dw.h | 4 ++
2 files changed, 81 insertions(+), 7 deletions(-)
diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index e8f70808352a..6b06b84c661b 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -46,6 +46,8 @@ struct dw_spi_enh_cfg {
u8 inst_l;
u8 addr_l;
u8 trans_t;
+ /* Address, dummy and data phases in DDR (SPI_CTRLR0.SPI_DDR_EN) */
+ bool addr_data_ddr;
};
#ifdef CONFIG_DEBUG_FS
@@ -418,6 +420,7 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
u32 cr0 = chip->cr0;
u32 speed_hz;
u16 clk_div;
+ u32 tde = 0;
/* CTRLR0[ 4/3: 0] or CTRLR0[ 20: 16] Data Frame Size */
cr0 |= (cfg->dfs - 1) << dws->dfs_offset;
@@ -468,11 +471,26 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
if (enh_cfg) {
cr0 = DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN;
+ if (enh_cfg->addr_data_ddr)
+ cr0 |= DW_SPI_ENH_CTRLR0_SPI_DDR_EN;
cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK, enh_cfg->wait_c);
cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_INST_L_MASK, enh_cfg->inst_l);
cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_ADDR_L_MASK, enh_cfg->addr_l);
cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK, enh_cfg->trans_t);
dw_writel(dws, DW_SPI_SPI_CTRLR0, cr0);
+
+ /*
+ * The recommended TDE is SCKDV / 4, and it must not exceed
+ * SCKDV / 2 - 1. DDR_DRIVE_EDGE only exists if DDR is supported.
+ */
+ if (dws->caps & DW_SPI_CAP_DDR) {
+ if (enh_cfg->addr_data_ddr)
+ tde = min3(clk_div / 4U, clk_div / 2U - 1,
+ FIELD_MAX(DW_SPI_DDR_DRIVE_EDGE_TDE_MASK));
+
+ dw_writel(dws, DW_SPI_DDR_DRIVE_EDGE,
+ FIELD_PREP(DW_SPI_DDR_DRIVE_EDGE_TDE_MASK, tde));
+ }
}
}
@@ -656,6 +674,49 @@ static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
return 0;
}
+static u32 dw_spi_enh_wait_cycles(const struct spi_mem_op *op)
+{
+ u32 wait_c;
+
+ if (!op->dummy.nbytes || !op->dummy.buswidth)
+ return 0;
+
+ wait_c = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth;
+ /* DTR transfers two bits per clock on each line. */
+ if (op->dummy.dtr)
+ wait_c >>= 1;
+
+ return wait_c;
+}
+
+/*
+ * SPI_DDR_EN switches the address, dummy and data phases to DDR all
+ * together, and only in the Dual/Quad/Octal frame formats with the
+ * address sent on the data lines. DDR instruction phase is not
+ * supported, so only accept 1S-xD-xD operations.
+ */
+static bool dw_spi_supports_enh_ddr_op(const struct spi_mem_op *op)
+{
+ if (op->cmd.dtr || op->cmd.nbytes != 1 || op->cmd.buswidth != 1)
+ return false;
+
+ if (op->data.dir == SPI_MEM_NO_DATA || !op->data.dtr ||
+ op->data.buswidth < 2)
+ return false;
+
+ if (op->addr.nbytes &&
+ (!op->addr.dtr || op->addr.buswidth != op->data.buswidth))
+ return false;
+
+ /* WAIT_CYCLES counts full clock cycles. */
+ if (op->dummy.nbytes &&
+ (!op->dummy.dtr || !op->dummy.buswidth ||
+ (op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth) % 2))
+ return false;
+
+ return true;
+}
+
static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
@@ -675,13 +736,14 @@ static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem,
if (op->dummy.nbytes && !op->dummy.buswidth)
return false;
+ if ((op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) &&
+ !dw_spi_supports_enh_ddr_op(op))
+ return false;
+
if (op->dummy.nbytes != 0 && op->data.dir == SPI_MEM_DATA_OUT)
return false;
- /* WAIT_CYCLES is a 5-bit field in SPI_CTRLR0 */
- if (op->dummy.nbytes != 0 &&
- op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth >
- FIELD_MAX(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK))
+ if (dw_spi_enh_wait_cycles(op) > FIELD_MAX(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK))
return false;
if ((dws->quirk_flags & DW_SPI_QUIRK_JHB100) && op->addr.nbytes &&
@@ -1074,8 +1136,10 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
else
enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L0;
- if (op->dummy.buswidth)
- enh_cfg.wait_c = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth;
+ enh_cfg.wait_c = dw_spi_enh_wait_cycles(op);
+
+ /* All phases but the instruction share the DDR setting, see supports_op */
+ enh_cfg.addr_data_ddr = op->data.dtr;
dw_spi_enable_chip(dws, 0);
@@ -1389,6 +1453,11 @@ static const struct spi_controller_mem_caps dw_spi_mem_caps = {
.per_op_freq = true,
};
+static const struct spi_controller_mem_caps dw_spi_ddr_mem_caps = {
+ .per_op_freq = true,
+ .dtr = true,
+};
+
int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
{
struct spi_controller *ctlr;
@@ -1445,7 +1514,8 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
ctlr->set_cs = dw_spi_set_cs;
if (dws->mem_ops.exec_op) {
ctlr->mem_ops = &dws->mem_ops;
- ctlr->mem_caps = &dw_spi_mem_caps;
+ ctlr->mem_caps = (dws->caps & DW_SPI_CAP_DDR) ?
+ &dw_spi_ddr_mem_caps : &dw_spi_mem_caps;
}
ctlr->max_speed_hz = dws->max_freq;
ctlr->flags = SPI_CONTROLLER_GPIO_SS;
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index f922e000e7a0..4c548fde89dd 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -67,6 +67,7 @@
#define DW_SPI_RX_SAMPLE_DLY 0xf0
#define DW_SPI_SPI_CTRLR0 0xf4
#define DW_SPI_CS_OVERRIDE 0xf4
+#define DW_SPI_DDR_DRIVE_EDGE 0xf8
/* Register offsets (StarFive JHB100 DWC SSI IP-cores) */
#define DW_SPI_JHB100_INST 0x1000
@@ -152,6 +153,9 @@
#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2 0x2
#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK GENMASK(1, 0)
+/* Bit fields in DDR_DRIVE_EDGE */
+#define DW_SPI_DDR_DRIVE_EDGE_TDE_MASK GENMASK(7, 0)
+
/* Mem/DMA operations helpers */
#define DW_SPI_WAIT_RETRIES 5
#define DW_SPI_BUF_SIZE \
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 3/5] mtd: spi-nor: Support mixed 1S-xD-xD DTR protocols in spi_nor_spimem_setup_op()
2026-10-28 12:10 [PATCH v2 0/5] Add DDR (Dual Data-Rate) support for DW SPI controller Changhuang Liang
2026-10-28 12:10 ` [PATCH v2 1/5] spi: dw: Detect DDR support in enhanced SPI mode Changhuang Liang
2026-10-28 12:10 ` [PATCH v2 2/5] spi: dw: Add DDR (Dual Data-Rate) support for " Changhuang Liang
@ 2026-10-28 12:10 ` Changhuang Liang
2026-10-28 12:10 ` [PATCH v2 4/5] mtd: spi-nor: debugfs: Correct DTR protocol names for 1S-xD-xD modes Changhuang Liang
2026-10-28 12:11 ` [PATCH v2 5/5] mtd: spi-nor: macronix: Add 1-4-4 DTR read support for MX66L1G45G Changhuang Liang
4 siblings, 0 replies; 7+ messages in thread
From: Changhuang Liang @ 2026-10-28 12:10 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Mark Brown,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra
Cc: linux-spi, linux-kernel, linux-mtd, Changhuang Liang
Currently spi_nor_spimem_setup_op() sets all four phases (cmd, addr,
dummy, data) to DTR whenever spi_nor_protocol_is_dtr(proto) is true.
This is too coarse: the 1S-xD-xD DTR protocols (1_1_1_DTR, 1_2_2_DTR,
1_4_4_DTR, 1_8_8_DTR) send the command phase in STR on a single line,
and only the address, dummy and data phases in DTR. SPIMEM supports
such mixed DTR modes.
Split the handling:
- For SNOR_PROTO_8_8_8_DTR, keep the existing behavior: set all
phases to DTR and use a 2-byte opcode (opcode + extension).
- For the other DTR protocols, set the command phase to STR and only
the address, dummy and data phases to DTR. Double op->dummy.nbytes
since DTR transfers 2 bytes per clock cycle, matching how the dummy
cycle count is specified for these protocols.
- Move the SWAP16 handling into the 8D-8D-8D branch as well, since
it only applies there.
No in-tree flash currently declares SNOR_HWCAPS_READ_1_x_x_DTR, so
this is not a bug fix but preparation for enabling these protocols.
A follow-up patch will enable 1S-4D-4D for the MX66L1G45G. The
8D-8D-8D path is unchanged.
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
drivers/mtd/spi-nor/core.c | 23 ++++++++++-------------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 90524c58602e..78b8ee58382e 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -98,13 +98,7 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor,
op->data.buswidth = spi_nor_get_protocol_data_nbits(proto);
if (spi_nor_protocol_is_dtr(proto)) {
- /*
- * SPIMEM supports mixed DTR modes, but right now we can only
- * have all phases either DTR or STR. IOW, SPIMEM can have
- * something like 4S-4D-4D, but SPI NOR can't. So, set all 4
- * phases to either DTR or STR.
- */
- op->cmd.dtr = true;
+ /* we only support 1S-nD-nD or 8D-8D-8D */
op->addr.dtr = true;
op->dummy.dtr = true;
op->data.dtr = true;
@@ -112,13 +106,16 @@ void spi_nor_spimem_setup_op(const struct spi_nor *nor,
/* 2 bytes per clock cycle in DTR mode. */
op->dummy.nbytes *= 2;
- ext = spi_nor_get_cmd_ext(nor, op);
- op->cmd.opcode = (op->cmd.opcode << 8) | ext;
- op->cmd.nbytes = 2;
- }
+ if (proto == SNOR_PROTO_8_8_8_DTR) {
+ op->cmd.dtr = true;
+ ext = spi_nor_get_cmd_ext(nor, op);
+ op->cmd.opcode = (op->cmd.opcode << 8) | ext;
+ op->cmd.nbytes = 2;
- if (proto == SNOR_PROTO_8_8_8_DTR && params->flags & SNOR_F_SWAP16)
- op->data.swap16 = true;
+ if (params->flags & SNOR_F_SWAP16)
+ op->data.swap16 = true;
+ }
+ }
}
/**
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 4/5] mtd: spi-nor: debugfs: Correct DTR protocol names for 1S-xD-xD modes
2026-10-28 12:10 [PATCH v2 0/5] Add DDR (Dual Data-Rate) support for DW SPI controller Changhuang Liang
` (2 preceding siblings ...)
2026-10-28 12:10 ` [PATCH v2 3/5] mtd: spi-nor: Support mixed 1S-xD-xD DTR protocols in spi_nor_spimem_setup_op() Changhuang Liang
@ 2026-10-28 12:10 ` Changhuang Liang
2026-10-28 12:11 ` [PATCH v2 5/5] mtd: spi-nor: macronix: Add 1-4-4 DTR read support for MX66L1G45G Changhuang Liang
4 siblings, 0 replies; 7+ messages in thread
From: Changhuang Liang @ 2026-10-28 12:10 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Mark Brown,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra
Cc: linux-spi, linux-kernel, linux-mtd, Changhuang Liang
The debugfs protocol names for the mixed DTR modes (1-1-1, 1-2-2,
1-4-4, 1-8-8) currently print the command phase as "1D", i.e. dual
transfer on a single line. That does not match the protocol
definition: these modes issue the opcode on a single line in single
data rate (S), and only the address, dummy and data phases use DTR
(D). The "1D" prefix is therefore wrong.
Rename them to "1S-xD-xD" so the names reflect that the first phase
is single-data-rate on one line, consistent with the "8D-8D-8D"
naming for SNOR_PROTO_8_8_8_DTR.
This only changes the strings printed by debugfs; no functional
behavior is affected.
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
drivers/mtd/spi-nor/debugfs.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c
index 3f8d586a7622..f9ec5697aab0 100644
--- a/drivers/mtd/spi-nor/debugfs.c
+++ b/drivers/mtd/spi-nor/debugfs.c
@@ -45,10 +45,10 @@ static const char *spi_nor_protocol_name(enum spi_nor_protocol proto)
case SNOR_PROTO_2_2_2: return "2S-2S-2S";
case SNOR_PROTO_4_4_4: return "4S-4S-4S";
case SNOR_PROTO_8_8_8: return "8S-8S-8S";
- case SNOR_PROTO_1_1_1_DTR: return "1D-1D-1D";
- case SNOR_PROTO_1_2_2_DTR: return "1D-2D-2D";
- case SNOR_PROTO_1_4_4_DTR: return "1D-4D-4D";
- case SNOR_PROTO_1_8_8_DTR: return "1D-8D-8D";
+ case SNOR_PROTO_1_1_1_DTR: return "1S-1D-1D";
+ case SNOR_PROTO_1_2_2_DTR: return "1S-2D-2D";
+ case SNOR_PROTO_1_4_4_DTR: return "1S-4D-4D";
+ case SNOR_PROTO_1_8_8_DTR: return "1S-8D-8D";
case SNOR_PROTO_8_8_8_DTR: return "8D-8D-8D";
}
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 5/5] mtd: spi-nor: macronix: Add 1-4-4 DTR read support for MX66L1G45G
2026-10-28 12:10 [PATCH v2 0/5] Add DDR (Dual Data-Rate) support for DW SPI controller Changhuang Liang
` (3 preceding siblings ...)
2026-10-28 12:10 ` [PATCH v2 4/5] mtd: spi-nor: debugfs: Correct DTR protocol names for 1S-xD-xD modes Changhuang Liang
@ 2026-10-28 12:11 ` Changhuang Liang
4 siblings, 0 replies; 7+ messages in thread
From: Changhuang Liang @ 2026-10-28 12:11 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Mark Brown,
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra
Cc: linux-spi, linux-kernel, linux-mtd, Changhuang Liang
The MX66L1G45G supports the 1-4-4 DTR read mode. This flash uses 6
dummy cycles by default in 1-4-4 DTR mode.
For 1S-4D-4D, its support bit is located in the 21st DWORD of the
BFPT table, while the dummy cycles are described in the 23rd DWORD.
Unfortunately, the BFPT of this flash model currently only supports
16 DWORDs, and the kernel's SFDP parser does not derive any
SNOR_HWCAPS_READ_1_4_4_DTR capability from the BFPT table.
Therefore, implement late_init to initialize support for this opcode,
configuring the read settings with the default 6 dummy cycles.
The SFDP of this part provides a 4BAIT table, which also advertises
the 4-byte 1-4-4 DTR opcode (0xEE). SFDP parsing converts the read
opcodes to their 4-byte variants and spi_nor_set_4byte_opcodes() is
skipped later on, but the 1-4-4 DTR read is only added in late_init,
after 4BAIT has been parsed, so it is not converted there. Select
the 4-byte opcode directly in that case.
Also set SPI_NOR_4B_OPCODES like MX66L51235F (0xc2 0x20 0x1a). The
part supports the 4-byte opcode set, so if 4BAIT parsing does not
complete, spi_nor_set_4byte_opcodes() still picks the 4-byte variants,
including 0xEE for 1-4-4 DTR. With 4BAIT parsed, as on the tested
part, the flag has no further effect.
Add a dedicated fixup for the part (SNOR_ID 0xc2, 0x20, 0x1b) that
keeps the existing QPP 4-byte post-SFDP fixup and additionally
advertises the 1-4-4 DTR read capability in late_init.
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
Logs for MX66L1G45G:
********************
+ cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
c2201b
+ cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
macronix
+ xxd -p /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
53464450060102ff00060110300000ffc2000104100100ff84000102c000
00ffffffffffffffffffffffffffffffffffe520fbffffffff3f44eb086b
083b04bbfeffffffffff00ffffff44eb0c200f5210d800ffd649c50085df
04e34403673830b030b0f7bdd55c4a9e29fff050f985ffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffff7fefffff215cdcffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffff003600279df9c06485cbffffffffffff
+ sha256sum /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
bec91ac20c9f7c63a00992fd2f779f8c5378d8f9afc699a2c741be5bdf268e35 /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
+ cat /sys/kernel/debug/spi-nor/spi0.0/capabilities
Supported read modes by the flash
1S-1S-1S
opcode 0x13
mode cycles 0
dummy cycles 0
1S-1S-2S
opcode 0x3c
mode cycles 0
dummy cycles 8
1S-2S-2S
opcode 0xbc
mode cycles 0
dummy cycles 4
1S-1S-4S
opcode 0x6c
mode cycles 0
dummy cycles 8
1S-4S-4S
opcode 0xec
mode cycles 2
dummy cycles 4
4S-4S-4S
opcode 0xec
mode cycles 2
dummy cycles 4
1S-4D-4D
opcode 0xee
mode cycles 0
dummy cycles 6
Supported page program modes by the flash
1S-1S-1S
opcode 0x12
1S-1S-4S
opcode 0x34
1S-4S-4S
opcode 0x3e
1S-1S-8S
opcode 0x84
1S-8S-8S
opcode 0x8e
+ cat /sys/kernel/debug/spi-nor/spi0.0/params
name (null)
id c2 20 1b c2 20 1b
size 128 MiB
write size 1
page size 256
address nbytes 4
flags 4B_OPCODES | HAS_4BAIT | SOFT_RESET
opcodes
read 0xee
dummy cycles 6
erase 0x21
program 0x3e
SR1 read 0x05
SR1 write 0x01
QE SR1 bit 6
8D extension none
protocols
read 1S-4D-4D
write 1S-4S-4S
register 1S-1S-1S
erase commands
21 (4.00 KiB) [1]
5c (32.0 KiB) [2]
dc (64.0 KiB) [3]
c7 (128 MiB)
sector map
region (in hex) | erase mask | overlaid
------------------+------------+---------
00000000-07ffffff | [ 1 ] | no
+ mtd_debug erase /dev/mtd0 0 2097152
Erased 2097152 bytes from address 0x00000000 in flash
+ mtd_debug read /dev/mtd0 0 2097152 spi_read
Copied 2097152 bytes from address 0x00000000 in flash to spi_read
+ hexdump spi_read
0000000 ffff ffff ffff ffff ffff ffff ffff ffff
*
0200000
+ sha256sum spi_read
4bda3a28f4ffe603c0ec1258c0034d65a1a0d35ab7bd523a834608adabf03cc5 spi_read
+ dd 'if=/dev/urandom' 'of=./spi_test' 'bs=1M' 'count=2'
2+0 records in
2+0 records out
+ mtd_debug write /dev/mtd0 0 2097152 spi_test
Copied 2097152 bytes from spi_test to address 0x00000000 in flash
+ mtd_debug read /dev/mtd0 0 2097152 spi_read
Copied 2097152 bytes from address 0x00000000 in flash to spi_read
+ sha256sum spi_read spi_test
4e6069fe661b4f2ad8b96620641219cd9b34758e1b5d8585af867ff9ab4c349c spi_read
4e6069fe661b4f2ad8b96620641219cd9b34758e1b5d8585af867ff9ab4c349c spi_test
+ mtd_debug info /dev/mtd0
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_NORFLASH
mtd.size = 134217728 (128M)
mtd.erasesize = 4096 (4K)
mtd.writesize = 1
mtd.oobsize = 0
regions = 0
---
drivers/mtd/spi-nor/macronix.c | 28 +++++++++++++++++++++++++++-
1 file changed, 27 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c
index b3fdb4b2fa1c..3401275f096b 100644
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -103,6 +103,26 @@ mx25l12805d_4pp3b_post_sfdp_fixups(struct spi_nor *nor)
return 0;
}
+static int mx66l1g45g_late_init_fixups(struct spi_nor *nor)
+{
+ struct spi_nor_flash_parameter *params = nor->params;
+ u8 opcode = SPINOR_OP_READ_1_4_4_DTR;
+
+ /*
+ * When a 4BAIT table is present, SFDP parsing already converted all
+ * read opcodes to their 4-byte variants and spi_nor_set_4byte_opcodes()
+ * is skipped later on, so select the 4-byte opcode here.
+ */
+ if (params->flags & SNOR_F_HAS_4BAIT)
+ opcode = SPINOR_OP_READ_1_4_4_DTR_4B;
+
+ params->hwcaps.mask |= SNOR_HWCAPS_READ_1_4_4_DTR;
+ spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_4_4_DTR],
+ 0, 6, opcode, SNOR_PROTO_1_4_4_DTR);
+
+ return 0;
+}
+
static const struct spi_nor_fixups mx25l25635_fixups = {
.post_bfpt = mx25l25635_post_bfpt_fixups,
.post_sfdp = macronix_qpp4b_post_sfdp_fixups,
@@ -120,6 +140,11 @@ static const struct spi_nor_fixups mx25l12805d_4pp3b_fixups = {
.post_sfdp = mx25l12805d_4pp3b_post_sfdp_fixups,
};
+static const struct spi_nor_fixups mx66l1g45g_fixups = {
+ .post_sfdp = macronix_qpp4b_post_sfdp_fixups,
+ .late_init = mx66l1g45g_late_init_fixups,
+};
+
static const struct flash_info macronix_nor_parts[] = {
{
.id = SNOR_ID(0xc2, 0x20, 0x10),
@@ -355,7 +380,8 @@ static const struct spi_nor_fixup macronix_fixups[] = {
{ .id = SNOR_ID(0xc2, 0x20, 0x19), .fixups = &mx25l25635_fixups },
{ .id = SNOR_ID(0xc2, 0x20, 0x1a), .fixups = ¯onix_qpp4b_fixups,
.fixup_flags = SPI_NOR_4B_OPCODES },
- { .id = SNOR_ID(0xc2, 0x20, 0x1b), .fixups = ¯onix_qpp4b_fixups },
+ { .id = SNOR_ID(0xc2, 0x20, 0x1b), .fixups = &mx66l1g45g_fixups,
+ .fixup_flags = SPI_NOR_4B_OPCODES },
{ .id = SNOR_ID(0xc2, 0x20, 0x1c), .fixups = ¯onix_qpp4b_fixups },
{ .id = SNOR_ID(0xc2, 0x25, 0x3a), .fixups = ¯onix_qpp4b_fixups },
{ .id = SNOR_ID(0xc2, 0x25, 0x3b), .fixups = ¯onix_qpp4b_fixups },
--
2.25.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/5] spi: dw: Add DDR (Dual Data-Rate) support for enhanced SPI mode
2026-10-28 12:10 ` [PATCH v2 2/5] spi: dw: Add DDR (Dual Data-Rate) support for " Changhuang Liang
@ 2026-10-28 16:39 ` Miquel Raynal
0 siblings, 0 replies; 7+ messages in thread
From: Miquel Raynal @ 2026-10-28 16:39 UTC (permalink / raw)
To: Changhuang Liang
Cc: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Mark Brown,
Richard Weinberger, Vignesh Raghavendra, linux-spi, linux-kernel,
linux-mtd
On 28/10/2026 at 05:10:57 -07, Changhuang Liang <changhuang.liang@starfivetech.com> wrote:
> Add support for 1S-xD-xD DDR transfers in the enhanced SPI mode of the
> DW SPI controller, on controllers that have DW_SPI_CAP_DDR.
>
> SPI_CTRLR0.SPI_DDR_EN switches the address, dummy and data phases to
> DDR all together, and it only applies to the Dual/Quad/Octal frame
> formats. So in supports_op() only accept DTR operations where the
> command is sent in STR on one line, the data phase uses two lines or
> more, and the address and dummy phases are DTR on the data lines.
> DDR instruction phase (8D-8D-8D) is not supported for now.
>
> For DDR transfers the dummy phase takes two bits per clock on each
> line, so the wait cycles must be halved. Factor the wait cycle
> calculation into dw_spi_enh_wait_cycles(), so that supports_op() and
> exec_op() use the same value, and refuse dummy phases that are not a
> whole number of clock cycles.
>
> When DDR is enabled, program the DDR_DRIVE_EDGE register. The
> recommended TDE value is SCKDV / 4, clamped to SCKDV / 2 - 1 and to
> the field width. The register is only written when the controller
> supports DDR.
>
> Finally, advertise DTR to the spi-mem layer through a dedicated
> dw_spi_ddr_mem_caps when the controller has DW_SPI_CAP_DDR.
>
> Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Looks correct to me.
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-28 16:39 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-28 12:10 [PATCH v2 0/5] Add DDR (Dual Data-Rate) support for DW SPI controller Changhuang Liang
2026-10-28 12:10 ` [PATCH v2 1/5] spi: dw: Detect DDR support in enhanced SPI mode Changhuang Liang
2026-10-28 12:10 ` [PATCH v2 2/5] spi: dw: Add DDR (Dual Data-Rate) support for " Changhuang Liang
2026-10-28 16:39 ` Miquel Raynal
2026-10-28 12:10 ` [PATCH v2 3/5] mtd: spi-nor: Support mixed 1S-xD-xD DTR protocols in spi_nor_spimem_setup_op() Changhuang Liang
2026-10-28 12:10 ` [PATCH v2 4/5] mtd: spi-nor: debugfs: Correct DTR protocol names for 1S-xD-xD modes Changhuang Liang
2026-10-28 12:11 ` [PATCH v2 5/5] mtd: spi-nor: macronix: Add 1-4-4 DTR read support for MX66L1G45G Changhuang Liang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®