The following are the details of patch:
- Add read/write scurity register functions to support some commands.
- Add suspend/resume commands.
- Add sfdp option to Kconfig and *_detect_sfdp function to support
  some specific commands. I also rearranged the setting steps in
  func spi_nor_scan to fit the sfdp method.
- The sfdp related functions have tested with Macronix chips, so I
  also add some commands for Macronix. Such as protection region
  (security OTP) and three types of islocked/lock/unlcok functions.
- Add some definitions and data structures to spi-nor.h to support
  above modifications.
Thanks!

Signed-off-by: Jim Kuo <[email protected]>
---
 drivers/mtd/spi-nor/Kconfig   |  11 +
 drivers/mtd/spi-nor/spi-nor.c | 730 +++++++++++++++++++++++++++++++++++++++---
 include/linux/mtd/spi-nor.h   | 201 ++++++++++++
 3 files changed, 899 insertions(+), 43 deletions(-)

diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig
index 64a4f0e..f40548b 100644
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -21,6 +21,17 @@ config MTD_SPI_NOR_USE_4K_SECTORS
          Please note that some tools/drivers/filesystems may not work with
          4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
 
+config MTD_SPI_NOR_SFDP
+       bool "Serial Flash Discoverable Parameters (SFDP) Probe"
+       default n
+       help
+         SFDP includes the most detailed information of SPI NOR flash memory.
+         It can be used to detect flash and get devices' parameters.
+
+         Attention that there are many flash flags can not covered by SFDP such
+         as SST_WRITE, USE_FSR, SPI_NOR_NO_FR and SECT_4K_PMC. Please do not
+         enable this option if your device is highly related with these flags.
+
 config SPI_FSL_QUADSPI
        tristate "Freescale Quad SPI controller"
        depends on ARCH_MXC
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index a6c7337..2340ead 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -119,6 +119,25 @@ static int read_cr(struct spi_nor *nor)
 }
 
 /*
+ * Read the security register, returning its value in the location
+ * Return the security register value.
+ * Returns negative if error occurred.
+ */
+static int read_scur(struct spi_nor *nor)
+{
+       int ret;
+       u8 val;
+
+       ret = nor->read_reg(nor, SPINOR_OP_RDSCUR, &val, 1);
+       if (ret < 0) {
+               pr_err("error %d reading SCUR\n", (int) ret);
+               return ret;
+       }
+
+       return val;
+}
+
+/*
  * Dummy Cycle calculation for different type of read.
  * It can be used to support more commands with
  * different dummy cycle requirements.
@@ -147,6 +166,15 @@ static inline int write_sr(struct spi_nor *nor, u8 val)
 }
 
 /*
+ * Set LDSO bit with Write Security Register command.
+ * Returns negative if error occurred.
+ */
+static inline int write_scur(struct spi_nor *nor)
+{
+       return nor->write_reg(nor, SPINOR_OP_WRSCUR, NULL, 0, 0);
+}
+
+/*
  * Set write enable latch with Write Enable command.
  * Returns negative if error occurred.
  */
@@ -802,19 +830,12 @@ time_out:
  * FLASH_PAGESIZE chunks.  The address range may be any size provided
  * it is within the physical boundaries.
  */
-static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
-       size_t *retlen, const u_char *buf)
+static int spi_nor_do_write(struct spi_nor *nor, loff_t to, size_t len,
+                       size_t *retlen, const u_char *buf)
 {
-       struct spi_nor *nor = mtd_to_spi_nor(mtd);
        u32 page_offset, page_size, i;
        int ret;
 
-       dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
-
-       ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
-       if (ret)
-               return ret;
-
        write_enable(nor);
 
        page_offset = to & (nor->page_size - 1);
@@ -835,7 +856,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, 
size_t len,
 
                        ret = spi_nor_wait_till_ready(nor);
                        if (ret)
-                               goto write_err;
+                               return ret;
 
                        write_enable(nor);
 
@@ -843,12 +864,416 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t 
to, size_t len,
                }
        }
 
-       ret = spi_nor_wait_till_ready(nor);
-write_err:
+       return 0;
+}
+
+static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
+                       size_t *retlen, const u_char *buf)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+       int ret;
+
+       dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
+
+       ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_WRITE);
+       if (ret)
+               return ret;
+
+       ret = spi_nor_do_write(nor, to, len, retlen, buf);
+       if (!ret)
+               ret = spi_nor_wait_till_ready(nor);
+
        spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
        return ret;
 }
 
