diff mbox series

mtd: spinand: Add support for XTX XT26G0xA

Message ID 20220401212951.1133780-1-felix@matouschek.org
State Changes Requested
Headers show
Series mtd: spinand: Add support for XTX XT26G0xA | expand

Commit Message

Felix Matouschek April 1, 2022, 9:29 p.m. UTC
Add support for XTX Technology XT26G01AXXXXX, XTX26G02AXXXXX and
XTX26G04AXXXXX SPI NAND.

These are 3V, 1G/2G/4Gbit serial SLC NAND flash devices with on-die ECC
(8bit strength per 512bytes).

Tested on Teltonika RUTX10 flashed with OpenWrt.

Datasheets available at
http://www.xtxtech.com/download/?AId=225
https://datasheet.lcsc.com/szlcsc/2005251034_XTX-XT26G01AWSEGA_C558841.pdf

Signed-off-by: Felix Matouschek <felix@matouschek.org>
---
 drivers/mtd/nand/spi/Makefile |   2 +-
 drivers/mtd/nand/spi/core.c   |   1 +
 drivers/mtd/nand/spi/xtx.c    | 122 ++++++++++++++++++++++++++++++++++
 include/linux/mtd/spinand.h   |   1 +
 4 files changed, 125 insertions(+), 1 deletion(-)
 create mode 100644 drivers/mtd/nand/spi/xtx.c

Comments

Miquel Raynal April 4, 2022, 8:41 a.m. UTC | #1
Hi Felix,

felix@matouschek.org wrote on Fri,  1 Apr 2022 23:29:51 +0200:

> Add support for XTX Technology XT26G01AXXXXX, XTX26G02AXXXXX and
> XTX26G04AXXXXX SPI NAND.
> 
> These are 3V, 1G/2G/4Gbit serial SLC NAND flash devices with on-die ECC
> (8bit strength per 512bytes).
> 
> Tested on Teltonika RUTX10 flashed with OpenWrt.
> 
> Datasheets available at
> http://www.xtxtech.com/download/?AId=225
> https://datasheet.lcsc.com/szlcsc/2005251034_XTX-XT26G01AWSEGA_C558841.pdf

You should prefer using the Links: for these. One nit below, looks good
to me otherwise.

And please Cc all the maintainers for your v2 ;)

> 
> Signed-off-by: Felix Matouschek <felix@matouschek.org>

Thanks,
Miquèl

