* [PATCH v2 00/10] hw/{block, ssi}: Fix spi-nor flash dummy byte handling
@ 2026-07-07 8:34 ` Bin Meng via qemu development
0 siblings, 0 replies; 21+ messages in thread
From: Bin Meng via qemu development @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU
Cc: Alistair Francis, Andrew Jeffery, Cédric Le Goater,
Edgar E. Iglesias, Hanna Reitz, Hao Wu, Jamin Lin, Joel Stanley,
Kane Chen, Kevin Wolf, Peter Maydell, Pierrick Bouvier,
Steven Lee, Troy Lee, Tyrone Ting, qemu-arm, qemu-block
This series fixes dummy-cycle accounting for SPI NOR fast-read
operations across the m25p80 flash model and several SSI controller
models.
The root problem is that the affected code paths mixed two different
units. Flash datasheets and controller registers often describe
fast-read dummy phases in clock cycles, while the SSI bus interface used
by these models transfers byte-sized words to the flash. As a result,
some paths treated cycle counts as byte counts directly, and other paths
compensated in controller-specific ways that duplicated flash command
semantics.
The first four patches fix the flash side. They make m25p80 keep
`needed_bytes` as a byte count and convert manufacturer-specific
dummy-cycle requirements to dummy byte transfers for Winbond,
Numonyx/Micron, Macronix, and Spansion flashes. The Numonyx/Micron
change also handles the standard-mode case where the dummy phase follows
the address bus width for output fast-read commands.
The next patches fix controller-generated dummy phases. NPCM7xx FIU
and Xilinx ZynqMP GQSPI now convert programmed dummy-cycle fields to
SSI byte transfers using the bus width selected for the dummy phase.
The ASPEED SMC direct-read path is updated the same way. The two ASPEED
reverts then remove older workarounds that either hard-coded FAST_READ_4
dummy behavior or snooped manual user-mode SPI streams to inject extra
dummy cycles. With m25p80 accounting fixed, user-mode byte streams
should be forwarded as supplied by the guest.
The final patch documents the intended ownership boundary: flash models
own command semantics and dummy-byte consumption, while controller
models own hardware-generated dummy phases and cycle-to-byte conversion.
Some background:
While recently working on some device modeling, I hit this issue
again. I then realized that I had sent a fix series for it more than
five years ago, but it never made it upstream.
This series picks up that old work, refreshes it against the current
tree, and fixes several issues in the SPI controller models. The original
series is available at [1]. I have tried to verify all the SPI controller
changes by booting real-world guest software to ensure the change does
not break anything.
[1] https://lore.kernel.org/qemu-devel/20210114150902.11515-1-bmeng.cn@gmail.com/
Changes in v2:
- correct the typo in the commit message: instrunction => instruction
- correct the typo in the commit message: genenic => generic
- change numonyx_extract_cfg_num_dummies() to numonyx_extract_cfg_dummy_bytes()
to avoid confusion
- change macronix_extract_cfg_num_dummies() to macronix_extract_cfg_dummy_bytes()
to avoid confusion
- change spansion_extract_cfg_num_dummies() to spansion_extract_cfg_dummy_bytes()
to avoid confusion
- use assert() when the dummy bit count is not byte-aligned
Bin Meng (10):
hw/block: m25p80: Fix dummy byte handling for Winbond flash
hw/block: m25p80: Fix dummy byte handling for Numonyx/Micron flash
hw/block: m25p80: Fix dummy byte handling for Macronix flash
hw/block: m25p80: Fix dummy byte handling for Spansion flash
hw/ssi: npcm7xx_fiu: Correct the dummy cycle emulation logic
hw/ssi: xilinx_spips: Fix dummy phase handling
hw/ssi: aspeed_smc: Fix direct-read dummy bytes
Revert "aspeed/smc: Fix number of dummy cycles for FAST_READ_4
command"
Revert "aspeed/smc: snoop SPI transfers to fake dummy cycles"
docs/devel: Document SSI dummy-cycle ownership
docs/devel/index-internals.rst | 1 +
docs/devel/ssi.rst | 132 ++++++++++++++++++++++++++++
hw/block/m25p80.c | 154 +++++++++++++++++++++------------
hw/ssi/aspeed_smc.c | 135 ++++-------------------------
hw/ssi/npcm7xx_fiu.c | 25 ++----
hw/ssi/trace-events | 1 -
hw/ssi/xilinx_spips.c | 122 +++++++++++++++++++-------
include/hw/ssi/aspeed_smc.h | 2 -
include/hw/ssi/xilinx_spips.h | 2 +-
9 files changed, 348 insertions(+), 226 deletions(-)
create mode 100644 docs/devel/ssi.rst
---
base-commit: 94826ec1370328375c3b6d1e80fdc94c8f46c348
branch: m25p80-v2
--
2.53.0
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH v2 01/10] hw/block: m25p80: Fix dummy byte handling for Winbond flash
2026-07-07 8:34 ` Bin Meng via qemu development
(?)
@ 2026-07-07 8:34 ` Bin Meng
-1 siblings, 0 replies; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU
Cc: Cédric Le Goater, Alistair Francis, Hanna Reitz, Kevin Wolf,
qemu-block
The m25p80 model uses s->needed_bytes to track how many bytes a
controller must send after an opcode before the flash model can enter
the data phase. For address-bearing commands this includes the address
bytes. For fast-read commands it also includes the dummy phase.
The tricky part is that flash datasheets describe the dummy phase in
clock cycles, while the QEMU SSI interface advances the flash model one
transferred byte at a time. The dummy clock count therefore has to be
converted to the number of SSI bytes that the controller will actually
emit.
Some controllers have drivers that push these dummy bytes into a FIFO.
Other controllers are programmed with a dummy-cycle count and generate
the clocks themselves. The flash model still has to use the same byte
count that a FIFO-style controller or the Linux spi-mem layer would use,
otherwise the model waits too long and drops the first data bytes.
Let's fix the inconsistency from the flash side first. We start from an
easy one, the Winbond flashes.
Per the Windbond W25Q256JV datasheet [1] instruction set table
(chapter 8.1.2, 8.1.3, 8.1.4, 8.1.5), fix the wrong number of
dummy bytes needed for fast read commands.
[1] https://www.winbond.com/resource-files/w25q256jv%20spi%20revb%2009202016.pdf
Fixes: fe8477052831 ("m25p80: Fix QIOR/DIOR handling for Winbond")
Fixes: 3830c7a460b8 ("m25p80: Fix WINBOND fast read command handling")
Fixes: cf6f1efe0b57 ("m25p80: Fast read commands family changes")
Signed-off-by: Bin Meng <bin.meng@processmission.com>
Tested-by: Cédric Le Goater <clg@redhat.com>
---
Changes in v2:
- correct the typo in the commit message: instrunction => instruction
hw/block/m25p80.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 4a4cda6602..59ecb32c0a 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -1004,7 +1004,7 @@ static void decode_fast_read_cmd(Flash *s)
s->needed_bytes += 1;
break;
case MAN_WINBOND:
- s->needed_bytes += 8;
+ s->needed_bytes += 1;
break;
case MAN_NUMONYX:
s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
@@ -1099,7 +1099,7 @@ static void decode_qio_read_cmd(Flash *s)
switch (get_man(s)) {
case MAN_WINBOND:
s->needed_bytes += WINBOND_CONTINUOUS_READ_MODE_CMD_LEN;
- s->needed_bytes += 4;
+ s->needed_bytes += 2;
break;
case MAN_SPANSION:
s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 02/10] hw/block: m25p80: Fix dummy byte handling for Numonyx/Micron flash
2026-07-07 8:34 ` Bin Meng via qemu development
(?)
(?)
@ 2026-07-07 8:34 ` Bin Meng
-1 siblings, 0 replies; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU
Cc: Cédric Le Goater, Alistair Francis, Hanna Reitz, Kevin Wolf,
qemu-block
Numonyx/Micron flashes [1] do not use one fixed dummy-phase width for all
fast-read commands. The volatile configuration register stores a number
of dummy clock cycles, and QEMU must convert that value to the number of
SSI bytes consumed by the flash model.
Keep the existing default: 10 dummy clocks in Quad I/O mode and 8 dummy
clocks otherwise. In Quad I/O and Dual I/O protocol modes, all command
phases are transferred on 4 or 2 lines, so the dummy clock count still
needs to be scaled by that bus width.
Standard SPI, also called extended SPI in the Micron datasheet, is more
subtle. Quad Output Fast Read (6Bh) and Dual Output Fast Read (3Bh) keep
the opcode and address phases on DQ0; their dummy phase is just a clock
gap before data is returned on four or two output lines. Do not scale the
dummy count for those output-only commands. Only Quad I/O Fast Read
(EBh) and Dual I/O Fast Read (BBh) transfer the address and dummy phases
on the 4-bit or 2-bit bus, so keep scaling those commands.
[1] https://docs.rs-online.com/cad7/0900766b8121bd3c.pdf
Fixes: 23af26856606 ("hw/block/m25p80: Fix Numonyx fast read dummy cycle count")
Signed-off-by: Bin Meng <bin.meng@processmission.com>
Tested-by: Cédric Le Goater <clg@redhat.com>
---
Changes in v2:
- change numonyx_extract_cfg_num_dummies() to numonyx_extract_cfg_dummy_bytes()
to avoid confusion
- use assert() when the dummy bit count is not byte-aligned
hw/block/m25p80.c | 55 ++++++++++++++++++++++++++++++++++++-----------
1 file changed, 42 insertions(+), 13 deletions(-)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 59ecb32c0a..83d1ce1f95 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -971,28 +971,57 @@ static uint8_t numonyx_mode(Flash *s)
}
}
-static uint8_t numonyx_extract_cfg_num_dummies(Flash *s)
+static uint8_t numonyx_extract_cfg_dummy_bytes(Flash *s)
{
- uint8_t num_dummies;
+ uint8_t dummy_bits;
uint8_t mode;
- assert(get_man(s) == MAN_NUMONYX);
mode = numonyx_mode(s);
- num_dummies = extract32(s->volatile_cfg, 4, 4);
+ dummy_bits = extract32(s->volatile_cfg, 4, 4);
- if (num_dummies == 0x0 || num_dummies == 0xf) {
+ /*
+ * The default nubmer of dummy cycles is only related to the SPI
+ * protocol mode. For QSPI it is 10, otherwise it is 8.
+ */
+ if (dummy_bits == 0x0 || dummy_bits == 0xf) {
+ dummy_bits = (mode == MODE_QIO) ? 10 : 8;
+ }
+
+ /*
+ * Convert the number of dummy cycles to bytes.
+ *
+ * In the Dual I/O and Quad I/O protocols, all command phases use 2 or 4
+ * lines. In standard/extended SPI mode the phase width depends on the
+ * command sequence: output-only fast reads keep the dummy clocks on the
+ * single address line, while input/output fast reads use the same 2-line
+ * or 4-line phase as the address.
+ */
+
+ if (mode == MODE_QIO) {
+ dummy_bits *= 4;
+ } else if (mode == MODE_DIO) {
+ dummy_bits *= 2;
+ } else {
switch (s->cmd_in_progress) {
case QIOR:
case QIOR4:
- num_dummies = 10;
+ dummy_bits *= 4;
break;
- default:
- num_dummies = (mode == MODE_QIO) ? 10 : 8;
+ case DIOR:
+ case DIOR4:
+ dummy_bits *= 2;
break;
- }
+ }
}
- return num_dummies;
+ /*
+ * Assert that the dummy bit count is byte-aligned
+ * as SSI core can only consume whole dummy bytes.
+ */
+ assert(dummy_bits % 8 == 0);
+
+ /* return the number of dummy bytes */
+ return dummy_bits / 8;
}
static void decode_fast_read_cmd(Flash *s)
@@ -1007,7 +1036,7 @@ static void decode_fast_read_cmd(Flash *s)
s->needed_bytes += 1;
break;
case MAN_NUMONYX:
- s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
+ s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
break;
case MAN_MACRONIX:
if (extract32(s->volatile_cfg, 6, 2) == 1) {
@@ -1059,7 +1088,7 @@ static void decode_dio_read_cmd(Flash *s)
);
break;
case MAN_NUMONYX:
- s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
+ s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
break;
case MAN_MACRONIX:
switch (extract32(s->volatile_cfg, 6, 2)) {
@@ -1109,7 +1138,7 @@ static void decode_qio_read_cmd(Flash *s)
);
break;
case MAN_NUMONYX:
- s->needed_bytes += numonyx_extract_cfg_num_dummies(s);
+ s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
break;
case MAN_MACRONIX:
switch (extract32(s->volatile_cfg, 6, 2)) {
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 03/10] hw/block: m25p80: Fix dummy byte handling for Macronix flash
2026-07-07 8:34 ` Bin Meng via qemu development
` (2 preceding siblings ...)
(?)
@ 2026-07-07 8:34 ` Bin Meng
-1 siblings, 0 replies; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU
Cc: Cédric Le Goater, Alistair Francis, Hanna Reitz, Kevin Wolf,
qemu-block
Macronix flashes expose DC[1:0] bits in the volatile configuration
register [1]. These bits select the number of dummy clock cycles
used by the fast-read command families.
Convert the Macronix dummy-cycle settings through per-command-family
tables and round up the non-byte-aligned cases that the byte-oriented
SSI model cannot represent exactly.
[1] https://www.macronix.com/Lists/Datasheet/Attachments/8657/MX66L51235F,%203V,%20512Mb,%20v1.1.pdf
Fixes: cf6f1efe0b57 ("m25p80: Fast read commands family changes")
Signed-off-by: Bin Meng <bin.meng@processmission.com>
Tested-by: Cédric Le Goater <clg@redhat.com>
---
Changes in v2:
- change macronix_extract_cfg_num_dummies() to macronix_extract_cfg_dummy_bytes()
to avoid confusion
- use assert() when the dummy bit count is not byte-aligned
hw/block/m25p80.c | 63 +++++++++++++++++++++++++++--------------------
1 file changed, 36 insertions(+), 27 deletions(-)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 83d1ce1f95..d7a9d79373 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -1024,6 +1024,39 @@ static uint8_t numonyx_extract_cfg_dummy_bytes(Flash *s)
return dummy_bits / 8;
}
+static uint8_t macronix_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width)
+{
+ static const uint8_t dummy_cycles_fast[4] = { 8, 6, 8, 10 };
+ static const uint8_t dummy_cycles_dio[4] = { 4, 6, 8, 10 };
+ static const uint8_t dummy_cycles_qio[4] = { 6, 4, 8, 10 };
+ const uint8_t *dummy_cycles = dummy_cycles_fast;
+ uint8_t dummy_bits;
+
+ switch (s->cmd_in_progress) {
+ case DIOR:
+ case DIOR4:
+ dummy_cycles = dummy_cycles_dio;
+ break;
+ case QIOR:
+ case QIOR4:
+ dummy_cycles = dummy_cycles_qio;
+ break;
+ default:
+ break;
+ }
+
+ dummy_bits = dummy_cycles[extract32(s->volatile_cfg, 6, 2)];
+ dummy_bits *= bus_width;
+
+ /*
+ * Assert that the dummy bit count is byte-aligned
+ * as SSI core can only consume whole dummy bytes.
+ */
+ assert(dummy_bits % 8 == 0);
+
+ return dummy_bits / 8;
+}
+
static void decode_fast_read_cmd(Flash *s)
{
s->needed_bytes = get_addr_length(s);
@@ -1039,11 +1072,7 @@ static void decode_fast_read_cmd(Flash *s)
s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
break;
case MAN_MACRONIX:
- if (extract32(s->volatile_cfg, 6, 2) == 1) {
- s->needed_bytes += 6;
- } else {
- s->needed_bytes += 8;
- }
+ s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 1);
break;
case MAN_SPANSION:
s->needed_bytes += extract32(s->spansion_cr2v,
@@ -1091,17 +1120,7 @@ static void decode_dio_read_cmd(Flash *s)
s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
break;
case MAN_MACRONIX:
- switch (extract32(s->volatile_cfg, 6, 2)) {
- case 1:
- s->needed_bytes += 6;
- break;
- case 2:
- s->needed_bytes += 8;
- break;
- default:
- s->needed_bytes += 4;
- break;
- }
+ s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 2);
break;
case MAN_ISSI:
/*
@@ -1141,17 +1160,7 @@ static void decode_qio_read_cmd(Flash *s)
s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
break;
case MAN_MACRONIX:
- switch (extract32(s->volatile_cfg, 6, 2)) {
- case 1:
- s->needed_bytes += 4;
- break;
- case 2:
- s->needed_bytes += 8;
- break;
- default:
- s->needed_bytes += 6;
- break;
- }
+ s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 4);
break;
case MAN_ISSI:
/*
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 04/10] hw/block: m25p80: Fix dummy byte handling for Spansion flash
2026-07-07 8:34 ` Bin Meng via qemu development
` (3 preceding siblings ...)
(?)
@ 2026-07-07 8:34 ` Bin Meng
2026-07-07 12:34 ` Philippe Mathieu-Daudé
-1 siblings, 1 reply; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU
Cc: Cédric Le Goater, Alistair Francis, Hanna Reitz, Kevin Wolf,
qemu-block
Spansion flashes expose the number of dummy clock cycles through CR2V
register [1]. The value is a cycle count, not a byte count, so the
m25p80 model has to convert it to the number of whole SSI transfer
bytes consumed while collecting read command data.
Add a helper that multiplies the CR2V dummy cycle count by the phase
width and rounds up non-byte-aligned counts, matching the byte-oriented
SSI model. The default eight-cycle configuration keeps the same byte
counts as before.
[1] https://www.infineon.com/assets/row/public/documents/10/49/infineon-s25fs128s-s25fs256s-1-datasheet-en.pdf
Fixes: cf6f1efe0b57 ("m25p80: Fast read commands family changes")
Signed-off-by: Bin Meng <bin.meng@processmission.com>
Tested-by: Cédric Le Goater <clg@redhat.com>
---
Changes in v2:
- change spansion_extract_cfg_num_dummies() to spansion_extract_cfg_dummy_bytes()
to avoid confusion
- use assert() when the dummy bit count is not byte-aligned
hw/block/m25p80.c | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index d7a9d79373..545e0b5728 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -1057,6 +1057,23 @@ static uint8_t macronix_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width)
return dummy_bits / 8;
}
+static uint8_t spansion_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width)
+{
+ uint8_t dummy_bits;
+
+ dummy_bits = extract32(s->spansion_cr2v, SPANSION_DUMMY_CLK_POS,
+ SPANSION_DUMMY_CLK_LEN);
+ dummy_bits *= bus_width;
+
+ /*
+ * Assert that the dummy bit count is byte-aligned
+ * as SSI core can only consume whole dummy bytes.
+ */
+ assert(dummy_bits % 8 == 0);
+
+ return dummy_bits / 8;
+}
+
static void decode_fast_read_cmd(Flash *s)
{
s->needed_bytes = get_addr_length(s);
@@ -1075,10 +1092,7 @@ static void decode_fast_read_cmd(Flash *s)
s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 1);
break;
case MAN_SPANSION:
- s->needed_bytes += extract32(s->spansion_cr2v,
- SPANSION_DUMMY_CLK_POS,
- SPANSION_DUMMY_CLK_LEN
- );
+ s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 1);
break;
case MAN_ISSI:
/*
@@ -1111,10 +1125,7 @@ static void decode_dio_read_cmd(Flash *s)
break;
case MAN_SPANSION:
s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
- s->needed_bytes += extract32(s->spansion_cr2v,
- SPANSION_DUMMY_CLK_POS,
- SPANSION_DUMMY_CLK_LEN
- );
+ s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 2);
break;
case MAN_NUMONYX:
s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
@@ -1151,10 +1162,7 @@ static void decode_qio_read_cmd(Flash *s)
break;
case MAN_SPANSION:
s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
- s->needed_bytes += extract32(s->spansion_cr2v,
- SPANSION_DUMMY_CLK_POS,
- SPANSION_DUMMY_CLK_LEN
- );
+ s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 4);
break;
case MAN_NUMONYX:
s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v2 04/10] hw/block: m25p80: Fix dummy byte handling for Spansion flash
2026-07-07 8:34 ` [PATCH v2 04/10] hw/block: m25p80: Fix dummy byte handling for Spansion flash Bin Meng
@ 2026-07-07 12:34 ` Philippe Mathieu-Daudé
2026-07-07 12:51 ` Cédric Le Goater
0 siblings, 1 reply; 21+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-07 12:34 UTC (permalink / raw)
To: Bin Meng, QEMU
Cc: Cédric Le Goater, Alistair Francis, Hanna Reitz, Kevin Wolf,
qemu-block
On 7/7/26 10:34, Bin Meng wrote:
> Spansion flashes expose the number of dummy clock cycles through CR2V
> register [1]. The value is a cycle count, not a byte count, so the
> m25p80 model has to convert it to the number of whole SSI transfer
> bytes consumed while collecting read command data.
>
> Add a helper that multiplies the CR2V dummy cycle count by the phase
> width and rounds up non-byte-aligned counts, matching the byte-oriented
> SSI model. The default eight-cycle configuration keeps the same byte
> counts as before.
>
> [1] https://www.infineon.com/assets/row/public/documents/10/49/infineon-s25fs128s-s25fs256s-1-datasheet-en.pdf
>
> Fixes: cf6f1efe0b57 ("m25p80: Fast read commands family changes")
> Signed-off-by: Bin Meng <bin.meng@processmission.com>
> Tested-by: Cédric Le Goater <clg@redhat.com>
>
> ---
>
> Changes in v2:
> - change spansion_extract_cfg_num_dummies() to spansion_extract_cfg_dummy_bytes()
> to avoid confusion
> - use assert() when the dummy bit count is not byte-aligned
>
> hw/block/m25p80.c | 32 ++++++++++++++++++++------------
> 1 file changed, 20 insertions(+), 12 deletions(-)
>
> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
> index d7a9d79373..545e0b5728 100644
> --- a/hw/block/m25p80.c
> +++ b/hw/block/m25p80.c
> @@ -1057,6 +1057,23 @@ static uint8_t macronix_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width)
> return dummy_bits / 8;
> }
>
> +static uint8_t spansion_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width)
> +{
> + uint8_t dummy_bits;
> +
> + dummy_bits = extract32(s->spansion_cr2v, SPANSION_DUMMY_CLK_POS,
> + SPANSION_DUMMY_CLK_LEN);
> + dummy_bits *= bus_width;
> +
> + /*
> + * Assert that the dummy bit count is byte-aligned
> + * as SSI core can only consume whole dummy bytes.
> + */
> + assert(dummy_bits % 8 == 0);
> +
> + return dummy_bits / 8;
> +}
> +
> static void decode_fast_read_cmd(Flash *s)
> {
> s->needed_bytes = get_addr_length(s);
> @@ -1075,10 +1092,7 @@ static void decode_fast_read_cmd(Flash *s)
> s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 1);
> break;
> case MAN_SPANSION:
> - s->needed_bytes += extract32(s->spansion_cr2v,
> - SPANSION_DUMMY_CLK_POS,
> - SPANSION_DUMMY_CLK_LEN
> - );
> + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 1);
> break;
> case MAN_ISSI:
> /*
> @@ -1111,10 +1125,7 @@ static void decode_dio_read_cmd(Flash *s)
> break;
> case MAN_SPANSION:
> s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
> - s->needed_bytes += extract32(s->spansion_cr2v,
> - SPANSION_DUMMY_CLK_POS,
> - SPANSION_DUMMY_CLK_LEN
> - );
> + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 2);
> break;
> case MAN_NUMONYX:
> s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
> @@ -1151,10 +1162,7 @@ static void decode_qio_read_cmd(Flash *s)
> break;
> case MAN_SPANSION:
> s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
> - s->needed_bytes += extract32(s->spansion_cr2v,
> - SPANSION_DUMMY_CLK_POS,
> - SANSION_DUMMY_CLK_LEN
> - );
> + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 4);
> break;
> case MAN_NUMONYX:
> s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
This breaks the test_arm_emcraft_sf2 functional test:
not ok 1 test_emcraft_sf2.EmcraftSf2Machine.test_arm_emcraft_sf2
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v2 04/10] hw/block: m25p80: Fix dummy byte handling for Spansion flash
2026-07-07 12:34 ` Philippe Mathieu-Daudé
@ 2026-07-07 12:51 ` Cédric Le Goater
2026-07-07 14:00 ` Philippe Mathieu-Daudé
2026-07-07 14:05 ` Cédric Le Goater
0 siblings, 2 replies; 21+ messages in thread
From: Cédric Le Goater @ 2026-07-07 12:51 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, Bin Meng, QEMU
Cc: Alistair Francis, Hanna Reitz, Kevin Wolf, qemu-block
On 7/7/26 14:34, Philippe Mathieu-Daudé wrote:
> On 7/7/26 10:34, Bin Meng wrote:
>> Spansion flashes expose the number of dummy clock cycles through CR2V
>> register [1]. The value is a cycle count, not a byte count, so the
>> m25p80 model has to convert it to the number of whole SSI transfer
>> bytes consumed while collecting read command data.
>>
>> Add a helper that multiplies the CR2V dummy cycle count by the phase
>> width and rounds up non-byte-aligned counts, matching the byte-oriented
>> SSI model. The default eight-cycle configuration keeps the same byte
>> counts as before.
>>
>> [1] https://www.infineon.com/assets/row/public/documents/10/49/infineon-s25fs128s-s25fs256s-1-datasheet-en.pdf
>>
>> Fixes: cf6f1efe0b57 ("m25p80: Fast read commands family changes")
>> Signed-off-by: Bin Meng <bin.meng@processmission.com>
>> Tested-by: Cédric Le Goater <clg@redhat.com>
>>
>> ---
>>
>> Changes in v2:
>> - change spansion_extract_cfg_num_dummies() to spansion_extract_cfg_dummy_bytes()
>> to avoid confusion
>> - use assert() when the dummy bit count is not byte-aligned
>>
>> hw/block/m25p80.c | 32 ++++++++++++++++++++------------
>> 1 file changed, 20 insertions(+), 12 deletions(-)
>>
>> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
>> index d7a9d79373..545e0b5728 100644
>> --- a/hw/block/m25p80.c
>> +++ b/hw/block/m25p80.c
>> @@ -1057,6 +1057,23 @@ static uint8_t macronix_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width)
>> return dummy_bits / 8;
>> }
>> +static uint8_t spansion_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width)
>> +{
>> + uint8_t dummy_bits;
>> +
>> + dummy_bits = extract32(s->spansion_cr2v, SPANSION_DUMMY_CLK_POS,
>> + SPANSION_DUMMY_CLK_LEN);
>> + dummy_bits *= bus_width;
>> +
>> + /*
>> + * Assert that the dummy bit count is byte-aligned
>> + * as SSI core can only consume whole dummy bytes.
>> + */
>> + assert(dummy_bits % 8 == 0);
>> +
>> + return dummy_bits / 8;
>> +}
>> +
>> static void decode_fast_read_cmd(Flash *s)
>> {
>> s->needed_bytes = get_addr_length(s);
>> @@ -1075,10 +1092,7 @@ static void decode_fast_read_cmd(Flash *s)
>> s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 1);
>> break;
>> case MAN_SPANSION:
>> - s->needed_bytes += extract32(s->spansion_cr2v,
>> - SPANSION_DUMMY_CLK_POS,
>> - SPANSION_DUMMY_CLK_LEN
>> - );
>> + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 1);
>> break;
>> case MAN_ISSI:
>> /*
>> @@ -1111,10 +1125,7 @@ static void decode_dio_read_cmd(Flash *s)
>> break;
>> case MAN_SPANSION:
>> s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
>> - s->needed_bytes += extract32(s->spansion_cr2v,
>> - SPANSION_DUMMY_CLK_POS,
>> - SPANSION_DUMMY_CLK_LEN
>> - );
>> + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 2);
>> break;
>> case MAN_NUMONYX:
>> s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
>> @@ -1151,10 +1162,7 @@ static void decode_qio_read_cmd(Flash *s)
>> break;
>> case MAN_SPANSION:
>> s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
>> - s->needed_bytes += extract32(s->spansion_cr2v,
>> - SPANSION_DUMMY_CLK_POS,
>> - SANSION_DUMMY_CLK_LEN
>> - );
>> + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 4);
>> break;
>> case MAN_NUMONYX:
>> s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
>
> This breaks the test_arm_emcraft_sf2 functional test:
>
> not ok 1 test_emcraft_sf2.EmcraftSf2Machine.test_arm_emcraft_sf2
>
No problem on my side.
C.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v2 04/10] hw/block: m25p80: Fix dummy byte handling for Spansion flash
2026-07-07 12:51 ` Cédric Le Goater
@ 2026-07-07 14:00 ` Philippe Mathieu-Daudé
2026-07-07 14:05 ` Cédric Le Goater
1 sibling, 0 replies; 21+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-07 14:00 UTC (permalink / raw)
To: Cédric Le Goater, Bin Meng, QEMU
Cc: Alistair Francis, Hanna Reitz, Kevin Wolf, qemu-block
On 7/7/26 14:51, Cédric Le Goater wrote:
> On 7/7/26 14:34, Philippe Mathieu-Daudé wrote:
>> On 7/7/26 10:34, Bin Meng wrote:
>>> Spansion flashes expose the number of dummy clock cycles through CR2V
>>> register [1]. The value is a cycle count, not a byte count, so the
>>> m25p80 model has to convert it to the number of whole SSI transfer
>>> bytes consumed while collecting read command data.
>>>
>>> Add a helper that multiplies the CR2V dummy cycle count by the phase
>>> width and rounds up non-byte-aligned counts, matching the byte-oriented
>>> SSI model. The default eight-cycle configuration keeps the same byte
>>> counts as before.
>>>
>>> [1] https://www.infineon.com/assets/row/public/documents/10/49/
>>> infineon-s25fs128s-s25fs256s-1-datasheet-en.pdf
>>>
>>> Fixes: cf6f1efe0b57 ("m25p80: Fast read commands family changes")
>>> Signed-off-by: Bin Meng <bin.meng@processmission.com>
>>> Tested-by: Cédric Le Goater <clg@redhat.com>
>>>
>>> ---
>>>
>>> Changes in v2:
>>> - change spansion_extract_cfg_num_dummies() to
>>> spansion_extract_cfg_dummy_bytes()
>>> to avoid confusion
>>> - use assert() when the dummy bit count is not byte-aligned
>>>
>>> hw/block/m25p80.c | 32 ++++++++++++++++++++------------
>>> 1 file changed, 20 insertions(+), 12 deletions(-)
>>>
>>> diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
>>> index d7a9d79373..545e0b5728 100644
>>> --- a/hw/block/m25p80.c
>>> +++ b/hw/block/m25p80.c
>>> @@ -1057,6 +1057,23 @@ static uint8_t
>>> macronix_extract_cfg_dummy_bytes(Flash *s, uint8_t bus_width)
>>> return dummy_bits / 8;
>>> }
>>> +static uint8_t spansion_extract_cfg_dummy_bytes(Flash *s, uint8_t
>>> bus_width)
>>> +{
>>> + uint8_t dummy_bits;
>>> +
>>> + dummy_bits = extract32(s->spansion_cr2v, SPANSION_DUMMY_CLK_POS,
>>> + SPANSION_DUMMY_CLK_LEN);
>>> + dummy_bits *= bus_width;
>>> +
>>> + /*
>>> + * Assert that the dummy bit count is byte-aligned
>>> + * as SSI core can only consume whole dummy bytes.
>>> + */
>>> + assert(dummy_bits % 8 == 0);
>>> +
>>> + return dummy_bits / 8;
>>> +}
>>> +
>>> static void decode_fast_read_cmd(Flash *s)
>>> {
>>> s->needed_bytes = get_addr_length(s);
>>> @@ -1075,10 +1092,7 @@ static void decode_fast_read_cmd(Flash *s)
>>> s->needed_bytes += macronix_extract_cfg_dummy_bytes(s, 1);
>>> break;
>>> case MAN_SPANSION:
>>> - s->needed_bytes += extract32(s->spansion_cr2v,
>>> - SPANSION_DUMMY_CLK_POS,
>>> - SPANSION_DUMMY_CLK_LEN
>>> - );
>>> + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 1);
>>> break;
>>> case MAN_ISSI:
>>> /*
>>> @@ -1111,10 +1125,7 @@ static void decode_dio_read_cmd(Flash *s)
>>> break;
>>> case MAN_SPANSION:
>>> s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
>>> - s->needed_bytes += extract32(s->spansion_cr2v,
>>> - SPANSION_DUMMY_CLK_POS,
>>> - SPANSION_DUMMY_CLK_LEN
>>> - );
>>> + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 2);
>>> break;
>>> case MAN_NUMONYX:
>>> s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
>>> @@ -1151,10 +1162,7 @@ static void decode_qio_read_cmd(Flash *s)
>>> break;
>>> case MAN_SPANSION:
>>> s->needed_bytes += SPANSION_CONTINUOUS_READ_MODE_CMD_LEN;
>>> - s->needed_bytes += extract32(s->spansion_cr2v,
>>> - SPANSION_DUMMY_CLK_POS,
>>> - SANSION_DUMMY_CLK_LEN
>>> - );
>>> + s->needed_bytes += spansion_extract_cfg_dummy_bytes(s, 4);
>>> break;
>>> case MAN_NUMONYX:
>>> s->needed_bytes += numonyx_extract_cfg_dummy_bytes(s);
>>
>> This breaks the test_arm_emcraft_sf2 functional test:
>>
>> not ok 1 test_emcraft_sf2.EmcraftSf2Machine.test_arm_emcraft_sf2
>>
>
>
> No problem on my side.
Odd, I can reliably reproduce... I queued the rest (except the docs/
one which lacked a SPDX license tag which I couldn't add myself).
I'll let the remaining 2 via your aspeed tree (this one is a fix
anyway).
Thanks,
Phil.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v2 04/10] hw/block: m25p80: Fix dummy byte handling for Spansion flash
2026-07-07 12:51 ` Cédric Le Goater
2026-07-07 14:00 ` Philippe Mathieu-Daudé
@ 2026-07-07 14:05 ` Cédric Le Goater
2026-07-07 14:26 ` Cédric Le Goater
1 sibling, 1 reply; 21+ messages in thread
From: Cédric Le Goater @ 2026-07-07 14:05 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, Bin Meng, QEMU
Cc: Alistair Francis, Hanna Reitz, Kevin Wolf, qemu-block
>> This breaks the test_arm_emcraft_sf2 functional test:
>>
>> not ok 1 test_emcraft_sf2.EmcraftSf2Machine.test_arm_emcraft_sf2
>>
>
>
> No problem on my side.
>
Sorry. you are right ! It fails too.
So we have a machine using a Spansion flash.
spi_flash = qdev_new("s25sl12801"); /* Spansion S25FL128SDPBHICO */
C.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v2 04/10] hw/block: m25p80: Fix dummy byte handling for Spansion flash
2026-07-07 14:05 ` Cédric Le Goater
@ 2026-07-07 14:26 ` Cédric Le Goater
2026-07-07 14:49 ` Bin Meng
2026-07-07 15:05 ` Philippe Mathieu-Daudé
0 siblings, 2 replies; 21+ messages in thread
From: Cédric Le Goater @ 2026-07-07 14:26 UTC (permalink / raw)
To: Philippe Mathieu-Daudé, Bin Meng, QEMU
Cc: Alistair Francis, Hanna Reitz, Kevin Wolf, qemu-block
Bin, Philippe,
On 7/7/26 16:05, Cédric Le Goater wrote:
>
>>> This breaks the test_arm_emcraft_sf2 functional test:
>>>
>>> not ok 1 test_emcraft_sf2.EmcraftSf2Machine.test_arm_emcraft_sf2
>>>
>>
>>
>> No problem on my side.
>>
> Sorry. you are right ! It fails too.
>
> So we have a machine using a Spansion flash.
>
> spi_flash = qdev_new("s25sl12801"); /* Spansion S25FL128SDPBHICO */
>
>
> C.
Here is the fix. Feel free to merge in patch 4.
Thanks,
C.
From 17ae23e5bc3629a5a301f14f47125f6f685ca9d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@redhat.com>
Date: Tue, 7 Jul 2026 16:23:29 +0200
Subject: [PATCH] hw/arm/msf2-som: Fix spansion-cr2nv value for S25FL128S
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The emcraft-sf2 board set spansion-cr2nv to 1, which the old m25p80
code treated as a byte count. With the dummy cycle to byte conversion
fix, CR2V=1 at SPI x1 is 1 bit, not byte-aligned, and triggers an
assertion. Use the S25FL128S default of 0x8 (8 cycles = 1 byte at
SPI x1), preserving the same runtime behavior.
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
hw/arm/msf2-som.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c
index caf6e7e1ad7d..1ada1a136130 100644
--- a/hw/arm/msf2-som.c
+++ b/hw/arm/msf2-som.c
@@ -84,7 +84,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
/* Attach SPI flash to SPI0 controller */
spi_bus = qdev_get_child_bus(dev, "spi0");
spi_flash = qdev_new("s25sl12801"); /* Spansion S25FL128SDPBHICO */
- qdev_prop_set_uint8(spi_flash, "spansion-cr2nv", 1);
+ qdev_prop_set_uint8(spi_flash, "spansion-cr2nv", 0x8);
if (dinfo) {
qdev_prop_set_drive_err(spi_flash, "drive",
blk_by_legacy_dinfo(dinfo), &error_fatal);
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v2 04/10] hw/block: m25p80: Fix dummy byte handling for Spansion flash
2026-07-07 14:26 ` Cédric Le Goater
@ 2026-07-07 14:49 ` Bin Meng
2026-07-07 15:05 ` Philippe Mathieu-Daudé
1 sibling, 0 replies; 21+ messages in thread
From: Bin Meng @ 2026-07-07 14:49 UTC (permalink / raw)
To: Cédric Le Goater
Cc: Philippe Mathieu-Daudé, Bin Meng, QEMU, Alistair Francis,
Hanna Reitz, Kevin Wolf, qemu-block
Hi Cédric,
On Tue, Jul 7, 2026 at 10:27 PM Cédric Le Goater <clg@redhat.com> wrote:
>
> Bin, Philippe,
>
> On 7/7/26 16:05, Cédric Le Goater wrote:
> >
> >>> This breaks the test_arm_emcraft_sf2 functional test:
> >>>
> >>> not ok 1 test_emcraft_sf2.EmcraftSf2Machine.test_arm_emcraft_sf2
> >>>
> >>
> >>
> >> No problem on my side.
> >>
> > Sorry. you are right ! It fails too.
> >
> > So we have a machine using a Spansion flash.
> >
> > spi_flash = qdev_new("s25sl12801"); /* Spansion S25FL128SDPBHICO */
> >
> >
> > C.
>
>
> Here is the fix. Feel free to merge in patch 4.
Thanks for the quick fix.
>
> Thanks,
>
> C.
>
> From 17ae23e5bc3629a5a301f14f47125f6f685ca9d9 Mon Sep 17 00:00:00 2001
> From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@redhat.com>
> Date: Tue, 7 Jul 2026 16:23:29 +0200
> Subject: [PATCH] hw/arm/msf2-som: Fix spansion-cr2nv value for S25FL128S
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> The emcraft-sf2 board set spansion-cr2nv to 1, which the old m25p80
> code treated as a byte count. With the dummy cycle to byte conversion
> fix, CR2V=1 at SPI x1 is 1 bit, not byte-aligned, and triggers an
> assertion. Use the S25FL128S default of 0x8 (8 cycles = 1 byte at
> SPI x1), preserving the same runtime behavior.
>
> Signed-off-by: Cédric Le Goater <clg@redhat.com>
> ---
> hw/arm/msf2-som.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c
> index caf6e7e1ad7d..1ada1a136130 100644
> --- a/hw/arm/msf2-som.c
> +++ b/hw/arm/msf2-som.c
> @@ -84,7 +84,7 @@ static void emcraft_sf2_s2s010_init(MachineState *machine)
> /* Attach SPI flash to SPI0 controller */
> spi_bus = qdev_get_child_bus(dev, "spi0");
> spi_flash = qdev_new("s25sl12801"); /* Spansion S25FL128SDPBHICO */
> - qdev_prop_set_uint8(spi_flash, "spansion-cr2nv", 1);
> + qdev_prop_set_uint8(spi_flash, "spansion-cr2nv", 0x8);
Indeed the default reset value of cr2nv[3:0] is 8 according to the
flash datasheet.
Reviewed-by: Bin Meng <bin.meng@processmission.com>
> if (dinfo) {
> qdev_prop_set_drive_err(spi_flash, "drive",
> blk_by_legacy_dinfo(dinfo), &error_fatal);
> --
Regards,
Bin
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v2 04/10] hw/block: m25p80: Fix dummy byte handling for Spansion flash
2026-07-07 14:26 ` Cédric Le Goater
2026-07-07 14:49 ` Bin Meng
@ 2026-07-07 15:05 ` Philippe Mathieu-Daudé
1 sibling, 0 replies; 21+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-07 15:05 UTC (permalink / raw)
To: Cédric Le Goater, Bin Meng, QEMU
Cc: Alistair Francis, Hanna Reitz, Kevin Wolf, qemu-block
On 7/7/26 16:26, Cédric Le Goater wrote:
> From 17ae23e5bc3629a5a301f14f47125f6f685ca9d9 Mon Sep 17 00:00:00 2001
> From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@redhat.com>
> Date: Tue, 7 Jul 2026 16:23:29 +0200
> Subject: [PATCH] hw/arm/msf2-som: Fix spansion-cr2nv value for S25FL128S
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> The emcraft-sf2 board set spansion-cr2nv to 1, which the old m25p80
> code treated as a byte count. With the dummy cycle to byte conversion
> fix, CR2V=1 at SPI x1 is 1 bit, not byte-aligned, and triggers an
> assertion. Use the S25FL128S default of 0x8 (8 cycles = 1 byte at
> SPI x1), preserving the same runtime behavior.
>
> Signed-off-by: Cédric Le Goater <clg@redhat.com>
> ---
> hw/arm/msf2-som.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
Tested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 05/10] hw/ssi: npcm7xx_fiu: Correct the dummy cycle emulation logic
2026-07-07 8:34 ` Bin Meng via qemu development
` (4 preceding siblings ...)
(?)
@ 2026-07-07 8:34 ` Bin Meng
-1 siblings, 0 replies; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU; +Cc: Alistair Francis, Hao Wu, Tyrone Ting, qemu-arm
Change send_dummy_bits() to send_dummy_bytes() as the FIU register
fields are programmed from spi_mem_op.dummy.nbytes, so they already
describe byte transfers.
Verified the changes by booting OpenBMC image on `gbs` machine all
the way to the Linux login shell:
$ qemu-system-arm -machine quanta-gbs-bmc -nographic \
-drive file=image.mtd,if=mtd,bus=0,unit=0,format=raw
Fixes: b821242c7b3b ("hw/ssi: NPCM7xx Flash Interface Unit device model")
Signed-off-by: Bin Meng <bin.meng@processmission.com>
---
(no changes since v1)
hw/ssi/npcm7xx_fiu.c | 25 ++++++++-----------------
1 file changed, 8 insertions(+), 17 deletions(-)
diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c
index 2d5bed005a..d41d877cfb 100644
--- a/hw/ssi/npcm7xx_fiu.c
+++ b/hw/ssi/npcm7xx_fiu.c
@@ -150,7 +150,7 @@ static uint64_t npcm7xx_fiu_flash_read(void *opaque, hwaddr addr,
NPCM7xxFIUState *fiu = f->fiu;
uint64_t value = 0;
uint32_t drd_cfg;
- int dummy_cycles;
+ int dummy_bytes;
int i;
if (fiu->active_cs != -1) {
@@ -180,10 +180,8 @@ static uint64_t npcm7xx_fiu_flash_read(void *opaque, hwaddr addr,
break;
}
- /* Flash chip model expects one transfer per dummy bit, not byte */
- dummy_cycles =
- (FIU_DRD_CFG_DBW(drd_cfg) * 8) >> FIU_DRD_CFG_ACCTYPE(drd_cfg);
- for (i = 0; i < dummy_cycles; i++) {
+ dummy_bytes = FIU_DRD_CFG_DBW(drd_cfg);
+ for (i = 0; i < dummy_bytes; i++) {
ssi_transfer(fiu->spi, 0);
}
@@ -305,20 +303,13 @@ static void send_address(SSIBus *spi, unsigned int addsiz, uint32_t addr)
}
}
-/* Send the number of dummy bits specified in the UMA config register. */
-static void send_dummy_bits(SSIBus *spi, uint32_t uma_cfg, uint32_t uma_cmd)
+/* Send the number of dummy bytes specified in the UMA config register */
+static void send_dummy_bytes(SSIBus *spi, uint32_t uma_cfg)
{
- unsigned int bits_per_clock = 1U << FIU_UMA_CFG_DBPCK(uma_cfg);
unsigned int i;
for (i = 0; i < FIU_UMA_CFG_DBSIZ(uma_cfg); i++) {
- /* Use bytes 0 and 1 first, then keep repeating byte 2 */
- unsigned int field = (i < 2) ? ((i + 1) * 8) : 24;
- unsigned int j;
-
- for (j = 0; j < 8; j += bits_per_clock) {
- ssi_transfer(spi, extract32(uma_cmd, field + j, bits_per_clock));
- }
+ ssi_transfer(spi, 0);
}
}
@@ -354,8 +345,8 @@ static void npcm7xx_fiu_uma_transaction(NPCM7xxFIUState *s)
ssi_transfer(s->spi, extract32(s->regs[reg], field, 8));
}
- /* Send dummy bits, if present. */
- send_dummy_bits(s->spi, uma_cfg, s->regs[NPCM7XX_FIU_UMA_CMD]);
+ /* Send dummy bytes, if present */
+ send_dummy_bytes(s->spi, uma_cfg);
/* Read data, if present. */
for (i = 0; i < FIU_UMA_CFG_RDATSIZ(uma_cfg); i++) {
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 06/10] hw/ssi: xilinx_spips: Fix dummy phase handling
2026-07-07 8:34 ` Bin Meng via qemu development
` (5 preceding siblings ...)
(?)
@ 2026-07-07 8:34 ` Bin Meng
-1 siblings, 0 replies; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU; +Cc: Alistair Francis, Edgar E. Iglesias, Peter Maydell, qemu-arm
The ZynqMP generic FIFO encodes dummy phases as a number of
dummy cycles. QEMU's SSI bus transfers whole bytes, so the
controller model must convert the programmed cycle count to the
number of SSI byte transfers needed for the selected SPI, dual SPI
or quad SPI mode.
The legacy Xilinx QSPI snoop paths had the opposite problem after
the m25p80 dummy handling was fixed. They still treated each dummy
byte queued through the FIFO as a request to generate several SSI
transfers based on the current link width. The flash model now
consumes dummy phases as byte counts, so the manual FIFO path should
forward one SSI transfer per dummy byte.
Update the Xilinx QSPI dummy accounting consistently for the generic
FIFO, manual FIFO and LQSPI direct-read paths. Also make the command
table report the dummy byte counts consumed by m25p80 for dual and
quad output reads, and account for the mode byte before LQSPI data
reads begin.
This matches the ZynqMP TRM (ug1085, v2.2 [1]) description of the
generic FIFO dummy cycle entry and keeps the controller side aligned
with the flash model's dummy byte ownership.
The description of the generic command fifo register says:
When [receive, transmit, data_xfer] = [0,0,1], the [immediate_data]
field represents the number of dummy cycle sent on the SPI interface.
[1] https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf
table 24‐22, an example of Generic FIFO Contents for Quad I/O Read Command (EBh)
Fixes: ef06ca3946e2 ("xilinx_spips: Add support for RX discard and RX drain")
Fixes: c95997a39de6 ("xilinx_spips: Add support for the ZynqMP Generic QSPI")
Signed-off-by: Bin Meng <bin.meng@processmission.com>
---
Changes in v2:
- correct the typo in the commit message: genenic => generic
hw/ssi/xilinx_spips.c | 122 +++++++++++++++++++++++++---------
include/hw/ssi/xilinx_spips.h | 2 +-
2 files changed, 90 insertions(+), 34 deletions(-)
diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c
index f6e717bc01..62058fdb88 100644
--- a/hw/ssi/xilinx_spips.c
+++ b/hw/ssi/xilinx_spips.c
@@ -192,6 +192,10 @@
FIELD(GQSPI_GF_SNAPSHOT, EXPONENT, 9, 1)
FIELD(GQSPI_GF_SNAPSHOT, DATA_XFER, 8, 1)
FIELD(GQSPI_GF_SNAPSHOT, IMMEDIATE_DATA, 0, 8)
+#define GQSPI_GF_MODE_SPI 1
+#define GQSPI_GF_MODE_DSPI 2
+#define GQSPI_GF_MODE_QSPI 3
+
#define R_GQSPI_MOD_ID (0x1fc / 4)
#define R_GQSPI_MOD_ID_RESET (0x10a0000)
@@ -237,7 +241,7 @@ static void xilinx_spips_update_cs(XilinxSPIPS *s, int field)
}
if (!(field & ((1 << (s->num_cs * s->num_busses)) - 1))) {
s->snoop_state = SNOOP_CHECKING;
- s->cmd_dummies = 0;
+ s->cmd_dummy_bytes = 0;
s->link_state = 1;
s->link_state_next = 1;
s->link_state_next_when = 0;
@@ -382,7 +386,7 @@ static void xilinx_spips_reset(DeviceState *d)
s->link_state_next = 1;
s->link_state_next_when = 0;
s->snoop_state = SNOOP_CHECKING;
- s->cmd_dummies = 0;
+ s->cmd_dummy_bytes = 0;
s->man_start_com = false;
xilinx_spips_update_ixr(s);
xilinx_spips_update_cs_lines(s);
@@ -457,6 +461,7 @@ static void xlnx_zynqmp_qspips_flush_fifo_g(XlnxZynqMPQSPIPS *s)
int i;
if (!s->regs[R_GQSPI_DATA_STS]) {
+ uint32_t prev_gf_snapshot = s->regs[R_GQSPI_GF_SNAPSHOT];
uint8_t imm;
s->regs[R_GQSPI_GF_SNAPSHOT] = fifo32_pop(&s->fifo_g);
@@ -484,7 +489,56 @@ static void xlnx_zynqmp_qspips_flush_fifo_g(XlnxZynqMPQSPIPS *s)
}
s->regs[R_GQSPI_DATA_STS] = 1ul << imm;
} else {
- s->regs[R_GQSPI_DATA_STS] = imm;
+ /*
+ * When [receive, transmit, data_xfer] = [0,0,1], it represents
+ * the number of dummy cycle sent on the SPI interface. We need
+ * to convert the number of dummy cycles to bytes according to
+ * the SPI mode being used.
+ *
+ * Ref: ug1085 v2.2 (December 2020) table 24‐22, an example of
+ * Generic FIFO Contents for Quad I/O Read Command (EBh)
+ */
+ if (!ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, TRANSMIT) &&
+ !ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT, RECIEVE)) {
+ uint8_t spi_mode = ARRAY_FIELD_EX32(s->regs, GQSPI_GF_SNAPSHOT,
+ SPI_MODE);
+ /*
+ * Some ZynqMP GQSPI drivers, such as Linux, use the data
+ * bus width in the dummy GENFIFO entry only to configure
+ * the controller mode. The immediate value is already
+ * the number of dummy cycles for the dummy phase, which
+ * follows the address bus width. Reuse the previous TX
+ * phase mode to convert cycles to SSI bytes.
+ *
+ * This does not make the model Linux-only. U-Boot emits
+ * the dummy entry with op->dummy.buswidth, so the entry
+ * mode already matches the dummy phase. Its opcode and
+ * address phases are immediate entries, not DATA_XFER TX
+ * entries, so the override below is not taken for U-Boot.
+ */
+ if (FIELD_EX32(prev_gf_snapshot, GQSPI_GF_SNAPSHOT,
+ DATA_XFER) &&
+ FIELD_EX32(prev_gf_snapshot, GQSPI_GF_SNAPSHOT,
+ TRANSMIT) &&
+ !FIELD_EX32(prev_gf_snapshot, GQSPI_GF_SNAPSHOT,
+ RECIEVE)) {
+ spi_mode = FIELD_EX32(prev_gf_snapshot,
+ GQSPI_GF_SNAPSHOT, SPI_MODE);
+ }
+
+ if (spi_mode == GQSPI_GF_MODE_QSPI) {
+ s->regs[R_GQSPI_DATA_STS] = ROUND_UP(imm * 4, 8) / 8;
+ } else if (spi_mode == GQSPI_GF_MODE_DSPI) {
+ s->regs[R_GQSPI_DATA_STS] = ROUND_UP(imm * 2, 8) / 8;
+ } else if (spi_mode == GQSPI_GF_MODE_SPI) {
+ s->regs[R_GQSPI_DATA_STS] = ROUND_UP(imm * 1, 8) / 8;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "Unknown SPI MODE: 0x%x ",
+ spi_mode);
+ }
+ } else {
+ s->regs[R_GQSPI_DATA_STS] = imm;
+ }
}
}
/* Zero length transfer check */
@@ -550,7 +604,7 @@ static void xlnx_zynqmp_qspips_flush_fifo_g(XlnxZynqMPQSPIPS *s)
}
}
-static int xilinx_spips_num_dummies(XilinxQSPIPS *qs, uint8_t command)
+static int xilinx_spips_num_dummy_bytes(XilinxQSPIPS *qs, uint8_t command)
{
if (!qs) {
/* The SPI device is not a QSPI device */
@@ -567,10 +621,11 @@ static int xilinx_spips_num_dummies(XilinxQSPIPS *qs, uint8_t command)
case QPP_4:
return 0;
case FAST_READ:
- case DOR:
- case QOR:
case FAST_READ_4:
+ return 1;
+ case DOR:
case DOR_4:
+ case QOR:
case QOR_4:
return 1;
case DIOR:
@@ -611,7 +666,6 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
int i;
uint8_t tx = 0;
uint8_t tx_rx[MAX_NUM_BUSSES] = { 0 };
- uint8_t dummy_cycles = 0;
uint8_t addr_length;
if (fifo8_is_empty(&s->tx_fifo)) {
@@ -631,26 +685,18 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
tx_rx[i] = tx;
}
} else {
- /*
- * Extract a dummy byte and generate dummy cycles according to the
- * link state
- */
tx = fifo8_pop(&s->tx_fifo);
- dummy_cycles = 8 / s->link_state;
+ for (i = 0; i < num_effective_busses(s); ++i) {
+ tx_rx[i] = tx;
+ }
}
for (i = 0; i < num_effective_busses(s); ++i) {
int bus = num_effective_busses(s) - 1 - i;
- if (dummy_cycles) {
- int d;
- for (d = 0; d < dummy_cycles; ++d) {
- tx_rx[0] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[0]);
- }
- } else {
- DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]);
- tx_rx[i] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[i]);
- DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]);
- }
+
+ DB_PRINT_L(debug_level, "tx = %02x\n", tx_rx[i]);
+ tx_rx[i] = ssi_transfer(s->spi[bus], (uint32_t)tx_rx[i]);
+ DB_PRINT_L(debug_level, "rx = %02x\n", tx_rx[i]);
}
if (s->regs[R_CMND] & R_CMND_RXFIFO_DRAIN) {
@@ -685,9 +731,9 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
switch (s->snoop_state) {
case (SNOOP_CHECKING):
/* Store the count of dummy bytes in the txfifo */
- s->cmd_dummies = xilinx_spips_num_dummies(q, tx);
+ s->cmd_dummy_bytes = xilinx_spips_num_dummy_bytes(q, tx);
addr_length = get_addr_length(s, tx);
- if (s->cmd_dummies < 0) {
+ if (s->cmd_dummy_bytes < 0) {
s->snoop_state = SNOOP_NONE;
} else {
s->snoop_state = SNOOP_ADDR + addr_length - 1;
@@ -697,14 +743,14 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
case DOR:
case DOR_4:
s->link_state_next = 2;
- s->link_state_next_when = addr_length + s->cmd_dummies;
+ s->link_state_next_when = addr_length + s->cmd_dummy_bytes;
break;
case QPP:
case QPP_4:
case QOR:
case QOR_4:
s->link_state_next = 4;
- s->link_state_next_when = addr_length + s->cmd_dummies;
+ s->link_state_next_when = addr_length + s->cmd_dummy_bytes;
break;
case DIOR:
case DIOR_4:
@@ -720,10 +766,10 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s)
/*
* Address has been transmitted, transmit dummy cycles now if needed
*/
- if (s->cmd_dummies < 0) {
+ if (s->cmd_dummy_bytes < 0) {
s->snoop_state = SNOOP_NONE;
} else {
- s->snoop_state = s->cmd_dummies;
+ s->snoop_state = s->cmd_dummy_bytes;
}
break;
case (SNOOP_STRIPING):
@@ -1152,11 +1198,13 @@ static void lqspi_load_cache(void *opaque, hwaddr addr)
XilinxQSPIPS *q = opaque;
XilinxSPIPS *s = opaque;
int i;
+ int dummy_bytes;
int flash_addr = ((addr & ~(LQSPI_CACHE_SIZE - 1))
/ num_effective_busses(s));
int peripheral = flash_addr >> LQSPI_ADDRESS_BITS;
int cache_entry = 0;
uint32_t u_page_save = s->regs[R_LQSPI_STS] & ~LQSPI_CFG_U_PAGE;
+ uint8_t command;
if (addr < q->lqspi_cached_addr ||
addr > q->lqspi_cached_addr + LQSPI_CACHE_SIZE - 4) {
@@ -1170,10 +1218,10 @@ static void lqspi_load_cache(void *opaque, hwaddr addr)
fifo8_reset(&s->rx_fifo);
/* instruction */
+ command = s->regs[R_LQSPI_CFG] & LQSPI_CFG_INST_CODE;
DB_PRINT_L(0, "pushing read instruction: %02x\n",
- (unsigned)(uint8_t)(s->regs[R_LQSPI_CFG] &
- LQSPI_CFG_INST_CODE));
- fifo8_push(&s->tx_fifo, s->regs[R_LQSPI_CFG] & LQSPI_CFG_INST_CODE);
+ (unsigned)command);
+ fifo8_push(&s->tx_fifo, command);
/* read address */
DB_PRINT_L(0, "pushing read address %06x\n", flash_addr);
if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_ADDR4) {
@@ -1183,14 +1231,22 @@ static void lqspi_load_cache(void *opaque, hwaddr addr)
fifo8_push(&s->tx_fifo, (uint8_t)(flash_addr >> 8));
fifo8_push(&s->tx_fifo, (uint8_t)flash_addr);
/* mode bits */
+ dummy_bytes = xilinx_spips_num_dummy_bytes(q, command);
if (s->regs[R_LQSPI_CFG] & LQSPI_CFG_MODE_EN) {
fifo8_push(&s->tx_fifo, extract32(s->regs[R_LQSPI_CFG],
LQSPI_CFG_MODE_SHIFT,
LQSPI_CFG_MODE_WIDTH));
+ if (dummy_bytes > 0) {
+ dummy_bytes--;
+ }
+ }
+ if (dummy_bytes < 0) {
+ dummy_bytes = extract32(s->regs[R_LQSPI_CFG],
+ LQSPI_CFG_DUMMY_SHIFT,
+ LQSPI_CFG_DUMMY_WIDTH);
}
/* dummy bytes */
- for (i = 0; i < (extract32(s->regs[R_LQSPI_CFG], LQSPI_CFG_DUMMY_SHIFT,
- LQSPI_CFG_DUMMY_WIDTH)); ++i) {
+ for (i = 0; i < dummy_bytes; ++i) {
DB_PRINT_L(0, "pushing dummy byte\n");
fifo8_push(&s->tx_fifo, 0);
}
diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h
index c8f6c5053c..d1eefa4ba6 100644
--- a/include/hw/ssi/xilinx_spips.h
+++ b/include/hw/ssi/xilinx_spips.h
@@ -69,7 +69,7 @@ struct XilinxSPIPS {
uint8_t num_busses;
uint8_t snoop_state;
- int cmd_dummies;
+ int cmd_dummy_bytes;
uint8_t link_state;
uint8_t link_state_next;
uint8_t link_state_next_when;
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 07/10] hw/ssi: aspeed_smc: Fix direct-read dummy bytes
2026-07-07 8:34 ` Bin Meng via qemu development
` (6 preceding siblings ...)
(?)
@ 2026-07-07 8:34 ` Bin Meng
-1 siblings, 0 replies; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU
Cc: Cédric Le Goater, Alistair Francis, Andrew Jeffery,
Cédric Le Goater, Jamin Lin, Joel Stanley, Kane Chen,
Peter Maydell, Steven Lee, Troy Lee, qemu-arm
m25p80 now consumes fast-read dummy phases as byte counts. The
ASPEED SMC direct-read path still treated the CEx dummy field as
raw cycles and emitted field * 8 SSI transfers. Convert the
ASPEED dummy field to SSI byte transfers using the selected
direct-read data width.
Fixes: ac2810defa9d ("aspeed/smc: handle dummy bytes when doing fast reads in command mode")
Signed-off-by: Bin Meng <bin.meng@processmission.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Tested-by: Cédric Le Goater <clg@redhat.com>
---
(no changes since v1)
hw/ssi/aspeed_smc.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index d87fbd798c..3f957d153f 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -449,19 +449,28 @@ static uint32_t aspeed_smc_check_segment_addr(const AspeedSMCFlash *fl,
return addr;
}
-static int aspeed_smc_flash_dummies(const AspeedSMCFlash *fl)
+static int aspeed_smc_flash_dummy_bytes(const AspeedSMCFlash *fl)
{
const AspeedSMCState *s = fl->controller;
uint32_t r_ctrl0 = s->regs[s->r_ctrl0 + fl->cs];
uint32_t dummy_high = (r_ctrl0 >> CTRL_DUMMY_HIGH_SHIFT) & 0x1;
uint32_t dummy_low = (r_ctrl0 >> CTRL_DUMMY_LOW_SHIFT) & 0x3;
- uint32_t dummies = ((dummy_high << 2) | dummy_low) * 8;
+ uint32_t dummy_bytes = (dummy_high << 2) | dummy_low;
- if (r_ctrl0 & CTRL_IO_DUAL_ADDR_DATA) {
- dummies /= 2;
+ /*
+ * Scale the controller dummy field to SSI byte transfers using the
+ * direct-read dummy/address bus width.
+ */
+ if ((r_ctrl0 & CTRL_IO_QPI) ||
+ ((r_ctrl0 & CTRL_IO_QUAD_DATA) &&
+ (r_ctrl0 & CTRL_IO_QUAD_ADDR_DATA))) {
+ dummy_bytes *= 4;
+ } else if ((r_ctrl0 & CTRL_IO_DUAL_DATA) &&
+ (r_ctrl0 & CTRL_IO_DUAL_ADDR_DATA)) {
+ dummy_bytes *= 2;
}
- return dummies;
+ return dummy_bytes;
}
static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr)
@@ -487,7 +496,7 @@ static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr)
* settings, let's check for fast read mode.
*/
if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) {
- for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) {
+ for (i = 0; i < aspeed_smc_flash_dummy_bytes(fl); i++) {
ssi_transfer(fl->controller->spi, s->regs[R_DUMMY_DATA] & 0xff);
}
}
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 08/10] Revert "aspeed/smc: Fix number of dummy cycles for FAST_READ_4 command"
2026-07-07 8:34 ` Bin Meng via qemu development
` (7 preceding siblings ...)
(?)
@ 2026-07-07 8:34 ` Bin Meng
-1 siblings, 0 replies; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU
Cc: Cédric Le Goater, Alistair Francis, Andrew Jeffery,
Cédric Le Goater, Jamin Lin, Joel Stanley, Kane Chen,
Peter Maydell, Steven Lee, Troy Lee, qemu-arm
This reverts commit 7faf6f1790dddf9f3acf6ddd95f7bbc1b4a755d0.
The incorrect implementation of dummy cycles in m25p80 model is now
corrected. Revert this commit.
Signed-off-by: Bin Meng <bin.meng@processmission.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Tested-by: Cédric Le Goater <clg@redhat.com>
---
(no changes since v1)
hw/ssi/aspeed_smc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 3f957d153f..58e5087b57 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -568,11 +568,11 @@ static int aspeed_smc_num_dummies(uint8_t command)
case FAST_READ:
case DOR:
case QOR:
- case FAST_READ_4:
case DOR_4:
case QOR_4:
return 1;
case DIOR:
+ case FAST_READ_4:
case DIOR_4:
return 2;
case QIOR:
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 09/10] Revert "aspeed/smc: snoop SPI transfers to fake dummy cycles"
2026-07-07 8:34 ` Bin Meng via qemu development
` (8 preceding siblings ...)
(?)
@ 2026-07-07 8:34 ` Bin Meng
-1 siblings, 0 replies; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU
Cc: Cédric Le Goater, Alistair Francis, Andrew Jeffery,
Cédric Le Goater, Jamin Lin, Joel Stanley, Kane Chen,
Peter Maydell, Steven Lee, Troy Lee, qemu-arm
This reverts commit f95c4bffdc4c53b29f89762cab4adc5a43f95daf.
The m25p80 model now accounts for fast-read dummy bytes in its
command decoder. In ASPEED SMC model user mode, guest software
already sends the complete byte stream, including any dummy
bytes needed by the flash. Hence the model should just forward
exactly the bytes supplied by the guest without the need of
decoding guest-supplied flash op codes to inject extra dummy
transfers.
Signed-off-by: Bin Meng <bin.meng@processmission.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Tested-by: Cédric Le Goater <clg@redhat.com>
---
(no changes since v1)
hw/ssi/aspeed_smc.c | 114 +-----------------------------------
hw/ssi/trace-events | 1 -
include/hw/ssi/aspeed_smc.h | 2 -
3 files changed, 2 insertions(+), 115 deletions(-)
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 58e5087b57..c8cc6cfa56 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -197,9 +197,6 @@
/* Flash opcodes. */
#define SPI_OP_READ 0x03 /* Read data bytes (low frequency) */
-#define SNOOP_OFF 0xFF
-#define SNOOP_START 0x0
-
/*
* Default segments mapping addresses and size for each peripheral per
* controller. These can be changed when board is initialized with the
@@ -537,104 +534,6 @@ static MemTxResult aspeed_smc_flash_read(void *opaque, hwaddr addr,
return MEMTX_OK;
}
-/*
- * TODO (clg@kaod.org): stolen from xilinx_spips.c. Should move to a
- * common include header.
- */
-typedef enum {
- READ = 0x3, READ_4 = 0x13,
- FAST_READ = 0xb, FAST_READ_4 = 0x0c,
- DOR = 0x3b, DOR_4 = 0x3c,
- QOR = 0x6b, QOR_4 = 0x6c,
- DIOR = 0xbb, DIOR_4 = 0xbc,
- QIOR = 0xeb, QIOR_4 = 0xec,
-
- PP = 0x2, PP_4 = 0x12,
- DPP = 0xa2,
- QPP = 0x32, QPP_4 = 0x34,
-} FlashCMD;
-
-static int aspeed_smc_num_dummies(uint8_t command)
-{
- switch (command) { /* check for dummies */
- case READ: /* no dummy bytes/cycles */
- case PP:
- case DPP:
- case QPP:
- case READ_4:
- case PP_4:
- case QPP_4:
- return 0;
- case FAST_READ:
- case DOR:
- case QOR:
- case DOR_4:
- case QOR_4:
- return 1;
- case DIOR:
- case FAST_READ_4:
- case DIOR_4:
- return 2;
- case QIOR:
- case QIOR_4:
- return 4;
- default:
- return -1;
- }
-}
-
-static bool aspeed_smc_do_snoop(AspeedSMCFlash *fl, uint64_t data,
- unsigned size)
-{
- AspeedSMCState *s = fl->controller;
- uint8_t addr_width = aspeed_smc_flash_addr_width(fl);
-
- trace_aspeed_smc_do_snoop(fl->cs, s->snoop_index, s->snoop_dummies,
- (uint8_t) data & 0xff);
-
- if (s->snoop_index == SNOOP_OFF) {
- return false; /* Do nothing */
-
- } else if (s->snoop_index == SNOOP_START) {
- uint8_t cmd = data & 0xff;
- int ndummies = aspeed_smc_num_dummies(cmd);
-
- /*
- * No dummy cycles are expected with the current command. Turn
- * off snooping and let the transfer proceed normally.
- */
- if (ndummies <= 0) {
- s->snoop_index = SNOOP_OFF;
- return false;
- }
-
- s->snoop_dummies = ndummies * 8;
-
- } else if (s->snoop_index >= addr_width + 1) {
-
- /* The SPI transfer has reached the dummy cycles sequence */
- for (; s->snoop_dummies; s->snoop_dummies--) {
- ssi_transfer(s->spi, s->regs[R_DUMMY_DATA] & 0xff);
- }
-
- /* If no more dummy cycles are expected, turn off snooping */
- if (!s->snoop_dummies) {
- s->snoop_index = SNOOP_OFF;
- } else {
- s->snoop_index += size;
- }
-
- /*
- * Dummy cycles have been faked already. Ignore the current
- * SPI transfer
- */
- return true;
- }
-
- s->snoop_index += size;
- return false;
-}
-
static MemTxResult aspeed_smc_flash_write(void *opaque, hwaddr addr,
uint64_t data, unsigned size, MemTxAttrs attrs)
{
@@ -652,10 +551,6 @@ static MemTxResult aspeed_smc_flash_write(void *opaque, hwaddr addr,
switch (aspeed_smc_flash_mode(fl)) {
case CTRL_USERMODE:
- if (aspeed_smc_do_snoop(fl, data, size)) {
- break;
- }
-
for (i = 0; i < size; i++) {
ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
}
@@ -717,7 +612,6 @@ static void aspeed_smc_flash_update_ctrl(AspeedSMCFlash *fl, uint32_t value)
s->regs[s->r_ctrl0 + fl->cs] = value;
if (unselect != s->unselect) {
- s->snoop_index = unselect ? SNOOP_OFF : SNOOP_START;
aspeed_smc_flash_do_select(fl, unselect);
}
}
@@ -763,9 +657,6 @@ static void aspeed_smc_reset_hold(Object *obj, ResetType type)
aspeed_smc_flash_set_segment_region(s, i,
asc->segment_to_reg(s, &asc->segments[i]));
}
-
- s->snoop_index = SNOOP_OFF;
- s->snoop_dummies = 0;
}
static MemTxResult aspeed_smc_read(void *opaque, hwaddr addr, uint64_t *data,
@@ -1293,11 +1184,10 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
static const VMStateDescription vmstate_aspeed_smc = {
.name = "aspeed.smc",
.version_id = 3,
- .minimum_version_id = 2,
+ .minimum_version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
- VMSTATE_UINT8(snoop_index, AspeedSMCState),
- VMSTATE_UINT8(snoop_dummies, AspeedSMCState),
+ VMSTATE_UNUSED_V(2, 2), /* was snoop_index/snoop_dummies */
VMSTATE_BOOL_V(unselect, AspeedSMCState, 3),
VMSTATE_END_OF_LIST()
}
diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
index 2f36cf96b8..b9d8648297 100644
--- a/hw/ssi/trace-events
+++ b/hw/ssi/trace-events
@@ -2,7 +2,6 @@
aspeed_smc_flash_set_segment(int cs, uint64_t reg, uint64_t start, uint64_t end) "CS%d segreg=0x%"PRIx64" [ 0x%"PRIx64" - 0x%"PRIx64" ]"
aspeed_smc_flash_read(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d"
-aspeed_smc_do_snoop(int cs, int index, int dummies, int data) "CS%d index:0x%x dummies:%d data:0x%x"
aspeed_smc_flash_write(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d"
aspeed_smc_read(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x"
diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
index 76831422c6..a273365689 100644
--- a/include/hw/ssi/aspeed_smc.h
+++ b/include/hw/ssi/aspeed_smc.h
@@ -80,8 +80,6 @@ struct AspeedSMCState {
AspeedSMCFlash flashes[ASPEED_SMC_CS_MAX];
- uint8_t snoop_index;
- uint8_t snoop_dummies;
bool unselect;
};
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v2 10/10] docs/devel: Document SSI dummy-cycle ownership
2026-07-07 8:34 ` Bin Meng via qemu development
` (9 preceding siblings ...)
(?)
@ 2026-07-07 8:34 ` Bin Meng
2026-07-07 13:10 ` Philippe Mathieu-Daudé
-1 siblings, 1 reply; 21+ messages in thread
From: Bin Meng @ 2026-07-07 8:34 UTC (permalink / raw)
To: QEMU; +Cc: Philippe Mathieu-Daudé, Pierrick Bouvier
Document the boundary between SPI/SSI controller models and SPI flash
models when representing fast-read dummy cycles. It explains that
flash models own command semantics, while controllers own
hardware-generated dummy transfers and cycle-to-byte conversion.
Signed-off-by: Bin Meng <bin.meng@processmission.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
(no changes since v1)
docs/devel/index-internals.rst | 1 +
docs/devel/ssi.rst | 132 +++++++++++++++++++++++++++++++++
2 files changed, 133 insertions(+)
create mode 100644 docs/devel/ssi.rst
diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst
index b89bab9b30..a8f5e310df 100644
--- a/docs/devel/index-internals.rst
+++ b/docs/devel/index-internals.rst
@@ -20,6 +20,7 @@ Details about QEMU's various subsystems including how to add features to them.
reset
s390-cpu-topology
s390-dasd-ipl
+ ssi
tracing
uefi-vars
vfio-iommufd
diff --git a/docs/devel/ssi.rst b/docs/devel/ssi.rst
new file mode 100644
index 0000000000..864b5d9320
--- /dev/null
+++ b/docs/devel/ssi.rst
@@ -0,0 +1,132 @@
+================================
+SSI devices and SPI flash models
+================================
+
+QEMU's Synchronous Serial Interface (SSI) bus models the full-duplex transfer
+of words between a controller and one selected peripheral. Most SPI flash
+models, including ``m25p80``, are attached to controllers through this bus.
+
+This page documents the expected boundary between a controller model and a
+flash model for SPI fast-read dummy cycles. The boundary is important because
+many real controllers expose dummy-cycle configuration in registers, while the
+flash model observes only the byte stream delivered through ``ssi_transfer()``.
+
+SSI transfer granularity
+------------------------
+
+``ssi_transfer()`` transfers one SSI word. Flash models that implement common
+SPI NOR command streams usually consume one 8-bit word at a time:
+
+* command opcode;
+* address bytes;
+* optional mode or continuous-read bytes;
+* dummy bytes;
+* data bytes.
+
+The SSI core does not model individual clock edges or the number of active SPI
+data lines. If a real transaction has a dummy phase expressed in clock cycles,
+the device model that generates transfers on the SSI bus must represent that
+phase as a number of dummy byte transfers.
+
+Flash model responsibilities
+----------------------------
+
+A SPI flash model owns the command semantics for the flash device:
+
+* which opcodes are recognized;
+* how many address bytes are required;
+* whether a command has mode bytes;
+* how many dummy bytes must be consumed before data can be returned;
+* manufacturer-specific differences in fast-read command behavior.
+
+For the ``m25p80`` model, ``needed_bytes`` is a byte count. It must not store
+raw dummy cycles. When a flash datasheet describes the dummy phase in cycles,
+the flash model converts the cycles to bytes using the bus width used for the
+dummy phase::
+
+ dummy_bytes = DIV_ROUND_UP(dummy_cycles * dummy_bus_width, 8)
+
+For SPI NOR fast-read commands modeled by ``m25p80``, the dummy phase follows
+the address phase width. For example, output-only dual and quad read commands
+such as DOR and QOR use one line for command, address, and dummy phases, then
+use two or four lines only for the data phase. Dual I/O and Quad I/O commands
+such as DIOR and QIOR use the wider bus for both address and dummy phases.
+
+If the exact dummy phase cannot be represented as a whole number of SSI byte
+transfers, the model should round up and log the limitation instead of silently
+treating cycles as bytes.
+
+Controller model responsibilities
+---------------------------------
+
+A controller model owns the behavior of the controller hardware:
+
+* how guest-visible registers select command, address width, bus width, and
+ dummy-cycle count;
+* whether the guest supplies dummy bytes in a transmit FIFO;
+* whether the controller itself generates the dummy phase for a memory-mapped,
+ direct-read, or other automatic transfer mode;
+* how chip-select state changes around controller-generated transfers.
+
+When guest software writes dummy bytes into a transmit FIFO or manual transfer
+path, the controller should pass those bytes to ``ssi_transfer()`` like any
+other guest-provided byte. It should not add more dummy transfers on behalf of
+the flash.
+
+When hardware registers instruct the controller to generate a dummy phase, the
+controller must emit dummy byte transfers before data transfers reach the flash
+model. The controller should convert the configured cycle count using the bus
+width that the controller uses during the dummy phase. For example:
+
+* 8 dummy cycles on a single data line become 1 dummy byte;
+* 8 dummy cycles on two data lines become 2 dummy bytes;
+* 8 dummy cycles on four data lines become 4 dummy bytes.
+
+The controller should not duplicate flash-specific opcode tables merely to
+guess which commands need dummy cycles. In automatic modes the controller
+already has enough hardware configuration to know whether it must generate a
+dummy phase. In manual modes the guest-provided byte stream is authoritative.
+
+Avoiding double counting
+------------------------
+
+Exactly one side should generate each dummy byte transfer seen by the flash:
+
+* If the guest sends dummy bytes through the controller, the controller forwards
+ them and the flash consumes them.
+* If the guest programs a controller dummy-cycle register, the controller
+ converts those cycles to dummy byte transfers and the flash consumes them.
+* The flash may know that a command requires dummy bytes, but it does not create
+ transfers on the SSI bus.
+
+Do not implement controller-side snooping that watches manual-mode opcode
+streams and injects extra dummy transfers based on flash opcodes. That mixes
+flash command semantics into the controller and is fragile when flash models
+gain correct dummy-byte accounting.
+
+Examples in the tree
+--------------------
+
+The following models illustrate the boundary:
+
+* ``hw/block/m25p80.c`` keeps fast-read dummy requirements as byte counts in
+ ``needed_bytes``. Manufacturer-specific helpers convert datasheet dummy
+ cycles to the byte stream expected by the model.
+* ``hw/ssi/aspeed_smc.c`` generates dummy byte transfers for direct fast-read
+ mode from controller registers, but manual user-mode writes are forwarded as
+ guest-provided bytes.
+* ``hw/ssi/npcm7xx_fiu.c`` converts the direct-read dummy configuration to the
+ number of dummy byte transfers sent before reading data.
+
+Review checklist
+----------------
+
+When adding or changing a SPI flash controller or flash model, check:
+
+* Are dummy counts stored in byte units when they drive flash state machines?
+* If a hardware register stores cycles, is the conversion to bytes based on the
+ bus width of the dummy phase?
+* Are manual guest-provided dummy bytes forwarded without extra injection?
+* Are automatic controller-generated dummy phases modeled by the controller?
+* Is flash-specific opcode knowledge kept in the flash model rather than copied
+ into controller snooping paths?
--
2.53.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v2 10/10] docs/devel: Document SSI dummy-cycle ownership
2026-07-07 8:34 ` [PATCH v2 10/10] docs/devel: Document SSI dummy-cycle ownership Bin Meng
@ 2026-07-07 13:10 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 21+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-07-07 13:10 UTC (permalink / raw)
To: Bin Meng, QEMU; +Cc: Pierrick Bouvier
On 7/7/26 10:34, Bin Meng wrote:
> Document the boundary between SPI/SSI controller models and SPI flash
> models when representing fast-read dummy cycles. It explains that
> flash models own command semantics, while controllers own
> hardware-generated dummy transfers and cycle-to-byte conversion.
>
> Signed-off-by: Bin Meng <bin.meng@processmission.com>
> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
>
> ---
>
> (no changes since v1)
>
> docs/devel/index-internals.rst | 1 +
> docs/devel/ssi.rst | 132 +++++++++++++++++++++++++++++++++
> 2 files changed, 133 insertions(+)
> create mode 100644 docs/devel/ssi.rst
Fails checkpatch.pl, missing:
-- >8 --
diff --git a/MAINTAINERS b/MAINTAINERS
index 1e239f21166..2a9cf15fc7a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2345,2 +2345,3 @@ M: Alistair Francis <alistair@alistair23.me>
S: Maintained
+F: docs/devel/ssi.rst
F: hw/ssi/*
diff --git a/docs/devel/ssi.rst b/docs/devel/ssi.rst
index 864b5d93204..7475d7f241a 100644
--- a/docs/devel/ssi.rst
+++ b/docs/devel/ssi.rst
@@ -1 +1,3 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
================================
---
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v2 00/10] hw/{block,ssi}: Fix spi-nor flash dummy byte handling
2026-07-07 8:34 ` Bin Meng via qemu development
` (10 preceding siblings ...)
(?)
@ 2026-07-07 9:11 ` Cédric Le Goater
-1 siblings, 0 replies; 21+ messages in thread
From: Cédric Le Goater @ 2026-07-07 9:11 UTC (permalink / raw)
To: Bin Meng, QEMU
Cc: Alistair Francis, Andrew Jeffery, Edgar E. Iglesias, Hanna Reitz,
Hao Wu, Jamin Lin, Joel Stanley, Kane Chen, Kevin Wolf,
Peter Maydell, Pierrick Bouvier, Steven Lee, Troy Lee,
Tyrone Ting, qemu-arm, qemu-block
On 7/7/26 10:34, Bin Meng wrote:
>
> This series fixes dummy-cycle accounting for SPI NOR fast-read
> operations across the m25p80 flash model and several SSI controller
> models.
>
> The root problem is that the affected code paths mixed two different
> units. Flash datasheets and controller registers often describe
> fast-read dummy phases in clock cycles, while the SSI bus interface used
> by these models transfers byte-sized words to the flash. As a result,
> some paths treated cycle counts as byte counts directly, and other paths
> compensated in controller-specific ways that duplicated flash command
> semantics.
>
> The first four patches fix the flash side. They make m25p80 keep
> `needed_bytes` as a byte count and convert manufacturer-specific
> dummy-cycle requirements to dummy byte transfers for Winbond,
> Numonyx/Micron, Macronix, and Spansion flashes. The Numonyx/Micron
> change also handles the standard-mode case where the dummy phase follows
> the address bus width for output fast-read commands.
>
> The next patches fix controller-generated dummy phases. NPCM7xx FIU
> and Xilinx ZynqMP GQSPI now convert programmed dummy-cycle fields to
> SSI byte transfers using the bus width selected for the dummy phase.
> The ASPEED SMC direct-read path is updated the same way. The two ASPEED
> reverts then remove older workarounds that either hard-coded FAST_READ_4
> dummy behavior or snooped manual user-mode SPI streams to inject extra
> dummy cycles. With m25p80 accounting fixed, user-mode byte streams
> should be forwarded as supplied by the guest.
>
> The final patch documents the intended ownership boundary: flash models
> own command semantics and dummy-byte consumption, while controller
> models own hardware-generated dummy phases and cycle-to-byte conversion.
>
> Some background:
>
> While recently working on some device modeling, I hit this issue
> again. I then realized that I had sent a fix series for it more than
> five years ago, but it never made it upstream.
>
> This series picks up that old work, refreshes it against the current
> tree, and fixes several issues in the SPI controller models. The original
> series is available at [1]. I have tried to verify all the SPI controller
> changes by booting real-world guest software to ensure the change does
> not break anything.
>
> [1] https://lore.kernel.org/qemu-devel/20210114150902.11515-1-bmeng.cn@gmail.com/
>
> Changes in v2:
> - correct the typo in the commit message: instrunction => instruction
> - correct the typo in the commit message: genenic => generic
> - change numonyx_extract_cfg_num_dummies() to numonyx_extract_cfg_dummy_bytes()
> to avoid confusion
> - change macronix_extract_cfg_num_dummies() to macronix_extract_cfg_dummy_bytes()
> to avoid confusion
> - change spansion_extract_cfg_num_dummies() to spansion_extract_cfg_dummy_bytes()
> to avoid confusion
> - use assert() when the dummy bit count is not byte-aligned
>
> Bin Meng (10):
> hw/block: m25p80: Fix dummy byte handling for Winbond flash
> hw/block: m25p80: Fix dummy byte handling for Numonyx/Micron flash
> hw/block: m25p80: Fix dummy byte handling for Macronix flash
> hw/block: m25p80: Fix dummy byte handling for Spansion flash
> hw/ssi: npcm7xx_fiu: Correct the dummy cycle emulation logic
> hw/ssi: xilinx_spips: Fix dummy phase handling
> hw/ssi: aspeed_smc: Fix direct-read dummy bytes
> Revert "aspeed/smc: Fix number of dummy cycles for FAST_READ_4
> command"
> Revert "aspeed/smc: snoop SPI transfers to fake dummy cycles"
> docs/devel: Document SSI dummy-cycle ownership
>
> docs/devel/index-internals.rst | 1 +
> docs/devel/ssi.rst | 132 ++++++++++++++++++++++++++++
> hw/block/m25p80.c | 154 +++++++++++++++++++++------------
> hw/ssi/aspeed_smc.c | 135 ++++-------------------------
> hw/ssi/npcm7xx_fiu.c | 25 ++----
> hw/ssi/trace-events | 1 -
> hw/ssi/xilinx_spips.c | 122 +++++++++++++++++++-------
> include/hw/ssi/aspeed_smc.h | 2 -
> include/hw/ssi/xilinx_spips.h | 2 +-
> 9 files changed, 348 insertions(+), 226 deletions(-)
> create mode 100644 docs/devel/ssi.rst
>
> ---
> base-commit: 94826ec1370328375c3b6d1e80fdc94c8f46c348
> branch: m25p80-v2
With some help of claude, here is a summary of the functional test coverage :
┌───────┬───────────────────┬──────────────────────────────────────────────────────────────────┐
│ Patch │ Component │ Functional Test Coverage │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0001 │ m25p80 (Winbond) │ COVERED — ast2600-evb (6), bletchley-bmc, catalina-bmc, │
│ │ │ ast2700a1-evb (3), ast2700a2-evb (3), ast2700fc (2), anacapa-bmc │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0002 │ m25p80 (Numonyx) │ PARTIAL — palmetto-bmc, romulus-bmc │
│ │ │ (default config only, no QIO/DIO mode test) │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0003 │ m25p80 (Macronix) │ PARTIAL — ast2500-evb (3), witherspoon-bmc, anacapa-bmc, │
│ │ │ fby4-bmc, gb200nvl-bmc, quanta-gsj (default DC only) │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0004 │ m25p80 (Spansion) │ NOT COVERED — no machine uses Spansion flash │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0005 │ npcm7xx_fiu │ PARTIAL — quanta-gsj (direct-read path only) │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0006 │ xilinx_spips │ NOT COVERED — no ZynqMP GQSPI machine in the suite │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0007 │ aspeed_smc │ COVERED — palmetto-bmc, ast2500-evb (3), romulus-bmc, │
│ │ direct-read │ witherspoon-bmc, ast2600-evb (6), anacapa-bmc, bletchley-bmc, │
│ │ │ catalina-bmc, gb200nvl-bmc, fby4-bmc, ast2700a1-evb (3), │
│ │ │ ast2700a2-evb (3), ast2700fc (2) │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0008 │ aspeed_smc │ COVERED — superseded by 0009 │
│ │ snoop table │ │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0009 │ aspeed_smc │ COVERED — all Aspeed Linux boot tests: ast2500-evb (3), │
│ │ snoop removal │ romulus-bmc, witherspoon-bmc, ast2600-evb (6), anacapa-bmc, │
│ │ │ bletchley-bmc, catalina-bmc, gb200nvl-bmc, fby4-bmc, │
│ │ │ ast2700a1-evb (3), ast2700a2-evb (3), ast2700fc (2) │
├───────┼───────────────────┼──────────────────────────────────────────────────────────────────┤
│ 0010 │ docs only │ N/A │
└───────┴───────────────────┴──────────────────────────────────────────────────────────────────┘
Coverage Gaps
1. Spansion flash — No functional test at all. Need a machine with
s25fl*/s25fs* flash or a dedicated unit test.
2. Xilinx ZynqMP GQSPI — No functional test. The xlnx-zcu102 or
xlnx-versal-virt machines would exercise this, but no test with
SPI flash operations exists in the suite.
3. Non-default volatile configuration — All tests boot with default
flash config register values. The complex dummy-cycle scaling
tables (Macronix DC[1:0], Numonyx volatile_cfg, Spansion CR2V)
are only tested at their default settings.
4. QIO/DIO command families — The U-Boot and Linux drivers used in
tests typically issue standard or dual/quad-output fast
reads. The quad-I/O and dual-I/O read commands (EBh, BBh) that
have the most different dummy handling may not be exercised.
5. NPCM UMA path — Only the direct-read FIU path is confirmed
exercised; the UMA transaction path in npcm7xx_fiu_uma_transaction()
may or may not be hit during kernel boot.
To be cross checked.
Thanks,
C.
^ permalink raw reply [flat|nested] 21+ messages in thread