+/*
+ * Power suspend and resume.
+ */
+static int spi_nor_suspend(struct mtd_info *mtd)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+       return nor->write_reg(nor, SPINOR_OP_DP, NULL, 0, 0);
+}
+
+static void spi_nor_resume(struct mtd_info *mtd)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+       nor->write_reg(nor, SPINOR_OP_RDP, NULL, 0, 0);
+}
+
+/*
+ * Read/Write manufacture or user protection area (One Time Program).
+ * Enter security OTP mode before access, and exit after access.
+ * The OTP area can be locked to prevent any modification, and there
+ * is no way to unlock it.
+ */
+static int macronix_otp(struct mtd_info *mtd, loff_t addr, size_t len,
+               size_t *retlen, u_char *buf, enum spi_nor_ops ops)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+       int ret;
+
+       ret = spi_nor_lock_and_prep(nor, ops);
+       if (ret)
+               return ret;
+
+       /* enter security OTP */
+       ret = nor->write_reg(nor, SPINOR_OP_ENSO, NULL, 0, 0);
+       if (ret) {
+               spi_nor_unlock_and_unprep(nor, ops);
+               return ret;
+       }
+
+       if (ops == SPI_NOR_OPS_READ)
+               nor->read(nor, addr, len, retlen, buf);
+
+       else { /* ops == SPI_NOR_OPS_WRITE */
+
+               /* exit if OTP has been locked */
+               if (read_scur(nor) & 0x03) {
+                       spi_nor_unlock_and_unprep(nor, ops);
+                       return 0;
+               }
+               spi_nor_do_write(nor, addr, len, retlen, buf);
+       }
+
+       /* eixt security OTP */
+       nor->write_reg(nor, SPINOR_OP_EXSO, NULL, 0, 0);
+
+       spi_nor_unlock_and_unprep(nor, ops);
+       return 0;
+}
+
+static int macronix_get_fact_prot_info(struct mtd_info *mtd,
+       size_t len, size_t *retlen, struct otp_info *buf)
+{
+       /*
+        * Factor area stores electronical serial number (ESN).
+        * And the area can not be modified.
+        */
+       buf->start  = 0;
+       buf->length = 0x10;
+       buf->locked = 1;
+       return 0;
+}
+
+static int macronix_read_fact_prot_reg(struct mtd_info *mtd,
+       loff_t from, size_t len, size_t *retlen, u_char *buf)
+{
+       if (from < 0 || from + len > 0x10)
+               return -EINVAL;
+       return macronix_otp(mtd, from, len, retlen, buf,
+                               SPI_NOR_OPS_READ);
+}
+
+static int macronix_get_user_prot_info(struct mtd_info *mtd,
+       size_t len, size_t *retlen, struct otp_info *buf)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+       buf->start  = 0x10;
+       if (mtd->size > 0x800000) {
+               /* The OTP size are 0x200 if the device exceeds 8MiB. */
+               buf->length = 0x1f0;
+       } else {
+               buf->length = 0x30;
+       }
+       buf->locked = (read_scur(nor) & 0x03) ? 1 : 0;
+       return 0;
+}
+
+static int macronix_read_user_prot_reg(struct mtd_info *mtd,
+       loff_t from, size_t len, size_t *retlen, u_char *buf)
+{
+       /*
+        * Macronix user and factor security area are the same.
+        * If the device is larger than 8 Mbyte, it only support
+        * factor area.
+        */
+       if (from < 0x10 || from + len > 0x40 ||
+               (mtd->size > 0x800000 && from + len > 0x200)) {
+               return -EINVAL;
+       }
+       return macronix_otp(mtd, from, len, retlen, buf,
+                               SPI_NOR_OPS_READ);
+}
+
+static int macronix_write_user_prot_reg(struct mtd_info *mtd,
+       loff_t to, size_t len, size_t *retlen, u_char *buf)
+{
+       if (to < 0x10 || to + len > 0x40 ||
+               (mtd->size > 0x800000 && to + len > 0x200)) {
+               return -EINVAL;
+       }
+       return macronix_otp(mtd, to, len, retlen, buf,
+                               SPI_NOR_OPS_WRITE);
+}
+
+/*
+ * Write LDSO bit (Lock-Down Security OTP) to 1.
+ */
+static int macronix_lock_user_prot_reg(struct mtd_info *mtd,
+                       loff_t from, size_t len)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+       if (len == 0)
+               return 0;
+
+       write_enable(nor);
+
+       return write_scur(nor);
+}
+
+/*
+ * Write protect selection command should be done before block lock.
+ */
+static int macronix_wpsel(struct spi_nor *nor)
+{
+       int retval;
+
+       /* read security register to check WPSEL status */
+       retval = read_scur(nor);
+       if (retval < 0)
+               return retval;
+       if ((u8)retval & 0x80)
+               return 0;
+
+       write_enable(nor);
+
+       return nor->write_reg(nor, SPINOR_OP_WPSEL, NULL, 0, 0);
+}
+
+static int macronix_gang_lock(struct mtd_info *mtd,
+                       enum spi_nor_ops ops)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+
+       write_enable(nor);
+
+       if (ops == SPI_NOR_OPS_LOCK)
+               return nor->write_reg(nor, SPINOR_OP_GBLK, NULL, 0, 0);
+       else /* ops == SPI_NOR_OPS_UNLOCK */
+               return nor->write_reg(nor, SPINOR_OP_GBULK, NULL, 0, 0);
+}
+
+static int macronix_read_lock_status(struct mtd_info *mtd, loff_t ofs,
+                       uint64_t len, enum macronix_lock_type type)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+       struct spi_nor_xfer_cfg cfg;
+       uint64_t end;
+       u32 move;
+       u8 status;
+       int ret;
+
+       /* do write protect selection first */
+       ret = macronix_wpsel(nor);
+       if (ret)
+               return ret;
+
+       ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_READ);
+       if (ret)
+               return ret;
+
+       if (type == MX_SBLK) {
+               cfg.cmd = SPINOR_OP_RDBLOCK;
+               cfg.addr_pins = nor->addr_width;
+       } else {
+               if (type == MX_SPB)
+                       cfg.cmd = SPINOR_OP_RDSPB;
+               else /* type == MX_DPB */
+                       cfg.cmd = SPINOR_OP_RDDPB;
+               cfg.addr_pins = 4;
+       }
+
+       cfg.cmd_pins = 1;
+       cfg.mode_pins = 0;
+       cfg.dummy_cycles = 0;
+
+       end = ofs + len - 1;
+
+       for (; ofs <= end && ofs < mtd->size; ofs += move) {
+               /* align the sector/blok size */
+               if (ofs < 0x10000 || ofs >= mtd->size - 0x10000)
+                       move = 0x1000;
+               else
+                       move = 0x10000;
+
+               cfg.addr = ofs;
+               nor->read_xfer(nor, &cfg, &status, 1);
+
+               /* status 0xff: protected, return 1 */
+               if (status == 0xff) {
+                       spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
+                       return 1;
+               }
+       }
+
+       spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
+       return 0;
+}
+
+/*
+ * Macronix individual block lock. There are many types of
+ * block protection used on different flash memory.
+ * Single lock: volatile lock for E-series chips.
+ * Solid protection bit: non-volatile.
+ * Dynamic protection: volatile.
+ */
+static int macronix_block_lock(struct mtd_info *mtd, loff_t ofs,
+               uint64_t len, enum macronix_lock_type type,
+               enum spi_nor_ops ops)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+       struct spi_nor_xfer_cfg cfg;
+       uint64_t end;
+       u32 move;
+       u8 *val, valtmp;
+       size_t datalen;
+       int ret;
+
+       /* do write protect selection first */
+       ret = macronix_wpsel(nor);
+       if (ret)
+               return ret;
+
+       /* use gang command to do whole chip lock and unlock */
+       if (len == mtd->size && type != MX_SPB)
+               return macronix_gang_lock(mtd, ops);
+
+       ret = spi_nor_lock_and_prep(nor, ops);
+       if (ret)
+               return ret;
+
+       if (type == MX_DPB) {
+               cfg.cmd = SPINOR_OP_WRDPB;
+               cfg.addr_pins = 4;
+               valtmp = (ops == SPI_NOR_OPS_LOCK) ? 0xff : 0;
+               val = &valtmp;
+               datalen = 1;
+       } else {
+               if (type == MX_SBLK) {
+                       cfg.cmd = (ops == SPI_NOR_OPS_LOCK) ?
+                               SPINOR_OP_SBLK : SPINOR_OP_SBULK;
+                       cfg.addr_pins = nor->addr_width;
+               } else { /* type == MX_SPB */
+                       cfg.cmd = SPINOR_OP_WRSPB;
+                       cfg.addr_pins = 4;
+               }
+               val = NULL;
+               datalen = 0;
+       }
+
+       cfg.cmd_pins = 1;
+       cfg.mode_pins = 0;
+       cfg.dummy_cycles = 0;
+       cfg.wren = 1;
+
+       /*
+        * The lock regions of Macronix are uniform. There use
+        * unit sector (4KB) in the first/last block, and unit
+        * block (64KB) in middle space of chips.
+        */
+
+       end = ofs + len - 1;
+
+       for (; ofs <= end && ofs < mtd->size; ofs += move) {
+               if (ofs < 0x10000 || ofs >= mtd->size - 0x10000)
+                       move = 0x1000;
+               else
+                       move = 0x10000;
+
+               cfg.addr = ofs;
+               nor->write_xfer(nor, &cfg, val, datalen);
+       }
+
+       spi_nor_unlock_and_unprep(nor, ops);
+       return 0;
+}
+
+/*
+ * Single block lock/unlock/islock command.
+ */
+static int macronix_single_lock(struct mtd_info *mtd, loff_t ofs,
+                               uint64_t len)
+{
+       return macronix_block_lock(mtd, ofs, len, MX_SBLK,
+                               SPI_NOR_OPS_LOCK);
+}
+
+static int macronix_single_unlock(struct mtd_info *mtd, loff_t ofs,
+                               uint64_t len)
+{
+       return macronix_block_lock(mtd, ofs, len, MX_SBLK,
+                               SPI_NOR_OPS_UNLOCK);
+}
+
+static int macronix_single_is_locked(struct mtd_info *mtd, loff_t ofs,
+                               uint64_t len)
+{
+       return macronix_read_lock_status(mtd, ofs, len, MX_SBLK);
+}
+
+/*
+ * Solid protection bit lock/unlock/islock command.
+ */
+static int macronix_solid_lock(struct mtd_info *mtd, loff_t ofs,
+                               uint64_t len)
+{
+       return macronix_block_lock(mtd, ofs, len, MX_SPB,
+                               SPI_NOR_OPS_LOCK);
+}
+
+static int macronix_solid_unlock(struct mtd_info *mtd, loff_t ofs,
+                               uint64_t len)
+{
+       struct spi_nor *nor = mtd_to_spi_nor(mtd);
+       int ret;
+
+       /* do write protect selection first */
+       ret = macronix_wpsel(nor);
+       if (ret)
+               return ret;
+
+       write_enable(nor);
+
+       /* erase solid bits will unlock all the blocks */
+       return nor->write_reg(nor, SPINOR_OP_ESSPB, NULL, 0, 0);
+}
+
+static int macronix_solid_is_locked(struct mtd_info *mtd, loff_t ofs,
+                               uint64_t len)
+{
+       return macronix_read_lock_status(mtd, ofs, len, MX_SPB);
+}
+
+/*
+ * Dynamic protection bit lock/unlock/islock command.
+ */
+static int macronix_dynamic_lock(struct mtd_info *mtd, loff_t ofs,
+                               uint64_t len)
+{
+       return macronix_block_lock(mtd, ofs, len, MX_DPB,
+                               SPI_NOR_OPS_LOCK);
+}
+
+static int macronix_dynamic_unlock(struct mtd_info *mtd, loff_t ofs,
+                               uint64_t len)
+{
+       return macronix_block_lock(mtd, ofs, len, MX_DPB,
+                               SPI_NOR_OPS_UNLOCK);
+}
+
+static int macronix_dynamic_is_locked(struct mtd_info *mtd, loff_t ofs,
+                               uint64_t len)
+{
+       return macronix_read_lock_status(mtd, ofs, len, MX_DPB);
+}
+
 static int macronix_quad_enable(struct spi_nor *nor)
 {
        int ret, val;
@@ -931,6 +1356,202 @@ static int set_quad_mode(struct spi_nor *nor, struct 
flash_info *info)
        }
 }
 
