How to check flag status register and How to apply Sector Erase Command(0x20)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to check flag status register and How to apply Sector Erase Command(0x20)

5,272 Views
erickang
Contributor II

Hi all,

I want to know how to add flag status check during SPI Flash write/erase for Micron 512Mb Serial Flash.
We have to change the QSPI-NOR density from 256Mb to 512Mb, because our RootFS is larger than QSPI-NOR density(256Mb).

Our development environment is like as below,
- AP/Board : i.MX6SX-SabreSDB
- OS : Linux 3.10.53
- Device : Micron 512Mb QSPI-NOR N25Q512A

Firstly I confirmed the 512Mb QSPI-NOR probing.
For this I modified the below files,

drivers/mtd/spi-nor/spi-nor.c
const struct spi_device_id spi_nor_ids[] = {
...
        { "n25q512a",    INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K) },
...
};

arch/arm/boot/dts/imx6sx-sdb.dts
&qspi2 {
...
        flash0: n25q512a@0 {
                #address-cells = <1>;
                #size-cells = <1>;
                compatible = "micron,n25q512a";
                spi-max-frequency = <29000000>;
                reg = <0>;
        };
...
};

I couldn't confirm the flash erase operation.
And I couldn't find the flag status check routine and the SECT_4K control routine in Freescale BSP.

In the Web-Site, I could find the relative code like as below,
- Check flag status register for Micron n25q512a
  . http://lists.infradead.org/pipermail/linux-mtd/2014-January/051382.html?
- mtd: st_spi_fsm: Add support for Micron N25Q512A (for STMicro)
  . http://www.gossamer-threads.com/lists/linux/kernel/2090517

But I couldn't find the relative code for i.MX6SX.
Could you give any guidance to apply Micron N25Q512A?

Best Regards,
Eric.

Labels (4)
0 Kudos
3 Replies

2,597 Views
erickang
Contributor II

Dear All,

I applied the patch I mentioned in the upper statement.

- Check flag status register for Micron n25q512a

  . http://lists.infradead.org/pipermail/linux-mtd/2014-January/051382.html?

For the patched files, I'll mention the end of this statement.

But I couldn't find Sector Erase code for SECT_4K,

drivers/mtd/spi-nor/spi-nor.c

const struct spi_device_id spi_nor_ids[] = {

...

        { "n25q512a",    INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K) },

...

};

So I modified the Sector Erase Size, from 4KB to 64KB. (SECT_4K -> SPI_NOR_QUAD_READ)

But the Flash seems not to be erased.

Could you tell me how to erase flash by using 4KB sector unit?

Best Regards,

Eric.

==================== Patch Files

diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 7eda71d..e53e522 100644

--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c

#01 : add Read.Flag.Status.Register opcode, 0x70
   - include/linux/mtd/spi-nor.h
@@ -38,6 +38,7 @@
/* Flash opcodes. */
#define OPCODE_WREN  0x06 /* Write enable */
#define OPCODE_RDSR  0x05 /* Read status register */
+#define OPCODE_RDFSR  0x70 /* Read flag status register */
#define OPCODE_WRSR  0x01 /* Write status register 1 byte */
#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */

#02 : FSR Register Bit.Field, as Ready : bit.7
   - include/linux/mtd/spi-nor.h
@@ -70,6 +71,8 @@
/* Status Register bits. */
#define SR_WIP   1 /* Write in progress */
#define SR_WEL   2 /* Write enable latch */
+/* Flag Status Register bits. */
+#define FSR_RDY   0x80 /* FSR ready */
/* meaning of other SR_* bits may differ between vendors */
#define SR_BP0   4 /* Block protect 0 */
#define SR_BP1   8 /* Block protect 1 */

#03 : add flag_status valiable
   - drivers/mtd/devices/m25p80.c
@@ -95,6 +98,7 @@  struct m25p {
  u8   program_opcode;
  u8   *command;
  bool   fast_read;
+ bool   flag_status;
};

static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)

#04 : add FSR read function
   - drivers/mtd/devices/m25p80.c
@@ -131,6 +135,28 @@  static int read_sr(struct m25p *flash)
}

/*
+ * Read the flag status register, returning its value in the location
+ * Return the status register value.
+ * Returns negative if error occurred.
+ */
+static int read_fsr(struct m25p *flash)
+{
+ ssize_t retval;
+ u8 code = OPCODE_RDFSR;
+ u8 val;
+
+ retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);
+
+ if (retval < 0) {
+  printk(&flash->spi->dev, "error %d reading FSR\n",
+    (int) retval);
+  return retval;
+ }
+
+ return val;
+}
+
+/*
  * Write status register 1 byte
  * Returns negative if error occurred.
  */

#05 : add wait_till_fsr_ready function
   - drivers/mtd/devices/m25p80.c
@@ -220,6 +246,30 @@  static int wait_till_ready(struct m25p *flash)
}

