bcm53xx: backport spi-nor changes and update bcm53xxspiflash
[openwrt/staging/chunkeey.git] / target / linux / bcm53xx / patches-3.14 / 003-mtd-spi-nor-from-3.19.patch
diff --git a/target/linux/bcm53xx/patches-3.14/003-mtd-spi-nor-from-3.19.patch b/target/linux/bcm53xx/patches-3.14/003-mtd-spi-nor-from-3.19.patch
new file mode 100644 (file)
index 0000000..5451b9c
--- /dev/null
@@ -0,0 +1,662 @@
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -26,7 +26,38 @@
+ /* Define max times to check status register before we give up. */
+ #define       MAX_READY_WAIT_JIFFIES  (40 * HZ) /* M25P16 specs 40s max chip erase */
+-#define JEDEC_MFR(_jedec_id)  ((_jedec_id) >> 16)
++#define SPI_NOR_MAX_ID_LEN    6
++
++struct flash_info {
++      /*
++       * This array stores the ID bytes.
++       * The first three bytes are the JEDIC ID.
++       * JEDEC ID zero means "no ID" (mostly older chips).
++       */
++      u8              id[SPI_NOR_MAX_ID_LEN];
++      u8              id_len;
++
++      /* The size listed here is what works with SPINOR_OP_SE, which isn't
++       * necessarily called a "sector" by the vendor.
++       */
++      unsigned        sector_size;
++      u16             n_sectors;
++
++      u16             page_size;
++      u16             addr_width;
++
++      u16             flags;
++#define       SECT_4K                 0x01    /* SPINOR_OP_BE_4K works uniformly */
++#define       SPI_NOR_NO_ERASE        0x02    /* No erase command needed */
++#define       SST_WRITE               0x04    /* use SST byte programming */
++#define       SPI_NOR_NO_FR           0x08    /* Can't do fastread */
++#define       SECT_4K_PMC             0x10    /* SPINOR_OP_BE_4K_PMC works uniformly */
++#define       SPI_NOR_DUAL_READ       0x20    /* Flash supports Dual Read */
++#define       SPI_NOR_QUAD_READ       0x40    /* Flash supports Quad Read */
++#define       USE_FSR                 0x80    /* use flag status register */
++};
++
++#define JEDEC_MFR(info)       ((info)->id[0])
+ static const struct spi_device_id *spi_nor_match_id(const char *name);
+@@ -98,7 +129,7 @@ static inline int spi_nor_read_dummy_cyc
+       case SPI_NOR_FAST:
+       case SPI_NOR_DUAL:
+       case SPI_NOR_QUAD:
+-              return 1;
++              return 8;
+       case SPI_NOR_NORMAL:
+               return 0;
+       }
+@@ -138,13 +169,14 @@ static inline struct spi_nor *mtd_to_spi
+ }
+ /* Enable/disable 4-byte addressing mode. */
+-static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
++static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
++                          int enable)
+ {
+       int status;
+       bool need_wren = false;
+       u8 cmd;
+-      switch (JEDEC_MFR(jedec_id)) {
++      switch (JEDEC_MFR(info)) {
+       case CFI_MFR_ST: /* Micron, actually */
+               /* Some Micron need WREN command; all will accept it */
+               need_wren = true;
+@@ -165,81 +197,74 @@ static inline int set_4byte(struct spi_n
+               return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
+       }
+ }
+-
+-static int spi_nor_wait_till_ready(struct spi_nor *nor)
++static inline int spi_nor_sr_ready(struct spi_nor *nor)
+ {
+-      unsigned long deadline;
+-      int sr;
+-
+-      deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+-
+-      do {
+-              cond_resched();
++      int sr = read_sr(nor);
++      if (sr < 0)
++              return sr;
++      else
++              return !(sr & SR_WIP);
++}
+-              sr = read_sr(nor);
+-              if (sr < 0)
+-                      break;
+-              else if (!(sr & SR_WIP))
+-                      return 0;
+-      } while (!time_after_eq(jiffies, deadline));
++static inline int spi_nor_fsr_ready(struct spi_nor *nor)
++{
++      int fsr = read_fsr(nor);
++      if (fsr < 0)
++              return fsr;
++      else
++              return fsr & FSR_READY;
++}
+-      return -ETIMEDOUT;
++static int spi_nor_ready(struct spi_nor *nor)
++{
++      int sr, fsr;
++      sr = spi_nor_sr_ready(nor);
++      if (sr < 0)
++              return sr;
++      fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
++      if (fsr < 0)
++              return fsr;
++      return sr && fsr;
+ }
+-static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor)
++/*
++ * Service routine to read status register until ready, or timeout occurs.
++ * Returns non-zero if error.
++ */
++static int spi_nor_wait_till_ready(struct spi_nor *nor)
+ {
+       unsigned long deadline;
+-      int sr;
+-      int fsr;
++      int timeout = 0, ret;
+       deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+-      do {
++      while (!timeout) {
++              if (time_after_eq(jiffies, deadline))
++                      timeout = 1;
++
++              ret = spi_nor_ready(nor);
++              if (ret < 0)
++                      return ret;
++              if (ret)
++                      return 0;
++
+               cond_resched();
++      }
+-              sr = read_sr(nor);
+-              if (sr < 0) {
+-                      break;
+-              } else if (!(sr & SR_WIP)) {
+-                      fsr = read_fsr(nor);
+-                      if (fsr < 0)
+-                              break;
+-                      if (fsr & FSR_READY)
+-                              return 0;
+-              }
+-      } while (!time_after_eq(jiffies, deadline));
++      dev_err(nor->dev, "flash operation timed out\n");
+       return -ETIMEDOUT;
+ }
+ /*
+- * Service routine to read status register until ready, or timeout occurs.
+- * Returns non-zero if error.
+- */
+-static int wait_till_ready(struct spi_nor *nor)
+-{
+-      return nor->wait_till_ready(nor);
+-}
+-
+-/*
+  * Erase the whole flash memory
+  *
+  * Returns 0 if successful, non-zero otherwise.
+  */
+ static int erase_chip(struct spi_nor *nor)
+ {
+-      int ret;
+-
+       dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
+-      /* Wait until finished previous write command. */
+-      ret = wait_till_ready(nor);
+-      if (ret)
+-              return ret;
+-
+-      /* Send write enable, then erase commands. */
+-      write_enable(nor);
+-
+       return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
+ }
+@@ -294,11 +319,17 @@ static int spi_nor_erase(struct mtd_info
+       /* whole-chip erase? */
+       if (len == mtd->size) {
++              write_enable(nor);
++
+               if (erase_chip(nor)) {
+                       ret = -EIO;
+                       goto erase_err;
+               }
++              ret = spi_nor_wait_till_ready(nor);
++              if (ret)
++                      goto erase_err;
++
+       /* REVISIT in some cases we could speed up erasing large regions
+        * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K.  We may have set up
+        * to use "small sector erase", but that's not always optimal.
+@@ -307,6 +338,8 @@ static int spi_nor_erase(struct mtd_info
+       /* "sector"-at-a-time erase */
+       } else {
+               while (len) {
++                      write_enable(nor);
++
+                       if (nor->erase(nor, addr)) {
+                               ret = -EIO;
+                               goto erase_err;
+@@ -314,9 +347,15 @@ static int spi_nor_erase(struct mtd_info
+                       addr += mtd->erasesize;
+                       len -= mtd->erasesize;
++
++                      ret = spi_nor_wait_till_ready(nor);
++                      if (ret)
++                              goto erase_err;
+               }
+       }
++      write_disable(nor);
++
+       spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
+       instr->state = MTD_ERASE_DONE;
+@@ -341,11 +380,6 @@ static int spi_nor_lock(struct mtd_info
+       if (ret)
+               return ret;
+-      /* Wait until finished previous command */
+-      ret = wait_till_ready(nor);
+-      if (ret)
+-              goto err;
+-
+       status_old = read_sr(nor);
+       if (offset < mtd->size - (mtd->size / 2))
+@@ -388,11 +422,6 @@ static int spi_nor_unlock(struct mtd_inf
+       if (ret)
+               return ret;
+-      /* Wait until finished previous command */
+-      ret = wait_till_ready(nor);
+-      if (ret)
+-              goto err;
+-
+       status_old = read_sr(nor);
+       if (offset+len > mtd->size - (mtd->size / 64))
+@@ -424,38 +453,34 @@ err:
+       return ret;
+ }
+-struct flash_info {
+-      /* JEDEC id zero means "no ID" (most older chips); otherwise it has
+-       * a high byte of zero plus three data bytes: the manufacturer id,
+-       * then a two byte device id.
+-       */
+-      u32             jedec_id;
+-      u16             ext_id;
+-
+-      /* The size listed here is what works with SPINOR_OP_SE, which isn't
+-       * necessarily called a "sector" by the vendor.
+-       */
+-      unsigned        sector_size;
+-      u16             n_sectors;
+-
+-      u16             page_size;
+-      u16             addr_width;
+-
+-      u16             flags;
+-#define       SECT_4K                 0x01    /* SPINOR_OP_BE_4K works uniformly */
+-#define       SPI_NOR_NO_ERASE        0x02    /* No erase command needed */
+-#define       SST_WRITE               0x04    /* use SST byte programming */
+-#define       SPI_NOR_NO_FR           0x08    /* Can't do fastread */
+-#define       SECT_4K_PMC             0x10    /* SPINOR_OP_BE_4K_PMC works uniformly */
+-#define       SPI_NOR_DUAL_READ       0x20    /* Flash supports Dual Read */
+-#define       SPI_NOR_QUAD_READ       0x40    /* Flash supports Quad Read */
+-#define       USE_FSR                 0x80    /* use flag status register */
+-};
+-
++/* Used when the "_ext_id" is two bytes at most */
+ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)    \
+       ((kernel_ulong_t)&(struct flash_info) {                         \
+-              .jedec_id = (_jedec_id),                                \
+-              .ext_id = (_ext_id),                                    \
++              .id = {                                                 \
++                      ((_jedec_id) >> 16) & 0xff,                     \
++                      ((_jedec_id) >> 8) & 0xff,                      \
++                      (_jedec_id) & 0xff,                             \
++                      ((_ext_id) >> 8) & 0xff,                        \
++                      (_ext_id) & 0xff,                               \
++                      },                                              \
++              .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),       \
++              .sector_size = (_sector_size),                          \
++              .n_sectors = (_n_sectors),                              \
++              .page_size = 256,                                       \
++              .flags = (_flags),                                      \
++      })
++
++#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)   \
++      ((kernel_ulong_t)&(struct flash_info) {                         \
++              .id = {                                                 \
++                      ((_jedec_id) >> 16) & 0xff,                     \
++                      ((_jedec_id) >> 8) & 0xff,                      \
++                      (_jedec_id) & 0xff,                             \
++                      ((_ext_id) >> 16) & 0xff,                       \
++                      ((_ext_id) >> 8) & 0xff,                        \
++                      (_ext_id) & 0xff,                               \
++                      },                                              \
++              .id_len = 6,                                            \
+               .sector_size = (_sector_size),                          \
+               .n_sectors = (_n_sectors),                              \
+               .page_size = 256,                                       \
+@@ -507,6 +532,9 @@ static const struct spi_device_id spi_no
+       { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+       { "mr25h10",  CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
++      /* Fujitsu */
++      { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },
++
+       /* GigaDevice */
+       { "gd25q32", INFO(0xc84016, 0, 64 * 1024,  64, SECT_4K) },
+       { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
+@@ -532,6 +560,7 @@ static const struct spi_device_id spi_no
+       { "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
+       /* Micron */
++      { "n25q032",     INFO(0x20ba16, 0, 64 * 1024,   64, 0) },
+       { "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, 0) },
+       { "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, 0) },
+       { "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, 0) },
+@@ -556,6 +585,7 @@ static const struct spi_device_id spi_no
+       { "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
+       { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
+       { "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
++      { "s25fl128s",  INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
+       { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
+       { "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
+       { "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },
+@@ -577,6 +607,7 @@ static const struct spi_device_id spi_no
+       { "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K | SST_WRITE) },
+       { "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K | SST_WRITE) },
+       { "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
++      { "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
+       /* ST Microelectronics -- newer production may have feature updates */
+       { "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },
+@@ -588,7 +619,6 @@ static const struct spi_device_id spi_no
+       { "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) },
+       { "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) },
+       { "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) },
+-      { "n25q032", INFO(0x20ba16,  0,  64 * 1024,  64, 0) },
+       { "m25p05-nonjedec",  INFO(0, 0,  32 * 1024,   2, 0) },
+       { "m25p10-nonjedec",  INFO(0, 0,  32 * 1024,   4, 0) },
+@@ -643,32 +673,24 @@ static const struct spi_device_id spi_no
+ static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
+ {
+       int                     tmp;
+-      u8                      id[5];
+-      u32                     jedec;
+-      u16                     ext_jedec;
++      u8                      id[SPI_NOR_MAX_ID_LEN];
+       struct flash_info       *info;
+-      tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5);
++      tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
+       if (tmp < 0) {
+               dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp);
+               return ERR_PTR(tmp);
+       }
+-      jedec = id[0];
+-      jedec = jedec << 8;
+-      jedec |= id[1];
+-      jedec = jedec << 8;
+-      jedec |= id[2];
+-
+-      ext_jedec = id[3] << 8 | id[4];
+       for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
+               info = (void *)spi_nor_ids[tmp].driver_data;
+-              if (info->jedec_id == jedec) {
+-                      if (info->ext_id == 0 || info->ext_id == ext_jedec)
++              if (info->id_len) {
++                      if (!memcmp(info->id, id, info->id_len))
+                               return &spi_nor_ids[tmp];
+               }
+       }
+-      dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec);
++      dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %2x, %2x\n",
++              id[0], id[1], id[2]);
+       return ERR_PTR(-ENODEV);
+ }
+@@ -703,11 +725,6 @@ static int sst_write(struct mtd_info *mt
+       if (ret)
+               return ret;
+-      /* Wait until finished previous write command. */
+-      ret = wait_till_ready(nor);
+-      if (ret)
+-              goto time_out;
+-
+       write_enable(nor);
+       nor->sst_write_second = false;
+@@ -719,7 +736,7 @@ static int sst_write(struct mtd_info *mt
+               /* write one byte. */
+               nor->write(nor, to, 1, retlen, buf);
+-              ret = wait_till_ready(nor);
++              ret = spi_nor_wait_till_ready(nor);
+               if (ret)
+                       goto time_out;
+       }
+@@ -731,7 +748,7 @@ static int sst_write(struct mtd_info *mt
+               /* write two bytes. */
+               nor->write(nor, to, 2, retlen, buf + actual);
+-              ret = wait_till_ready(nor);
++              ret = spi_nor_wait_till_ready(nor);
+               if (ret)
+                       goto time_out;
+               to += 2;
+@@ -740,7 +757,7 @@ static int sst_write(struct mtd_info *mt
+       nor->sst_write_second = false;
+       write_disable(nor);
+-      ret = wait_till_ready(nor);
++      ret = spi_nor_wait_till_ready(nor);
+       if (ret)
+               goto time_out;
+@@ -751,7 +768,7 @@ static int sst_write(struct mtd_info *mt
+               nor->program_opcode = SPINOR_OP_BP;
+               nor->write(nor, to, 1, retlen, buf + actual);
+-              ret = wait_till_ready(nor);
++              ret = spi_nor_wait_till_ready(nor);
+               if (ret)
+                       goto time_out;
+               write_disable(nor);
+@@ -779,11 +796,6 @@ static int spi_nor_write(struct mtd_info
+       if (ret)
+               return ret;
+-      /* Wait until finished previous write command. */
+-      ret = wait_till_ready(nor);
+-      if (ret)
+-              goto write_err;
+-
+       write_enable(nor);
+       page_offset = to & (nor->page_size - 1);
+@@ -802,16 +814,20 @@ static int spi_nor_write(struct mtd_info
+                       if (page_size > nor->page_size)
+                               page_size = nor->page_size;
+-                      wait_till_ready(nor);
++                      ret = spi_nor_wait_till_ready(nor);
++                      if (ret)
++                              goto write_err;
++
+                       write_enable(nor);
+                       nor->write(nor, to + i, page_size, retlen, buf + i);
+               }
+       }
++      ret = spi_nor_wait_till_ready(nor);
+ write_err:
+       spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
+-      return 0;
++      return ret;
+ }
+ static int macronix_quad_enable(struct spi_nor *nor)
+@@ -824,7 +840,7 @@ static int macronix_quad_enable(struct s
+       nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
+       nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
+-      if (wait_till_ready(nor))
++      if (spi_nor_wait_till_ready(nor))
+               return 1;
+       ret = read_sr(nor);
+@@ -874,11 +890,11 @@ static int spansion_quad_enable(struct s
+       return 0;
+ }
+-static int set_quad_mode(struct spi_nor *nor, u32 jedec_id)
++static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
+ {
+       int status;
+-      switch (JEDEC_MFR(jedec_id)) {
++      switch (JEDEC_MFR(info)) {
+       case CFI_MFR_MACRONIX:
+               status = macronix_quad_enable(nor);
+               if (status) {
+@@ -904,11 +920,6 @@ static int spi_nor_check(struct spi_nor
+               return -EINVAL;
+       }
+-      if (!nor->read_id)
+-              nor->read_id = spi_nor_read_id;
+-      if (!nor->wait_till_ready)
+-              nor->wait_till_ready = spi_nor_wait_till_ready;
+-
+       return 0;
+ }
+@@ -926,16 +937,24 @@ int spi_nor_scan(struct spi_nor *nor, co
+       if (ret)
+               return ret;
+-      id = spi_nor_match_id(name);
+-      if (!id)
++      /* Try to auto-detect if chip name wasn't specified */
++      if (!name)
++              id = spi_nor_read_id(nor);
++      else
++              id = spi_nor_match_id(name);
++      if (IS_ERR_OR_NULL(id))
+               return -ENOENT;
+       info = (void *)id->driver_data;
+-      if (info->jedec_id) {
++      /*
++       * If caller has specified name of flash model that can normally be
++       * detected using JEDEC, let's verify it.
++       */
++      if (name && info->id_len) {
+               const struct spi_device_id *jid;
+-              jid = nor->read_id(nor);
++              jid = spi_nor_read_id(nor);
+               if (IS_ERR(jid)) {
+                       return PTR_ERR(jid);
+               } else if (jid != id) {
+@@ -960,9 +979,9 @@ int spi_nor_scan(struct spi_nor *nor, co
+        * up with the software protection bits set
+        */
+-      if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL ||
+-          JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL ||
+-          JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) {
++      if (JEDEC_MFR(info) == CFI_MFR_ATMEL ||
++          JEDEC_MFR(info) == CFI_MFR_INTEL ||
++          JEDEC_MFR(info) == CFI_MFR_SST) {
+               write_enable(nor);
+               write_sr(nor, 0);
+       }
+@@ -977,7 +996,7 @@ int spi_nor_scan(struct spi_nor *nor, co
+       mtd->_read = spi_nor_read;
+       /* nor protection support for STmicro chips */
+-      if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
++      if (JEDEC_MFR(info) == CFI_MFR_ST) {
+               mtd->_lock = spi_nor_lock;
+               mtd->_unlock = spi_nor_unlock;
+       }
+@@ -988,9 +1007,8 @@ int spi_nor_scan(struct spi_nor *nor, co
+       else
+               mtd->_write = spi_nor_write;
+-      if ((info->flags & USE_FSR) &&
+-          nor->wait_till_ready == spi_nor_wait_till_ready)
+-              nor->wait_till_ready = spi_nor_wait_till_fsr_ready;
++      if (info->flags & USE_FSR)
++              nor->flags |= SNOR_F_USE_FSR;
+ #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+       /* prefer "small sector" erase if possible */
+@@ -1031,7 +1049,7 @@ int spi_nor_scan(struct spi_nor *nor, co
+       /* Quad/Dual-read mode takes precedence over fast/normal */
+       if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
+-              ret = set_quad_mode(nor, info->jedec_id);
++              ret = set_quad_mode(nor, info);
+               if (ret) {
+                       dev_err(dev, "quad mode not supported\n");
+                       return ret;
+@@ -1067,7 +1085,7 @@ int spi_nor_scan(struct spi_nor *nor, co
+       else if (mtd->size > 0x1000000) {
+               /* enable 4-byte addressing if the device exceeds 16MiB */
+               nor->addr_width = 4;
+-              if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
++              if (JEDEC_MFR(info) == CFI_MFR_AMD) {
+                       /* Dedicated 4-byte command set */
+                       switch (nor->flash_read) {
+                       case SPI_NOR_QUAD:
+@@ -1088,7 +1106,7 @@ int spi_nor_scan(struct spi_nor *nor, co
+                       nor->erase_opcode = SPINOR_OP_SE_4B;
+                       mtd->erasesize = info->sector_size;
+               } else
+-                      set_4byte(nor, info->jedec_id, 1);
++                      set_4byte(nor, info, 1);
+       } else {
+               nor->addr_width = 3;
+       }
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -116,6 +116,10 @@ enum spi_nor_ops {
+       SPI_NOR_OPS_UNLOCK,
+ };
++enum spi_nor_option_flags {
++      SNOR_F_USE_FSR          = BIT(0),
++};
++
+ /**
+  * struct spi_nor - Structure for defining a the SPI NOR layer
+  * @mtd:              point to a mtd_info structure
+@@ -129,6 +133,7 @@ enum spi_nor_ops {
+  * @program_opcode:   the program opcode
+  * @flash_read:               the mode of the read
+  * @sst_write_second: used by the SST write operation
++ * @flags:            flag options for the current SPI-NOR (SNOR_F_*)
+  * @cfg:              used by the read_xfer/write_xfer
+  * @cmd_buf:          used by the write_reg
+  * @prepare:          [OPTIONAL] do some preparations for the
+@@ -139,9 +144,6 @@ enum spi_nor_ops {
+  * @write_xfer:               [OPTIONAL] the writefundamental primitive
+  * @read_reg:         [DRIVER-SPECIFIC] read out the register
+  * @write_reg:                [DRIVER-SPECIFIC] write data to the register
+- * @read_id:          [REPLACEABLE] read out the ID data, and find
+- *                    the proper spi_device_id
+- * @wait_till_ready:  [REPLACEABLE] wait till the NOR becomes ready
+  * @read:             [DRIVER-SPECIFIC] read data from the SPI NOR
+  * @write:            [DRIVER-SPECIFIC] write data to the SPI NOR
+  * @erase:            [DRIVER-SPECIFIC] erase a sector of the SPI NOR
+@@ -160,6 +162,7 @@ struct spi_nor {
+       u8                      program_opcode;
+       enum read_mode          flash_read;
+       bool                    sst_write_second;
++      u32                     flags;
+       struct spi_nor_xfer_cfg cfg;
+       u8                      cmd_buf[SPI_NOR_MAX_CMD_SIZE];
+@@ -172,8 +175,6 @@ struct spi_nor {
+       int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
+       int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
+                       int write_enable);
+-      const struct spi_device_id *(*read_id)(struct spi_nor *nor);
+-      int (*wait_till_ready)(struct spi_nor *nor);
+       int (*read)(struct spi_nor *nor, loff_t from,
+                       size_t len, size_t *retlen, u_char *read_buf);