> ---
>  drivers/mtd/nand/spi/Makefile |   2 +-
>  drivers/mtd/nand/spi/core.c   |   1 +
>  drivers/mtd/nand/spi/xtx.c    | 122 ++++++++++++++++++++++++++++++++++
>  include/linux/mtd/spinand.h   |   1 +
>  4 files changed, 125 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/mtd/nand/spi/xtx.c
> 
> diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
> index 9662b9c1d5a9..80dabe6ff0f3 100644
> --- a/drivers/mtd/nand/spi/Makefile
> +++ b/drivers/mtd/nand/spi/Makefile
> @@ -1,3 +1,3 @@
>  # SPDX-License-Identifier: GPL-2.0
> -spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o
> +spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
>  obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
> diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
> index ff8336870bc0..d5b685d1605e 100644
> --- a/drivers/mtd/nand/spi/core.c
> +++ b/drivers/mtd/nand/spi/core.c
> @@ -933,6 +933,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
>  	&paragon_spinand_manufacturer,
>  	&toshiba_spinand_manufacturer,
>  	&winbond_spinand_manufacturer,
> +	&xtx_spinand_manufacturer,
>  };
>  
>  static int spinand_manufacturer_match(struct spinand_device *spinand,
> diff --git a/drivers/mtd/nand/spi/xtx.c b/drivers/mtd/nand/spi/xtx.c
> new file mode 100644
> index 000000000000..95fe37790f87
> --- /dev/null
> +++ b/drivers/mtd/nand/spi/xtx.c
> @@ -0,0 +1,122 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Author:
> + * Felix Matouschek <felix@matouschek.org>
> + */
> +
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/mtd/spinand.h>
> +
> +#define SPINAND_MFR_XTX	0x0B
> +
> +#define XT26G0XA_STATUS_ECC_MASK	GENMASK(5, 2)
> +#define XT26G0XA_STATUS_ECC_NO_DETECTED	(0 << 2)
> +#define XT26G0XA_STATUS_ECC_8_CORRECTED	(3 << 4)
> +#define XT26G0XA_STATUS_ECC_UNCOR_ERROR	(2 << 4)
> +
> +static SPINAND_OP_VARIANTS(read_cache_variants,
> +		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
> +		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
> +		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
> +		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
> +		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
> +		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
> +
> +static SPINAND_OP_VARIANTS(write_cache_variants,
> +		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
> +		SPINAND_PROG_LOAD(true, 0, NULL, 0));
> +
> +static SPINAND_OP_VARIANTS(update_cache_variants,
> +		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
> +		SPINAND_PROG_LOAD(false, 0, NULL, 0));
> +
> +static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
> +				   struct mtd_oob_region *region)
> +{
> +	if (section)
> +		return -ERANGE;
> +
> +	region->offset = 8;
> +	region->length = 40;

Only 48 OOB bytes?

Maybe we can be more future proof by assuming that ECC bytes will
always be at the end (or at a specific offset) and what will change is
the amount of bytes used for storing the ECC information if, let's say,
the company decides to increase the strength.

Same below.

> +
> +	return 0;
> +}
> +
> +static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
> +				   struct mtd_oob_region *region)
> +{
> +	if (section)
> +		return -ERANGE;
> +
> +	region->offset = 1;
> +	region->length = 7;
> +
> +	return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = {
> +	.ecc = xt26g0xa_ooblayout_ecc,
> +	.free = xt26g0xa_ooblayout_free,
> +};
> +
> +static int xt26g0xa_ecc_get_status(struct spinand_device *spinand,
> +					 u8 status)
> +{
> +	switch (status & XT26G0XA_STATUS_ECC_MASK) {
> +	case XT26G0XA_STATUS_ECC_NO_DETECTED:
> +		return 0;
> +	case XT26G0XA_STATUS_ECC_8_CORRECTED:
> +		return 8;
> +	case XT26G0XA_STATUS_ECC_UNCOR_ERROR:
> +		return -EBADMSG;
> +	default: /* (1 << 2) through (7 << 2) are 1-7 corrected errors */
> +		return (status & XT26G0XA_STATUS_ECC_MASK) >> 2;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct spinand_info xtx_spinand_table[] = {
> +	SPINAND_INFO("XT26G01A",
> +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
> +		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
> +		     NAND_ECCREQ(8, 512),
> +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
> +					      &write_cache_variants,
> +					      &update_cache_variants),
> +		     SPINAND_HAS_QE_BIT,
> +		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
> +				     xt26g0xa_ecc_get_status)),
> +	SPINAND_INFO("XT26G02A",
> +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2),
> +		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
> +		     NAND_ECCREQ(8, 512),
> +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
> +					      &write_cache_variants,
> +					      &update_cache_variants),
> +		     SPINAND_HAS_QE_BIT,
> +		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
> +				     xt26g0xa_ecc_get_status)),
> +	SPINAND_INFO("XT26G04A",
> +		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3),
> +		     NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1),
> +		     NAND_ECCREQ(8, 512),
> +		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
> +					      &write_cache_variants,
> +					      &update_cache_variants),
> +		     SPINAND_HAS_QE_BIT,
> +		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
> +				     xt26g0xa_ecc_get_status)),
> +};
> +
> +static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
> +};
> +
> +const struct spinand_manufacturer xtx_spinand_manufacturer = {
> +	.id = SPINAND_MFR_XTX,
> +	.name = "XTX",
> +	.chips = xtx_spinand_table,
> +	.nchips = ARRAY_SIZE(xtx_spinand_table),
> +	.ops = &xtx_spinand_manuf_ops,
> +};
> diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
> index 3aa28240a77f..5584d3bb6556 100644
> --- a/include/linux/mtd/spinand.h
> +++ b/include/linux/mtd/spinand.h
> @@ -266,6 +266,7 @@ extern const struct spinand_manufacturer micron_spinand_manufacturer;
>  extern const struct spinand_manufacturer paragon_spinand_manufacturer;
>  extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
>  extern const struct spinand_manufacturer winbond_spinand_manufacturer;
> +extern const struct spinand_manufacturer xtx_spinand_manufacturer;
>  
>  /**
>   * struct spinand_op_variants - SPI NAND operation variants


Thanks,
Miquèl
Felix Matouschek April 6, 2022, 4:16 p.m. UTC | #2
Hi Miquel,

thank you for your review.

Am Montag, dem 04.04.2022 um 10:41 +0200 schrieb Miquel Raynal:

> > +static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int
> > section,
> > +                                  struct mtd_oob_region *region)
> > +{
> > +       if (section)
> > +               return -ERANGE;
> > +
> > +       region->offset = 8;
> > +       region->length = 40;
> 
> Only 48 OOB bytes?
> 
> Maybe we can be more future proof by assuming that ECC bytes will
> always be at the end (or at a specific offset) and what will change
> is
> the amount of bytes used for storing the ECC information if, let's
> say,
> the company decides to increase the strength.
> 
> Same below.

I'm not quite sure if I interpreted that data-sheet correctly. 
The section describing the array organization (page 7) says that there
are 2k + 64 bytes per page. The section describing the ECC protection
(page 41) says that only offsets 808H to 82FH can be used for user data
and are covered by ECC.

> > +static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int
> > section,
> > +                                  struct mtd_oob_region *region)
> > +{
> > +       if (section)
> > +               return -ERANGE;
> > +
> > +       region->offset = 1;
> > +       region->length = 7;
> > +
> > +       return 0;
> > +}

The data-sheet says that only offsets 801H to 807H can be used for user
data and are not covered by ECC (offset 800H is reserved for the bad
block mark).

The remaining 16 bytes from offset 830H to 83FH are read-only when ECC
is enabled, so they are of no use? Can you please advise if there is
something I can change?

Thank you,
Felix
Chuanhong Guo April 7, 2022, 7:09 a.m. UTC | #3
Hi!

On Thu, Apr 7, 2022 at 12:20 AM Felix Matouschek <felix@matouschek.org> wrote:
> The data-sheet says that only offsets 801H to 807H can be used for user
> data and are not covered by ECC (offset 800H is reserved for the bad
> block mark).
>
> The remaining 16 bytes from offset 830H to 83FH are read-only when ECC
> is enabled, so they are of no use? Can you please advise if there is
> something I can change?

According to page 30 of the datasheet[0], the 64Byte OOB layout is:
8 bytes: OOB not protected by ECC
40 bytes: OOB protected by ECC
16 bytes: ECC parity data

so your OOB layout should be:
ECC:
region->offset = 48;
region->length = 16;

Free:
/* Reserve 1 byte for the BBM. */
region->offset = 1;
region->length = 47;

