diff mbox series

[v2,1/2] mtd: spi-nor: giga: gd25lq64c: Disable quad mode according to bus width

Message ID 20230816104245.2676965-1-hsinyi@chromium.org (mailing list archive)
State New, archived
Headers show
Series [v2,1/2] mtd: spi-nor: giga: gd25lq64c: Disable quad mode according to bus width | expand

Commit Message

Hsin-Yi Wang Aug. 16, 2023, 10:38 a.m. UTC
gd25lq64c has Quad Enable Requirement flag parsed as
BFPT_DWORD15_QER_SR2_BIT1_BUGGY in BFPT, even if spi-{rx/tx}-bus-width
set as non QUAD, eg. 0, 1, 2... Thus quad_enable will not be NULL and
quad enable (QE) bit will be set to 1 by default. According to
datasheet[1], if QE bit is enabled, WP pin will become IO pin and the
system can't use write protection feature, and it's also not recommended
to set QE bit to 1[1].

Add a post_bfpt fixup that reads spi-rx-bus-width to remove quad_enable
if the width is set to below QUAD mode.

[1]
https://www.gigadevice.com.cn/Public/Uploads/uploadfile/files/20220714/DS-00012-GD25LQ64C-Rev3.4.pdf
page 13

Signed-off-by: Hsin-Yi Wang <hsinyi@chromium.org>
---
 drivers/mtd/spi-nor/gigadevice.c | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

Comments

Hsin-Yi Wang Aug. 16, 2023, 10:46 a.m. UTC | #1
On Wed, Aug 16, 2023 at 6:42 PM Hsin-Yi Wang <hsinyi@chromium.org> wrote:
>
> gd25lq64c has Quad Enable Requirement flag parsed as
> BFPT_DWORD15_QER_SR2_BIT1_BUGGY in BFPT, even if spi-{rx/tx}-bus-width
> set as non QUAD, eg. 0, 1, 2... Thus quad_enable will not be NULL and
> quad enable (QE) bit will be set to 1 by default. According to
> datasheet[1], if QE bit is enabled, WP pin will become IO pin and the
> system can't use write protection feature, and it's also not recommended
> to set QE bit to 1[1].
>
> Add a post_bfpt fixup that reads spi-rx-bus-width to remove quad_enable
> if the width is set to below QUAD mode.
>
> [1]
> https://www.gigadevice.com.cn/Public/Uploads/uploadfile/files/20220714/DS-00012-GD25LQ64C-Rev3.4.pdf
> page 13
>
> Signed-off-by: Hsin-Yi Wang <hsinyi@chromium.org>
> ---
change log:
v1->v2: read bus width property in driver instead of creating a
duplicate dt property.
v1 link: https://lore.kernel.org/lkml/20230815154412.713846-1-hsinyi@chromium.org/
---
>  drivers/mtd/spi-nor/gigadevice.c | 28 +++++++++++++++++++++++++++-
>  1 file changed, 27 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c
> index d57ddaf1525b3..8ea89e1858f9b 100644
> --- a/drivers/mtd/spi-nor/gigadevice.c
> +++ b/drivers/mtd/spi-nor/gigadevice.c
> @@ -33,6 +33,31 @@ static const struct spi_nor_fixups gd25q256_fixups = {
>         .post_bfpt = gd25q256_post_bfpt,
>  };
>
> +static int
> +gd25lq64c_post_bfpt(struct spi_nor *nor,
> +                   const struct sfdp_parameter_header *bfpt_header,
> +                   const struct sfdp_bfpt *bfpt)
> +{
> +       struct device_node *np = spi_nor_get_flash_node(nor);
> +       u32 value;
> +
> +       /*
> +        * Even if spi-{tx,rx}-bus-width is set to DUAL mode, due to the QER
> +        * flag parsed from BFPT is BFPT_DWORD15_QER_SR2_BIT1_BUGGY, so the
> +        * quad_enable will be set and QE bit set to 1.
> +        */
> +       if (!of_property_read_u32(np, "spi-rx-bus-width", &value)) {
> +               if (value <= 2)
> +                       nor->params->quad_enable = NULL;
> +       }
> +
> +       return 0;
> +}
> +
> +static struct spi_nor_fixups gd25lq64c_fixups = {
> +       .post_bfpt = gd25lq64c_post_bfpt,
> +};
> +
>  static const struct flash_info gigadevice_nor_parts[] = {
>         { "gd25q16", INFO(0xc84015, 0, 64 * 1024,  32)
>                 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
> @@ -53,7 +78,8 @@ static const struct flash_info gigadevice_nor_parts[] = {
>         { "gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128)
>                 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
>                 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
> -                             SPI_NOR_QUAD_READ) },
> +                             SPI_NOR_QUAD_READ)
> +               .fixups = &gd25lq64c_fixups },
>         { "gd25lq128d", INFO(0xc86018, 0, 64 * 1024, 256)
>                 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
>                 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
> --
> 2.41.0.694.ge786442a9b-goog
>
Michael Walle Aug. 16, 2023, 11:51 a.m. UTC | #2
Am 2023-08-16 12:38, schrieb Hsin-Yi Wang:
> gd25lq64c has Quad Enable Requirement flag parsed as
> BFPT_DWORD15_QER_SR2_BIT1_BUGGY in BFPT, even if spi-{rx/tx}-bus-width
> set as non QUAD, eg. 0, 1, 2... Thus quad_enable will not be NULL and
> quad enable (QE) bit will be set to 1 by default. According to
> datasheet[1], if QE bit is enabled, WP pin will become IO pin and the
> system can't use write protection feature, and it's also not 
> recommended
> to set QE bit to 1[1].
> 
> Add a post_bfpt fixup that reads spi-rx-bus-width to remove quad_enable
> if the width is set to below QUAD mode.
> 
> [1]
> https://www.gigadevice.com.cn/Public/Uploads/uploadfile/files/20220714/DS-00012-GD25LQ64C-Rev3.4.pdf
> page 13
> 
> Signed-off-by: Hsin-Yi Wang <hsinyi@chromium.org>
> ---
>  drivers/mtd/spi-nor/gigadevice.c | 28 +++++++++++++++++++++++++++-
>  1 file changed, 27 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mtd/spi-nor/gigadevice.c 
> b/drivers/mtd/spi-nor/gigadevice.c
> index d57ddaf1525b3..8ea89e1858f9b 100644
> --- a/drivers/mtd/spi-nor/gigadevice.c
> +++ b/drivers/mtd/spi-nor/gigadevice.c
> @@ -33,6 +33,31 @@ static const struct spi_nor_fixups gd25q256_fixups = 
> {
>  	.post_bfpt = gd25q256_post_bfpt,
>  };
> 
> +static int
> +gd25lq64c_post_bfpt(struct spi_nor *nor,
> +		    const struct sfdp_parameter_header *bfpt_header,
> +		    const struct sfdp_bfpt *bfpt)
> +{
> +	struct device_node *np = spi_nor_get_flash_node(nor);
> +	u32 value;
> +
> +	/*
> +	 * Even if spi-{tx,rx}-bus-width is set to DUAL mode, due to the QER
> +	 * flag parsed from BFPT is BFPT_DWORD15_QER_SR2_BIT1_BUGGY, so the
> +	 * quad_enable will be set and QE bit set to 1.
> +	 */
> +	if (!of_property_read_u32(np, "spi-rx-bus-width", &value)) {
> +		if (value <= 2)
> +			nor->params->quad_enable = NULL;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct spi_nor_fixups gd25lq64c_fixups = {
> +	.post_bfpt = gd25lq64c_post_bfpt,

No. Please fix it in the core and not just for this part. To me it seems
like a fundamental problem and that commit 39d1e3340c73 ("mtd: spi-nor:
Fix clearing of QE bit on lock()/unlock()") is broken in that regard.
Tudor?

-michael
Tudor Ambarus Aug. 16, 2023, 12:16 p.m. UTC | #3
On 8/16/23 12:51, Michael Walle wrote:
> Am 2023-08-16 12:38, schrieb Hsin-Yi Wang:
>> gd25lq64c has Quad Enable Requirement flag parsed as
>> BFPT_DWORD15_QER_SR2_BIT1_BUGGY in BFPT, even if spi-{rx/tx}-bus-width
>> set as non QUAD, eg. 0, 1, 2... Thus quad_enable will not be NULL and
>> quad enable (QE) bit will be set to 1 by default. According to
>> datasheet[1], if QE bit is enabled, WP pin will become IO pin and the
>> system can't use write protection feature, and it's also not recommended
>> to set QE bit to 1[1].
>>
>> Add a post_bfpt fixup that reads spi-rx-bus-width to remove quad_enable
>> if the width is set to below QUAD mode.
>>
>> [1]
>> https://www.gigadevice.com.cn/Public/Uploads/uploadfile/files/20220714/DS-00012-GD25LQ64C-Rev3.4.pdf
>> page 13
>>
>> Signed-off-by: Hsin-Yi Wang <hsinyi@chromium.org>
>> ---
>>  drivers/mtd/spi-nor/gigadevice.c | 28 +++++++++++++++++++++++++++-
>>  1 file changed, 27 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c
>> index d57ddaf1525b3..8ea89e1858f9b 100644
>> --- a/drivers/mtd/spi-nor/gigadevice.c
>> +++ b/drivers/mtd/spi-nor/gigadevice.c
>> @@ -33,6 +33,31 @@ static const struct spi_nor_fixups gd25q256_fixups = {
>>      .post_bfpt = gd25q256_post_bfpt,
>>  };
>>
>> +static int
>> +gd25lq64c_post_bfpt(struct spi_nor *nor,
>> +            const struct sfdp_parameter_header *bfpt_header,
>> +            const struct sfdp_bfpt *bfpt)
>> +{
>> +    struct device_node *np = spi_nor_get_flash_node(nor);
>> +    u32 value;
>> +
>> +    /*
>> +     * Even if spi-{tx,rx}-bus-width is set to DUAL mode, due to the QER
>> +     * flag parsed from BFPT is BFPT_DWORD15_QER_SR2_BIT1_BUGGY, so the
>> +     * quad_enable will be set and QE bit set to 1.
>> +     */
>> +    if (!of_property_read_u32(np, "spi-rx-bus-width", &value)) {
>> +        if (value <= 2)
>> +            nor->params->quad_enable = NULL;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static struct spi_nor_fixups gd25lq64c_fixups = {
>> +    .post_bfpt = gd25lq64c_post_bfpt,
> 
> No. Please fix it in the core and not just for this part. To me it seems

The core seems fine to me. We already adjust the hw caps by keeping just the
hardware capabilities supported by both the SPI controller and the flash,
see spi_nor_spimem_adjust_hwcaps(). If you set spi-rx-bus-width = <2>; 
(spi_nor_get_protocol_width(nor->read_proto) will be 2, thus the quad enable
method will not be called. Are you sure you don't have the quad enable bit
set by the bootloaders? Please add some prints and check whether the
quad_enable method is called or not.

> like a fundamental problem and that commit 39d1e3340c73 ("mtd: spi-nor:
> Fix clearing of QE bit on lock()/unlock()") is broken in that regard.

what's wrong with the mentioned commit?

Cheers,
ta
> Tudor?
> 
> -michael
Michael Walle Aug. 16, 2023, 12:22 p.m. UTC | #4
Hi,

>> like a fundamental problem and that commit 39d1e3340c73 ("mtd: 
>> spi-nor:
>> Fix clearing of QE bit on lock()/unlock()") is broken in that regard.
> 
> what's wrong with the mentioned commit?

         } else if (nor->params->quad_enable) {
                 /*
                  * If the Status Register 2 Read command (35h) is not
                  * supported, we should at least be sure we don't
                  * change the value of the SR2 Quad Enable bit.
                  *
                  * We can safely assume that when the Quad Enable method 
is
                  * set, the value of the QE bit is one, as a consequence 
of the
                  * nor->params->quad_enable() call.
                  *
                  * We can safely assume that the Quad Enable bit is 
present in
                  * the Status Register 2 at BIT(1). According to the 
JESD216
                  * revB standard, BFPT DWORDS[15], bits 22:20, the 
16-bit
                  * Write Status (01h) command is available just for the 
cases
                  * in which the QE bit is described in SR2 at BIT(1).
                  */
                 sr_cr[1] = SR2_QUAD_EN_BIT1;
         } else {
                 sr_cr[1] = 0;
         }

"We can safely assume that when the Quad Enable method..". We cannot, if 
we
don't have 4 I/O lines. The quad_enable is just the op how to do it, but 
not
*if* can do it. It seems to be missing the same check as the
spi_nor_quad_enable(). But I'm not sure if it's that simple.

-michael
Tudor Ambarus Aug. 16, 2023, 12:34 p.m. UTC | #5
On 8/16/23 13:22, Michael Walle wrote:
> Hi,
> 
>>> like a fundamental problem and that commit 39d1e3340c73 ("mtd: spi-nor:
>>> Fix clearing of QE bit on lock()/unlock()") is broken in that regard.
>>
>> what's wrong with the mentioned commit?
> 
>         } else if (nor->params->quad_enable) {
>                 /*
>                  * If the Status Register 2 Read command (35h) is not
>                  * supported, we should at least be sure we don't
>                  * change the value of the SR2 Quad Enable bit.
>                  *
>                  * We can safely assume that when the Quad Enable method is
>                  * set, the value of the QE bit is one, as a consequence of the
>                  * nor->params->quad_enable() call.
>                  *
>                  * We can safely assume that the Quad Enable bit is present in
>                  * the Status Register 2 at BIT(1). According to the JESD216
>                  * revB standard, BFPT DWORDS[15], bits 22:20, the 16-bit
>                  * Write Status (01h) command is available just for the cases
>                  * in which the QE bit is described in SR2 at BIT(1).
>                  */
>                 sr_cr[1] = SR2_QUAD_EN_BIT1;
>         } else {
>                 sr_cr[1] = 0;
>         }
> 
> "We can safely assume that when the Quad Enable method..". We cannot, if we
> don't have 4 I/O lines. The quad_enable is just the op how to do it, but not
> *if* can do it. It seems to be missing the same check as the
> spi_nor_quad_enable(). But I'm not sure if it's that simple.
> 

I see. Then extending the if condition should do the trick, as
spi_nor_write_16bit_sr_and_check() is called after setup. Something
like:

if (spi_nor_get_protocol_width(nor->read_proto) == 4 &&
    spi_nor_get_protocol_width(nor->write_proto) == 4 &&
    nor->params->quad_enable)

Is this what Hsin-Yi is hitting?
Michael Walle Aug. 16, 2023, 12:37 p.m. UTC | #6
Hi,

>>>> like a fundamental problem and that commit 39d1e3340c73 ("mtd: 
>>>> spi-nor:
>>>> Fix clearing of QE bit on lock()/unlock()") is broken in that 
>>>> regard.
>>> 
>>> what's wrong with the mentioned commit?
>> 
>>         } else if (nor->params->quad_enable) {
>>                 /*
>>                  * If the Status Register 2 Read command (35h) is not
>>                  * supported, we should at least be sure we don't
>>                  * change the value of the SR2 Quad Enable bit.
>>                  *
>>                  * We can safely assume that when the Quad Enable 
>> method is
>>                  * set, the value of the QE bit is one, as a 
>> consequence of the
>>                  * nor->params->quad_enable() call.
>>                  *
>>                  * We can safely assume that the Quad Enable bit is 
>> present in
>>                  * the Status Register 2 at BIT(1). According to the 
>> JESD216
>>                  * revB standard, BFPT DWORDS[15], bits 22:20, the 
>> 16-bit
>>                  * Write Status (01h) command is available just for 
>> the cases
>>                  * in which the QE bit is described in SR2 at BIT(1).
>>                  */
>>                 sr_cr[1] = SR2_QUAD_EN_BIT1;
>>         } else {
>>                 sr_cr[1] = 0;
>>         }
>> 
>> "We can safely assume that when the Quad Enable method..". We cannot, 
>> if we
>> don't have 4 I/O lines. The quad_enable is just the op how to do it, 
>> but not
>> *if* can do it. It seems to be missing the same check as the
>> spi_nor_quad_enable(). But I'm not sure if it's that simple.
>> 
> 
> I see. Then extending the if condition should do the trick, as
> spi_nor_write_16bit_sr_and_check() is called after setup. Something
> like:
> 
> if (spi_nor_get_protocol_width(nor->read_proto) == 4 &&
>     spi_nor_get_protocol_width(nor->write_proto) == 4 &&
>     nor->params->quad_enable)
> 
> Is this what Hsin-Yi is hitting?

Hopefully :)

-michael
Hsin-Yi Wang Aug. 16, 2023, 6:24 p.m. UTC | #7
On Wed, Aug 16, 2023 at 8:34 PM Tudor Ambarus <tudor.ambarus@linaro.org> wrote:
>
>
>
> On 8/16/23 13:22, Michael Walle wrote:
> > Hi,
> >
> >>> like a fundamental problem and that commit 39d1e3340c73 ("mtd: spi-nor:
> >>> Fix clearing of QE bit on lock()/unlock()") is broken in that regard.
> >>
> >> what's wrong with the mentioned commit?
> >
> >         } else if (nor->params->quad_enable) {
> >                 /*
> >                  * If the Status Register 2 Read command (35h) is not
> >                  * supported, we should at least be sure we don't
> >                  * change the value of the SR2 Quad Enable bit.
> >                  *
> >                  * We can safely assume that when the Quad Enable method is
> >                  * set, the value of the QE bit is one, as a consequence of the
> >                  * nor->params->quad_enable() call.
> >                  *
> >                  * We can safely assume that the Quad Enable bit is present in
> >                  * the Status Register 2 at BIT(1). According to the JESD216
> >                  * revB standard, BFPT DWORDS[15], bits 22:20, the 16-bit
> >                  * Write Status (01h) command is available just for the cases
> >                  * in which the QE bit is described in SR2 at BIT(1).
> >                  */
> >                 sr_cr[1] = SR2_QUAD_EN_BIT1;
> >         } else {
> >                 sr_cr[1] = 0;
> >         }
> >
> > "We can safely assume that when the Quad Enable method..". We cannot, if we
> > don't have 4 I/O lines. The quad_enable is just the op how to do it, but not
> > *if* can do it. It seems to be missing the same check as the
> > spi_nor_quad_enable(). But I'm not sure if it's that simple.
> >
>
> I see. Then extending the if condition should do the trick, as
> spi_nor_write_16bit_sr_and_check() is called after setup. Something
> like:
>
> if (spi_nor_get_protocol_width(nor->read_proto) == 4 &&
>     spi_nor_get_protocol_width(nor->write_proto) == 4 &&
>     nor->params->quad_enable)
>
> Is this what Hsin-Yi is hitting?

Yes, it is. Adding these checks can solve the issue. The read out
width for both read and write is 1, which matches the default bus
width.

Thanks.
diff mbox series

Patch

diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c
index d57ddaf1525b3..8ea89e1858f9b 100644
--- a/drivers/mtd/spi-nor/gigadevice.c
+++ b/drivers/mtd/spi-nor/gigadevice.c
@@ -33,6 +33,31 @@  static const struct spi_nor_fixups gd25q256_fixups = {
 	.post_bfpt = gd25q256_post_bfpt,
 };
 
+static int
+gd25lq64c_post_bfpt(struct spi_nor *nor,
+		    const struct sfdp_parameter_header *bfpt_header,
+		    const struct sfdp_bfpt *bfpt)
+{
+	struct device_node *np = spi_nor_get_flash_node(nor);
+	u32 value;
+
+	/*
+	 * Even if spi-{tx,rx}-bus-width is set to DUAL mode, due to the QER
+	 * flag parsed from BFPT is BFPT_DWORD15_QER_SR2_BIT1_BUGGY, so the
+	 * quad_enable will be set and QE bit set to 1.
+	 */
+	if (!of_property_read_u32(np, "spi-rx-bus-width", &value)) {
+		if (value <= 2)
+			nor->params->quad_enable = NULL;
+	}
+
+	return 0;
+}
+
+static struct spi_nor_fixups gd25lq64c_fixups = {
+	.post_bfpt = gd25lq64c_post_bfpt,
+};
+
 static const struct flash_info gigadevice_nor_parts[] = {
 	{ "gd25q16", INFO(0xc84015, 0, 64 * 1024,  32)
 		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
@@ -53,7 +78,8 @@  static const struct flash_info gigadevice_nor_parts[] = {
 	{ "gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128)
 		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
 		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
-			      SPI_NOR_QUAD_READ) },
+			      SPI_NOR_QUAD_READ)
+		.fixups = &gd25lq64c_fixups },
 	{ "gd25lq128d", INFO(0xc86018, 0, 64 * 1024, 256)
 		FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
 		NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |