mtd: spi-nor: drop replaceable wait-till-ready function pointer
[cascardo/linux.git] / drivers / mtd / spi-nor / spi-nor.c
index b5ad6be..bfb67f1 100644 (file)
@@ -28,6 +28,8 @@
 
 #define JEDEC_MFR(_jedec_id)   ((_jedec_id) >> 16)
 
+static const struct spi_device_id *spi_nor_match_id(const char *name);
+
 /*
  * Read the status register, returning its value in the location
  * Return the status register value.
@@ -96,7 +98,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
        case SPI_NOR_FAST:
        case SPI_NOR_DUAL:
        case SPI_NOR_QUAD:
-               return 1;
+               return 8;
        case SPI_NOR_NORMAL:
                return 0;
        }
@@ -163,62 +165,60 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
                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 ret;
 
        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;
-               }
+               ret = spi_nor_ready(nor);
+               if (ret < 0)
+                       return ret;
+               if (ret)
+                       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.
- */
-static int wait_till_ready(struct spi_nor *nor)
-{
-       return nor->wait_till_ready(nor);
-}
-
 /*
  * Erase the whole flash memory
  *
@@ -226,15 +226,8 @@ static int wait_till_ready(struct spi_nor *nor)
  */
 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);
 
@@ -297,6 +290,10 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
                        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.
@@ -312,6 +309,10 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 
                        addr += mtd->erasesize;
                        len -= mtd->erasesize;
+
+                       ret = spi_nor_wait_till_ready(nor);
+                       if (ret)
+                               goto erase_err;
                }
        }
 
@@ -339,11 +340,6 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        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))
@@ -386,11 +382,6 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        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))
@@ -473,7 +464,7 @@ struct flash_info {
  * more nor chips.  This current list focusses on newer chips, which
  * have been converging on command sets which including JEDEC ID.
  */
-const struct spi_device_id spi_nor_ids[] = {
+static const struct spi_device_id spi_nor_ids[] = {
        /* Atmel -- some are (confusingly) marketed as "DataFlash" */
        { "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) },
        { "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) },
@@ -611,6 +602,7 @@ const struct spi_device_id spi_nor_ids[] = {
        { "m25px32-s0", INFO(0x207316,  0, 64 * 1024, 64, SECT_4K) },
        { "m25px32-s1", INFO(0x206316,  0, 64 * 1024, 64, SECT_4K) },
        { "m25px64",    INFO(0x207117,  0, 64 * 1024, 128, 0) },
+       { "m25px80",    INFO(0x207114,  0, 64 * 1024, 16, 0) },
 
        /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
        { "w25x10", INFO(0xef3011, 0, 64 * 1024,  2,  SECT_4K) },
@@ -623,7 +615,6 @@ const struct spi_device_id spi_nor_ids[] = {
        { "w25q32dw", INFO(0xef6016, 0, 64 * 1024,  64, SECT_4K) },
        { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
        { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
-       { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
        { "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SECT_4K) },
        { "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) },
        { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
@@ -637,7 +628,6 @@ const struct spi_device_id spi_nor_ids[] = {
        { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
        { },
 };
-EXPORT_SYMBOL_GPL(spi_nor_ids);
 
 static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
 {
@@ -671,11 +661,6 @@ static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
        return ERR_PTR(-ENODEV);
 }
 
-static const struct spi_device_id *jedec_probe(struct spi_nor *nor)
-{
-       return nor->read_id(nor);
-}
-
 static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
                        size_t *retlen, u_char *buf)
 {
@@ -707,11 +692,6 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
        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;
@@ -723,7 +703,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
 
                /* 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;
        }
@@ -735,7 +715,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
 
                /* 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;
@@ -744,7 +724,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
        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;
 
@@ -755,7 +735,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
                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);
@@ -783,11 +763,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
        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);
@@ -806,16 +781,20 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
                        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)
@@ -828,7 +807,7 @@ static int macronix_quad_enable(struct spi_nor *nor)
        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);
@@ -908,19 +887,13 @@ static int spi_nor_check(struct spi_nor *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;
 }
 
-int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
-                       enum read_mode mode)
+int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 {
+       const struct spi_device_id      *id = NULL;
        struct flash_info               *info;
-       struct flash_platform_data      *data;
        struct device *dev = nor->dev;
        struct mtd_info *mtd = nor->mtd;
        struct device_node *np = dev->of_node;
@@ -931,34 +904,16 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
        if (ret)
                return ret;
 
-       /* Platform data helps sort out which chip type we have, as
-        * well as how this board partitions it.  If we don't have
-        * a chip ID, try the JEDEC id commands; they'll work for most
-        * newer chips, even if we don't recognize the particular chip.
-        */
-       data = dev_get_platdata(dev);
-       if (data && data->type) {
-               const struct spi_device_id *plat_id;
-
-               for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) {
-                       plat_id = &spi_nor_ids[i];
-                       if (strcmp(data->type, plat_id->name))
-                               continue;
-                       break;
-               }
-
-               if (i < ARRAY_SIZE(spi_nor_ids) - 1)
-                       id = plat_id;
-               else
-                       dev_warn(dev, "unrecognized id %s\n", data->type);
-       }
+       id = spi_nor_match_id(name);
+       if (!id)
+               return -ENOENT;
 
        info = (void *)id->driver_data;
 
        if (info->jedec_id) {
                const struct spi_device_id *jid;
 
-               jid = jedec_probe(nor);
+               jid = spi_nor_read_id(nor);
                if (IS_ERR(jid)) {
                        return PTR_ERR(jid);
                } else if (jid != id) {
@@ -990,11 +945,8 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
                write_sr(nor, 0);
        }
 
-       if (data && data->name)
-               mtd->name = data->name;
-       else
+       if (!mtd->name)
                mtd->name = dev_name(dev);
-
        mtd->type = MTD_NORFLASH;
        mtd->writesize = 1;
        mtd->flags = MTD_CAP_NORFLASH;
@@ -1014,10 +966,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;
+       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;
@@ -1025,7 +977,9 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
        } else if (info->flags & SECT_4K_PMC) {
                nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
                mtd->erasesize = 4096;
-       } else {
+       } else
+#endif
+       {
                nor->erase_opcode = SPINOR_OP_SE;
                mtd->erasesize = info->sector_size;
        }
@@ -1141,7 +1095,7 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
 }
 EXPORT_SYMBOL_GPL(spi_nor_scan);
 
-const struct spi_device_id *spi_nor_match_id(char *name)
+static const struct spi_device_id *spi_nor_match_id(const char *name)
 {
        const struct spi_device_id *id = spi_nor_ids;
 
@@ -1152,7 +1106,6 @@ const struct spi_device_id *spi_nor_match_id(char *name)
        }
        return NULL;
 }
-EXPORT_SYMBOL_GPL(spi_nor_match_id);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>");