+ Marek

Hi Insop, 

On Mon, Jan 06, 2014 at 05:21:17AM +0000, Insop Song wrote:
> In order to use Micron n25q512a, MTD, two changes are required as follows:
> - jedec code should be fixed

I have a feeling there are more than one "n25q512a" device, with
different IDs.

> - flag status should be read for writing.
> 
> Check flag status register for Micron n25q512a
>     - Programing Micron n25q512a requires to check flag status register
>     - According to datasheet
>       "
>       The flag status register must be read any time a PROGRAM, ERASE,
>       SUSPEND/RESUME command is issued, or after a RESET command while device
>       is busy. The cycle is not complete until bit 7 of the flag status
>       register output 1.
>       "
>     - Ref: 
> https://www.micron.com/~/media/Documents/Products/Data%20Sheet/NOR%20Flash/Serial%20NOR/N25Q/n25q_512mb_1ce_3v_65nm.pdf

Hmm, are you sure that all Micron n25q512a need to check the flag status
register? I'll check my datasheets when I'm back in the office, but this
seems peculiar.
 
> Signed-off-by: Insop Song <[email protected]>
> ---
>  drivers/mtd/devices/m25p80.c |   91 
> +++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 90 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
> index 7eda71d..e53e522 100644
> --- a/drivers/mtd/devices/m25p80.c
> +++ b/drivers/mtd/devices/m25p80.c
> @@ -38,6 +38,7 @@
>  /* Flash opcodes. */
>  #define      OPCODE_WREN             0x06    /* Write enable */
>  #define      OPCODE_RDSR             0x05    /* Read status register */
> +#define      OPCODE_RDFSR            0x70    /* Read flag status register */
>  #define      OPCODE_WRSR             0x01    /* Write status register 1 byte 
> */
>  #define      OPCODE_NORM_READ        0x03    /* Read data bytes (low 
> frequency) */
>  #define      OPCODE_FAST_READ        0x0b    /* Read data bytes (high 
> frequency) */
> @@ -70,6 +71,8 @@
>  /* Status Register bits. */
>  #define      SR_WIP                  1       /* Write in progress */
>  #define      SR_WEL                  2       /* Write enable latch */
> +/* Flag Status Register bits. */
> +#define      FSR_RDY                 0x80    /* FSR ready */
>  /* meaning of other SR_* bits may differ between vendors */
>  #define      SR_BP0                  4       /* Block protect 0 */
>  #define      SR_BP1                  8       /* Block protect 1 */
> @@ -95,6 +98,7 @@ struct m25p {
>       u8                      program_opcode;
>       u8                      *command;
>       bool                    fast_read;
> +     bool                    flag_status;
>  };
>  
>  static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
> @@ -131,6 +135,28 @@ 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) {
> +             printk(&flash->spi->dev, "error %d reading FSR\n",
> +                             (int) retval);
> +             return retval;
> +     }
> +
> +     return val;
> +}
> +
> +/*
>   * Write status register 1 byte
>   * Returns negative if error occurred.
>   */
> @@ -220,6 +246,30 @@ 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 sr;
> +
> +     deadline = jiffies + MAX_READY_WAIT_JIFFIES;
> +
> +     do {
> +             if ((sr = read_fsr(flash)) < 0)
> +                     break;
> +             else if ((sr & FSR_RDY))
> +                     return 0;
> +
> +             cond_resched();
> +
> +     } while (!time_after_eq(jiffies, deadline));
> +
> +     return 1;
> +}
> +
> +/*
>   * Erase the whole flash memory
>   *
>   * Returns 0 if successful, non-zero otherwise.
> @@ -273,6 +323,14 @@ static int erase_sector(struct m25p *flash, u32 offset)
>       if (wait_till_ready(flash))
>               return 1;
>  
> +     /* Flag status, Wait until finished previous write command. */
> +     if (flash->flag_status == true) {
> +             if (wait_till_fsr_ready(flash)) {
> +                     mutex_unlock(&flash->lock);
> +                     return 1;
> +             }
> +     }
> +
>       /* Send write enable, then erase commands. */
>       write_enable(flash);
>  
> @@ -427,6 +485,14 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, 
> size_t len,
>  
>       mutex_lock(&flash->lock);
>  
> +     /* Flag status, Wait until finished previous write command. */
> +     if (flash->flag_status == true) {
> +             if (wait_till_fsr_ready(flash)) {
> +                     mutex_unlock(&flash->lock);
> +                     return 1;
> +             }
> +     }
> +
>       /* Wait until finished previous write command. */
>       if (wait_till_ready(flash)) {
>               mutex_unlock(&flash->lock);
> @@ -457,6 +523,14 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, 
> size_t len,
>               t[1].len = page_size;
>               spi_sync(flash->spi, &m);
>  
> +             /* Flag status, Wait until finished previous write command. */
> +             if (flash->flag_status == true) {
> +                     if (wait_till_fsr_ready(flash)) {
> +                             mutex_unlock(&flash->lock);
> +                             return 1;
> +                     }
> +             }
> +
>               *retlen = m.actual_length - m25p_cmdsz(flash);
>  
>               /* write everything in flash->page_size chunks */
> @@ -477,6 +551,14 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, 
> size_t len,
>  
>                       spi_sync(flash->spi, &m);
>  
> +                     /* Flag status, Wait until finished previous write 
> command. */
> +                     if (flash->flag_status == true) {
> +                             if (wait_till_fsr_ready(flash)) {
> +                                     mutex_unlock(&flash->lock);
> +                                     return 1;
> +                             }
> +                     }
> +
>                       *retlen += m.actual_length - m25p_cmdsz(flash);
>               }
>       }
> @@ -782,7 +864,7 @@ static const struct spi_device_id m25p_ids[] = {
>       { "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, 0) },
>       { "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) },
> +     { "n25q512a",    INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K) },

You probably want to figure out the distinction between the old table
entry and the new one, and assign your new entry a new string
accordingly.

>  
>       /* PMC */
>       { "pm25lv512",   INFO(0,        0, 32 * 1024,    2, SECT_4K_PMC) },
> @@ -996,6 +1078,13 @@ static int m25p_probe(struct spi_device *spi)
>       spi_set_drvdata(spi, flash);
>  
>       /*
> +      * Micron n25q512a requires to check flag status register
> +      */
> +     flash->flag_status = false;
> +     if (strcmp(id->name, "n25q512a") == 0)
> +             flash->flag_status = true;;

This doesn't look good at all. If any other flash start to need this, we
don't want to code each ID string here. That's fragile and
non-scaleable. If we need this option, you need to add another flag to
the m25p_ids[] table, I guess...

> +
> +     /*
>        * Atmel, SST and Intel/Numonyx serial flash tend to power
>        * up with the software protection bits set
>        */

Brian
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to