/*
+ * Service routine to read flag status register until ready, or timeout occurs.
+ * Returns non-zero if error.
+ */
+static int wait_till_fsr_ready(struct m25p *flash)
+{
+ unsigned long deadline;
+ int sr;
+
+ deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+
+ do {
+  if ((sr = read_fsr(flash)) < 0)
+   break;
+  else if ((sr & FSR_RDY))
+   return 0;
+
+  cond_resched();
+
+ } while (!time_after_eq(jiffies, deadline));
+
+ return 1;
+}
+
+/*
  * Erase the whole flash memory
  *
  * Returns 0 if successful, non-zero otherwise.

#06 : wait until previous erase command operation complete
   - drivers/mtd/devices/m25p80.c
@@ -273,6 +323,14 @@  static int erase_sector(struct m25p *flash, u32 offset)
  if (wait_till_ready(flash))
   return 1;

+ /* Flag status, Wait until finished previous write command. */
+ if (flash->flag_status == true) {
+  if (wait_till_fsr_ready(flash)) {
+   mutex_unlock(&flash->lock);
+   return 1;
+  }
+ }
+
  /* Send write enable, then erase commands. */
  write_enable(flash);

#07 : wait until previous write command operation complete
   - drivers/mtd/devices/m25p80.c
@@ -427,6 +485,14 @@  static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,

  mutex_lock(&flash->lock);

+ /* Flag status, Wait until finished previous write command. */
+ if (flash->flag_status == true) {
+  if (wait_till_fsr_ready(flash)) {
+   mutex_unlock(&flash->lock);
+   return 1;
+  }
+ }
+
  /* Wait until finished previous write command. */
  if (wait_till_ready(flash)) {
   mutex_unlock(&flash->lock);

#08 : wait until previous write command operation complete : after sync
   - drivers/mtd/devices/m25p80.c
@@ -457,6 +523,14 @@  static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
   t[1].len = page_size;
   spi_sync(flash->spi, &m);

+  /* Flag status, Wait until finished previous write command. */
+  if (flash->flag_status == true) {
+   if (wait_till_fsr_ready(flash)) {
+    mutex_unlock(&flash->lock);
+    return 1;
+   }
+  }
+
   *retlen = m.actual_length - m25p_cmdsz(flash);

   /* write everything in flash->page_size chunks */

#09 : wait until previous write command operation complete : after sync
   - drivers/mtd/devices/m25p80.c
@@ -477,6 +551,14 @@  static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,

    spi_sync(flash->spi, &m);

+   /* Flag status, Wait until finished previous write command. */
+   if (flash->flag_status == true) {
+    if (wait_till_fsr_ready(flash)) {
+     mutex_unlock(&flash->lock);
+     return 1;
+    }
+   }
+
    *retlen += m.actual_length - m25p_cmdsz(flash);
   }
  }

#10 : change Manufacture.ID of Micron.512Mb.Flash
   - drivers/mtd/spi-nor/spi-nor.c
@@ -782,7 +864,7 @@  static const struct spi_device_id m25p_ids[] = {
  { "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, 0) },
  { "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, 0) },
  { "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SECT_4K) },
- { "n25q512a",    INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) },
+ { "n25q512a",    INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K) },

  /* PMC */
  { "pm25lv512",   INFO(0,        0, 32 * 1024,    2, SECT_4K_PMC) },

#11 : set flag_status if Flash is Micron.512Mb
@@ -996,6 +1078,13 @@  static int m25p_probe(struct spi_device *spi)
  spi_set_drvdata(spi, flash);

  /*
+  * Micron n25q512a requires to check flag status register
+  */
+ flash->flag_status = false;
+ if (strcmp(id->name, "n25q512a") == 0)
+  flash->flag_status = true;;
+
+ /*
   * Atmel, SST and Intel/Numonyx serial flash tend to power
   * up with the software protection bits set
   */

0 Kudos

2,597 Views
alejandrolozan1
NXP Employee
NXP Employee

Hi,

The board I can test with has  a different SPI NOR memory.

If you try with the flash_erase command the amount of erased data is 64K?

How are you testing the memory erase?

I am checking the code in 3.14.28 and it seems that the memory has the 4K sector flag as expected.

Nor even one sector is erased?

Best Regards,

Alejandro

0 Kudos

2,597 Views
erickang
Contributor II

Hi Alejandro,

Thanks for your response.

These days I couldn't confirm your response, because I couldn't access the Freescale Community.

Today I'm confirming your response.

I performed the flash_erase for 64K sector size.

For this, I configured the the "const struct spi_device_id spi_nor_ids" value like as below,

drivers/mtd/spi-nor/spi-nor.c

const struct spi_device_id spi_nor_ids[] = {

...

        { "n25q512a",    INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K) },     // original : 4KB sector erase

        { "n25q512a",    INFO(0x20ba20, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },     // original : 64KB sector erase

...

};

The 64KB erasing operation seems well, but the erasing speed was so fast.

And I could confirm the flash was not erased through other Flash programmer.

I heard you are checking the 4KB erasing code in 3.14.28.

Could you inform me if you have any update?

Best Regards,

Eric.

0 Kudos