summaryrefslogtreecommitdiff
path: root/drivers/mtd/spi-nor
diff options
context:
space:
mode:
authorgrmoore@altera.com <grmoore@altera.com>2014-04-29 10:29:51 -0500
committerBrian Norris <computersforpeace@gmail.com>2014-07-11 19:10:35 -0700
commitc14deddec1fbd8c9757c53a49dbfd2dc83265f21 (patch)
tree32b8abbae0d7c3ebfdc205b7bb3a8ee45da37e87 /drivers/mtd/spi-nor
parentfdf43a420f5c2b737b0e65c4c3330867903b098a (diff)
mtd: spi-nor: add support for flag status register on Micron chips
Some new Micron flash chips require reading the flag status register to determine when operations have completed. Furthermore, chips with multi-die stacks of the 65nm 256Mb QSPI also require reading the status register before reading the flag status register. This patch adds support for the flag status register in the n25q512ax3 and n25q00 Micron QSPI flash chips. Signed-off-by: Graham Moore <grmoore@altera.com> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Diffstat (limited to 'drivers/mtd/spi-nor')
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c52
1 files changed, 52 insertions, 0 deletions
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index c713c8656710..7da3a7067c35 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -48,6 +48,25 @@ static int read_sr(struct spi_nor *nor)
}
/*
+ * 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.
* Returns negative if error occured.
@@ -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) \
@@ -488,6 +534,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 +1013,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;