--- /dev/null
+From f156b23df6a84efb2f6686156be94d4988568954 Mon Sep 17 00:00:00 2001
+From: Pratyush Yadav <p.yadav@ti.com>
+Date: Tue, 8 Jul 2025 17:16:45 +0800
+Subject: mtd: spi-nor: core: avoid odd length/address reads on 8D-8D-8D mode
+
+From: Pratyush Yadav <p.yadav@ti.com>
+
+commit f156b23df6a84efb2f6686156be94d4988568954 upstream.
+
+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
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/spi-nor/core.c | 76 ++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 75 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi-nor/core.c
++++ b/drivers/mtd/spi-nor/core.c
+@@ -2037,6 +2037,76 @@ static const struct flash_info *spi_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)
+ {
+@@ -2054,7 +2124,11 @@ static int spi_nor_read(struct mtd_info
+ while (len) {
+ loff_t addr = from;
+
+- 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;
--- /dev/null
+From 17926cd770ec837ed27d9856cf07f2da8dda4131 Mon Sep 17 00:00:00 2001
+From: Pratyush Yadav <p.yadav@ti.com>
+Date: Tue, 8 Jul 2025 17:16:46 +0800
+Subject: mtd: spi-nor: core: avoid odd length/address writes in 8D-8D-8D mode
+
+From: Pratyush Yadav <p.yadav@ti.com>
+
+commit 17926cd770ec837ed27d9856cf07f2da8dda4131 upstream.
+
+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
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mtd/spi-nor/core.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 68 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi-nor/core.c
++++ b/drivers/mtd/spi-nor/core.c
+@@ -2152,6 +2152,68 @@ read_err:
+ }
+
+ /*
++ * 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
+ * it is within the physical boundaries.
+@@ -2187,7 +2249,12 @@ static int spi_nor_write(struct mtd_info
+ 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);
+ spi_nor_unlock_device(nor);
+ if (ret < 0)
+ goto write_err;