Merge remote-tracking branch 'iwlwifi-fixes/master' into NEXT
[sfrench/cifs-2.6.git] / drivers / mtd / spi-nor / spi-nor.c
index c713c865671026a220332263e3086eb7c10674a4..b5ad6bebf5e79b014134af83d62e2fb6a5c6f917 100644 (file)
@@ -47,6 +47,25 @@ static int read_sr(struct spi_nor *nor)
        return val;
 }
 
+/*
+ * 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 spi_nor *nor)
+{
+       int ret;
+       u8 val;
+
+       ret = nor->read_reg(nor, SPINOR_OP_RDFSR, &val, 1);
+       if (ret < 0) {
+               pr_err("error %d reading FSR\n", ret);
+               return ret;
+       }
+
+       return val;
+}
+
 /*
  * Read configuration register, returning its value in the
  * location. Return the configuration register value.
@@ -165,6 +184,32 @@ static int spi_nor_wait_till_ready(struct spi_nor *nor)
        return -ETIMEDOUT;
 }
 
+static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor)
+{
+       unsigned long deadline;
+       int sr;
+       int fsr;
+
+       deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+
+       do {
+               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));
+
+       return -ETIMEDOUT;
+}
+
 /*
  * Service routine to read status register until ready, or timeout occurs.
  * Returns non-zero if error.
@@ -402,6 +447,7 @@ struct flash_info {
 #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 INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)     \
@@ -449,6 +495,7 @@ const struct spi_device_id spi_nor_ids[] = {
        { "en25q32b",   INFO(0x1c3016, 0, 64 * 1024,   64, 0) },
        { "en25p64",    INFO(0x1c2017, 0, 64 * 1024,  128, 0) },
        { "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128, SECT_4K) },
+       { "en25qh128",  INFO(0x1c7018, 0, 64 * 1024,  256, 0) },
        { "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512, 0) },
 
        /* ESMT */
@@ -488,6 +535,8 @@ const struct spi_device_id spi_nor_ids[] = {
        { "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) },
+       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) },
+       { "n25q00",      INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },
 
        /* PMC */
        { "pm25lv512",   INFO(0,        0, 32 * 1024,    2, SECT_4K_PMC) },
@@ -965,6 +1014,10 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
        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;
+
        /* prefer "small sector" erase if possible */
        if (info->flags & SECT_4K) {
                nor->erase_opcode = SPINOR_OP_BE_4K;