+/*
+ * Read SFDP ia an fundamental but important command for doing
+ * flash scan and probe.
+ */
+static int sfdp_read(struct spi_nor *nor, loff_t from, size_t len,
+               void *buf)
+{
+       struct spi_nor_xfer_cfg cfg;
+
+       cfg.cmd = SPINOR_OP_RDSFDP;
+       cfg.cmd_pins = 1;
+       cfg.addr = from;
+       cfg.addr_pins = 3;
+       cfg.mode_pins = 0;
+       cfg.dummy_cycles = 8;
+
+       return nor->read_xfer(nor, &cfg, (u8 *)buf, len);
+}
+
+/*
+ * Setup Macronix device with sfdp parameters.
+ */
+static void macronix_detect_sfdp(struct spi_nor *nor,
+                       struct sfdp_param_header *facthdr)
+{
+       struct sfdp_macronix_params mxchip;
+       struct mtd_info *mtd = nor->mtd;
+       u32 ptr, len;
+
+       /* get macronix parameters */
+       ptr = facthdr->pointer[0] + (facthdr->pointer[1] << 8) +
+                       (facthdr->pointer[2] << 16);
+       len = (facthdr->length * 4/*DWORDs*/ < sizeof(mxchip)) ?
+                       (facthdr->length * 4) : sizeof(mxchip);
+       if (sfdp_read(nor, ptr, len, &mxchip))
+               return;
+
+       /* security OTP support */
+       if (mxchip.protection_param & 0x0800) {
+               mtd->_get_fact_prot_info  = macronix_get_fact_prot_info;
+               mtd->_read_fact_prot_reg  = macronix_read_fact_prot_reg;
+               mtd->_get_user_prot_info  = macronix_get_user_prot_info;
+               mtd->_read_user_prot_reg  = macronix_read_user_prot_reg;
+               mtd->_write_user_prot_reg = macronix_write_user_prot_reg;
+               mtd->_lock_user_prot_reg  = macronix_lock_user_prot_reg;
+       }
+
+       /* individual block lock support */
+       if (mxchip.protection_param & 0x0001) {
+
+               switch ((mxchip.protection_param >> 2) & 0xff) {
+               case SPINOR_OP_SBLK:
+                       mtd->_lock      = macronix_single_lock;
+                       mtd->_unlock    = macronix_single_unlock;
+                       mtd->_is_locked = macronix_single_is_locked;
+                       break;
+               case SPINOR_OP_WRSPB:
+                       mtd->_lock      = macronix_solid_lock;
+                       mtd->_unlock    = macronix_solid_unlock;
+                       mtd->_is_locked = macronix_solid_is_locked;
+                       break;
+               case SPINOR_OP_WRDPB:
+                       mtd->_lock      = macronix_dynamic_lock;
+                       mtd->_unlock    = macronix_dynamic_unlock;
+                       mtd->_is_locked = macronix_dynamic_is_locked;
+                       break;
+               default:
+                       pr_err("Can not find Macronix protection 
parameters.\n");
+                       return;
+               }
+
+               if (!(mxchip.protection_param & 0x0400))
+                       mtd->_unlock(mtd, 0, mtd->size);
+       }
+}
+
+/*
+ * Check if the SPI NOR is SFDP compliant, returns 1 if it is, 0 otherwise.
+ * Directly update mtd information.
+ */
+static int spi_nor_detect_sfdp(struct spi_nor *nor, enum read_mode mode)
+{
+       struct spi_nor_sfdp_params sfdp;
+       struct sfdp_jedec_params jedec;
+       struct sfdp_param_header *jedechdr = NULL, *facthdr = NULL;
+       struct mtd_info *mtd = nor->mtd;
+       struct flash_info info;
+       u32 ptr, len;
+       int i, sect;
+
+       /* get JEDEC parameters */
+       sfdp_read(nor, 0, 4/*sizeof(sfdp.signature)*/, &sfdp.signature);
+       if (sfdp.signature != SFDP_SIGNATURE)
+               return 0;
+       if (sfdp_read(nor, 4, 4, &sfdp.minor_rev))
+               return 0;
+
+       if (sfdp.num_of_param_header < 1)
+               return 0;
+
+       if (sfdp_read(nor, 8,/*sizeof(sfdp.signature) + revision + header_num*/
+               sizeof(struct sfdp_param_header)*(sfdp.num_of_param_header+1),
+                       sfdp.header)) {
+               return 0;
+       }
+
+       /*
+        * Get JEDEC and manufacture parameter header.
+        * Attention that num_of_param_header is zero base.
+        */
+       for (i = 0; i <= sfdp.num_of_param_header; i++) {
+               if (sfdp.header[i].id == 0)
+                       jedechdr = &sfdp.header[i];
+               else if (sfdp.header[i].id != 0xFF)
+                       facthdr = &sfdp.header[i];
+               if (jedechdr && facthdr)
+                       break;
+       }
+       if (!jedechdr || !facthdr || jedechdr->major_rev == 0)
+               return 0;
+
+       /* read JEDEC parameters */
+       ptr = jedechdr->pointer[0] + (jedechdr->pointer[1] << 8) +
+                       (jedechdr->pointer[2] << 16);
+       len = (jedechdr->length * 4/*DWORDs*/ < sizeof(jedec)) ?
+               jedechdr->length * 4 : sizeof(jedec);
+       if (sfdp_read(nor, ptr, len, &jedec))
+               return 0;
+
+       mtd->writesize = (jedec.f_param & 0x04) ? 64 : 1;
+       mtd->size = jedec.flash_density + 1;
+
+       /* max number of sector type = 4 */
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+       for (sect = 0; sect < 4; sect++)
+               if (jedec.e_type[sect].size_power == 12)
+                       break;
+       if (sect == 4)
+#endif
+               /* use largest sector as erase unit */
+               for (sect = 0, i = 1; i < 4; i++) {
+                       if (jedec.e_type[i].size_power >
+                                       jedec.e_type[sect].size_power)
+                               sect = i;
+               }
+
+       nor->erase_opcode = jedec.e_type[sect].opcode;
+       mtd->erasesize = (1 << jedec.e_type[sect].size_power);
+       if (nor->erase_opcode == 0xff)
+               mtd->flags |= MTD_NO_ERASE;
+
+       if (mode == SPI_NOR_QUAD && jedec.fr_support & 0x20) {
+               if (set_quad_mode(nor, &info))
+                       return 0;
+               nor->flash_read = SPI_NOR_QUAD;
+       } else if (mode == SPI_NOR_DUAL && jedec.fr_support & 0x01) {
+               nor->flash_read = SPI_NOR_DUAL;
+       }
+
+       nor->addr_width = ((jedec.fr_support >> 1) & 0x03) ? 4 : 3;
+
+       /*
+        * Most of JEDEC 1.6 parameters are no use to mtd.
+        * They are reserved in the struct 'spi_nor_sfdp_params'
+        * for future usage.
+        */
+
+       if (jedechdr->major_rev > 1 || jedechdr->minor_rev >= 6) {
+               nor->page_size = 1 << ((jedec.p_param >> 4) & 0x0F);
+               mtd->writebufsize = nor->page_size;
+       }
+
+       dev_info(nor->dev, "sfdp %u.%u compliant, jedec rev %u.%u\n",
+                               sfdp.major_rev, sfdp.minor_rev,
+                               jedechdr->major_rev, jedechdr->minor_rev);
+
+       /*
+        * Get the manufacture defined parameters to mtd:
+        * protection area, lock, unlock and islock function.
+        */
+
+       switch (facthdr->id) {
+       case CFI_MFR_MACRONIX:
+               macronix_detect_sfdp(nor, facthdr);
+               break;
+
+       case CFI_MFR_ST:
+               /* nor protection support for STmicro chips */
+               mtd->_lock = spi_nor_lock;
+               mtd->_unlock = spi_nor_unlock;
+               break;
+       }
+
+       return 1;
+}
+
 static int spi_nor_check(struct spi_nor *nor)
 {
        if (!nor->dev || !nor->read || !nor->write ||
@@ -1000,7 +1621,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, 
enum read_mode mode)
 
        if (JEDEC_MFR(info) == CFI_MFR_ATMEL ||
            JEDEC_MFR(info) == CFI_MFR_INTEL ||
-           JEDEC_MFR(info) == CFI_MFR_SST) {
+           JEDEC_MFR(info) == CFI_MFR_SST ||
+           JEDEC_MFR(info) == CFI_MFR_MACRONIX) {
                write_enable(nor);
                write_sr(nor, 0);
        }
@@ -1008,17 +1630,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, 
enum read_mode mode)
        if (!mtd->name)
                mtd->name = dev_name(dev);
        mtd->type = MTD_NORFLASH;
-       mtd->writesize = 1;
        mtd->flags = MTD_CAP_NORFLASH;
-       mtd->size = info->sector_size * info->n_sectors;
        mtd->_erase = spi_nor_erase;
        mtd->_read = spi_nor_read;
-
-       /* nor protection support for STmicro chips */
-       if (JEDEC_MFR(info) == CFI_MFR_ST) {
-               mtd->_lock = spi_nor_lock;
-               mtd->_unlock = spi_nor_unlock;
-       }
+       mtd->_suspend = spi_nor_suspend;
+       mtd->_resume = spi_nor_resume;
 
        /* sst nor chips use AAI word program */
        if (info->flags & SST_WRITE)
@@ -1029,21 +1645,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, 
enum read_mode mode)
        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 */
