* [PATCH 6.1.y 0/2] mtd: spi-nor: avoid odd length/address accesses in 8D-8D-8D mode
@ 2026-03-25 9:23 liyin.zhang.cn
2026-03-25 9:23 ` [PATCH 1/2] mtd: spi-nor: core: avoid odd length/address reads on " liyin.zhang.cn
2026-03-25 9:23 ` [PATCH 2/2] mtd: spi-nor: core: avoid odd length/address writes in " liyin.zhang.cn
0 siblings, 2 replies; 3+ messages in thread
From: liyin.zhang.cn @ 2026-03-25 9:23 UTC (permalink / raw)
To: stable
From: Liyin Zhang <liyin.zhang.cn@windriver.com>
This series backports two patches from Pratyush Yadav that fix read and
write issues when the transfer length or address is odd in 8D-8D-8D
(octal DTR) mode.
And we actually encounter the same issues on different hardwares which
supporting octal dtr in multiple earlier releases.
Now, both patches are already present in mainline, 6.19, 6.18 and 6.12 stable-rc.
The patches for 6.6.y have just been sent.
This series is for 6.1.y branch.
Pratyush Yadav (2):
mtd: spi-nor: core: avoid odd length/address reads on 8D-8D-8D mode
mtd: spi-nor: core: avoid odd length/address writes in 8D-8D-8D mode
drivers/mtd/spi-nor/core.c | 145 ++++++++++++++++++++++++++++++++++++-
1 file changed, 143 insertions(+), 2 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] mtd: spi-nor: core: avoid odd length/address reads on 8D-8D-8D mode
2026-03-25 9:23 [PATCH 6.1.y 0/2] mtd: spi-nor: avoid odd length/address accesses in 8D-8D-8D mode liyin.zhang.cn
@ 2026-03-25 9:23 ` liyin.zhang.cn
2026-03-25 9:23 ` [PATCH 2/2] mtd: spi-nor: core: avoid odd length/address writes in " liyin.zhang.cn
1 sibling, 0 replies; 3+ messages in thread
From: liyin.zhang.cn @ 2026-03-25 9:23 UTC (permalink / raw)
To: stable
From: Pratyush Yadav <p.yadav@ti.com>
[ Upstream commit f156b23df6a84efb2f6686156be94d4988568954 ]
On Octal DTR capable flashes like Micron Xcella reads cannot start or
end at an odd address in Octal DTR mode. Extra bytes need to be read at
the start or end to make sure both the start address and length remain
even.
To avoid allocating too much extra memory, thereby putting unnecessary
memory pressure on the system, the temporary buffer containing the extra
padding bytes is capped at PAGE_SIZE bytes. The rest of the 2-byte
aligned part should be read directly in the main buffer.
Signed-off-by: Pratyush Yadav <p.yadav@ti.com>
Reviewed-by: Michael Walle <michael@walle.cc>
Signed-off-by: Luke Wang <ziniu.wang_1@nxp.com>
Signed-off-by: Pratyush Yadav <pratyush@kernel.org>
Link: https://lore.kernel.org/r/20250708091646.292-1-ziniu.wang_1@nxp.com
[ Resolve conflict in drivers/mtd/spi-nor/core.c.
In spi_nor_read(), 6.1.y contains a spi_nor_convert_addr() call
before spi_nor_read_data(), introduced by 364995962803 ("mtd:
spi-nor: Add a ->convert_addr() method"), which does not exist in
mainline. This call is specific to Xilinx S3AN flashes, which use a
non-standard address format. In mainline, S3AN flash support was
removed entirely, and the corresponding spi_nor_convert_addr() call
was dropped by 9539d12d9f52 ("mtd: spi-nor: get rid of non-power-of-2
page size handling"). Keep the existing spi_nor_convert_addr() call
and insert the new spi_nor_octal_dtr_read() branch after it. ]
Signed-off-by: Liyin Zhang <liyin.zhang.cn@windriver.com>
---
drivers/mtd/spi-nor/core.c | 76 +++++++++++++++++++++++++++++++++++++-
1 file changed, 75 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index a9000b0ebe69..2939ffbaad2b 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1677,6 +1677,76 @@ static const struct flash_info *spi_nor_detect(struct spi_nor *nor)
return info;
}
+/*
+ * On Octal DTR capable flashes, reads cannot start or end at an odd
+ * address in Octal DTR mode. Extra bytes need to be read at the start
+ * or end to make sure both the start address and length remain even.
+ */
+static int spi_nor_octal_dtr_read(struct spi_nor *nor, loff_t from, size_t len,
+ u_char *buf)
+{
+ u_char *tmp_buf;
+ size_t tmp_len;
+ loff_t start, end;
+ int ret, bytes_read;
+
+ if (IS_ALIGNED(from, 2) && IS_ALIGNED(len, 2))
+ return spi_nor_read_data(nor, from, len, buf);
+ else if (IS_ALIGNED(from, 2) && len > PAGE_SIZE)
+ return spi_nor_read_data(nor, from, round_down(len, PAGE_SIZE),
+ buf);
+
+ tmp_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!tmp_buf)
+ return -ENOMEM;
+
+ start = round_down(from, 2);
+ end = round_up(from + len, 2);
+
+ /*
+ * Avoid allocating too much memory. The requested read length might be
+ * quite large. Allocating a buffer just as large (slightly bigger, in
+ * fact) would put unnecessary memory pressure on the system.
+ *
+ * For example if the read is from 3 to 1M, then this will read from 2
+ * to 4098. The reads from 4098 to 1M will then not need a temporary
+ * buffer so they can proceed as normal.
+ */
+ tmp_len = min_t(size_t, end - start, PAGE_SIZE);
+
+ ret = spi_nor_read_data(nor, start, tmp_len, tmp_buf);
+ if (ret == 0) {
+ ret = -EIO;
+ goto out;
+ }
+ if (ret < 0)
+ goto out;
+
+ /*
+ * More bytes are read than actually requested, but that number can't be
+ * reported to the calling function or it will confuse its calculations.
+ * Calculate how many of the _requested_ bytes were read.
+ */
+ bytes_read = ret;
+
+ if (from != start)
+ ret -= from - start;
+
+ /*
+ * Only account for extra bytes at the end if they were actually read.
+ * For example, if the total length was truncated because of temporary
+ * buffer size limit then the adjustment for the extra bytes at the end
+ * is not needed.
+ */
+ if (start + bytes_read == end)
+ ret -= end - (from + len);
+
+ memcpy(buf, tmp_buf + (from - start), ret);
+out:
+ kfree(tmp_buf);
+ return ret;
+}
+
static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
@@ -1694,7 +1764,11 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
addr = spi_nor_convert_addr(nor, addr);
- ret = spi_nor_read_data(nor, addr, len, buf);
+ if (nor->read_proto == SNOR_PROTO_8_8_8_DTR)
+ ret = spi_nor_octal_dtr_read(nor, addr, len, buf);
+ else
+ ret = spi_nor_read_data(nor, addr, len, buf);
+
if (ret == 0) {
/* We shouldn't see 0-length reads */
ret = -EIO;
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/2] mtd: spi-nor: core: avoid odd length/address writes in 8D-8D-8D mode
2026-03-25 9:23 [PATCH 6.1.y 0/2] mtd: spi-nor: avoid odd length/address accesses in 8D-8D-8D mode liyin.zhang.cn
2026-03-25 9:23 ` [PATCH 1/2] mtd: spi-nor: core: avoid odd length/address reads on " liyin.zhang.cn
@ 2026-03-25 9:23 ` liyin.zhang.cn
1 sibling, 0 replies; 3+ messages in thread
From: liyin.zhang.cn @ 2026-03-25 9:23 UTC (permalink / raw)
To: stable
From: Pratyush Yadav <p.yadav@ti.com>
[ Upstream commit 17926cd770ec837ed27d9856cf07f2da8dda4131 ]
On Octal DTR capable flashes like Micron Xcella the writes cannot start
or end at an odd address in Octal DTR mode. Extra 0xff bytes need to be
appended or prepended to make sure the start address and end address are
even. 0xff is used because on NOR flashes a program operation can only
flip bits from 1 to 0, not the other way round. 0 to 1 flip needs to
happen via erases.
Signed-off-by: Pratyush Yadav <p.yadav@ti.com>
Reviewed-by: Michael Walle <michael@walle.cc>
Signed-off-by: Luke Wang <ziniu.wang_1@nxp.com>
Signed-off-by: Pratyush Yadav <pratyush@kernel.org>
Link: https://lore.kernel.org/r/20250708091646.292-2-ziniu.wang_1@nxp.com
[ Resolve conflict in drivers/mtd/spi-nor/core.c.
In spi_nor_write(), the spi_nor_lock_device() and
spi_nor_unlock_device() mechanism was not yet introduced in 6.1.y.
Drop the spi_nor_unlock_device() call from the patch. ]
Signed-off-by: Liyin Zhang <liyin.zhang.cn@windriver.com>
---
drivers/mtd/spi-nor/core.c | 69 +++++++++++++++++++++++++++++++++++++-
1 file changed, 68 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 2939ffbaad2b..91622d9c9b03 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1790,6 +1790,68 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
return ret;
}
+/*
+ * On Octal DTR capable flashes, writes cannot start or end at an odd address
+ * in Octal DTR mode. Extra 0xff bytes need to be appended or prepended to
+ * make sure the start address and end address are even. 0xff is used because
+ * on NOR flashes a program operation can only flip bits from 1 to 0, not the
+ * other way round. 0 to 1 flip needs to happen via erases.
+ */
+static int spi_nor_octal_dtr_write(struct spi_nor *nor, loff_t to, size_t len,
+ const u8 *buf)
+{
+ u8 *tmp_buf;
+ size_t bytes_written;
+ loff_t start, end;
+ int ret;
+
+ if (IS_ALIGNED(to, 2) && IS_ALIGNED(len, 2))
+ return spi_nor_write_data(nor, to, len, buf);
+
+ tmp_buf = kmalloc(nor->params->page_size, GFP_KERNEL);
+ if (!tmp_buf)
+ return -ENOMEM;
+
+ memset(tmp_buf, 0xff, nor->params->page_size);
+
+ start = round_down(to, 2);
+ end = round_up(to + len, 2);
+
+ memcpy(tmp_buf + (to - start), buf, len);
+
+ ret = spi_nor_write_data(nor, start, end - start, tmp_buf);
+ if (ret == 0) {
+ ret = -EIO;
+ goto out;
+ }
+ if (ret < 0)
+ goto out;
+
+ /*
+ * More bytes are written than actually requested, but that number can't
+ * be reported to the calling function or it will confuse its
+ * calculations. Calculate how many of the _requested_ bytes were
+ * written.
+ */
+ bytes_written = ret;
+
+ if (to != start)
+ ret -= to - start;
+
+ /*
+ * Only account for extra bytes at the end if they were actually
+ * written. For example, if for some reason the controller could only
+ * complete a partial write then the adjustment for the extra bytes at
+ * the end is not needed.
+ */
+ if (start + bytes_written == end)
+ ret -= end - (to + len);
+
+out:
+ kfree(tmp_buf);
+ return ret;
+}
+
/*
* Write an address range to the nor chip. Data must be written in
* FLASH_PAGESIZE chunks. The address range may be any size provided
@@ -1834,7 +1896,12 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
if (ret)
goto write_err;
- ret = spi_nor_write_data(nor, addr, page_remain, buf + i);
+ if (nor->write_proto == SNOR_PROTO_8_8_8_DTR)
+ ret = spi_nor_octal_dtr_write(nor, addr, page_remain,
+ buf + i);
+ else
+ ret = spi_nor_write_data(nor, addr, page_remain,
+ buf + i);
if (ret < 0)
goto write_err;
written = ret;
--
2.34.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-03-25 9:23 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-25 9:23 [PATCH 6.1.y 0/2] mtd: spi-nor: avoid odd length/address accesses in 8D-8D-8D mode liyin.zhang.cn
2026-03-25 9:23 ` [PATCH 1/2] mtd: spi-nor: core: avoid odd length/address reads on " liyin.zhang.cn
2026-03-25 9:23 ` [PATCH 2/2] mtd: spi-nor: core: avoid odd length/address writes in " liyin.zhang.cn
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox