From: Vignesh Raghavendra <vigneshr@ti.com>
To: Tokunori Ikegami <ikegami.t@gmail.com>,
Boris Brezillon <bbrezillon@kernel.org>,
Marek Vasut <marek.vasut@gmail.com>,
Richard Weinberger <richard@nod.at>,
Rob Herring <robh+dt@kernel.org>
Cc: <linux-mtd@lists.infradead.org>,
Miquel Raynal <miquel.raynal@bootlin.com>,
<devicetree@vger.kernel.org>,
Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>,
Mason Yang <masonccyang@mxic.com.tw>,
<linux-arm-kernel@lists.infradead.org>,
<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v8 1/5] mtd: cfi_cmdset_0002: Add support for polling status register
Date: Wed, 26 Jun 2019 11:47:17 +0530 [thread overview]
Message-ID: <b7ace44e-5b19-769c-cd00-004c698c44bb@ti.com> (raw)
In-Reply-To: <ade6097f-bd02-1e6d-017e-cf6bd06e03bd@gmail.com>
On 25/06/19 10:31 PM, Tokunori Ikegami wrote:
> Hi,
>
> Thanks for the fix.
>
> Reviewed-by: Tokunori Ikegami <ikegami.t@gmail.com>
>
> 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@ti.com>
>> ---
>> 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
next prev parent reply other threads:[~2019-06-26 6:16 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-06-25 7:57 [PATCH v8 0/5] MTD: Add Initial Hyperbus support Vignesh Raghavendra
2019-06-25 7:57 ` [PATCH v8 1/5] mtd: cfi_cmdset_0002: Add support for polling status register Vignesh Raghavendra
2019-06-25 17:01 ` Tokunori Ikegami
2019-06-26 6:17 ` Vignesh Raghavendra [this message]
2019-06-25 7:57 ` [PATCH v8 2/5] dt-bindings: mtd: Add binding documentation for HyperFlash Vignesh Raghavendra
2019-06-25 7:57 ` [PATCH v8 3/5] mtd: Add support for HyperBus memory devices Vignesh Raghavendra
2019-06-25 20:00 ` Sergei Shtylyov
2019-07-02 17:53 ` Sergei Shtylyov
2019-07-03 4:41 ` Vignesh Raghavendra
2019-07-03 18:14 ` Sergei Shtylyov
2019-07-04 18:35 ` Vignesh Raghavendra
2019-07-11 19:26 ` Sergei Shtylyov
2019-07-12 4:52 ` Vignesh Raghavendra
2022-06-27 15:28 ` Geert Uytterhoeven
2022-07-02 17:10 ` Raghavendra, Vignesh
2022-07-03 8:33 ` Geert Uytterhoeven
2019-06-25 7:57 ` [PATCH v8 4/5] dt-bindings: mtd: Add bindings for TI's AM654 HyperBus memory controller Vignesh Raghavendra
2019-06-25 7:57 ` [PATCH v8 5/5] mtd: hyperbus: Add driver for TI's " Vignesh Raghavendra
2019-06-28 14:35 ` [PATCH v8 0/5] MTD: Add Initial Hyperbus support Vignesh Raghavendra
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=b7ace44e-5b19-769c-cd00-004c698c44bb@ti.com \
--to=vigneshr@ti.com \
--cc=bbrezillon@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=ikegami.t@gmail.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=marek.vasut@gmail.com \
--cc=masonccyang@mxic.com.tw \
--cc=miquel.raynal@bootlin.com \
--cc=richard@nod.at \
--cc=robh+dt@kernel.org \
--cc=sergei.shtylyov@cogentembedded.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).