Re: [PATCH v8 1/5] mtd: cfi_cmdset_0002: Add support for polling status register

From: Vignesh Raghavendra
Date: Wed Jun 26 2019 - 02:16:53 EST




On 25/06/19 10:31 PM, Tokunori Ikegami wrote:
> Hi,
>
> Thanks for the fix.
>
> Reviewed-by: Tokunori Ikegami <ikegami.t@xxxxxxxxx>
>
> I have just tested the patch quickly on my local environment that uses
> the cfi_cmdset_0002 flash device but not HyperFlash family.
> So tested as not affected by the change.
>

Thanks for testing!

> On 2019/06/25 16:57, Vignesh Raghavendra wrote:
>> HyperFlash devices are compliant with CFI AMD/Fujitsu Extended Command
>> Set (0x0002) for flash operations, therefore
>> drivers/mtd/chips/cfi_cmdset_0002.c can be used as is. But these devices
>> do not support DQ polling method of determining chip ready/good status.
>> These flashes provide Status Register whose bits can be polled to know
>> status of flash operation.
>>
>> Cypress HyperFlash datasheet here[1], talks about CFI Amd/Fujitsu
>> Extended Query version 1.5. Bit 0 of "Software Features supported" field
>> of CFI Primary Vendor-Specific Extended Query table indicates
>> presence/absence of status register and Bit 1 indicates whether or not
>> DQ polling is supported. Using these bits, its possible to determine
>> whether flash supports DQ polling or need to use Status Register.
>>
>> Add support for polling Status Register to know device ready/status of
>> erase/write operations when DQ polling is not supported.
>> Print error messages on erase/program failure by looking at related
>> Status Register bits.
>>
>> [1] https://www.cypress.com/file/213346/download
>>
>> Signed-off-by: Vignesh Raghavendra <vigneshr@xxxxxx>
>> ---
>> v8:
>> Fix up status register polling to support banked flashes in patch 1/5.
>>
>>
>> Â drivers/mtd/chips/cfi_cmdset_0002.c | 130 ++++++++++++++++++++++++----
>> Â include/linux/mtd/cfi.hÂÂÂÂÂÂÂÂÂÂÂÂ |ÂÂ 7 ++
>> Â 2 files changed, 120 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c
>> b/drivers/mtd/chips/cfi_cmdset_0002.c
>> index c8fa5906bdf9..09f8aaf1e763 100644
>> --- a/drivers/mtd/chips/cfi_cmdset_0002.c
>> +++ b/drivers/mtd/chips/cfi_cmdset_0002.c
>> @@ -49,6 +49,16 @@
>> Â #define SST49LF008AÂÂÂÂÂÂÂ 0x005a
>> Â #define AT49BV6416ÂÂÂÂÂÂÂ 0x00d6
>> Â +/*
>> + * Status Register bit description. Used by flash devices that don't
>> + * support DQ polling (e.g. HyperFlash)
>> + */
>> +#define CFI_SR_DRBÂÂÂÂÂÂÂ BIT(7)
>> +#define CFI_SR_ESBÂÂÂÂÂÂÂ BIT(5)
>> +#define CFI_SR_PSBÂÂÂÂÂÂÂ BIT(4)
>> +#define CFI_SR_WBASBÂÂÂÂÂÂÂ BIT(3)
>> +#define CFI_SR_SLSBÂÂÂÂÂÂÂ BIT(1)
>> +
>> Â static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t,
>> size_t *, u_char *);
>> Â static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t,
>> size_t *, const u_char *);
>> Â static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t,
>> size_t, size_t *, const u_char *);
>> @@ -97,6 +107,50 @@ static struct mtd_chip_driver cfi_amdstd_chipdrv = {
>> ÂÂÂÂÂ .moduleÂÂÂÂÂÂÂ = THIS_MODULE
>> Â };
>> Â +/*
>> + * Use status register to poll for Erase/write completion when DQ is not
>> + * supported. This is indicated by Bit[1:0] of SoftwareFeatures field in
>> + * CFI Primary Vendor-Specific Extended Query table 1.5
>> + */
>> +static int cfi_use_status_reg(struct cfi_private *cfi)
>> +{
>> +ÂÂÂ struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
>> +ÂÂÂ u8 poll_mask = CFI_POLL_STATUS_REG | CFI_POLL_DQ;
>> +
>> +ÂÂÂ return extp->MinorVersion >= '5' &&
>> +ÂÂÂÂÂÂÂ (extp->SoftwareFeatures & poll_mask) == CFI_POLL_STATUS_REG;
>> +}
>> +
>> +static void cfi_check_err_status(struct map_info *map, struct flchip
>> *chip,
>
> Just a minor comment.
> I though that it is better to be called this function in chip_good()
> instead of to be called separately.
> But I can understand that the chip_good() is called repeatedly in
> do_erase_chip(), do_erase_oneblock() and do_write_buffer() until timeout.
> Also do_write_oneword() is also possible to be changed to call the
> chip_good() repeatedly instead of chip_ready().
> So additional logics is needed to avoid the repeated error messages in
> the function above.
> Anyway it is okay for the current implementation to me.
>

Will do that as an incremental change once base support for HyperFlash
is in.

Regards
Vignesh

> Regards,
> Ikegami
>
>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long adr)
>> +{
>> +ÂÂÂ struct cfi_private *cfi = map->fldrv_priv;
>> +ÂÂÂ map_word status;
>> +
>> +ÂÂÂ if (!cfi_use_status_reg(cfi))
>> +ÂÂÂÂÂÂÂ return;
>> +
>> +ÂÂÂ cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,
>> +ÂÂÂÂÂÂÂÂÂÂÂÂ cfi->device_type, NULL);
>> +ÂÂÂ status = map_read(map, adr);
>> +
>> +ÂÂÂ if (map_word_bitsset(map, status, CMD(0x3a))) {
>> +ÂÂÂÂÂÂÂ unsigned long chipstatus = MERGESTATUS(status);
>> +
>> +ÂÂÂÂÂÂÂ if (chipstatus & CFI_SR_ESB)
>> +ÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s erase operation failed, status %lx\n",
>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ map->name, chipstatus);
>> +ÂÂÂÂÂÂÂ if (chipstatus & CFI_SR_PSB)
>> +ÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s program operation failed, status %lx\n",
>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ map->name, chipstatus);
>> +ÂÂÂÂÂÂÂ if (chipstatus & CFI_SR_WBASB)
>> +ÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s buffer program command aborted, status %lx\n",
>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ map->name, chipstatus);
>> +ÂÂÂÂÂÂÂ if (chipstatus & CFI_SR_SLSB)
>> +ÂÂÂÂÂÂÂÂÂÂÂ pr_err("%s sector write protected, status %lx\n",
>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ map->name, chipstatus);
>> +ÂÂÂ }
>> +}
>> Â Â /* #define DEBUG_CFI_FEATURES */
>> Â @@ -742,10 +796,25 @@ static struct mtd_info
>> *cfi_amdstd_setup(struct mtd_info *mtd)
>> ÂÂ * correctly and is therefore not doneÂÂÂ (particularly with
>> interleaved chips
>> ÂÂ * as each chip must be checked independently of the others).
>> ÂÂ */
>> -static int __xipram chip_ready(struct map_info *map, unsigned long addr)
>> +static int __xipram chip_ready(struct map_info *map, struct flchip
>> *chip,
>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long addr)
>> Â {
>> +ÂÂÂ struct cfi_private *cfi = map->fldrv_priv;
>> ÂÂÂÂÂ map_word d, t;
>> Â +ÂÂÂ if (cfi_use_status_reg(cfi)) {
>> +ÂÂÂÂÂÂÂ map_word ready = CMD(CFI_SR_DRB);
>> +ÂÂÂÂÂÂÂ /*
>> +ÂÂÂÂÂÂÂÂ * For chips that support status register, check device
>> +ÂÂÂÂÂÂÂÂ * ready bit
>> +ÂÂÂÂÂÂÂÂ */
>> +ÂÂÂÂÂÂÂ cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,
>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cfi->device_type, NULL);
>> +ÂÂÂÂÂÂÂ d = map_read(map, addr);
>> +
>> +ÂÂÂÂÂÂÂ return map_word_andequal(map, d, ready, ready);
>> +ÂÂÂ }
>> +
>> ÂÂÂÂÂ d = map_read(map, addr);
>> ÂÂÂÂÂ t = map_read(map, addr);
>> Â @@ -767,10 +836,30 @@ static int __xipram chip_ready(struct map_info
>> *map, unsigned long addr)
>> ÂÂ * as each chip must be checked independently of the others).
>> ÂÂ *
>> ÂÂ */
>> -static int __xipram chip_good(struct map_info *map, unsigned long
>> addr, map_word expected)
>> +static int __xipram chip_good(struct map_info *map, struct flchip *chip,
>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ unsigned long addr, map_word expected)
>> Â {
>> +ÂÂÂ struct cfi_private *cfi = map->fldrv_priv;
>> ÂÂÂÂÂ map_word oldd, curd;
>> Â +ÂÂÂ if (cfi_use_status_reg(cfi)) {
>> +ÂÂÂÂÂÂÂ map_word ready = CMD(CFI_SR_DRB);
>> +ÂÂÂÂÂÂÂ map_word err = CMD(CFI_SR_PSB | CFI_SR_ESB);
>> +ÂÂÂÂÂÂÂ /*
>> +ÂÂÂÂÂÂÂÂ * For chips that support status register, check device
>> +ÂÂÂÂÂÂÂÂ * ready bit and Erase/Program status bit to know if
>> +ÂÂÂÂÂÂÂÂ * operation succeeded.
>> +ÂÂÂÂÂÂÂÂ */
>> +ÂÂÂÂÂÂÂ cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,
>> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cfi->device_type, NULL);
>> +ÂÂÂÂÂÂÂ curd = map_read(map, addr);
>> +
>> +ÂÂÂÂÂÂÂ if (map_word_andequal(map, curd, ready, ready))
>> +ÂÂÂÂÂÂÂÂÂÂÂ return !map_word_bitsset(map, curd, err);
>> +
>> +ÂÂÂÂÂÂÂ return 0;
>> +ÂÂÂ }
>> +
>> ÂÂÂÂÂ oldd = map_read(map, addr);
>> ÂÂÂÂÂ curd = map_read(map, addr);
>> Â @@ -792,7 +881,7 @@ static int get_chip(struct map_info *map, struct
>> flchip *chip, unsigned long adr
>> Â ÂÂÂÂÂ case FL_STATUS:
>> ÂÂÂÂÂÂÂÂÂ for (;;) {
>> -ÂÂÂÂÂÂÂÂÂÂÂ if (chip_ready(map, adr))
>> +ÂÂÂÂÂÂÂÂÂÂÂ if (chip_ready(map, chip, adr))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> Â ÂÂÂÂÂÂÂÂÂÂÂÂÂ if (time_after(jiffies, timeo)) {
>> @@ -830,7 +919,7 @@ static int get_chip(struct map_info *map, struct
>> flchip *chip, unsigned long adr
>> ÂÂÂÂÂÂÂÂÂ chip->state = FL_ERASE_SUSPENDING;
>> ÂÂÂÂÂÂÂÂÂ chip->erase_suspended = 1;
>> ÂÂÂÂÂÂÂÂÂ for (;;) {
>> -ÂÂÂÂÂÂÂÂÂÂÂ if (chip_ready(map, adr))
>> +ÂÂÂÂÂÂÂÂÂÂÂ if (chip_ready(map, chip, adr))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> Â ÂÂÂÂÂÂÂÂÂÂÂÂÂ if (time_after(jiffies, timeo)) {
>> @@ -1362,7 +1451,7 @@ static int do_otp_lock(struct map_info *map,
>> struct flchip *chip, loff_t adr,
>> ÂÂÂÂÂ /* wait for chip to become ready */
>> ÂÂÂÂÂ timeo = jiffies + msecs_to_jiffies(2);
>> ÂÂÂÂÂ for (;;) {
>> -ÂÂÂÂÂÂÂ if (chip_ready(map, adr))
>> +ÂÂÂÂÂÂÂ if (chip_ready(map, chip, adr))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> Â ÂÂÂÂÂÂÂÂÂ if (time_after(jiffies, timeo)) {
>> @@ -1628,22 +1717,24 @@ static int __xipram do_write_oneword(struct
>> map_info *map, struct flchip *chip,
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ continue;
>> ÂÂÂÂÂÂÂÂÂ }
>> Â -ÂÂÂÂÂÂÂ if (time_after(jiffies, timeo) && !chip_ready(map, adr)){
>> +ÂÂÂÂÂÂÂ if (time_after(jiffies, timeo) &&
>> +ÂÂÂÂÂÂÂÂÂÂÂ !chip_ready(map, chip, adr)) {
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ xip_enable(map, chip, adr);
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ printk(KERN_WARNING "MTD %s(): software timeout\n",
>> __func__);
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ xip_disable(map, chip, adr);
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> ÂÂÂÂÂÂÂÂÂ }
>> Â -ÂÂÂÂÂÂÂ if (chip_ready(map, adr))
>> +ÂÂÂÂÂÂÂ if (chip_ready(map, chip, adr))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> Â ÂÂÂÂÂÂÂÂÂ /* Latency issues. Drop the lock, wait a while and retry */
>> ÂÂÂÂÂÂÂÂÂ UDELAY(map, chip, adr, 1);
>> ÂÂÂÂÂ }
>> ÂÂÂÂÂ /* Did we succeed? */
>> -ÂÂÂ if (!chip_good(map, adr, datum)) {
>> +ÂÂÂ if (!chip_good(map, chip, adr, datum)) {
>> ÂÂÂÂÂÂÂÂÂ /* reset on all failures. */
>> +ÂÂÂÂÂÂÂ cfi_check_err_status(map, chip, adr);
>> ÂÂÂÂÂÂÂÂÂ map_write(map, CMD(0xF0), chip->start);
>> ÂÂÂÂÂÂÂÂÂ /* FIXME - should have reset delay before continuing */
>> Â @@ -1881,10 +1972,11 @@ static int __xipram do_write_buffer(struct
>> map_info *map, struct flchip *chip,
>> ÂÂÂÂÂÂÂÂÂÂ * We check "time_after" and "!chip_good" before checking
>> "chip_good" to avoid
>> ÂÂÂÂÂÂÂÂÂÂ * the failure due to scheduling.
>> ÂÂÂÂÂÂÂÂÂÂ */
>> -ÂÂÂÂÂÂÂ if (time_after(jiffies, timeo) && !chip_good(map, adr, datum))
>> +ÂÂÂÂÂÂÂ if (time_after(jiffies, timeo) &&
>> +ÂÂÂÂÂÂÂÂÂÂÂ !chip_good(map, chip, adr, datum))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> Â -ÂÂÂÂÂÂÂ if (chip_good(map, adr, datum)) {
>> +ÂÂÂÂÂÂÂ if (chip_good(map, chip, adr, datum)) {
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ xip_enable(map, chip, adr);
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ goto op_done;
>> ÂÂÂÂÂÂÂÂÂ }
>> @@ -1901,6 +1993,7 @@ static int __xipram do_write_buffer(struct
>> map_info *map, struct flchip *chip,
>> ÂÂÂÂÂÂ * See e.g.
>> ÂÂÂÂÂÂ *
>> http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf
>>
>> ÂÂÂÂÂÂ */
>> +ÂÂÂ cfi_check_err_status(map, chip, adr);
>> ÂÂÂÂÂ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂÂ cfi->device_type, NULL);
>> ÂÂÂÂÂ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
>> @@ -2018,7 +2111,7 @@ static int cfi_amdstd_panic_wait(struct map_info
>> *map, struct flchip *chip,
>> ÂÂÂÂÂÂ * If the driver thinks the chip is idle, and no toggle bits
>> ÂÂÂÂÂÂ * are changing, then the chip is actually idle for sure.
>> ÂÂÂÂÂÂ */
>> -ÂÂÂ if (chip->state == FL_READY && chip_ready(map, adr))
>> +ÂÂÂ if (chip->state == FL_READY && chip_ready(map, chip, adr))
>> ÂÂÂÂÂÂÂÂÂ return 0;
>> Â ÂÂÂÂÂ /*
>> @@ -2035,7 +2128,7 @@ static int cfi_amdstd_panic_wait(struct map_info
>> *map, struct flchip *chip,
>> Â ÂÂÂÂÂÂÂÂÂ /* wait for the chip to become ready */
>> ÂÂÂÂÂÂÂÂÂ for (i = 0; i < jiffies_to_usecs(timeo); i++) {
>> -ÂÂÂÂÂÂÂÂÂÂÂ if (chip_ready(map, adr))
>> +ÂÂÂÂÂÂÂÂÂÂÂ if (chip_ready(map, chip, adr))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ return 0;
>> Â ÂÂÂÂÂÂÂÂÂÂÂÂÂ udelay(1);
>> @@ -2099,14 +2192,15 @@ static int do_panic_write_oneword(struct
>> map_info *map, struct flchip *chip,
>> ÂÂÂÂÂ map_write(map, datum, adr);
>> Â ÂÂÂÂÂ for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) {
>> -ÂÂÂÂÂÂÂ if (chip_ready(map, adr))
>> +ÂÂÂÂÂÂÂ if (chip_ready(map, chip, adr))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> Â ÂÂÂÂÂÂÂÂÂ udelay(1);
>> ÂÂÂÂÂ }
>> Â -ÂÂÂ if (!chip_good(map, adr, datum)) {
>> +ÂÂÂ if (!chip_good(map, chip, adr, datum)) {
>> ÂÂÂÂÂÂÂÂÂ /* reset on all failures. */
>> +ÂÂÂÂÂÂÂ cfi_check_err_status(map, chip, adr);
>> ÂÂÂÂÂÂÂÂÂ map_write(map, CMD(0xF0), chip->start);
>> ÂÂÂÂÂÂÂÂÂ /* FIXME - should have reset delay before continuing */
>> Â @@ -2300,7 +2394,7 @@ static int __xipram do_erase_chip(struct
>> map_info *map, struct flchip *chip)
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ chip->erase_suspended = 0;
>> ÂÂÂÂÂÂÂÂÂ }
>> Â -ÂÂÂÂÂÂÂ if (chip_good(map, adr, map_word_ff(map)))
>> +ÂÂÂÂÂÂÂ if (chip_good(map, chip, adr, map_word_ff(map)))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> Â ÂÂÂÂÂÂÂÂÂ if (time_after(jiffies, timeo)) {
>> @@ -2316,6 +2410,7 @@ static int __xipram do_erase_chip(struct
>> map_info *map, struct flchip *chip)
>> ÂÂÂÂÂ /* Did we succeed? */
>> ÂÂÂÂÂ if (ret) {
>> ÂÂÂÂÂÂÂÂÂ /* reset on all failures. */
>> +ÂÂÂÂÂÂÂ cfi_check_err_status(map, chip, adr);
>> ÂÂÂÂÂÂÂÂÂ map_write(map, CMD(0xF0), chip->start);
>> ÂÂÂÂÂÂÂÂÂ /* FIXME - should have reset delay before continuing */
>> Â @@ -2396,7 +2491,7 @@ static int __xipram do_erase_oneblock(struct
>> map_info *map, struct flchip *chip,
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ chip->erase_suspended = 0;
>> ÂÂÂÂÂÂÂÂÂ }
>> Â -ÂÂÂÂÂÂÂ if (chip_good(map, adr, map_word_ff(map)))
>> +ÂÂÂÂÂÂÂ if (chip_good(map, chip, adr, map_word_ff(map)))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> Â ÂÂÂÂÂÂÂÂÂ if (time_after(jiffies, timeo)) {
>> @@ -2412,6 +2507,7 @@ static int __xipram do_erase_oneblock(struct
>> map_info *map, struct flchip *chip,
>> ÂÂÂÂÂ /* Did we succeed? */
>> ÂÂÂÂÂ if (ret) {
>> ÂÂÂÂÂÂÂÂÂ /* reset on all failures. */
>> +ÂÂÂÂÂÂÂ cfi_check_err_status(map, chip, adr);
>> ÂÂÂÂÂÂÂÂÂ map_write(map, CMD(0xF0), chip->start);
>> ÂÂÂÂÂÂÂÂÂ /* FIXME - should have reset delay before continuing */
>> Â @@ -2589,7 +2685,7 @@ static int __maybe_unused do_ppb_xxlock(struct
>> map_info *map,
>> ÂÂÂÂÂÂ */
>> ÂÂÂÂÂ timeo = jiffies + msecs_to_jiffies(2000);ÂÂÂ /* 2s max
>> (un)locking */
>> ÂÂÂÂÂ for (;;) {
>> -ÂÂÂÂÂÂÂ if (chip_ready(map, adr))
>> +ÂÂÂÂÂÂÂ if (chip_ready(map, chip, adr))
>> ÂÂÂÂÂÂÂÂÂÂÂÂÂ break;
>> Â ÂÂÂÂÂÂÂÂÂ if (time_after(jiffies, timeo)) {
>> diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h
>> index 208c87cf2e3e..c98a21108688 100644
>> --- a/include/linux/mtd/cfi.h
>> +++ b/include/linux/mtd/cfi.h
>> @@ -219,6 +219,13 @@ struct cfi_pri_amdstd {
>>  uint8_t VppMin;
>>  uint8_t VppMax;
>>  uint8_t TopBottom;
>> +ÂÂÂ /* Below field are added from version 1.5 */
>> + uint8_t ProgramSuspend;
>> + uint8_t UnlockBypass;
>> + uint8_t SecureSiliconSector;
>> + uint8_t SoftwareFeatures;
>> +#define CFI_POLL_STATUS_REGÂÂÂ BIT(0)
>> +#define CFI_POLL_DQÂÂÂÂÂÂÂ BIT(1)
>> Â } __packed;
>> Â Â /* Vendor-Specific PRI for Atmel chips (command set 0x0002) */

--
Regards
Vignesh