[PATCH 4/4] mtd: spinand: micron: Support for new Micron SPI NAND flashes
From: Shivamurthy Shastri (sshivamurthy)
Date: Tue Mar 26 2019 - 06:52:40 EST
Driver is redesigned using parameter page to support Micron SPI NAND
flashes.
Support for selecting die is enabled for multi-die flashes.
Turn OOB layout generic.
Fixup some of the parameter page data as per Micron datasheet.
Signed-off-by: Shivamurthy Shastri <sshivamurthy@xxxxxxxxxx>
---
drivers/mtd/nand/spi/micron.c | 109 +++++++++++++++++++++++++---------
1 file changed, 80 insertions(+), 29 deletions(-)
diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c
index 7d7b1f7fcf71..663bb2809036 100644
--- a/drivers/mtd/nand/spi/micron.c
+++ b/drivers/mtd/nand/spi/micron.c
@@ -14,7 +14,7 @@
#define MICRON_STATUS_ECC_MASK GENMASK(7, 4)
#define MICRON_STATUS_ECC_NO_BITFLIPS (0 << 4)
-#define MICRON_STATUS_ECC_1TO3_BITFLIPS (1 << 4)
+#define MICRON_STATUS_ECC_1TO3_BITFLIPS BIT(4)
#define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4)
#define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4)
@@ -34,38 +34,38 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
SPINAND_PROG_LOAD(false, 0, NULL, 0));
-static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *region)
+static int ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
- region->offset = 64;
- region->length = 64;
+ region->offset = mtd->oobsize / 2;
+ region->length = mtd->oobsize / 2;
return 0;
}
-static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *region)
+static int ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
/* Reserve 2 bytes for the BBM. */
region->offset = 2;
- region->length = 62;
+ region->length = (mtd->oobsize / 2) - 2;
return 0;
}
-static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = {
- .ecc = mt29f2g01abagd_ooblayout_ecc,
- .free = mt29f2g01abagd_ooblayout_free,
+static const struct mtd_ooblayout_ops ooblayout = {
+ .ecc = ooblayout_ecc,
+ .free = ooblayout_free,
};
-static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand,
- u8 status)
+static int ecc_get_status(struct spinand_device *spinand,
+ u8 status)
{
switch (status & MICRON_STATUS_ECC_MASK) {
case STATUS_ECC_NO_BITFLIPS:
@@ -90,22 +90,23 @@ static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand,
return -EINVAL;
}
-static const struct spinand_info micron_spinand_table[] = {
- SPINAND_INFO("MT29F2G01ABAGD", 0x24,
- NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1),
- NAND_ECCREQ(8, 512),
- SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
- &write_cache_variants,
- &update_cache_variants),
- 0,
- SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout,
- mt29f2g01abagd_ecc_get_status)),
-};
+static int mt29f8g_select_target(struct spinand_device *spinand,
+ unsigned int target)
+{
+ struct spi_mem_op op = SPINAND_SET_FEATURE_OP(0xd0,
+ spinand->scratchbuf);
+
+ if (target == 1)
+ target = 0x40;
+
+ *spinand->scratchbuf = target;
+ return spi_mem_exec_op(spinand->spimem, &op);
+}
static int micron_spinand_detect(struct spinand_device *spinand)
{
+ const struct spi_mem_op *op;
u8 *id = spinand->id.data;
- int ret;
/*
* Micron SPI NAND read ID need a dummy byte,
@@ -114,16 +115,66 @@ static int micron_spinand_detect(struct spinand_device *spinand)
if (id[1] != SPINAND_MFR_MICRON)
return 0;
- ret = spinand_match_and_init(spinand, micron_spinand_table,
- ARRAY_SIZE(micron_spinand_table), id[2]);
- if (ret)
- return ret;
+ spinand->flags = 0;
+ spinand->eccinfo.get_status = ecc_get_status;
+ spinand->eccinfo.ooblayout = &ooblayout;
+ spinand->select_target = mt29f8g_select_target;
+
+ op = spinand_select_op_variant(spinand,
+ &read_cache_variants);
+ if (!op)
+ return -ENOTSUPP;
+
+ spinand->op_templates.read_cache = op;
+
+ op = spinand_select_op_variant(spinand,
+ &write_cache_variants);
+ if (!op)
+ return -ENOTSUPP;
+
+ spinand->op_templates.write_cache = op;
+
+ op = spinand_select_op_variant(spinand,
+ &update_cache_variants);
+ spinand->op_templates.update_cache = op;
return 1;
}
+static int micron_spinand_init(struct spinand_device *spinand)
+{
+ /*
+ * Some of the Micron flashes enable this BIT by default,
+ * and there is a chance of read failure due to this.
+ */
+ return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE, 0);
+}
+
+static void micron_fixup_param_page(struct spinand_device *spinand,
+ struct nand_onfi_params *p)
+{
+ /**
+ * As per Micron datasheets vendor[88] is defined as
+ * die_select_feature
+ */
+ if (p->vendor[83] && !p->interleaved_bits)
+ spinand->base.memorg.planes_per_lun = 1 << p->vendor[0];
+
+ spinand->base.memorg.ntargets = p->lun_count;
+ spinand->base.memorg.luns_per_target = 1;
+
+ /**
+ * As per Micron datasheets,
+ * vendor[82] is ECC maximum correctability
+ */
+ spinand->base.ecc.requirements.strength = p->vendor[82];
+ spinand->base.ecc.requirements.step_size = 512;
+}
+
static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {
.detect = micron_spinand_detect,
+ .init = micron_spinand_init,
+ .fixup_param_page = micron_fixup_param_page,
};
const struct spinand_manufacturer micron_spinand_manufacturer = {
--
2.17.1