diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-27 14:20:25 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-27 14:20:25 +0100 |
| commit | f71ba4e4b46853bb860f4b183579be925150a44a (patch) | |
| tree | f1ca178d04906c6832e1a11480de6e8a5ba73c9f | |
| parent | 9a4c76c29742fa662c8e0faea4812fd3aa2a57cf (diff) | |
| parent | f97bdc8ec1dc7b33781a702eeba55326c206be56 (diff) | |
| download | linux-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.yaml | 25 | ||||
| -rw-r--r-- | drivers/memory/omap-gpmc.c | 9 | ||||
| -rw-r--r-- | drivers/mtd/nand/ecc-realtek.c | 1 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/atmel/nand-controller.c | 3 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/nand_ids.c | 3 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/nand_toshiba.c | 4 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/omap2.c | 6 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/pl35x-nand-controller.c | 20 | ||||
| -rw-r--r-- | drivers/mtd/nand/raw/qcom_nandc.c | 30 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/Makefile | 2 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/core.c | 1 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/fmsh.c | 95 | ||||
| -rw-r--r-- | drivers/mtd/nand/spi/heyangtek.c | 132 | ||||
| -rw-r--r-- | drivers/mtd/parsers/redboot.c | 9 | ||||
| -rw-r--r-- | include/linux/mtd/nand-qpic-common.h | 2 | ||||
| -rw-r--r-- | include/linux/mtd/spinand.h | 1 | ||||
| -rw-r--r-- | include/linux/platform_data/mtd-nand-omap2.h | 7 |
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, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, ¶gon_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 */ |