-       if (info->flags & SECT_4K) {
-               nor->erase_opcode = SPINOR_OP_BE_4K;
-               mtd->erasesize = 4096;
-       } else if (info->flags & SECT_4K_PMC) {
-               nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
-               mtd->erasesize = 4096;
-       } else
-#endif
-       {
-               nor->erase_opcode = SPINOR_OP_SE;
-               mtd->erasesize = info->sector_size;
-       }
-
        if (info->flags & SPI_NOR_NO_ERASE)
                mtd->flags |= MTD_NO_ERASE;
 
@@ -1066,6 +1667,45 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, 
enum read_mode mode)
        if (info->flags & SPI_NOR_NO_FR)
                nor->flash_read = SPI_NOR_NORMAL;
 
+       /*
+        * We prepare to use SFDP to replace flash info struct, but
+        * there are many flags not covered by SFDP such as SST_WRITE,
+        * USE_FSR, SPI_NOR_NO_FR and SECT_4K_PMC. This part still need
+        * to be modified by each flash manufacturer. Finally, we hope
+        * we can setup spi-nor all from SFDP.
+        */
+
+#ifndef CONFIG_MTD_SPI_NOR_SFDP
+       if (0)
+#endif
+               /* If the chip is SFDP compliant, update the mtd info. */
+               if (spi_nor_detect_sfdp(nor, mode))
+                       goto scan_done;
+
+       mtd->writesize = 1;
+       mtd->size = info->sector_size * info->n_sectors;
+
+       /* nor protection support for STmicro chips */
+       if (JEDEC_MFR(info) == CFI_MFR_ST) {
+               mtd->_lock = spi_nor_lock;
+               mtd->_unlock = spi_nor_unlock;
+       }
+
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+       /* prefer "small sector" erase if possible */
+       if (info->flags & SECT_4K) {
+               nor->erase_opcode = SPINOR_OP_BE_4K;
+               mtd->erasesize = 4096;
+       } else if (info->flags & SECT_4K_PMC) {
+               nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
+               mtd->erasesize = 4096;
+       } else
+#endif
+       {
+               nor->erase_opcode = SPINOR_OP_SE;
+               mtd->erasesize = info->sector_size;
+       }
+
        /* 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);
@@ -1078,6 +1718,16 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, 
enum read_mode mode)
                nor->flash_read = SPI_NOR_DUAL;
        }
 
+       if (info->addr_width)
+               nor->addr_width = info->addr_width;
+       else if (mtd->size > 0x1000000) {
+               /* enable 4-byte addressing if the device exceeds 16MiB */
+               nor->addr_width = 4;
+       } else {
+               nor->addr_width = 3;
+       }
+scan_done:
+
        /* Default commands */
        switch (nor->flash_read) {
        case SPI_NOR_QUAD:
@@ -1099,11 +1749,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, 
enum read_mode mode)
 
        nor->program_opcode = SPINOR_OP_PP;
 
