[PATCH v2 3/3] mtd: spi-nand: macronix: Add randomizer support

Cheng Ming Lin posted 3 patches 3 weeks, 1 day ago
[PATCH v2 3/3] mtd: spi-nand: macronix: Add randomizer support
Posted by Cheng Ming Lin 3 weeks, 1 day ago
From: Cheng Ming Lin <chengminglin@mxic.com.tw>

Enable randomizer function by specific flowchart to set the default value
of RANDEN to 1.

Randomizer introduces a new DT property "mxic,randopt" to define the
randomizer area per page.

The penalty of randomizer are subpage accesses prohibited and more time
period is needed in program operation and entering deep power-down mode.
i.e., tPROG 320us to 360us (randomizer enabled).

Signed-off-by: Cheng Ming Lin <chengminglin@mxic.com.tw>
---
 drivers/mtd/nand/spi/macronix.c | 89 +++++++++++++++++++++++++++++----
 1 file changed, 79 insertions(+), 10 deletions(-)

diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c
index edf63b999..7450ee1d3 100644
--- a/drivers/mtd/nand/spi/macronix.c
+++ b/drivers/mtd/nand/spi/macronix.c
@@ -14,6 +14,10 @@
 #define MACRONIX_ECCSR_BF_LAST_PAGE(eccsr) FIELD_GET(GENMASK(3, 0), eccsr)
 #define MACRONIX_ECCSR_BF_ACCUMULATED_PAGES(eccsr) FIELD_GET(GENMASK(7, 4), eccsr)
 #define MACRONIX_CFG_CONT_READ         BIT(2)
+#define MACRONIX_CFG_ENPGM BIT(0)
+#define MACRONIX_CFG_RANDEN BIT(1)
+#define MACRONIX_CFG_RANDOPT BIT(2)
+#define MACRONIX_FEATURE_ADDR_RANDOMIZER 0x10
 #define MACRONIX_FEATURE_ADDR_READ_RETRY 0x70
 #define MACRONIX_NUM_READ_RETRY_MODES 5
 
@@ -155,6 +159,61 @@ static int macronix_set_read_retry(struct spinand_device *spinand,
 	return spi_mem_exec_op(spinand->spimem, &op);
 }
 
+static int macronix_set_randomizer(struct spinand_device *spinand)
+{
+	struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
+	struct nand_device *nand = spinand_to_nand(spinand);
+	struct device_node *dn = nanddev_get_of_node(nand);
+	int randopt, ret;
+	u8 cfg, status;
+
+	ret = spinand_read_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER, &cfg);
+	if (ret)
+		return ret;
+	if (cfg)
+		return 0;
+
+	cfg = MACRONIX_CFG_ENPGM | MACRONIX_CFG_RANDEN;
+	randopt = of_property_read_bool(dn, "mxic,randopt");
+	if (randopt)
+		cfg |= MACRONIX_CFG_RANDOPT;
+
+	ret = spinand_write_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER, cfg);
+	if (ret)
+		return ret;
+
+	ret = spinand_write_enable_op(spinand);
+	if (ret)
+		return ret;
+
+	ret = spi_mem_exec_op(spinand->spimem, &exec_op);
+	if (ret)
+		return ret;
+
+	ret = spinand_wait(spinand,
+			   SPINAND_WRITE_INITIAL_DELAY_US,
+			   SPINAND_WRITE_POLL_DELAY_US,
+			   &status);
+	if (ret)
+		return ret;
+
+	if (status & STATUS_PROG_FAILED)
+		return -EIO;
+
+	ret = spinand_read_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER, &status);
+	if (ret)
+		return ret;
+	if (status != cfg)
+		return -EIO;
+
+	cfg &= ~MACRONIX_CFG_ENPGM;
+	ret = spinand_write_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER, cfg);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static const struct spinand_info macronix_spinand_table[] = {
 	SPINAND_INFO("MX35LF1GE4AB",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12),
@@ -213,7 +272,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX35LF2G24AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
@@ -225,7 +285,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_HAS_PROG_PLANE_SELECT_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX35LF2G24AD-Z4I8",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x64, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -236,7 +297,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX35LF4G24AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35, 0x03),
 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1),
@@ -248,7 +310,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_HAS_PROG_PLANE_SELECT_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX35LF4G24AD-Z4I8",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x75, 0x03),
 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -259,7 +322,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX31LF1GE4BC",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e),
 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
@@ -305,7 +369,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
 				     macronix_ecc_get_status),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX35UF4G24AD-Z4I8",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xf5, 0x03),
 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -317,7 +382,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
 				     macronix_ecc_get_status),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX35UF4GE4AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7, 0x03),
 		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
@@ -355,7 +421,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
 				     macronix_ecc_get_status),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX35UF2G24AD-Z4I8",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe4, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -367,7 +434,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
 				     macronix_ecc_get_status),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX35UF2GE4AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
@@ -413,7 +481,8 @@ static const struct spinand_info macronix_spinand_table[] = {
 		     SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
 				     macronix_ecc_get_status),
 		     SPINAND_READ_RETRY(MACRONIX_NUM_READ_RETRY_MODES,
-					macronix_set_read_retry)),
+					macronix_set_read_retry),
+		     SPINAND_RANDOMIZER(macronix_set_randomizer)),
 	SPINAND_INFO("MX35UF1GE4AD",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96, 0x03),
 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
-- 
2.25.1
Re: [PATCH v2 3/3] mtd: spi-nand: macronix: Add randomizer support
Posted by Miquel Raynal 3 weeks, 1 day ago
> +static int macronix_set_randomizer(struct spinand_device *spinand)
> +{
> +	struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
> +	struct nand_device *nand = spinand_to_nand(spinand);
> +	struct device_node *dn = nanddev_get_of_node(nand);
> +	int randopt, ret;
> +	u8 cfg, status;
> +
> +	ret = spinand_read_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER, &cfg);
> +	if (ret)
> +		return ret;
> +	if (cfg)
> +		return 0;
> +
> +	cfg = MACRONIX_CFG_ENPGM | MACRONIX_CFG_RANDEN;
> +	randopt = of_property_read_bool(dn, "mxic,randopt");

Isn't that a leftover?

Thanks,
Miquèl
Re: [PATCH v2 3/3] mtd: spi-nand: macronix: Add randomizer support
Posted by Cheng Ming Lin 3 weeks, 1 day ago
Hi Miquel,

Miquel Raynal <miquel.raynal@bootlin.com> 於 2025年9月10日 週三 下午5:12寫道:
>
>
> > +static int macronix_set_randomizer(struct spinand_device *spinand)
> > +{
> > +     struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
> > +     struct nand_device *nand = spinand_to_nand(spinand);
> > +     struct device_node *dn = nanddev_get_of_node(nand);
> > +     int randopt, ret;
> > +     u8 cfg, status;
> > +
> > +     ret = spinand_read_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER, &cfg);
> > +     if (ret)
> > +             return ret;
> > +     if (cfg)
> > +             return 0;
> > +
> > +     cfg = MACRONIX_CFG_ENPGM | MACRONIX_CFG_RANDEN;
> > +     randopt = of_property_read_bool(dn, "mxic,randopt");
>
> Isn't that a leftover?

The property "mxic,randopt" corresponds to bit2 of the configuration
register 0x10. Its purpose is to control whether the randomizer also
covers the initial portion of the spare area.

Since the randomizer feature is only documented in Macronix datasheets,
it is not clear whether the "randopt" bit is used by all vendors. For
this reason, the handling is placed inside macronix.c.

For reference, please see page 28 of the following document:
Link: https://www.mxic.com.tw/Lists/Datasheet/Attachments/9036/MX35LF4G24AD,%203V,%204Gb,%20v1.4.pdf

>
> Thanks,
> Miquèl

Thanks,
Cheng Ming Lin
Re: [PATCH v2 3/3] mtd: spi-nand: macronix: Add randomizer support
Posted by Miquel Raynal 3 weeks, 1 day ago
On 10/09/2025 at 17:26:13 +08, Cheng Ming Lin <linchengming884@gmail.com> wrote:

> Hi Miquel,
>
> Miquel Raynal <miquel.raynal@bootlin.com> 於 2025年9月10日 週三 下午5:12寫道:
>>
>>
>> > +static int macronix_set_randomizer(struct spinand_device *spinand)
>> > +{
>> > +     struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
>> > +     struct nand_device *nand = spinand_to_nand(spinand);
>> > +     struct device_node *dn = nanddev_get_of_node(nand);
>> > +     int randopt, ret;
>> > +     u8 cfg, status;
>> > +
>> > +     ret = spinand_read_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER, &cfg);
>> > +     if (ret)
>> > +             return ret;
>> > +     if (cfg)
>> > +             return 0;
>> > +
>> > +     cfg = MACRONIX_CFG_ENPGM | MACRONIX_CFG_RANDEN;
>> > +     randopt = of_property_read_bool(dn, "mxic,randopt");
>>
>> Isn't that a leftover?
>
> The property "mxic,randopt" corresponds to bit2 of the configuration
> register 0x10. Its purpose is to control whether the randomizer also
> covers the initial portion of the spare area.

Do we need a DT property for that?

In any case, it must be declared in the bindings (which I do not think
it is?).

> Since the randomizer feature is only documented in Macronix datasheets,
> it is not clear whether the "randopt" bit is used by all vendors. For
> this reason, the handling is placed inside macronix.c.

Didn't we discuss in the past of a volatile version for SPI NAND
devices? I thought we could use a volatile approach instead.

Thanks,
Miquèl
Re: [PATCH v2 3/3] mtd: spi-nand: macronix: Add randomizer support
Posted by Cheng Ming Lin 3 weeks ago
Hi Miquel,

Miquel Raynal <miquel.raynal@bootlin.com> 於 2025年9月10日 週三 下午5:43寫道:
>
> On 10/09/2025 at 17:26:13 +08, Cheng Ming Lin <linchengming884@gmail.com> wrote:
>
> > Hi Miquel,
> >
> > Miquel Raynal <miquel.raynal@bootlin.com> 於 2025年9月10日 週三 下午5:12寫道:
> >>
> >>
> >> > +static int macronix_set_randomizer(struct spinand_device *spinand)
> >> > +{
> >> > +     struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0);
> >> > +     struct nand_device *nand = spinand_to_nand(spinand);
> >> > +     struct device_node *dn = nanddev_get_of_node(nand);
> >> > +     int randopt, ret;
> >> > +     u8 cfg, status;
> >> > +
> >> > +     ret = spinand_read_reg_op(spinand, MACRONIX_FEATURE_ADDR_RANDOMIZER, &cfg);
> >> > +     if (ret)
> >> > +             return ret;
> >> > +     if (cfg)
> >> > +             return 0;
> >> > +
> >> > +     cfg = MACRONIX_CFG_ENPGM | MACRONIX_CFG_RANDEN;
> >> > +     randopt = of_property_read_bool(dn, "mxic,randopt");
> >>
> >> Isn't that a leftover?
> >
> > The property "mxic,randopt" corresponds to bit2 of the configuration
> > register 0x10. Its purpose is to control whether the randomizer also
> > covers the initial portion of the spare area.
>
> Do we need a DT property for that?
>
> In any case, it must be declared in the bindings (which I do not think
> it is?).
>
> > Since the randomizer feature is only documented in Macronix datasheets,
> > it is not clear whether the "randopt" bit is used by all vendors. For
> > this reason, the handling is placed inside macronix.c.
>
> Didn't we discuss in the past of a volatile version for SPI NAND
> devices? I thought we could use a volatile approach instead.

I think a DT property makes sense here. Once the randomizer feature is
enabled, the range it covers must be defined as well. If we rely on a
volatile approach, the randomizer coverage might change dynamically,
which I believe is less safe and harder to guarantee consistency.

>
> Thanks,
> Miquèl

Thanks,
Cheng Ming Lin