[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20250818135141.2096-1-gigadevice2025@gmail.com>
Date: Mon, 18 Aug 2025 21:51:41 +0800
From: Teng Wu <gigadevice2025@...il.com>
To: miquel.raynal@...tlin.com,
richard@....at,
vigneshr@...com
Cc: linux-mtd@...ts.infradead.org,
linux-kernel@...r.kernel.org,
teng.wu@...adevice.com,
Teng Wu <gigadevice2025@...il.com>
Subject: [PATCH] mtd: spinand: gigadevice: Add continuous read support
GD5F1GM9 supports a high-speed Continuous Read (CR) mode which allows
the entire memory array to be read out by a single read command
without re-issuing the 13h “Page Read to Cache” command for every page.
Datasheet summary
Feature register B0[3] (NR bit)
1 = Normal Read (default)
0 = Continuous Read (CR)
ECC status register 7Ch
Normal read: bits [3:0] → ECC status of last page
Continuous read: bits [7:4] → accumulated max bit-flips over the
whole sequential read operation.
Datasheet:
https://github.com/WT-886/DATASHEET/blob/main/GD5F1GM9-v1.0.pdf
Signed-off-by: Teng Wu <gigadevice2025@...il.com>
---
drivers/mtd/nand/spi/gigadevice.c | 107 +++++++++++++++++++++++++++++-
1 file changed, 105 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c
index 93e40431dbe2..72ad36c9a126 100644
--- a/drivers/mtd/nand/spi/gigadevice.c
+++ b/drivers/mtd/nand/spi/gigadevice.c
@@ -4,6 +4,7 @@
* Chuanhong Guo <gch981213@...il.com>
*/
+#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
@@ -23,6 +24,18 @@
#define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS (1 << 4)
#define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4)
+/* Feature bit definitions */
+#define GD_FEATURE_NR BIT(3) /* Normal Read(1=normal,0=continuous) */
+#define GD_FEATURE_CRDC BIT(2) /* Continuous Read Dummy */
+
+/* ECC status extraction helpers */
+#define GD_ECCSR_LAST_PAGE(eccsr) FIELD_GET(GENMASK(3, 0), eccsr)
+#define GD_ECCSR_ACCUMULATED(eccsr) FIELD_GET(GENMASK(7, 4), eccsr)
+
+struct gigadevice_priv {
+ bool continuous_read;
+};
+
static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
@@ -63,6 +76,74 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
+static int gd5fxgm9_get_eccsr(struct spinand_device *spinand, u8 *eccsr)
+{
+ struct gigadevice_priv *priv = spinand->priv;
+ struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_DUMMY(1, 1),
+ SPI_MEM_OP_DATA_IN(1, eccsr, 1));
+ int ret;
+
+ ret = spi_mem_exec_op(spinand->spimem, &op);
+ if (ret)
+ return ret;
+
+ if (priv->continuous_read)
+ *eccsr = GD_ECCSR_ACCUMULATED(*eccsr);
+ else
+ *eccsr = GD_ECCSR_LAST_PAGE(*eccsr);
+
+ return 0;
+}
+
+static int gd5fxgm9_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ u8 eccsr;
+ int ret;
+
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
+ ret = gd5fxgm9_get_eccsr(spinand, spinand->scratchbuf);
+ if (ret)
+ return nanddev_get_ecc_conf(nand)->strength;
+
+ eccsr = *spinand->scratchbuf;
+ if (WARN_ON(!eccsr || eccsr > nanddev_get_ecc_conf(nand)->strength))
+ return nanddev_get_ecc_conf(nand)->strength;
+
+ return eccsr;
+
+ case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
+ return 8;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int gd5fxgm9_set_continuous_read(struct spinand_device *spinand, bool enable)
+{
+ struct gigadevice_priv *priv = spinand->priv;
+ int ret;
+
+ ret = spinand_upd_cfg(spinand, GD_FEATURE_NR,
+ enable ? 0 : GD_FEATURE_NR);
+ if (ret)
+ return ret;
+
+ priv->continuous_read = enable;
+
+ return 0;
+}
+
static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@@ -542,7 +623,8 @@ static const struct spinand_info gigadevice_spinand_table[] = {
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
- gd5fxgq4uexxg_ecc_get_status)),
+ gd5fxgm9_ecc_get_status),
+ SPINAND_CONT_READ(gd5fxgm9_set_continuous_read)),
SPINAND_INFO("GD5F1GM9RExxG",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81, 0x01),
NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
@@ -552,10 +634,31 @@ static const struct spinand_info gigadevice_spinand_table[] = {
&update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
- gd5fxgq4uexxg_ecc_get_status)),
+ gd5fxgm9_ecc_get_status),
+ SPINAND_CONT_READ(gd5fxgm9_set_continuous_read)),
};
+static int gd5fxgm9_spinand_init(struct spinand_device *spinand)
+{
+ struct gigadevice_priv *priv;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ spinand->priv = priv;
+
+ return 0;
+}
+
+static void gd5fxgm9_spinand_cleanup(struct spinand_device *spinand)
+{
+ kfree(spinand->priv);
+}
+
static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
+ .init = gd5fxgm9_spinand_init,
+ .cleanup = gd5fxgm9_spinand_cleanup,
};
const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
--
2.43.0
Powered by blists - more mailing lists