#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.
case SPI_NOR_FAST:
case SPI_NOR_DUAL:
case SPI_NOR_QUAD:
- return 1;
+ return 8;
case SPI_NOR_NORMAL:
return 0;
}
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
*
*/
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);
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.
addr += mtd->erasesize;
len -= mtd->erasesize;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ goto erase_err;
}
}
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))
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))
* 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) },
{ "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) },
{ "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) },
{ "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)
{
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)
{
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;
/* 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;
}
/* 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;
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;
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);
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);
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)
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);
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;
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) {
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;
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;
} 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;
}
}
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;
}
return NULL;
}
-EXPORT_SYMBOL_GPL(spi_nor_match_id);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huang Shijie <shijie8@gmail.com>");