summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-27 14:20:25 +0100
committerMark Brown <broonie@kernel.org>2026-07-27 14:20:25 +0100
commitf71ba4e4b46853bb860f4b183579be925150a44a (patch)
treef1ca178d04906c6832e1a11480de6e8a5ba73c9f
parent9a4c76c29742fa662c8e0faea4812fd3aa2a57cf (diff)
parentf97bdc8ec1dc7b33781a702eeba55326c206be56 (diff)
downloadlinux-next-f71ba4e4b46853bb860f4b183579be925150a44a.tar.gz
linux-next-f71ba4e4b46853bb860f4b183579be925150a44a.zip
Merge branch 'nand/next' of https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git
-rw-r--r--Documentation/devicetree/bindings/mtd/qcom,nandc.yaml25
-rw-r--r--drivers/memory/omap-gpmc.c9
-rw-r--r--drivers/mtd/nand/ecc-realtek.c1
-rw-r--r--drivers/mtd/nand/raw/atmel/nand-controller.c3
-rw-r--r--drivers/mtd/nand/raw/nand_ids.c3
-rw-r--r--drivers/mtd/nand/raw/nand_toshiba.c4
-rw-r--r--drivers/mtd/nand/raw/omap2.c6
-rw-r--r--drivers/mtd/nand/raw/pl35x-nand-controller.c20
-rw-r--r--drivers/mtd/nand/raw/qcom_nandc.c30
-rw-r--r--drivers/mtd/nand/spi/Makefile2
-rw-r--r--drivers/mtd/nand/spi/core.c1
-rw-r--r--drivers/mtd/nand/spi/fmsh.c95
-rw-r--r--drivers/mtd/nand/spi/heyangtek.c132
-rw-r--r--drivers/mtd/parsers/redboot.c9
-rw-r--r--include/linux/mtd/nand-qpic-common.h2
-rw-r--r--include/linux/mtd/spinand.h1
-rw-r--r--include/linux/platform_data/mtd-nand-omap2.h7
17 files changed, 321 insertions, 29 deletions
diff --git a/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml b/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml
index 5511389960f0..a916cac53af6 100644
--- a/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml
+++ b/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml
@@ -22,17 +22,20 @@ properties:
- qcom,ipq4019-nand
- qcom,ipq6018-nand
- qcom,ipq8074-nand
+ - qcom,mdm9607-nand
- qcom,sdx55-nand
reg:
maxItems: 1
clocks:
+ minItems: 1
items:
- description: Core Clock
- description: Always ON Clock
clock-names:
+ minItems: 1
items:
- const: core
- const: aon
@@ -101,6 +104,27 @@ allOf:
items:
- const: rxtx
+ # On MDM9607, the OS can only control a single clock.
+ # The 3 hardware clocks (core, aon, ahb) are invisible to the OS.
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,mdm9607-nand
+ then:
+ properties:
+ clocks:
+ maxItems: 1
+ clock-names:
+ maxItems: 1
+ else:
+ properties:
+ clocks:
+ minItems: 2
+ clock-names:
+ minItems: 2
+
- if:
properties:
compatible:
@@ -121,6 +145,7 @@ allOf:
- qcom,ipq4019-nand
- qcom,ipq6018-nand
- qcom,ipq8074-nand
+ - qcom,mdm9607-nand
- qcom,sdx55-nand
then:
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
index 958d2b0ea54a..48af31a54b55 100644
--- a/drivers/memory/omap-gpmc.c
+++ b/drivers/memory/omap-gpmc.c
@@ -2129,6 +2129,13 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np,
of_property_read_bool(np, "gpmc,time-para-granularity");
}
+static int gpmc_child_is_nand(struct device_node *child)
+{
+ /* This has to match drivers/mtd/nand/raw/omap2.c's omap_nand_ids[] */
+ return of_device_is_compatible(child, "ti,omap2-nand") ||
+ of_device_is_compatible(child, "ti,am64-nand");
+}
+
/**
* gpmc_probe_generic_child - configures the gpmc for a child device
* @pdev: pointer to gpmc platform device
@@ -2220,7 +2227,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
goto err;
}
- if (of_match_node(omap_nand_ids, child)) {
+ if (gpmc_child_is_nand(child)) {
/* NAND specific setup */
val = 8;
of_property_read_u32(child, "nand-bus-width", &val);
diff --git a/drivers/mtd/nand/ecc-realtek.c b/drivers/mtd/nand/ecc-realtek.c
index 7d003fd72027..76d75e2107df 100644
--- a/drivers/mtd/nand/ecc-realtek.c
+++ b/drivers/mtd/nand/ecc-realtek.c
@@ -450,6 +450,7 @@ static const struct of_device_id rtl_ecc_of_ids[] = {
},
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, rtl_ecc_of_ids);
static struct platform_driver rtl_ecc_driver = {
.driver = {
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
index e7fdf532c5fe..8a19408abb63 100644
--- a/drivers/mtd/nand/raw/atmel/nand-controller.c
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -1799,8 +1799,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
* Legacy bindings only allow connecting a single NAND with a unique CS
* line to the controller.
*/
- nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs),
- GFP_KERNEL);
+ nand = devm_kzalloc(nc->dev, struct_size(nand, cs, 1), GFP_KERNEL);
if (!nand)
return -ENOMEM;
diff --git a/drivers/mtd/nand/raw/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c
index 62a8cf86d9e2..08707ce9d7c0 100644
--- a/drivers/mtd/nand/raw/nand_ids.c
+++ b/drivers/mtd/nand/raw/nand_ids.c
@@ -29,6 +29,9 @@ struct nand_flash_dev nand_flash_ids[] = {
{"TC58NVG0S3E 1G 3.3V 8-bit",
{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512), },
+ {"TC58NVG1S3H 2G 3.3V 8-bit",
+ { .id = {0x98, 0xda, 0x90, 0x15, 0x76} },
+ SZ_2K, SZ_256, SZ_128K, 0, 5, 128, NAND_ECC_INFO(8, SZ_512) },
{"TC58NVG2S0F 4G 3.3V 8-bit",
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
diff --git a/drivers/mtd/nand/raw/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c
index d3d34d71921f..32062b26a1f6 100644
--- a/drivers/mtd/nand/raw/nand_toshiba.c
+++ b/drivers/mtd/nand/raw/nand_toshiba.c
@@ -287,7 +287,9 @@ static int toshiba_nand_init(struct nand_chip *chip)
if (!strncmp("TC58NVG0S3E", chip->parameters.model,
sizeof("TC58NVG0S3E") - 1))
tc58nvg0s3e_init(chip);
- if ((!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
+ if ((!strncmp("TC58NVG1S3H", chip->parameters.model,
+ sizeof("TC58NVG1S3H") - 1)) ||
+ (!strncmp("TH58NVG2S3HBAI4", chip->parameters.model,
sizeof("TH58NVG2S3HBAI4") - 1)) ||
(!strncmp("TH58NVG3S0HBAI4", chip->parameters.model,
sizeof("TH58NVG3S0HBAI4") - 1)))
diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
index 39e297486721..552f36da79fc 100644
--- a/drivers/mtd/nand/raw/omap2.c
+++ b/drivers/mtd/nand/raw/omap2.c
@@ -2318,7 +2318,11 @@ static void omap_nand_remove(struct platform_device *pdev)
nand_cleanup(nand_chip);
}
-/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */
+static const struct of_device_id omap_nand_ids[] = {
+ { .compatible = "ti,omap2-nand" },
+ { .compatible = "ti,am64-nand" },
+ { }
+};
MODULE_DEVICE_TABLE(of, omap_nand_ids);
static struct platform_driver omap_nand_driver = {
diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c
index 06f8f1e14b9c..bd89aaadd1b2 100644
--- a/drivers/mtd/nand/raw/pl35x-nand-controller.c
+++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c
@@ -862,8 +862,11 @@ static int pl35x_nfc_setup_interface(struct nand_chip *chip, int cs,
PL35X_SMC_NAND_TAR_CYCLES(tmgs.t_ar) |
PL35X_SMC_NAND_TRR_CYCLES(tmgs.t_rr);
- writel(plnand->timings, nfc->conf_regs + PL35X_SMC_CYCLES);
- pl35x_smc_update_regs(nfc);
+ /*
+ * Reset nfc->selected_chip so the next command will cause the timing
+ * registers to be updated in ->*_select_target().
+ */
+ nfc->selected_chip = NULL;
return 0;
}
@@ -914,7 +917,6 @@ static int pl35x_nand_init_hw_ecc_controller(struct pl35x_nandc *nfc,
chip->ecc.steps = mtd->writesize / chip->ecc.size;
chip->ecc.read_page = pl35x_nand_read_page_hwecc;
chip->ecc.write_page = pl35x_nand_write_page_hwecc;
- chip->ecc.write_page_raw = nand_monolithic_write_page_raw;
pl35x_smc_set_ecc_pg_size(nfc, chip, mtd->writesize);
nfc->ecc_buf = devm_kmalloc(nfc->dev, chip->ecc.bytes * chip->ecc.steps,
@@ -973,18 +975,19 @@ static int pl35x_nand_attach_chip(struct nand_chip *chip)
switch (chip->ecc.engine_type) {
case NAND_ECC_ENGINE_TYPE_ON_DIE:
- dev_dbg(nfc->dev, "Using on-die ECC\n");
+ dev_dbg(nfc->dev, "Using on-die hardware ECC\n");
/* Keep these legacy BBT descriptors for ON_DIE situations */
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
fallthrough;
case NAND_ECC_ENGINE_TYPE_NONE:
+ dev_dbg(nfc->dev, "Using no ECC engine\n");
+ break;
case NAND_ECC_ENGINE_TYPE_SOFT:
- dev_dbg(nfc->dev, "Using software ECC (Hamming 1-bit/512B)\n");
- chip->ecc.write_page_raw = nand_monolithic_write_page_raw;
+ dev_dbg(nfc->dev, "Using software ECC\n");
break;
case NAND_ECC_ENGINE_TYPE_ON_HOST:
- dev_dbg(nfc->dev, "Using hardware ECC\n");
+ dev_dbg(nfc->dev, "Using on-host hardware ECC\n");
ret = pl35x_nand_init_hw_ecc_controller(nfc, chip);
if (ret)
return ret;
@@ -995,6 +998,9 @@ static int pl35x_nand_attach_chip(struct nand_chip *chip)
return -EINVAL;
}
+ chip->ecc.read_page_raw = nand_monolithic_read_page_raw;
+ chip->ecc.write_page_raw = nand_monolithic_write_page_raw;
+
return 0;
}
diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c
index 4b80ce084d9a..d7642db2e2df 100644
--- a/drivers/mtd/nand/raw/qcom_nandc.c
+++ b/drivers/mtd/nand/raw/qcom_nandc.c
@@ -1564,7 +1564,7 @@ static int qcom_op_cmd_mapping(struct nand_chip *chip, u8 opcode,
cmd = OP_FETCH_ID;
break;
case NAND_CMD_PARAM:
- if (nandc->props->qpic_version2)
+ if (nandc->props->has_onfi_read_op)
cmd = OP_PAGE_READ_ONFI_READ;
else
cmd = OP_PAGE_READ;
@@ -1903,7 +1903,7 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_
nandc->regs->ecc_buf_cfg = cpu_to_le32(ECC_CFG_ECC_DISABLE);
/* configure CMD1 and VLD for ONFI param probing in QPIC v1 */
- if (!nandc->props->qpic_version2) {
+ if (!nandc->props->has_onfi_read_op) {
nandc->regs->vld = cpu_to_le32((nandc->vld & ~READ_START_VLD));
nandc->regs->cmd1 = cpu_to_le32((nandc->cmd1 & ~READ_ADDR_MASK) |
FIELD_PREP(READ_ADDR_MASK, NAND_CMD_PARAM));
@@ -1911,7 +1911,7 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_
nandc->regs->exec = cpu_to_le32(1);
- if (!nandc->props->qpic_version2) {
+ if (!nandc->props->has_onfi_read_op) {
nandc->regs->orig_cmd1 = cpu_to_le32(nandc->cmd1);
nandc->regs->orig_vld = cpu_to_le32(nandc->vld);
}
@@ -1925,7 +1925,7 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_
else
nandc_set_read_loc_first(chip, reg_base, 0, len, 1);
- if (!nandc->props->qpic_version2) {
+ if (!nandc->props->has_onfi_read_op) {
qcom_write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->cmd1, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL);
}
@@ -1939,7 +1939,7 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_
nandc->buf_count, 0);
/* restore CMD1 and VLD regs */
- if (!nandc->props->qpic_version2) {
+ if (!nandc->props->has_onfi_read_op) {
qcom_write_reg_dma(nandc, &nandc->regs->orig_cmd1, NAND_DEV_CMD1_RESTORE, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->orig_vld, NAND_DEV_CMD_VLD_RESTORE, 1,
NAND_BAM_NEXT_SGL);
@@ -2041,7 +2041,7 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
if (!nandc->props->nandc_part_of_qpic)
nandc_write(nandc, SFLASHC_BURST_CFG, 0);
- if (!nandc->props->qpic_version2)
+ if (!nandc->props->has_onfi_read_op)
nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD),
NAND_DEV_CMD_VLD_VAL);
@@ -2063,7 +2063,7 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
}
/* save the original values of these registers */
- if (!nandc->props->qpic_version2) {
+ if (!nandc->props->has_onfi_read_op) {
nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1));
nandc->vld = NAND_DEV_CMD_VLD_VAL;
}
@@ -2280,7 +2280,7 @@ static int qcom_nandc_probe(struct platform_device *pdev)
if (IS_ERR(nandc->core_clk))
return PTR_ERR(nandc->core_clk);
- nandc->aon_clk = devm_clk_get(dev, "aon");
+ nandc->aon_clk = devm_clk_get_optional(dev, "aon");
if (IS_ERR(nandc->aon_clk))
return PTR_ERR(nandc->aon_clk);
@@ -2381,10 +2381,20 @@ static const struct qcom_nandc_props ipq8074_nandc_props = {
.bam_offset = 0x30000,
};
+static const struct qcom_nandc_props mdm9607_nandc_props = {
+ .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
+ .supports_bam = true,
+ .nandc_part_of_qpic = true,
+ .has_onfi_read_op = true,
+ .dev_cmd_reg_start = 0x7000,
+ .bam_offset = 0x30000,
+};
+
static const struct qcom_nandc_props sdx55_nandc_props = {
.ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT),
.supports_bam = true,
.nandc_part_of_qpic = true,
+ .has_onfi_read_op = true,
.qpic_version2 = true,
.dev_cmd_reg_start = 0x7000,
.bam_offset = 0x30000,
@@ -2412,6 +2422,10 @@ static const struct of_device_id qcom_nandc_of_match[] = {
.data = &ipq8074_nandc_props,
},
{
+ .compatible = "qcom,mdm9607-nand",
+ .data = &mdm9607_nandc_props,
+ },
+ {
.compatible = "qcom,sdx55-nand",
.data = &sdx55_nandc_props,
},
diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
index a47bd22cd309..b5ccb44860df 100644
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
spinand-objs := core.o otp.o
-spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o
+spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o heyangtek.o
spinand-objs += macronix.o micron.o paragon.o skyhigh.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 74bb5ee83b31..8bf9301f25e7 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1359,6 +1359,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
&fmsh_spinand_manufacturer,
&foresee_spinand_manufacturer,
&gigadevice_spinand_manufacturer,
+ &heyangtek_spinand_manufacturer,
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
&paragon_spinand_manufacturer,
diff --git a/drivers/mtd/nand/spi/fmsh.c b/drivers/mtd/nand/spi/fmsh.c
index f417955f7d1c..be5db9f9502b 100644
--- a/drivers/mtd/nand/spi/fmsh.c
+++ b/drivers/mtd/nand/spi/fmsh.c
@@ -9,6 +9,16 @@
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
+#define FM25G01B_STATUS_ECC_MASK (7 << 4)
+ #define FM25G01B_STATUS_ECC_NO_BITFLIPS (0 << 4)
+ #define FM25G01B_STATUS_ECC_1_3_BITFLIPS (1 << 4)
+ #define FM25G01B_STATUS_ECC_4_BITFLIPS (2 << 4)
+ #define FM25G01B_STATUS_ECC_5_BITFLIPS (3 << 4)
+ #define FM25G01B_STATUS_ECC_6_BITFLIPS (4 << 4)
+ #define FM25G01B_STATUS_ECC_7_BITFLIPS (5 << 4)
+ #define FM25G01B_STATUS_ECC_8_BITFLIPS (6 << 4)
+ #define FM25G01B_STATUS_ECC_UNCOR_ERROR (7 << 4)
+
#define FM25S01BI3_STATUS_ECC_MASK (7 << 4)
#define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4)
#define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4)
@@ -34,6 +44,66 @@ 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 fm25g01b_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section)
+ return -ERANGE;
+
+ region->offset = 64;
+ region->length = 64;
+
+ return 0;
+}
+
+static int fm25g01b_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;
+
+ return 0;
+}
+
+static int fm25g01b_ecc_get_status(struct spinand_device *spinand,
+ u8 status)
+{
+ switch (status & FM25G01B_STATUS_ECC_MASK) {
+ case FM25G01B_STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case FM25G01B_STATUS_ECC_1_3_BITFLIPS:
+ return 3;
+
+ case FM25G01B_STATUS_ECC_4_BITFLIPS:
+ return 4;
+
+ case FM25G01B_STATUS_ECC_5_BITFLIPS:
+ return 5;
+
+ case FM25G01B_STATUS_ECC_6_BITFLIPS:
+ return 6;
+
+ case FM25G01B_STATUS_ECC_7_BITFLIPS:
+ return 7;
+
+ case FM25G01B_STATUS_ECC_8_BITFLIPS:
+ return 8;
+
+ case FM25G01B_STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
static int fm25s01a_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@@ -102,6 +172,11 @@ static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
return 0;
}
+static const struct mtd_ooblayout_ops fm25g01b_ooblayout = {
+ .ecc = fm25g01b_ooblayout_ecc,
+ .free = fm25g01b_ooblayout_free,
+};
+
static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
.ecc = fm25s01a_ooblayout_ecc,
.free = fm25s01a_ooblayout_free,
@@ -113,6 +188,26 @@ static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
};
static const struct spinand_info fmsh_spinand_table[] = {
+ SPINAND_INFO("FM25G01B",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd1),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1),
+ NAND_ECCREQ(8, 528),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&fm25g01b_ooblayout,
+ fm25g01b_ecc_get_status)),
+ SPINAND_INFO("FM25G02B",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd2),
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1),
+ NAND_ECCREQ(8, 528),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&fm25g01b_ooblayout,
+ fm25g01b_ecc_get_status)),
SPINAND_INFO("FM25S01A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
diff --git a/drivers/mtd/nand/spi/heyangtek.c b/drivers/mtd/nand/spi/heyangtek.c
new file mode 100644
index 000000000000..7fc50fd3de08
--- /dev/null
+++ b/drivers/mtd/nand/spi/heyangtek.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Authors:
+ * Andrey Zolotarev <andrey.zolotarev@keenetic.com> - the main driver logic
+ * Aleksei Sviridkin <f@lex.la> - adaptation to the mainline Linux kernel
+ *
+ * Based on:
+ * https://github.com/keenetic/kernel-49/commit/bacade569fb12bc0ad31ba09bca9b890118fbca7
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_HEYANGTEK 0xc9
+
+#define HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS (3 << 4)
+
+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),
+ SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(0, 1, NULL, 0, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+ SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
+ SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
+
+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));
+
+/*
+ * HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken
+ * from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each
+ * 16-byte section, the bad block marker sits in byte 0 and the remaining
+ * bytes are exposed as free.
+ */
+static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ region->offset = (16 * section) + 8;
+ region->length = 8;
+
+ return 0;
+}
+
+static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ if (section) {
+ region->offset = 16 * section;
+ region->length = 8;
+ } else {
+ /* section 0 has one byte reserved for the bad block marker */
+ region->offset = 1;
+ region->length = 7;
+ }
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops hyf1gq4_ooblayout = {
+ .ecc = hyf1gq4_ooblayout_ecc,
+ .free = hyf1gq4_ooblayout_free,
+};
+
+static int hyf1gq4_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+
+ case STATUS_ECC_HAS_BITFLIPS:
+ /*
+ * The die exposes only a coarse 2-bit ECC status and has no
+ * register for the exact bitflip count. This code means
+ * "corrected, below the refresh threshold", so report half of
+ * the ECC strength as a representative value.
+ */
+ return nanddev_get_ecc_conf(nand)->strength / 2;
+
+ case HYF1GQ4_STATUS_ECC_LIMIT_BITFLIPS:
+ /*
+ * "Corrected, refresh recommended": report the full ECC
+ * strength so the upper layers relocate the data.
+ */
+ return nanddev_get_ecc_conf(nand)->strength;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct spinand_info heyangtek_spinand_table[] = {
+ SPINAND_INFO("HYF1GQ4UDACAE",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x21),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+ NAND_ECCREQ(4, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&hyf1gq4_ooblayout,
+ hyf1gq4_ecc_get_status)),
+};
+
+static const struct spinand_manufacturer_ops heyangtek_spinand_manuf_ops = {
+};
+
+const struct spinand_manufacturer heyangtek_spinand_manufacturer = {
+ .id = SPINAND_MFR_HEYANGTEK,
+ .name = "HeYangTek",
+ .chips = heyangtek_spinand_table,
+ .nchips = ARRAY_SIZE(heyangtek_spinand_table),
+ .ops = &heyangtek_spinand_manuf_ops,
+};
diff --git a/drivers/mtd/parsers/redboot.c b/drivers/mtd/parsers/redboot.c
index bf162c44eafe..120b2eab21fc 100644
--- a/drivers/mtd/parsers/redboot.c
+++ b/drivers/mtd/parsers/redboot.c
@@ -192,6 +192,7 @@ nogood:
for (i = 0; i < numslots; i++) {
struct fis_list *new_fl, **prev;
+ size_t name_len;
if (buf[i].name[0] == 0xff) {
if (buf[i].name[1] == 0xff) {
@@ -203,8 +204,14 @@ nogood:
if (!redboot_checksum(&buf[i]))
break;
+ name_len = strnlen(buf[i].name, sizeof(buf[i].name));
+ if (name_len == sizeof(buf[i].name)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
new_fl = kmalloc_obj(struct fis_list);
- namelen += strlen(buf[i].name) + 1;
+ namelen += name_len + 1;
if (!new_fl) {
ret = -ENOMEM;
goto out;
diff --git a/include/linux/mtd/nand-qpic-common.h b/include/linux/mtd/nand-qpic-common.h
index 006ca8c978a9..437448995187 100644
--- a/include/linux/mtd/nand-qpic-common.h
+++ b/include/linux/mtd/nand-qpic-common.h
@@ -443,6 +443,7 @@ struct qcom_nand_controller {
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
* @supports_bam - whether NAND controller is using BAM
* @nandc_part_of_qpic - whether NAND controller is part of qpic IP
+ * @has_onfi_read_op - whether ONFI param page read command is supported
* @qpic_version2 - flag to indicate QPIC IP version 2
* @use_codeword_fixup - whether NAND has different layout for boot partitions
*/
@@ -452,6 +453,7 @@ struct qcom_nandc_props {
u32 bam_offset;
bool supports_bam;
bool nandc_part_of_qpic;
+ bool has_onfi_read_op;
bool qpic_version2;
bool use_codeword_fixup;
};
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index ec6efcfeef83..5f4c00ae72a7 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -437,6 +437,7 @@ extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
extern const struct spinand_manufacturer foresee_spinand_manufacturer;
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
+extern const struct spinand_manufacturer heyangtek_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer;
extern const struct spinand_manufacturer paragon_spinand_manufacturer;
diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h
index 8c2f1f185353..2ddb624b97fd 100644
--- a/include/linux/platform_data/mtd-nand-omap2.h
+++ b/include/linux/platform_data/mtd-nand-omap2.h
@@ -7,7 +7,6 @@
#define _MTD_NAND_OMAP2_H
#include <linux/mtd/partitions.h>
-#include <linux/mod_devicetable.h>
#define GPMC_BCH_NUM_REMAINDER 8
@@ -63,10 +62,4 @@ struct gpmc_nand_regs {
void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER];
};
-static const struct of_device_id omap_nand_ids[] = {
- { .compatible = "ti,omap2-nand", },
- { .compatible = "ti,am64-nand", },
- {},
-};
-
#endif /* _MTD_NAND_OMAP2_H */