-       if (info->addr_width)
-               nor->addr_width = info->addr_width;
-       else if (mtd->size > 0x1000000) {
-               /* enable 4-byte addressing if the device exceeds 16MiB */
-               nor->addr_width = 4;
+       if (info->addr_width == 4) {
                if (JEDEC_MFR(info) == CFI_MFR_AMD) {
                        /* Dedicated 4-byte command set */
                        switch (nor->flash_read) {
@@ -1126,8 +1772,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, 
enum read_mode mode)
                        mtd->erasesize = info->sector_size;
                } else
                        set_4byte(nor, info, 1);
-       } else {
-               nor->addr_width = 3;
        }
 
        nor->read_dummy = spi_nor_read_dummy_cycles(nor);
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 63aeccf..8f8f892 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -35,6 +35,11 @@
 #define SPINOR_OP_RDID         0x9f    /* Read JEDEC ID */
 #define SPINOR_OP_RDCR         0x35    /* Read configuration register */
 #define SPINOR_OP_RDFSR                0x70    /* Read flag status register */
+#define SPINOR_OP_WRSCUR       0x2f    /* Write security register */
+#define SPINOR_OP_RDSCUR       0x2b    /* Read security register */
+#define SPINOR_OP_SUSPEND      0xb0    /* Erase/program suspend */
+#define SPINOR_OP_RESUME       0x30    /* Erase/program resume */
+#define SPINOR_OP_RDSFDP       0x5a    /* Read SFDP */
 
 /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
 #define SPINOR_OP_READ4                0x13    /* Read data bytes (low 
frequency) */
@@ -53,6 +58,23 @@
 #define SPINOR_OP_EN4B         0xb7    /* Enter 4-byte mode */
 #define SPINOR_OP_EX4B         0xe9    /* Exit 4-byte mode */
 
