From: Ashok Reddy Soma <ashok.reddy.s...@xilinx.com>

In a dual parallel configuration, halve the read offset.
Determine whether the read offset points to the lower or
upper flash in a dual stacked configuration and set the
corresponding flags accordingly.

Include support for cases where the read involves an odd
number of bytes.

Extend support for cross-die reads in flash memory devices
that contain multiple dies within them.

Signed-off-by: Ashok Reddy Soma <ashok.reddy.s...@xilinx.com>
Signed-off-by: Michal Simek <michal.si...@xilinx.com>
Signed-off-by: Tejas Bhumkar <tejas.arvind.bhum...@amd.com>
---
 drivers/mtd/spi/spi-nor-core.c | 61 ++++++++++++++++++++++++++++++----
 include/spi.h                  |  3 +-
 2 files changed, 56 insertions(+), 8 deletions(-)

diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c
index e505648e5d..f6e7592458 100644
--- a/drivers/mtd/spi/spi-nor-core.c
+++ b/drivers/mtd/spi/spi-nor-core.c
@@ -1503,11 +1503,8 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t 
from, size_t len,
        struct spi_nor *nor = mtd_to_spi_nor(mtd);
        int ret;
        u32 offset = from;
-       u32 stack_shift = 0;
-       u32 read_len = 0;
-       u32 rem_bank_len = 0;
-       u8 bank;
-       u8 is_ofst_odd = 0;
+       u32 bank_size, stack_shift = 0, read_len = 0, rem_bank_len = 0;
+       u8 bank, cur_bank, nxt_bank, is_ofst_odd = 0;
 
        dev_dbg(nor->dev, "from 0x%08x, len %zd\n", (u32)from, len);
 
@@ -1541,6 +1538,40 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t 
from, size_t len,
                        }
                }
 
+               if (nor->addr_width == 4) {
+                       /*
+                        * Some flash devices like N25Q512 have multiple dies
+                        * in it. Read operation in these devices is bounded
+                        * by its die segment. In a continuous read, across
+                        * multiple dies, when the last byte of the selected
+                        * die segment is read, the next byte read is the
+                        * first byte of the same die segment. This is Die
+                        * cross over issue. So to handle this issue, split
+                        * a read transaction, that spans across multiple
+                        * banks, into one read per bank. Bank size is 16MB
+                        * for single and dual stacked mode and 32MB for dual
+                        * parallel mode.
+                        */
+                       if (nor->spi && nor->spi->multi_die) {
+                               bank_size = SZ_16M;
+                               if (nor->flags & SNOR_F_HAS_PARALLEL)
+                                       bank_size <<= 1;
+                               cur_bank = offset / bank_size;
+                               nxt_bank = (offset + len) / bank_size;
+                               if (cur_bank != nxt_bank)
+                                       rem_bank_len = (bank_size *
+                                                       (cur_bank + 1)) -
+                                                       offset;
+                               else
+                                       rem_bank_len = (mtd->size >>
+                                                       stack_shift) -
+                                                       offset;
+                       } else {
+                               rem_bank_len = (mtd->size >> stack_shift) -
+                                               offset;
+                       }
+               }
+
                if (nor->flags & SNOR_F_HAS_PARALLEL)
                        offset /= 2;
 
@@ -1552,6 +1583,15 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t 
from, size_t len,
 #endif
                }
 
+               if (len < rem_bank_len)
+                       read_len = len;
+               else
+                       read_len = rem_bank_len;
+
+               ret = spi_nor_wait_till_ready(nor);
+               if (ret)
+                       goto read_err;
+
                ret = nor->read(nor, offset, read_len, buf);
                if (ret == 0) {
                        /* We shouldn't see 0-length reads */
@@ -1561,8 +1601,15 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t 
from, size_t len,
                if (ret < 0)
                        goto read_err;
 
-               *retlen += ret;
-               buf += ret;
+               if (is_ofst_odd == 1) {
+                       memcpy(buf, (buf + 1), (len - 1));
+                       *retlen += (ret - 1);
+                       buf += ret - 1;
+                       is_ofst_odd = 0;
+               } else {
+                       *retlen += ret;
+                       buf += ret;
+               }
                from += ret;
                len -= ret;
        }
diff --git a/include/spi.h b/include/spi.h
index eb015ecbf5..9014066ee3 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -165,7 +165,7 @@ struct spi_slave {
        unsigned int max_write_size;
        void *memory_map;
 
-       u8 flags;
+       u32 flags;
 #define SPI_XFER_BEGIN         BIT(0)  /* Assert CS before transfer */
 #define SPI_XFER_END           BIT(1)  /* Deassert CS after transfer */
 #define SPI_XFER_ONCE          (SPI_XFER_BEGIN | SPI_XFER_END)
@@ -179,6 +179,7 @@ struct spi_slave {
         */
        bool multi_cs_cap;
        u32 bytemode;
+       bool multi_die;                 /* flash with multiple dies */
 };
 
 /**
-- 
2.27.0

Reply via email to