[0]: https://pese.oss-cn-shenzhen.aliyuncs.com/pdfs/2005251034_XTX-XT26G01AWSEGA_C558841.pdf
Miquel Raynal April 7, 2022, 7:50 a.m. UTC | #4
gch981213@gmail.com wrote on Thu, 7 Apr 2022 15:09:43 +0800:

> Hi!
> 
> On Thu, Apr 7, 2022 at 12:20 AM Felix Matouschek <felix@matouschek.org> wrote:
> > The data-sheet says that only offsets 801H to 807H can be used for user
> > data and are not covered by ECC (offset 800H is reserved for the bad
> > block mark).
> >
> > The remaining 16 bytes from offset 830H to 83FH are read-only when ECC
> > is enabled, so they are of no use? Can you please advise if there is
> > something I can change?  
> 
> According to page 30 of the datasheet[0], the 64Byte OOB layout is:
> 8 bytes: OOB not protected by ECC
> 40 bytes: OOB protected by ECC
> 16 bytes: ECC parity data
> 
> so your OOB layout should be:
> ECC:
> region->offset = 48;
> region->length = 16;
> 
> Free:
> /* Reserve 1 byte for the BBM. */
> region->offset = 1;
> region->length = 47;
> 
> [0]: https://pese.oss-cn-shenzhen.aliyuncs.com/pdfs/2005251034_XTX-XT26G01AWSEGA_C558841.pdf

Agreed!

Thanks,
Miquèl
Felix Matouschek April 8, 2022, 4:22 p.m. UTC | #5
Hi!

Thank you for your guidance! I posted a v2.

Regards,
Felix

Am 07.04.22 um 09:09 schrieb Chuanhong Guo:
> Hi!
> 
> On Thu, Apr 7, 2022 at 12:20 AM Felix Matouschek <felix@matouschek.org> wrote:
>> The data-sheet says that only offsets 801H to 807H can be used for user
>> data and are not covered by ECC (offset 800H is reserved for the bad
>> block mark).
>>
>> The remaining 16 bytes from offset 830H to 83FH are read-only when ECC
>> is enabled, so they are of no use? Can you please advise if there is
>> something I can change?
> 
> According to page 30 of the datasheet[0], the 64Byte OOB layout is:
> 8 bytes: OOB not protected by ECC
> 40 bytes: OOB protected by ECC
> 16 bytes: ECC parity data
> 
> so your OOB layout should be:
> ECC:
> region->offset = 48;
> region->length = 16;
> 
> Free:
> /* Reserve 1 byte for the BBM. */
> region->offset = 1;
> region->length = 47;
> 
> [0]: https://pese.oss-cn-shenzhen.aliyuncs.com/pdfs/2005251034_XTX-XT26G01AWSEGA_C558841.pdf
diff mbox series