+/* Used for Macronix flashes. */
+#define SPINOR_OP_DP           0xb9    /* Deep power-down */
+#define SPINOR_OP_RDP          0xab    /* Release from deep power-down */
+#define SPINOR_OP_ENSO         0xb1    /* Enter secured OTP */
+#define SPINOR_OP_EXSO         0xc1    /* Exit secured OTP */
+#define SPINOR_OP_WPSEL                0x68    /* Write protect selection */
+#define SPINOR_OP_SBLK         0x36    /* Single block lock */
+#define SPINOR_OP_SBULK                0x39    /* Single block unlock */
+#define SPINOR_OP_GBLK         0x7e    /* Gang block lock */
+#define SPINOR_OP_GBULK                0x98    /* Gang block unlock */
+#define SPINOR_OP_RDBLOCK      0x3c    /* Read block lock status */
+#define SPINOR_OP_WRSPB                0xe3    /* Write solid protection bit */
+#define SPINOR_OP_RDSPB                0xe2    /* Read solid protection bit */
+#define SPINOR_OP_ESSPB                0xe4    /* Rease solid protection bit */
+#define SPINOR_OP_WRDPB                0xe1    /* Write dynamic protection bit 
*/
+#define SPINOR_OP_RDDPB                0xe0    /* Write dynamic protection bit 
*/
+
 /* Used for Spansion flashes only. */
 #define SPINOR_OP_BRWR         0x17    /* Bank register write */
 
@@ -80,6 +102,185 @@ enum read_mode {
        SPI_NOR_QUAD,
 };
 
+enum macronix_lock_type {
+       MX_SBLK = 0,
+       MX_SPB,
+       MX_DPB,
+};
+
+/**
+ * struct sfdp_vendor_macronix - Macronix SFDP parameter table
+ * @vcc_supply_max_voltage:    Maximun voltage of chip
+ * @vcc_supply_min_voltage:    Minimun voltage of chip
+ * @f_param:                   Macronix flash parameters:
+ *                              Bit 0: support of H/W Reset# pin
+ *                              Bit 1: support of H/W Hold# pin
+ *                              Bit 2: support of deep power down mode
+ *                              Bit 3: support of S/W Reset
+ *                              Bit 4~11: software reset instruction
+ *                              Bit 12: support of program suspend/resume
+ *                              Bit 13: support of erase suspend/resume
+ *                              Bit 15: support of wrap-around read
+ * @wrap_around_read_opcode:   Wrap around read instrction
+ * @wrap_around_read_length:   Wrap around read length
+ * @protection_param:          Protection related parameters:
+ *                              Bit 0: support of individual block lock
+ *                              Bit 1: individual lock bits
+ *                              Bit 2~9: individual block lock opcode
+ *                              Bit 10: default status of individual lock
+ *                              Bit 11: support of security OTP
+ *                              Bit 12: support of read lock
+ *                              Bit 13: support of permanent lock
+ */
+struct sfdp_macronix_params {
+       u16 vcc_supply_max_voltage;
+       u16 vcc_supply_min_voltage;
+       u16 f_param;
+       u8 wrap_around_read_opcode;
+       u8 wrap_around_read_length;
+       u16 protection_param;
+       u8 reserved[6];
+};
+
+/**
+ * struct sfdp_erase_type - SFDP erase type parameter
+ * @size_power:                Erase size (2 to the n-th power)
+ * @opcode:            Erase instruction
+ */
+struct sfdp_erase_type {
+       u8 size_power;
+       u8 opcode;
+};
+
+/**
+ * struct sfdp_jedec_params - Basic JEDEC parameters of SFDP
+ * @f_param:           JEDEC flash parameters:
+ *                      Bit 0~1: Block/sector erase size
+ *                      Bit 2: write granularity (0 - 1B, 1 - 64B)
+ *                      Bit 3~4: command of write enable
+ * @be_4k_opcode:      4KByte block/sector erase instruction
+ * @fr_support:                Fast read supports:
+ *                      Bit 0: support of fast read 1-1-2
+ *                      Bit 1~2: address width
+ *                      Bit 3: support of DTR
+ *                      Bit 4~6: support of fast read 1-2-2, 1-4-4, 1-1-4
+ * @flash_density:     Flash memory density
+ * @fr_XXX_param:      (X-X-X) Fast read parameters:
+ *                      Bit 0~4: number of dummy clocks
+ *                      Bit 5~7: number of mode clocks
+ * @fr_XXX_opcode:     (X-X-X) Fast read instruction
+ * @fr_222_444_support:        Fast read (2-2-2/4-4-4) supports:
+ *                      Bit 0: support of fast read 2-2-2
+ *                      Bit 4: support of fast read 4-4-4
+ * @erase_type:                Erase type (size & instruction)
+ * @e_param:           Erase parameters (timing):
+ *                      Bit 0~3: multiplier from typical to max erase time
+ *                      Bit 4~32: typical time of each type of erase
+ * @p_param:           Program parameters:
+ *                      Bit 0~3: multiplier from typical to max program time
+ *                      Bit 4~7: page size (2 to the n-th power)
+ *                      Bit 8~30: typical time of PP, BP and chip erase
+ * @susp_param:                Suspend/Resume parameters:
+ *                      Bit 0~7: prohibited operations during suspend
+ *                      Bit 9~30: timing defininition
+ *                      Bit 31: support of suspend/resume
+ * @p_resu_opcode:     Program resume instruction
+ * @p_susp_opcode:     Program suspend instruction
+ * @resu_opcode:       Write/Erase resume instruction
+ * @susp_opcode:       Write/Erase suspend instruction
+ * @dp_param:          Deep powerdown parameters:
+ *                      Bit 2~14: delay and SR polliong
+ *                      Bit 15~22: exit deep powerdown instruction
+ *                      Bit 23~30: enter deep powerdown instruction
+ *                      Bit 31: support of deep powerdown
+ * @fr_quad_param:     (X-4-4) Fast read parameters:
+ *                      Bit 0~8: 4-4-4 mode disable/enable sequences
+ *                      Bit 9: support of fast read 0-4-4
+ *                      Bit 10~19: 0-4-4 mode exit/entry method
+ *                      Bit 20~22: quad enable requirements (QER)
+ *                      Bit 23: HOLD or RESET disable
+ * @en4b_param:                Method of enter 4-byte address
+ * @ex4b_softreset_param: method of exit 4-byte address and software reset
+ * @sr_param:          Volatile/Non-volatile status register 1 parameters
+ */
+struct sfdp_jedec_params {
+       u8 f_param;
+       u8 be_4k_opcode;
+       u8 fr_support;
+       u8 reserved1;
+       u32 flash_density;
+
+       u8 fr_144_param;
+       u8 fr_144_opcode;
+       u8 fr_114_param;
+       u8 fr_114_opcode;
+       u8 fr_112_param;
+       u8 fr_112_opcode;
+       u8 fr_122_param;
+       u8 fr_122_opcode;
+
+       u8 fr_222_444_support;
+       u8 reserved2[5];
+       u8 fr_222_param;
+       u8 fr_222_opcode;
+       u8 reserved3[2];
+       u8 fr_444_param;
+       u8 fr_444_opcode;
+
+       struct sfdp_erase_type e_type[4];
+
+       u32 e_param;
+       u32 p_param;
+       u32 susp_param;
+       u8 p_resu_opcode;
+       u8 p_susp_opcode;
+       u8 resu_opcode;
+       u8 susp_opcode;
+       u32 dp_param;
+       u32 fr_quad_param;
+       u8 en4b_param;
+       u16 ex4b_softreset_param;
+       u8 sr_param;
+};
+
+/**
+ * struct sfdp_param_header - SFDP parameter header
+ * @id:                        Parameter ID LSB (manufacture ID)
+ * @minor_rev:         Parameter table minor revision number
+ * @major_rev:         Parameter table major revision number
+ * @length:            Parameter table length (DWORDs)
+ * @pointer:           Parameter table pointer (start address)
+ * @id_reserved:       Parameter ID MSB
+ */
+struct sfdp_param_header {
+       u8 id;
+       u8 minor_rev;
+       u8 major_rev;
+       u8 length;
+       u8 pointer[3];
+       u8 id_reserved;
+};
+
+/* SFDP signature (ASCII: 'S' 'F' 'D' 'P') */
+#define SFDP_SIGNATURE         0x50444653
+
+/**
+ * struct spi_nor_sfdp_params - Serial Flash Discoverable Parameters
+ * @signature:         SFDP signature for identification
+ * @minor_rev:         SFDP minor revision number
+ * @major_rev:         SFDP major revision number
+ * @num_of_param_header: Number of parameter headers (0 indicates 1 header)
+ * @header:            Header of JEDEC or vendor's parameters
+ */
+struct spi_nor_sfdp_params {
+       u32 signature;
+       u8 minor_rev;
+       u8 major_rev;
+       u8 num_of_param_header;
+       u8 reserved0;
+       struct sfdp_param_header header[5];
+};
+
 /**
  * struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
  * @wren:              command for "Write Enable", or 0x00 for not required
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to