RE: [PATCH] Add support for flag status register on Micron chips.

From: Insop Song
Date: Tue Apr 08 2014 - 12:52:53 EST



On Tuesday, April 08, 2014 9:13 AM, Graham Moore <grmoore@xxxxxxxxxx>
>
> From: Graham Moore <grmoore@xxxxxxxxxx>
>
> 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 n25q512a1 and
> n25q00 Micron QSPI flash chips.
>

Reviewed his change, and it is more generic then my previous patch.

Reviewed-by: Insop Song <insop.song@xxxxxxxxxxxxx>

> Signed-off-by: Graham Moore <grmoore@xxxxxxxxxx>
> ---
> drivers/mtd/devices/m25p80.c | 94
> +++++++++++++++++++++++++++++++++++-------
> 1 file changed, 80 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index ad19139..38306aa 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -39,6 +39,7 @@
> #define OPCODE_WREN 0x06 /* Write enable */
> #define OPCODE_RDSR 0x05 /* Read status register */
> #define OPCODE_WRSR 0x01 /* Write status
> register 1 byte */
> +#define OPCODE_RDFSR 0x70 /* read flag status
> register */
> #define OPCODE_NORM_READ 0x03 /* Read data bytes (low
> frequency) */
> #define OPCODE_FAST_READ 0x0b /* Read data bytes (high
> frequency) */
> #define OPCODE_QUAD_READ 0x6b /* Read data bytes */
> @@ -81,6 +82,9 @@
>
> #define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */
>
> +/* Flag Status Register bits */
> +#define FSR_READY 0x80 /* FSR ready */
> +
> /* Configuration Register bits. */
> #define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */
>
> @@ -108,6 +112,7 @@ struct m25p {
> u8 read_opcode;
> u8 program_opcode;
> u8 *command;
> + int (*wait_till_ready)(struct m25p *flash);
> enum read_type flash_read;
> };
>
> @@ -145,6 +150,27 @@ 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) {
> + dev_err(&flash->spi->dev, "error %d reading FSR\n",
> + (int) retval);
> + return retval;
> + }
> +
> + return val;
> +}
> +/*
> * Read configuration register, returning its value in the
> * location. Return the configuration register value.
> * Returns negative if error occured.
> @@ -233,7 +259,7 @@ static inline int set_4byte(struct m25p *flash, u32
> jedec_id, int enable)
> * Service routine to read status register until ready, or timeout occurs.
> * Returns non-zero if error.
> */
> -static int wait_till_ready(struct m25p *flash)
> +static int _wait_till_ready(struct m25p *flash)
> {
> unsigned long deadline;
> int sr;
> @@ -254,6 +280,37 @@ 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 fsr;
> + int sr;
> +
> + deadline = jiffies + MAX_READY_WAIT_JIFFIES;
> +
> + do {
> + sr = read_sr(flash);
> + if (sr < 0)
> + break;
> + /* only check fsr if sr not busy */
> + if (!(sr & SR_WIP)) {
> + fsr = read_fsr(flash);
> + if (fsr < 0)
> + break;
> + if (fsr & FSR_READY)
> + return 0;
> + }
> +
> + cond_resched();
> +
> + } while (!time_after_eq(jiffies, deadline));
> +
> + return 1;
> +}
> +/*
> * Write status Register and configuration register with 2 bytes
> * The first byte will be written to the status register, while the
> * second byte will be written to the configuration register.
> @@ -280,7 +337,7 @@ static int macronix_quad_enable(struct m25p *flash)
>
> spi_write(flash->spi, &cmd, 2);
>
> - if (wait_till_ready(flash))
> + if (flash->wait_till_ready(flash))
> return 1;
>
> ret = read_sr(flash);
> @@ -351,7 +408,7 @@ static int erase_chip(struct m25p *flash)
> (long long)(flash->mtd.size >> 10));
>
> /* Wait until finished previous write command. */
> - if (wait_till_ready(flash))
> + if (flash->wait_till_ready(flash))
> return 1;
>
> /* Send write enable, then erase commands. */ @@ -391,7 +448,7
> @@ static int erase_sector(struct m25p *flash, u32 offset)
> __func__, flash->mtd.erasesize / 1024, offset);
>
> /* Wait until finished previous write command. */
> - if (wait_till_ready(flash))
> + if (flash->wait_till_ready(flash))
> return 1;
>
> /* Send write enable, then erase commands. */ @@ -536,7 +593,7
> @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
> mutex_lock(&flash->lock);
>
> /* Wait till previous write/erase is done. */
> - if (wait_till_ready(flash)) {
> + if (flash->wait_till_ready(flash)) {
> /* REVISIT status return?? */
> mutex_unlock(&flash->lock);
> return 1;
> @@ -585,7 +642,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t
> to, size_t len,
> mutex_lock(&flash->lock);
>
> /* Wait until finished previous write command. */
> - if (wait_till_ready(flash)) {
> + if (flash->wait_till_ready(flash)) {
> mutex_unlock(&flash->lock);
> return 1;
> }
> @@ -628,7 +685,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t
> to, size_t len,
> t[1].tx_buf = buf + i;
> t[1].len = page_size;
>
> - wait_till_ready(flash);
> + flash->wait_till_ready(flash);
>
> write_enable(flash);
>
> @@ -668,7 +725,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to,
> size_t len,
> mutex_lock(&flash->lock);
>
> /* Wait until finished previous write command. */
> - ret = wait_till_ready(flash);
> + ret = flash->wait_till_ready(flash);
> if (ret)
> goto time_out;
>
> @@ -683,7 +740,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to,
> size_t len,
> /* write one byte. */
> t[1].len = 1;
> spi_sync(flash->spi, &m);
> - ret = wait_till_ready(flash);
> + ret = flash->wait_till_ready(flash);
> if (ret)
> goto time_out;
> *retlen += m.actual_length - m25p_cmdsz(flash); @@ -702,7
> +759,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
> t[1].tx_buf = buf + actual;
>
> spi_sync(flash->spi, &m);
> - ret = wait_till_ready(flash);
> + ret = flash->wait_till_ready(flash);
> if (ret)
> goto time_out;
> *retlen += m.actual_length - cmd_sz;
> @@ -710,7 +767,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to,
> size_t len,
> to += 2;
> }
> write_disable(flash);
> - ret = wait_till_ready(flash);
> + ret = flash->wait_till_ready(flash);
> if (ret)
> goto time_out;
>
> @@ -724,7 +781,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to,
> size_t len,
> t[1].tx_buf = buf + actual;
>
> spi_sync(flash->spi, &m);
> - ret = wait_till_ready(flash);
> + ret = flash->wait_till_ready(flash);
> if (ret)
> goto time_out;
> *retlen += m.actual_length - m25p_cmdsz(flash); @@ -745,7
> +802,7 @@ static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t
> len)
>
> mutex_lock(&flash->lock);
> /* Wait until finished previous command */
> - if (wait_till_ready(flash)) {
> + if (flash->wait_till_ready(flash)) {
> res = 1;
> goto err;
> }
> @@ -790,7 +847,7 @@ static int m25p80_unlock(struct mtd_info *mtd, loff_t
> ofs, uint64_t len)
>
> mutex_lock(&flash->lock);
> /* Wait until finished previous command */
> - if (wait_till_ready(flash)) {
> + if (flash->wait_till_ready(flash)) {
> res = 1;
> goto err;
> }
> @@ -856,6 +913,7 @@ struct flash_info {
> #define M25P_NO_FR 0x08 /* Can't do fastread */
> #define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC
> works uniformly */
> #define M25P80_QUAD_READ 0x20 /* Flash supports Quad Read
> */
> +#define USE_FSR 0x40 /* use flag status
> register */
> };
>
> #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
> @@ -941,6 +999,8 @@ static const struct spi_device_id m25p_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) },
> + { "n25q512a1", 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) },
> @@ -1206,6 +1266,12 @@ static int m25p_probe(struct spi_device *spi)
> if (info->flags & M25P_NO_ERASE)
> flash->mtd.flags |= MTD_NO_ERASE;
>
> + if (info->flags & USE_FSR)
> + flash->wait_till_ready = &_wait_till_fsr_ready;
> + else
> + flash->wait_till_ready = &_wait_till_ready;
> +
> +
> ppdata.of_node = spi->dev.of_node;
> flash->mtd.dev.parent = &spi->dev;
> flash->page_size = info->page_size;
> --
> 1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/