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/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index b73596a8e021..6049ba2d6bcb 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -2565,6 +2565,12 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) ret = -EAGAIN; break; case FL_PM_SUSPENDED: + case FL_SHUTDOWN: + /* + * Already suspended, or put into array mode by the + * reboot notifier ahead of an imminent power-off. + * Either way there is nothing to do. + */ break; } mutex_unlock(&chip->mutex); diff --git a/drivers/mtd/devices/mtd_intel_dg.c b/drivers/mtd/devices/mtd_intel_dg.c index f2fa8f68d190..a566e86eb5e3 100644 --- a/drivers/mtd/devices/mtd_intel_dg.c +++ b/drivers/mtd/devices/mtd_intel_dg.c @@ -780,7 +780,7 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev, dev_name(&aux_dev->dev), invm->regions[i].name); if (!name) { ret = -ENOMEM; - goto err; + goto err_norpm; } nvm->regions[n].name = name; diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index f447902d707e..1bb3dba2631d 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -101,7 +101,6 @@ config MTD_PHYSMAP_IXP4XX depends on MTD_PHYSMAP_OF depends on ARM select MTD_COMPLEX_MAPPINGS - select MTD_CFI_BE_BYTE_SWAP if CPU_BIG_ENDIAN default ARCH_IXP4XX help This provides some extra DT physmap parsing for the Intel IXP4xx diff --git a/drivers/mtd/maps/map_funcs.c b/drivers/mtd/maps/map_funcs.c index 1a4add9e119a..b0054933cae0 100644 --- a/drivers/mtd/maps/map_funcs.c +++ b/drivers/mtd/maps/map_funcs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS + * Out-of-line map I/O functions for simple maps when CONFIG_MTD_COMPLEX_MAPPINGS * is enabled. */ diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 39df7ce8f55f..1da7ef06a7a9 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -392,6 +392,9 @@ static void mtdoops_notify_remove(struct mtd_info *mtd) cxt->mtd = NULL; flush_work(&cxt->work_erase); flush_work(&cxt->work_write); + vfree(cxt->oops_page_used); + cxt->oops_page_used = NULL; + cxt->oops_pages = 0; } diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 4b41550fd374..ddded0dbe77e 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -258,7 +258,8 @@ int mtd_add_partition(struct mtd_info *parent, const char *name, /* the direct offset is expected */ if (offset == MTDPART_OFS_APPEND || - offset == MTDPART_OFS_NXTBLK) + offset == MTDPART_OFS_NXTBLK || + offset == MTDPART_OFS_RETAIN) return -EINVAL; if (length == MTDPART_SIZ_FULL) diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index f33f753f0a9f..92e38ece8931 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -1452,6 +1452,7 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) debugfs_failed: del_mtd_blktrans_dev(mbd_dev); + mbd_dev = NULL; cleanup: mtdswap_cleanup(d); 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/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index c1f766cb225a..527165ccc839 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -7,6 +7,7 @@ */ #include #include +#include #include #include #include @@ -732,6 +733,31 @@ static int common_nfc_set_geometry(struct gpmi_nand_data *this) return err; } +#ifdef CONFIG_DEBUG_FS +static void bch_remove_debugfs(void *data) +{ + struct gpmi_nand_data *this = data; + + debugfs_remove_recursive(this->dbg_root); + this->dbg_root = NULL; +} + +static void bch_create_debugfs(struct gpmi_nand_data *this) +{ + struct bch_geometry *bch_geo = &this->bch_geometry; + + this->dbg_root = debugfs_create_dir("gpmi-nand", NULL); + this->dbg_bch_geo.data = (void *)bch_geo; + this->dbg_bch_geo.size = sizeof(struct bch_geometry); + this->raw_mode = true; + debugfs_create_blob("bch_geometry", 0444, this->dbg_root, &this->dbg_bch_geo); + debugfs_create_bool("raw_mode", 0444, this->dbg_root, &this->raw_mode); + devm_add_action_or_reset(this->dev, bch_remove_debugfs, this); +} +#else +static void bch_create_debugfs(struct gpmi_nand_data *this) {} +#endif /* CONFIG_DEBUG_FS */ + /* Configures the geometry for BCH. */ static int bch_set_geometry(struct gpmi_nand_data *this) { @@ -2282,6 +2308,9 @@ static int gpmi_init_last(struct gpmi_nand_data *this) if (ret) return ret; + /* save BCH geometry to debugfs if CONFIG_DEBUG_FS is enabled */ + bch_create_debugfs(this); + /* Init the nand_ecc_ctrl{} */ ecc->read_page = gpmi_ecc_read_page; ecc->write_page = gpmi_ecc_write_page; diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 3e9bc985e44a..80c32efbaef8 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -161,6 +161,12 @@ struct gpmi_nand_data { #define DMA_CHANS 8 struct dma_chan *dma_chans[DMA_CHANS]; struct completion dma_done; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_root; + struct debugfs_blob_wrapper dbg_bch_geo; + bool raw_mode; +#endif }; /* BCH : Status Block Completion Codes */ diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c index 97b4e7f3e1bb..e6594548b7e0 100644 --- a/drivers/mtd/nand/raw/mpc5121_nfc.c +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -618,14 +617,14 @@ static int mpc5121_nfc_probe(struct platform_device *op) struct clk *clk; struct device *dev = &op->dev; struct mpc5121_nfc_prv *prv; - struct resource res; struct mtd_info *mtd; struct nand_chip *chip; - unsigned long regs_paddr, regs_size; const __be32 *chips_no; + void __iomem *regs; int resettime = 0; int retval = 0; int rev, len; + int irq; /* * Check SoC revision. This driver supports only NFC @@ -637,6 +636,14 @@ static int mpc5121_nfc_probe(struct platform_device *op) return -ENXIO; } + regs = devm_platform_ioremap_resource(op, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + irq = platform_get_irq(op, 0); + if (irq < 0) + return irq; + prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL); if (!prv) return -ENOMEM; @@ -660,17 +667,7 @@ static int mpc5121_nfc_probe(struct platform_device *op) return retval; } - prv->irq = irq_of_parse_and_map(dn, 0); - if (!prv->irq) { - dev_err(dev, "Error mapping IRQ!\n"); - return -EINVAL; - } - - retval = of_address_to_resource(dn, 0, &res); - if (retval) { - dev_err(dev, "Error parsing memory region!\n"); - return retval; - } + prv->irq = irq; chips_no = of_get_property(dn, "chips", &len); if (!chips_no || len != sizeof(*chips_no)) { @@ -678,19 +675,7 @@ static int mpc5121_nfc_probe(struct platform_device *op) return -EINVAL; } - regs_paddr = res.start; - regs_size = resource_size(&res); - - if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) { - dev_err(dev, "Error requesting memory region!\n"); - return -EBUSY; - } - - prv->regs = devm_ioremap(dev, regs_paddr, regs_size); - if (!prv->regs) { - dev_err(dev, "Error mapping memory region!\n"); - return -ENOMEM; - } + prv->regs = regs; mtd->name = "MPC5121 NAND"; chip->legacy.dev_ready = mpc5121_nfc_dev_ready; 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_onfi.c b/drivers/mtd/nand/raw/nand_onfi.c index cd3ad373883e..b5e304c35738 100644 --- a/drivers/mtd/nand/raw/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -35,16 +35,21 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip, struct nand_onfi_params *p) { struct nand_device *base = &chip->base; + struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_props requirements; struct onfi_ext_param_page *ep; struct onfi_ext_section *s; struct onfi_ext_ecc_info *ecc; + size_t remaining, section_len; uint8_t *cursor; int ret; int len; int i; len = le16_to_cpu(p->ext_param_page_length) * 16; + if (len < sizeof(*ep)) + return -EINVAL; + ep = kmalloc(len, GFP_KERNEL); if (!ep) return -ENOMEM; @@ -77,11 +82,29 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip, /* find the ECC section. */ cursor = (uint8_t *)(ep + 1); + remaining = len - sizeof(*ep); for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { s = ep->sections + i; - if (s->type == ONFI_SECTION_TYPE_2) + section_len = s->length * 16; + if (section_len > remaining) { + dev_dbg(&mtd->dev, + "ONFI extended parameter section %d exceeds page\n", + i); + goto ext_out; + } + + if (s->type == ONFI_SECTION_TYPE_2) { + if (section_len < sizeof(*ecc)) { + dev_dbg(&mtd->dev, + "ONFI extended parameter ECC section %d is too short\n", + i); + goto ext_out; + } break; - cursor += s->length * 16; + } + + cursor += section_len; + remaining -= section_len; } if (i == ONFI_EXT_SECTION_MAX) { pr_debug("We can not find the ECC section.\n"); 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, @@ -2411,6 +2421,10 @@ static const struct of_device_id qcom_nandc_of_match[] = { .compatible = "qcom,ipq8074-nand", .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/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 02647565c8ba..45ccbce91551 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -79,6 +79,12 @@ #define NFC_REG_H6_MDMA_BUF_ADDR 0x0210 #define NFC_REG_H6_MDMA_CNT 0x0214 +#define NFC_H6_MDMA_STA_DESC0_COMPLETE BIT(0) + +#define NFC_MDMA_DESC_LAST BIT(2) +#define NFC_MDMA_DESC_FIRST BIT(3) +#define NFC_MDMA_DESC_SIZE_MASK GENMASK(15, 0) + #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -237,6 +243,18 @@ struct sunxi_nand_hw_ecc { u32 ecc_ctl; }; +#define SUNXI_NFC_TIMING_STEPS 4 + +/* Delay arrays contain internal NDFC clock cycles for field values 0 to 3. */ +struct sunxi_nfc_timings { + /* Internal clock cycles used by T1-T4, T7 and T11. */ + u8 setup_cycles; + s32 tWB[SUNXI_NFC_TIMING_STEPS]; + s32 tADL[SUNXI_NFC_TIMING_STEPS]; + s32 tWHR[SUNXI_NFC_TIMING_STEPS]; + s32 tRHW[SUNXI_NFC_TIMING_STEPS]; +}; + /** * struct sunxi_nand_chip - stores NAND chip device related information * @@ -267,12 +285,19 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) return container_of(nand, struct sunxi_nand_chip, nand); } +struct sunxi_nfc_mdma_desc { + __le32 config; + __le32 size; + __le32 buf; + __le32 next; +} __packed __aligned(4); + /* * NAND Controller capabilities structure: stores NAND controller capabilities * for distinction between compatible strings. * - * @has_mdma: Use mbus dma mode, otherwise general dma - * through MBUS on A23/A33 needs extra configuration. + * @has_mdma: Use A23/A33-style MBUS DMA registers + * @has_mdma_desc: MBUS DMA uses H6-style descriptors * @has_ecc_block_512: If the ECC can handle 512B or only 1024B chunks * @has_ecc_clk: If the controller needs an ECC clock. * @has_mbus_clk: If the controller needs a mbus clock. @@ -301,9 +326,11 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * bytes to write * @nuser_data_tab: Size of @user_data_len_tab * @sram_size: Size of the NAND controller SRAM + * @timings: Controller timing characteristics */ struct sunxi_nfc_caps { bool has_mdma; + bool has_mdma_desc; bool has_ecc_block_512; bool has_ecc_clk; bool has_mbus_clk; @@ -327,6 +354,7 @@ struct sunxi_nfc_caps { unsigned int nuser_data_tab; unsigned int max_ecc_steps; int sram_size; + const struct sunxi_nfc_timings *timings; }; /** @@ -346,6 +374,9 @@ struct sunxi_nfc_caps { * controller * @complete: a completion object used to wait for NAND controller events * @dmac: the DMA channel attached to the NAND controller + * @use_mdma: use an internal MBUS DMA backend + * @mdma_desc: H6-style MBUS DMA descriptor + * @mdma_desc_dma: DMA address of @mdma_desc * @caps: NAND Controller capabilities */ struct sunxi_nfc { @@ -362,6 +393,9 @@ struct sunxi_nfc { struct list_head chips; struct completion complete; struct dma_chan *dmac; + bool use_mdma; + struct sunxi_nfc_mdma_desc *mdma_desc; + dma_addr_t mdma_desc_dma; const struct sunxi_nfc_caps *caps; }; @@ -466,7 +500,10 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, { struct dma_async_tx_descriptor *dmad; enum dma_transfer_direction tdir; + dma_addr_t buf_dma; dma_cookie_t dmat; + int len = chunksize * nchunks; + u32 data_blocks = nchunks; int ret; if (ddir == DMA_FROM_DEVICE) @@ -474,12 +511,21 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, else tdir = DMA_MEM_TO_DEV; - sg_init_one(sg, buf, nchunks * chunksize); + sg_init_one(sg, buf, len); ret = dma_map_sg(nfc->dev, sg, 1, ddir); if (!ret) return -ENOMEM; - if (!nfc->caps->has_mdma) { + buf_dma = sg_dma_address(sg); + + if (nfc->mdma_desc && + (len > NFC_MDMA_DESC_SIZE_MASK || !IS_ALIGNED(len, 8) || + !IS_ALIGNED(buf_dma, 4))) { + ret = -EINVAL; + goto err_unmap_buf; + } + + if (!nfc->use_mdma) { dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); if (!dmad) { ret = -EINVAL; @@ -489,14 +535,35 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, nfc->regs + NFC_REG_CTL); - writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); + + /* H6/H616 use one enable bit per ECC data block. */ + if (nfc->caps->has_mdma_desc) + data_blocks = GENMASK(nchunks - 1, 0); + writel(data_blocks, nfc->regs + NFC_REG_SECTOR_NUM); writel(chunksize, nfc->regs + NFC_REG_CNT); - if (nfc->caps->has_mdma) { + if (nfc->use_mdma) writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL); - writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT); - writel(sg_dma_address(sg), nfc->regs + NFC_REG_MDMA_ADDR); + + if (nfc->mdma_desc) { + struct sunxi_nfc_mdma_desc *desc = nfc->mdma_desc; + + desc->config = cpu_to_le32(NFC_MDMA_DESC_FIRST | + NFC_MDMA_DESC_LAST); + desc->size = cpu_to_le32(len); + /* Descriptor words are little-endian DMA memory, not MMIO. */ + desc->buf = cpu_to_le32(buf_dma); + desc->next = cpu_to_le32(nfc->mdma_desc_dma); + + writel(NFC_H6_MDMA_STA_DESC0_COMPLETE, + nfc->regs + NFC_REG_H6_MDMA_STA); + dma_wmb(); + writel(nfc->mdma_desc_dma, + nfc->regs + NFC_REG_H6_MDMA_DLBA_REG); + } else if (nfc->caps->has_mdma) { + writel(len, nfc->regs + NFC_REG_MDMA_CNT); + writel(buf_dma, nfc->regs + NFC_REG_MDMA_ADDR); } else { dmat = dmaengine_submit(dmad); @@ -525,6 +592,14 @@ static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc, nfc->regs + NFC_REG_CTL); } +static void sunxi_nfc_dma_op_abort(struct sunxi_nfc *nfc) +{ + if (nfc->use_mdma) + sunxi_nfc_rst(nfc); + else + dmaengine_terminate_all(nfc->dmac); +} + static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs) { struct mtd_info *mtd = nand_to_mtd(nand); @@ -1202,7 +1277,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf wait = NFC_CMD_INT_FLAG; - if (nfc->caps->has_mdma) + if (nfc->use_mdma) wait |= NFC_DMA_INT_FLAG; else dma_async_issue_pending(nfc->dmac); @@ -1211,8 +1286,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf nfc->regs + NFC_REG_CMD); ret = sunxi_nfc_wait_events(nfc, wait, false, 0); - if (ret && !nfc->caps->has_mdma) - dmaengine_terminate_all(nfc->dmac); + if (ret) + sunxi_nfc_dma_op_abort(nfc); sunxi_nfc_randomizer_disable(nand); sunxi_nfc_hw_ecc_disable(nand); @@ -1613,7 +1688,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, wait = NFC_CMD_INT_FLAG; - if (nfc->caps->has_mdma) + if (nfc->use_mdma) wait |= NFC_DMA_INT_FLAG; else dma_async_issue_pending(nfc->dmac); @@ -1623,8 +1698,8 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, nfc->regs + NFC_REG_CMD); ret = sunxi_nfc_wait_events(nfc, wait, false, 0); - if (ret && !nfc->caps->has_mdma) - dmaengine_terminate_all(nfc->dmac); + if (ret) + sunxi_nfc_dma_op_abort(nfc); sunxi_nfc_randomizer_disable(nand); sunxi_nfc_hw_ecc_disable(nand); @@ -1667,8 +1742,21 @@ static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page) return nand_prog_page_end_op(nand); } -static const s32 tWB_lut[] = {6, 12, 16, 20}; -static const s32 tRHW_lut[] = {4, 8, 12, 20}; +static const struct sunxi_nfc_timings sun4i_a10_nfc_timings = { + .setup_cycles = 1, + .tWB = { 6, 12, 16, 20 }, + .tADL = { 7, 15, 23, 31 }, + .tWHR = { 7, 15, 23, 31 }, + .tRHW = { 4, 8, 12, 20 }, +}; + +static const struct sunxi_nfc_timings sun50i_h616_nfc_timings = { + .setup_cycles = 2, + .tWB = { 28, 44, 60, 76 }, + .tADL = { 0, 12, 28, 44 }, + .tWHR = { 0, 12, 28, 44 }, + .tRHW = { 8, 24, 40, 56 }, +}; static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, u32 clk_period) @@ -1693,6 +1781,7 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, { struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + const struct sunxi_nfc_timings *nfc_timings = nfc->caps->timings; const struct nand_sdr_timings *timings; u32 min_clk_period = 0; s32 tWB, tADL, tWHR, tRHW, tCAD; @@ -1703,20 +1792,28 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, return -ENOTSUPP; /* T1 <=> tCLS */ - if (timings->tCLS_min > min_clk_period) - min_clk_period = timings->tCLS_min; + if (timings->tCLS_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tCLS_min, + nfc_timings->setup_cycles); /* T2 <=> tCLH */ - if (timings->tCLH_min > min_clk_period) - min_clk_period = timings->tCLH_min; + if (timings->tCLH_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tCLH_min, + nfc_timings->setup_cycles); /* T3 <=> tCS */ - if (timings->tCS_min > min_clk_period) - min_clk_period = timings->tCS_min; + if (timings->tCS_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tCS_min, + nfc_timings->setup_cycles); /* T4 <=> tCH */ - if (timings->tCH_min > min_clk_period) - min_clk_period = timings->tCH_min; + if (timings->tCH_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tCH_min, + nfc_timings->setup_cycles); /* T5 <=> tWP */ if (timings->tWP_min > min_clk_period) @@ -1727,8 +1824,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, min_clk_period = timings->tWH_min; /* T7 <=> tALS */ - if (timings->tALS_min > min_clk_period) - min_clk_period = timings->tALS_min; + if (timings->tALS_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tALS_min, + nfc_timings->setup_cycles); /* T8 <=> tDS */ if (timings->tDS_min > min_clk_period) @@ -1743,8 +1842,10 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); /* T11 <=> tALH */ - if (timings->tALH_min > min_clk_period) - min_clk_period = timings->tALH_min; + if (timings->tALH_min > + min_clk_period * nfc_timings->setup_cycles) + min_clk_period = DIV_ROUND_UP(timings->tALH_min, + nfc_timings->setup_cycles); /* T12 <=> tRP */ if (timings->tRP_min > min_clk_period) @@ -1763,17 +1864,25 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); /* T16 - T19 + tCAD */ - if (timings->tWB_max > (min_clk_period * 20)) - min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20); + if (timings->tWB_max > + (min_clk_period * nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1])) + min_clk_period = DIV_ROUND_UP(timings->tWB_max, + nfc_timings->tWB[SUNXI_NFC_TIMING_STEPS - 1]); - if (timings->tADL_min > (min_clk_period * 32)) - min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32); + if (timings->tADL_min > + (min_clk_period * nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1])) + min_clk_period = DIV_ROUND_UP(timings->tADL_min, + nfc_timings->tADL[SUNXI_NFC_TIMING_STEPS - 1]); - if (timings->tWHR_min > (min_clk_period * 32)) - min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32); + if (timings->tWHR_min > + (min_clk_period * nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1])) + min_clk_period = DIV_ROUND_UP(timings->tWHR_min, + nfc_timings->tWHR[SUNXI_NFC_TIMING_STEPS - 1]); - if (timings->tRHW_min > (min_clk_period * 20)) - min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20); + if (timings->tRHW_min > + (min_clk_period * nfc_timings->tRHW[SUNXI_NFC_TIMING_STEPS - 1])) + min_clk_period = DIV_ROUND_UP(timings->tRHW_min, + nfc_timings->tRHW[SUNXI_NFC_TIMING_STEPS - 1]); /* * In non-EDO, tREA should be less than tRP to guarantee that the @@ -1789,26 +1898,28 @@ static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, if (timings->tREA_max > min_clk_period && !timings->tRLOH_min) min_clk_period = timings->tREA_max; - tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, + tWB = sunxi_nand_lookup_timing(nfc_timings->tWB, timings->tWB_max, min_clk_period); if (tWB < 0) { dev_err(nfc->dev, "unsupported tWB\n"); return tWB; } - tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; - if (tADL > 3) { + tADL = sunxi_nand_lookup_timing(nfc_timings->tADL, + timings->tADL_min, min_clk_period); + if (tADL < 0) { dev_err(nfc->dev, "unsupported tADL\n"); - return -EINVAL; + return tADL; } - tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; - if (tWHR > 3) { + tWHR = sunxi_nand_lookup_timing(nfc_timings->tWHR, + timings->tWHR_min, min_clk_period); + if (tWHR < 0) { dev_err(nfc->dev, "unsupported tWHR\n"); - return -EINVAL; + return tWHR; } - tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, + tRHW = sunxi_nand_lookup_timing(nfc_timings->tRHW, timings->tRHW_min, min_clk_period); if (tRHW < 0) { dev_err(nfc->dev, "unsupported tRHW\n"); @@ -2073,11 +2184,13 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->write_oob = sunxi_nfc_hw_ecc_write_oob; mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); - if (nfc->dmac || nfc->caps->has_mdma) { + if (nfc->dmac || nfc->use_mdma) { ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma; nand->options |= NAND_USES_DMA; + if (nfc->mdma_desc) + nand->buf_align = 4; } else { ecc->read_page = sunxi_nfc_hw_ecc_read_page; ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; @@ -2426,8 +2539,32 @@ static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r) { int ret; - if (nfc->caps->has_mdma) + if (nfc->caps->has_mdma_desc) { + ret = dma_set_mask_and_coherent(nfc->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_warn(nfc->dev, + "failed to set MBUS DMA mask, using PIO: %d\n", + ret); + return 0; + } + + nfc->mdma_desc = + dmam_alloc_coherent(nfc->dev, sizeof(*nfc->mdma_desc), + &nfc->mdma_desc_dma, GFP_KERNEL); + if (!nfc->mdma_desc) { + dev_warn(nfc->dev, + "failed to allocate MBUS DMA descriptor, using PIO\n"); + return 0; + } + + nfc->use_mdma = true; return 0; + } + + if (nfc->caps->has_mdma) { + nfc->use_mdma = true; + return 0; + } nfc->dmac = dma_request_chan(nfc->dev, "rxtx"); if (IS_ERR(nfc->dmac)) { @@ -2595,6 +2732,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), .max_ecc_steps = 16, .sram_size = 1024, + .timings = &sun4i_a10_nfc_timings, }; static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { @@ -2617,9 +2755,11 @@ static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10), .max_ecc_steps = 16, .sram_size = 1024, + .timings = &sun4i_a10_nfc_timings, }; static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { + .has_mdma_desc = true, .has_ecc_clk = true, .has_mbus_clk = true, .reg_io_data = NFC_REG_A23_IO_DATA, @@ -2641,6 +2781,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = { .nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6), .max_ecc_steps = 32, .sram_size = 8192, + .timings = &sun50i_h616_nfc_timings, }; static const struct of_device_id sunxi_nfc_ids[] = { 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..6352e9c9e502 100644 --- a/drivers/mtd/nand/spi/fmsh.c +++ b/drivers/mtd/nand/spi/fmsh.c @@ -9,6 +9,16 @@ #include #include +#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,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 SPINAND_OP_VARIANTS(fm25g_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 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 +180,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 +196,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(&fm25g_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(&fm25g_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 - the main driver logic + * Aleksei Sviridkin - adaptation to the mainline Linux kernel + * + * Based on: + * https://github.com/keenetic/kernel-49/commit/bacade569fb12bc0ad31ba09bca9b890118fbca7 + */ + +#include +#include +#include + +#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/afs.c b/drivers/mtd/parsers/afs.c index 26116694c821..7ab3d50f565e 100644 --- a/drivers/mtd/parsers/afs.c +++ b/drivers/mtd/parsers/afs.c @@ -235,6 +235,9 @@ static int afs_parse_v2_partition(struct mtd_info *mtd, pr_debug("Parsing v2 partition @%08x-%08x\n", off, off + mtd->erasesize); + if (mtd->erasesize < sizeof(footer)) + return -EINVAL; + /* First read the footer */ ptr = off + mtd->erasesize - sizeof(footer); ret = mtd_read(mtd, ptr, sizeof(footer), &sz, (u_char *)footer); @@ -245,6 +248,8 @@ static int afs_parse_v2_partition(struct mtd_info *mtd, } name = (char *) &footer[0]; version = footer[9]; + if (footer[8] > mtd->erasesize - sizeof(footer)) + return -EINVAL; ptr = off + mtd->erasesize - sizeof(footer) - footer[8]; pr_debug("found image \"%s\", version %08x, info @%08x\n", @@ -278,6 +283,8 @@ static int afs_parse_v2_partition(struct mtd_info *mtd, entrypoint = imginfo[pad]; attributes = imginfo[pad+1]; region_count = imginfo[pad+2]; + if (region_count > (ARRAY_SIZE(imginfo) - pad - 3) / 4) + return -EINVAL; block_start = imginfo[20]; block_end = imginfo[21]; 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 @@ static int parse_redboot_partitions(struct mtd_info *master, 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 @@ static int parse_redboot_partitions(struct mtd_info *master, 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 -#include #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 */