Patch

diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
index 9662b9c1d5a9..80dabe6ff0f3 100644
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,3 +1,3 @@ 
 # SPDX-License-Identifier: GPL-2.0
-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o
+spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
 obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index ff8336870bc0..d5b685d1605e 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -933,6 +933,7 @@  static const struct spinand_manufacturer *spinand_manufacturers[] = {
 	&paragon_spinand_manufacturer,
 	&toshiba_spinand_manufacturer,
 	&winbond_spinand_manufacturer,
+	&xtx_spinand_manufacturer,
 };
 
 static int spinand_manufacturer_match(struct spinand_device *spinand,
diff --git a/drivers/mtd/nand/spi/xtx.c b/drivers/mtd/nand/spi/xtx.c
new file mode 100644
index 000000000000..95fe37790f87
--- /dev/null
+++ b/drivers/mtd/nand/spi/xtx.c
@@ -0,0 +1,122 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author:
+ * Felix Matouschek <felix@matouschek.org>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_XTX	0x0B
+
+#define XT26G0XA_STATUS_ECC_MASK	GENMASK(5, 2)
+#define XT26G0XA_STATUS_ECC_NO_DETECTED	(0 << 2)
+#define XT26G0XA_STATUS_ECC_8_CORRECTED	(3 << 4)
+#define XT26G0XA_STATUS_ECC_UNCOR_ERROR	(2 << 4)
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+		SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+		SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+static int xt26g0xa_ooblayout_ecc(struct mtd_info *mtd, int section,
+				   struct mtd_oob_region *region)
+{
+	if (section)
+		return -ERANGE;
+
+	region->offset = 8;
+	region->length = 40;
+
+	return 0;
+}
+
+static int xt26g0xa_ooblayout_free(struct mtd_info *mtd, int section,
+				   struct mtd_oob_region *region)
+{
+	if (section)
+		return -ERANGE;
+
+	region->offset = 1;
+	region->length = 7;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops xt26g0xa_ooblayout = {
+	.ecc = xt26g0xa_ooblayout_ecc,
+	.free = xt26g0xa_ooblayout_free,
+};
+
+static int xt26g0xa_ecc_get_status(struct spinand_device *spinand,
+					 u8 status)
+{
+	switch (status & XT26G0XA_STATUS_ECC_MASK) {
+	case XT26G0XA_STATUS_ECC_NO_DETECTED:
+		return 0;
+	case XT26G0XA_STATUS_ECC_8_CORRECTED:
+		return 8;
+	case XT26G0XA_STATUS_ECC_UNCOR_ERROR:
+		return -EBADMSG;
+	default: /* (1 << 2) through (7 << 2) are 1-7 corrected errors */
+		return (status & XT26G0XA_STATUS_ECC_MASK) >> 2;
+	}
+
+	return -EINVAL;
+}
+
+static const struct spinand_info xtx_spinand_table[] = {
+	SPINAND_INFO("XT26G01A",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
+		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
+				     xt26g0xa_ecc_get_status)),
+	SPINAND_INFO("XT26G02A",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE2),
+		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
+				     xt26g0xa_ecc_get_status)),
+	SPINAND_INFO("XT26G04A",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE3),
+		     NAND_MEMORG(1, 2048, 64, 128, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&xt26g0xa_ooblayout,
+				     xt26g0xa_ecc_get_status)),
+};
+
+static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
+};
+
+const struct spinand_manufacturer xtx_spinand_manufacturer = {
+	.id = SPINAND_MFR_XTX,
+	.name = "XTX",
+	.chips = xtx_spinand_table,
+	.nchips = ARRAY_SIZE(xtx_spinand_table),
+	.ops = &xtx_spinand_manuf_ops,
+};
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 3aa28240a77f..5584d3bb6556 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -266,6 +266,7 @@  extern const struct spinand_manufacturer micron_spinand_manufacturer;
 extern const struct spinand_manufacturer paragon_spinand_manufacturer;
 extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
 extern const struct spinand_manufacturer winbond_spinand_manufacturer;
+extern const struct spinand_manufacturer xtx_spinand_manufacturer;
 
 /**
  * struct spinand_op_variants - SPI NAND operation variants