From a126483e82957172b8a93ebb1d30fb2b1df3cbbc Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 6 Jun 2019 17:07:55 +0000 Subject: mtd: spinand: Fix max_bad_eraseblocks_per_lun info in memorg The 1Gb Macronix chip can have a maximum of 20 bad blocks, while the 2Gb version has twice as many blocks and therefore the maximum number of bad blocks is 40. The 4Gb GigaDevice GD5F4GQ4xA has twice as many blocks as its 2Gb counterpart and therefore a maximum of 80 bad blocks. Fixes: 377e517b5fa5 ("mtd: nand: Add max_bad_eraseblocks_per_lun info to memorg") Reported-by: Emil Lenngren Signed-off-by: Frieder Schrempf Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/gigadevice.c | 2 +- drivers/mtd/nand/spi/macronix.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index e5586390026a..e6c646007cda 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -180,7 +180,7 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), SPINAND_INFO("GD5F4GQ4xA", 0xF4, - NAND_MEMORG(1, 2048, 64, 64, 4096, 40, 1, 1, 1), + NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 6502727049a8..21def3f8fb36 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -100,7 +100,7 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO("MX35LF1GE4AB", 0x12, - NAND_MEMORG(1, 2048, 64, 64, 1024, 40, 1, 1, 1), + 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, @@ -109,7 +109,7 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), SPINAND_INFO("MX35LF2GE4AB", 0x22, - NAND_MEMORG(1, 2048, 64, 64, 2048, 20, 2, 1, 1), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, -- cgit From c403ec33b613a15d9fd8dde37f246b79cd56b5df Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Sat, 29 Jun 2019 03:22:48 +0200 Subject: mtd: rawnand: ingenic: Fix ingenic_ecc dependency If MTD_NAND_JZ4780 is y and MTD_NAND_JZ4780_BCH is m, which select CONFIG_MTD_NAND_INGENIC_ECC to m, building fails: drivers/mtd/nand/raw/ingenic/ingenic_nand.o: In function `ingenic_nand_remove': ingenic_nand.c:(.text+0x177): undefined reference to `ingenic_ecc_release' drivers/mtd/nand/raw/ingenic/ingenic_nand.o: In function `ingenic_nand_ecc_correct': ingenic_nand.c:(.text+0x2ee): undefined reference to `ingenic_ecc_correct' To fix that, the ingenic_nand and ingenic_ecc modules have been fused into one single module. - The ingenic_ecc.c code is now compiled in only if $(CONFIG_MTD_NAND_INGENIC_ECC) is set. This is now a boolean instead of tristate. - To avoid changing the module name, the ingenic_nand.c file is moved to ingenic_nand_drv.c. Then the module name is still ingenic_nand. - Since ingenic_ecc.c is no more a module, the module-specific macros have been dropped, and the functions are no more exported for use by the ingenic_nand driver. Fixes: 15de8c6efd0e ("mtd: rawnand: ingenic: Separate top-level and SoC specific code") Signed-off-by: Paul Cercueil Reported-by: Arnd Bergmann Reported-by: Hulk Robot Cc: YueHaibing Cc: stable@vger.kernel.org Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/ingenic/Kconfig | 2 +- drivers/mtd/nand/raw/ingenic/Makefile | 4 +- drivers/mtd/nand/raw/ingenic/ingenic_ecc.c | 9 - drivers/mtd/nand/raw/ingenic/ingenic_nand.c | 530 ------------------------ drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c | 530 ++++++++++++++++++++++++ 5 files changed, 534 insertions(+), 541 deletions(-) delete mode 100644 drivers/mtd/nand/raw/ingenic/ingenic_nand.c create mode 100644 drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c diff --git a/drivers/mtd/nand/raw/ingenic/Kconfig b/drivers/mtd/nand/raw/ingenic/Kconfig index 19a96ce515c1..66b7cffdb0c2 100644 --- a/drivers/mtd/nand/raw/ingenic/Kconfig +++ b/drivers/mtd/nand/raw/ingenic/Kconfig @@ -16,7 +16,7 @@ config MTD_NAND_JZ4780 if MTD_NAND_JZ4780 config MTD_NAND_INGENIC_ECC - tristate + bool config MTD_NAND_JZ4740_ECC tristate "Hardware BCH support for JZ4740 SoC" diff --git a/drivers/mtd/nand/raw/ingenic/Makefile b/drivers/mtd/nand/raw/ingenic/Makefile index 1ac4f455baea..b63d36889263 100644 --- a/drivers/mtd/nand/raw/ingenic/Makefile +++ b/drivers/mtd/nand/raw/ingenic/Makefile @@ -2,7 +2,9 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_JZ4780) += ingenic_nand.o -obj-$(CONFIG_MTD_NAND_INGENIC_ECC) += ingenic_ecc.o +ingenic_nand-y += ingenic_nand_drv.o +ingenic_nand-$(CONFIG_MTD_NAND_INGENIC_ECC) += ingenic_ecc.o + obj-$(CONFIG_MTD_NAND_JZ4740_ECC) += jz4740_ecc.o obj-$(CONFIG_MTD_NAND_JZ4725B_BCH) += jz4725b_bch.o obj-$(CONFIG_MTD_NAND_JZ4780_BCH) += jz4780_bch.o diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c index d3e085c5685a..c954189606f6 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c @@ -30,7 +30,6 @@ int ingenic_ecc_calculate(struct ingenic_ecc *ecc, { return ecc->ops->calculate(ecc, params, buf, ecc_code); } -EXPORT_SYMBOL(ingenic_ecc_calculate); /** * ingenic_ecc_correct() - detect and correct bit errors @@ -51,7 +50,6 @@ int ingenic_ecc_correct(struct ingenic_ecc *ecc, { return ecc->ops->correct(ecc, params, buf, ecc_code); } -EXPORT_SYMBOL(ingenic_ecc_correct); /** * ingenic_ecc_get() - get the ECC controller device @@ -111,7 +109,6 @@ struct ingenic_ecc *of_ingenic_ecc_get(struct device_node *of_node) } return ecc; } -EXPORT_SYMBOL(of_ingenic_ecc_get); /** * ingenic_ecc_release() - release the ECC controller device @@ -122,7 +119,6 @@ void ingenic_ecc_release(struct ingenic_ecc *ecc) clk_disable_unprepare(ecc->clk); put_device(ecc->dev); } -EXPORT_SYMBOL(ingenic_ecc_release); int ingenic_ecc_probe(struct platform_device *pdev) { @@ -159,8 +155,3 @@ int ingenic_ecc_probe(struct platform_device *pdev) return 0; } EXPORT_SYMBOL(ingenic_ecc_probe); - -MODULE_AUTHOR("Alex Smith "); -MODULE_AUTHOR("Harvey Hunt "); -MODULE_DESCRIPTION("Ingenic ECC common driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand.c deleted file mode 100644 index d7b7c0f13909..000000000000 --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand.c +++ /dev/null @@ -1,530 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Ingenic JZ47xx NAND driver - * - * Copyright (c) 2015 Imagination Technologies - * Author: Alex Smith - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "ingenic_ecc.h" - -#define DRV_NAME "ingenic-nand" - -/* Command delay when there is no R/B pin. */ -#define RB_DELAY_US 100 - -struct jz_soc_info { - unsigned long data_offset; - unsigned long addr_offset; - unsigned long cmd_offset; - const struct mtd_ooblayout_ops *oob_layout; -}; - -struct ingenic_nand_cs { - unsigned int bank; - void __iomem *base; -}; - -struct ingenic_nfc { - struct device *dev; - struct ingenic_ecc *ecc; - const struct jz_soc_info *soc_info; - struct nand_controller controller; - unsigned int num_banks; - struct list_head chips; - int selected; - struct ingenic_nand_cs cs[]; -}; - -struct ingenic_nand { - struct nand_chip chip; - struct list_head chip_list; - - struct gpio_desc *busy_gpio; - struct gpio_desc *wp_gpio; - unsigned int reading: 1; -}; - -static inline struct ingenic_nand *to_ingenic_nand(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct ingenic_nand, chip); -} - -static inline struct ingenic_nfc *to_ingenic_nfc(struct nand_controller *ctrl) -{ - return container_of(ctrl, struct ingenic_nfc, controller); -} - -static int qi_lb60_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section || !ecc->total) - return -ERANGE; - - oobregion->length = ecc->total; - oobregion->offset = 12; - - return 0; -} - -static int qi_lb60_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section) - return -ERANGE; - - oobregion->length = mtd->oobsize - ecc->total - 12; - oobregion->offset = 12 + ecc->total; - - return 0; -} - -const struct mtd_ooblayout_ops qi_lb60_ooblayout_ops = { - .ecc = qi_lb60_ooblayout_ecc, - .free = qi_lb60_ooblayout_free, -}; - -static int jz4725b_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section || !ecc->total) - return -ERANGE; - - oobregion->length = ecc->total; - oobregion->offset = 3; - - return 0; -} - -static int jz4725b_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section) - return -ERANGE; - - oobregion->length = mtd->oobsize - ecc->total - 3; - oobregion->offset = 3 + ecc->total; - - return 0; -} - -static const struct mtd_ooblayout_ops jz4725b_ooblayout_ops = { - .ecc = jz4725b_ooblayout_ecc, - .free = jz4725b_ooblayout_free, -}; - -static void ingenic_nand_select_chip(struct nand_chip *chip, int chipnr) -{ - struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); - struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller); - struct ingenic_nand_cs *cs; - - /* Ensure the currently selected chip is deasserted. */ - if (chipnr == -1 && nfc->selected >= 0) { - cs = &nfc->cs[nfc->selected]; - jz4780_nemc_assert(nfc->dev, cs->bank, false); - } - - nfc->selected = chipnr; -} - -static void ingenic_nand_cmd_ctrl(struct nand_chip *chip, int cmd, - unsigned int ctrl) -{ - struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); - struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller); - struct ingenic_nand_cs *cs; - - if (WARN_ON(nfc->selected < 0)) - return; - - cs = &nfc->cs[nfc->selected]; - - jz4780_nemc_assert(nfc->dev, cs->bank, ctrl & NAND_NCE); - - if (cmd == NAND_CMD_NONE) - return; - - if (ctrl & NAND_ALE) - writeb(cmd, cs->base + nfc->soc_info->addr_offset); - else if (ctrl & NAND_CLE) - writeb(cmd, cs->base + nfc->soc_info->cmd_offset); -} - -static int ingenic_nand_dev_ready(struct nand_chip *chip) -{ - struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); - - return !gpiod_get_value_cansleep(nand->busy_gpio); -} - -static void ingenic_nand_ecc_hwctl(struct nand_chip *chip, int mode) -{ - struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); - - nand->reading = (mode == NAND_ECC_READ); -} - -static int ingenic_nand_ecc_calculate(struct nand_chip *chip, const u8 *dat, - u8 *ecc_code) -{ - struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); - struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller); - struct ingenic_ecc_params params; - - /* - * Don't need to generate the ECC when reading, the ECC engine does it - * for us as part of decoding/correction. - */ - if (nand->reading) - return 0; - - params.size = nand->chip.ecc.size; - params.bytes = nand->chip.ecc.bytes; - params.strength = nand->chip.ecc.strength; - - return ingenic_ecc_calculate(nfc->ecc, ¶ms, dat, ecc_code); -} - -static int ingenic_nand_ecc_correct(struct nand_chip *chip, u8 *dat, - u8 *read_ecc, u8 *calc_ecc) -{ - struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); - struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller); - struct ingenic_ecc_params params; - - params.size = nand->chip.ecc.size; - params.bytes = nand->chip.ecc.bytes; - params.strength = nand->chip.ecc.strength; - - return ingenic_ecc_correct(nfc->ecc, ¶ms, dat, read_ecc); -} - -static int ingenic_nand_attach_chip(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct ingenic_nfc *nfc = to_ingenic_nfc(chip->controller); - int eccbytes; - - if (chip->ecc.strength == 4) { - /* JZ4740 uses 9 bytes of ECC to correct maximum 4 errors */ - chip->ecc.bytes = 9; - } else { - chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) * - (chip->ecc.strength / 8); - } - - switch (chip->ecc.mode) { - case NAND_ECC_HW: - if (!nfc->ecc) { - dev_err(nfc->dev, "HW ECC selected, but ECC controller not found\n"); - return -ENODEV; - } - - chip->ecc.hwctl = ingenic_nand_ecc_hwctl; - chip->ecc.calculate = ingenic_nand_ecc_calculate; - chip->ecc.correct = ingenic_nand_ecc_correct; - /* fall through */ - case NAND_ECC_SOFT: - dev_info(nfc->dev, "using %s (strength %d, size %d, bytes %d)\n", - (nfc->ecc) ? "hardware ECC" : "software ECC", - chip->ecc.strength, chip->ecc.size, chip->ecc.bytes); - break; - case NAND_ECC_NONE: - dev_info(nfc->dev, "not using ECC\n"); - break; - default: - dev_err(nfc->dev, "ECC mode %d not supported\n", - chip->ecc.mode); - return -EINVAL; - } - - /* The NAND core will generate the ECC layout for SW ECC */ - if (chip->ecc.mode != NAND_ECC_HW) - return 0; - - /* Generate ECC layout. ECC codes are right aligned in the OOB area. */ - eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes; - - if (eccbytes > mtd->oobsize - 2) { - dev_err(nfc->dev, - "invalid ECC config: required %d ECC bytes, but only %d are available", - eccbytes, mtd->oobsize - 2); - return -EINVAL; - } - - /* - * The generic layout for BBT markers will most likely overlap with our - * ECC bytes in the OOB, so move the BBT markers outside the OOB area. - */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) - chip->bbt_options |= NAND_BBT_NO_OOB; - - /* For legacy reasons we use a different layout on the qi,lb60 board. */ - if (of_machine_is_compatible("qi,lb60")) - mtd_set_ooblayout(mtd, &qi_lb60_ooblayout_ops); - else - mtd_set_ooblayout(mtd, nfc->soc_info->oob_layout); - - return 0; -} - -static const struct nand_controller_ops ingenic_nand_controller_ops = { - .attach_chip = ingenic_nand_attach_chip, -}; - -static int ingenic_nand_init_chip(struct platform_device *pdev, - struct ingenic_nfc *nfc, - struct device_node *np, - unsigned int chipnr) -{ - struct device *dev = &pdev->dev; - struct ingenic_nand *nand; - struct ingenic_nand_cs *cs; - struct resource *res; - struct nand_chip *chip; - struct mtd_info *mtd; - const __be32 *reg; - int ret = 0; - - cs = &nfc->cs[chipnr]; - - reg = of_get_property(np, "reg", NULL); - if (!reg) - return -EINVAL; - - cs->bank = be32_to_cpu(*reg); - - jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND); - - res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr); - cs->base = devm_ioremap_resource(dev, res); - if (IS_ERR(cs->base)) - return PTR_ERR(cs->base); - - nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL); - if (!nand) - return -ENOMEM; - - nand->busy_gpio = devm_gpiod_get_optional(dev, "rb", GPIOD_IN); - - if (IS_ERR(nand->busy_gpio)) { - ret = PTR_ERR(nand->busy_gpio); - dev_err(dev, "failed to request busy GPIO: %d\n", ret); - return ret; - } else if (nand->busy_gpio) { - nand->chip.legacy.dev_ready = ingenic_nand_dev_ready; - } - - nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW); - - if (IS_ERR(nand->wp_gpio)) { - ret = PTR_ERR(nand->wp_gpio); - dev_err(dev, "failed to request WP GPIO: %d\n", ret); - return ret; - } - - chip = &nand->chip; - mtd = nand_to_mtd(chip); - mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev), - cs->bank); - if (!mtd->name) - return -ENOMEM; - mtd->dev.parent = dev; - - chip->legacy.IO_ADDR_R = cs->base + nfc->soc_info->data_offset; - chip->legacy.IO_ADDR_W = cs->base + nfc->soc_info->data_offset; - chip->legacy.chip_delay = RB_DELAY_US; - chip->options = NAND_NO_SUBPAGE_WRITE; - chip->legacy.select_chip = ingenic_nand_select_chip; - chip->legacy.cmd_ctrl = ingenic_nand_cmd_ctrl; - chip->ecc.mode = NAND_ECC_HW; - chip->controller = &nfc->controller; - nand_set_flash_node(chip, np); - - chip->controller->ops = &ingenic_nand_controller_ops; - ret = nand_scan(chip, 1); - if (ret) - return ret; - - ret = mtd_device_register(mtd, NULL, 0); - if (ret) { - nand_release(chip); - return ret; - } - - list_add_tail(&nand->chip_list, &nfc->chips); - - return 0; -} - -static void ingenic_nand_cleanup_chips(struct ingenic_nfc *nfc) -{ - struct ingenic_nand *chip; - - while (!list_empty(&nfc->chips)) { - chip = list_first_entry(&nfc->chips, - struct ingenic_nand, chip_list); - nand_release(&chip->chip); - list_del(&chip->chip_list); - } -} - -static int ingenic_nand_init_chips(struct ingenic_nfc *nfc, - struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np; - int i = 0; - int ret; - int num_chips = of_get_child_count(dev->of_node); - - if (num_chips > nfc->num_banks) { - dev_err(dev, "found %d chips but only %d banks\n", - num_chips, nfc->num_banks); - return -EINVAL; - } - - for_each_child_of_node(dev->of_node, np) { - ret = ingenic_nand_init_chip(pdev, nfc, np, i); - if (ret) { - ingenic_nand_cleanup_chips(nfc); - return ret; - } - - i++; - } - - return 0; -} - -static int ingenic_nand_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - unsigned int num_banks; - struct ingenic_nfc *nfc; - int ret; - - num_banks = jz4780_nemc_num_banks(dev); - if (num_banks == 0) { - dev_err(dev, "no banks found\n"); - return -ENODEV; - } - - nfc = devm_kzalloc(dev, struct_size(nfc, cs, num_banks), GFP_KERNEL); - if (!nfc) - return -ENOMEM; - - nfc->soc_info = device_get_match_data(dev); - if (!nfc->soc_info) - return -EINVAL; - - /* - * Check for ECC HW before we call nand_scan_ident, to prevent us from - * having to call it again if the ECC driver returns -EPROBE_DEFER. - */ - nfc->ecc = of_ingenic_ecc_get(dev->of_node); - if (IS_ERR(nfc->ecc)) - return PTR_ERR(nfc->ecc); - - nfc->dev = dev; - nfc->num_banks = num_banks; - - nand_controller_init(&nfc->controller); - INIT_LIST_HEAD(&nfc->chips); - - ret = ingenic_nand_init_chips(nfc, pdev); - if (ret) { - if (nfc->ecc) - ingenic_ecc_release(nfc->ecc); - return ret; - } - - platform_set_drvdata(pdev, nfc); - return 0; -} - -static int ingenic_nand_remove(struct platform_device *pdev) -{ - struct ingenic_nfc *nfc = platform_get_drvdata(pdev); - - if (nfc->ecc) - ingenic_ecc_release(nfc->ecc); - - ingenic_nand_cleanup_chips(nfc); - - return 0; -} - -static const struct jz_soc_info jz4740_soc_info = { - .data_offset = 0x00000000, - .cmd_offset = 0x00008000, - .addr_offset = 0x00010000, - .oob_layout = &nand_ooblayout_lp_ops, -}; - -static const struct jz_soc_info jz4725b_soc_info = { - .data_offset = 0x00000000, - .cmd_offset = 0x00008000, - .addr_offset = 0x00010000, - .oob_layout = &jz4725b_ooblayout_ops, -}; - -static const struct jz_soc_info jz4780_soc_info = { - .data_offset = 0x00000000, - .cmd_offset = 0x00400000, - .addr_offset = 0x00800000, - .oob_layout = &nand_ooblayout_lp_ops, -}; - -static const struct of_device_id ingenic_nand_dt_match[] = { - { .compatible = "ingenic,jz4740-nand", .data = &jz4740_soc_info }, - { .compatible = "ingenic,jz4725b-nand", .data = &jz4725b_soc_info }, - { .compatible = "ingenic,jz4780-nand", .data = &jz4780_soc_info }, - {}, -}; -MODULE_DEVICE_TABLE(of, ingenic_nand_dt_match); - -static struct platform_driver ingenic_nand_driver = { - .probe = ingenic_nand_probe, - .remove = ingenic_nand_remove, - .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(ingenic_nand_dt_match), - }, -}; -module_platform_driver(ingenic_nand_driver); - -MODULE_AUTHOR("Alex Smith "); -MODULE_AUTHOR("Harvey Hunt "); -MODULE_DESCRIPTION("Ingenic JZ47xx NAND driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c new file mode 100644 index 000000000000..d7b7c0f13909 --- /dev/null +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Ingenic JZ47xx NAND driver + * + * Copyright (c) 2015 Imagination Technologies + * Author: Alex Smith + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ingenic_ecc.h" + +#define DRV_NAME "ingenic-nand" + +/* Command delay when there is no R/B pin. */ +#define RB_DELAY_US 100 + +struct jz_soc_info { + unsigned long data_offset; + unsigned long addr_offset; + unsigned long cmd_offset; + const struct mtd_ooblayout_ops *oob_layout; +}; + +struct ingenic_nand_cs { + unsigned int bank; + void __iomem *base; +}; + +struct ingenic_nfc { + struct device *dev; + struct ingenic_ecc *ecc; + const struct jz_soc_info *soc_info; + struct nand_controller controller; + unsigned int num_banks; + struct list_head chips; + int selected; + struct ingenic_nand_cs cs[]; +}; + +struct ingenic_nand { + struct nand_chip chip; + struct list_head chip_list; + + struct gpio_desc *busy_gpio; + struct gpio_desc *wp_gpio; + unsigned int reading: 1; +}; + +static inline struct ingenic_nand *to_ingenic_nand(struct mtd_info *mtd) +{ + return container_of(mtd_to_nand(mtd), struct ingenic_nand, chip); +} + +static inline struct ingenic_nfc *to_ingenic_nfc(struct nand_controller *ctrl) +{ + return container_of(ctrl, struct ingenic_nfc, controller); +} + +static int qi_lb60_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section || !ecc->total) + return -ERANGE; + + oobregion->length = ecc->total; + oobregion->offset = 12; + + return 0; +} + +static int qi_lb60_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + oobregion->length = mtd->oobsize - ecc->total - 12; + oobregion->offset = 12 + ecc->total; + + return 0; +} + +const struct mtd_ooblayout_ops qi_lb60_ooblayout_ops = { + .ecc = qi_lb60_ooblayout_ecc, + .free = qi_lb60_ooblayout_free, +}; + +static int jz4725b_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section || !ecc->total) + return -ERANGE; + + oobregion->length = ecc->total; + oobregion->offset = 3; + + return 0; +} + +static int jz4725b_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + oobregion->length = mtd->oobsize - ecc->total - 3; + oobregion->offset = 3 + ecc->total; + + return 0; +} + +static const struct mtd_ooblayout_ops jz4725b_ooblayout_ops = { + .ecc = jz4725b_ooblayout_ecc, + .free = jz4725b_ooblayout_free, +}; + +static void ingenic_nand_select_chip(struct nand_chip *chip, int chipnr) +{ + struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); + struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller); + struct ingenic_nand_cs *cs; + + /* Ensure the currently selected chip is deasserted. */ + if (chipnr == -1 && nfc->selected >= 0) { + cs = &nfc->cs[nfc->selected]; + jz4780_nemc_assert(nfc->dev, cs->bank, false); + } + + nfc->selected = chipnr; +} + +static void ingenic_nand_cmd_ctrl(struct nand_chip *chip, int cmd, + unsigned int ctrl) +{ + struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); + struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller); + struct ingenic_nand_cs *cs; + + if (WARN_ON(nfc->selected < 0)) + return; + + cs = &nfc->cs[nfc->selected]; + + jz4780_nemc_assert(nfc->dev, cs->bank, ctrl & NAND_NCE); + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_ALE) + writeb(cmd, cs->base + nfc->soc_info->addr_offset); + else if (ctrl & NAND_CLE) + writeb(cmd, cs->base + nfc->soc_info->cmd_offset); +} + +static int ingenic_nand_dev_ready(struct nand_chip *chip) +{ + struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); + + return !gpiod_get_value_cansleep(nand->busy_gpio); +} + +static void ingenic_nand_ecc_hwctl(struct nand_chip *chip, int mode) +{ + struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); + + nand->reading = (mode == NAND_ECC_READ); +} + +static int ingenic_nand_ecc_calculate(struct nand_chip *chip, const u8 *dat, + u8 *ecc_code) +{ + struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); + struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller); + struct ingenic_ecc_params params; + + /* + * Don't need to generate the ECC when reading, the ECC engine does it + * for us as part of decoding/correction. + */ + if (nand->reading) + return 0; + + params.size = nand->chip.ecc.size; + params.bytes = nand->chip.ecc.bytes; + params.strength = nand->chip.ecc.strength; + + return ingenic_ecc_calculate(nfc->ecc, ¶ms, dat, ecc_code); +} + +static int ingenic_nand_ecc_correct(struct nand_chip *chip, u8 *dat, + u8 *read_ecc, u8 *calc_ecc) +{ + struct ingenic_nand *nand = to_ingenic_nand(nand_to_mtd(chip)); + struct ingenic_nfc *nfc = to_ingenic_nfc(nand->chip.controller); + struct ingenic_ecc_params params; + + params.size = nand->chip.ecc.size; + params.bytes = nand->chip.ecc.bytes; + params.strength = nand->chip.ecc.strength; + + return ingenic_ecc_correct(nfc->ecc, ¶ms, dat, read_ecc); +} + +static int ingenic_nand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct ingenic_nfc *nfc = to_ingenic_nfc(chip->controller); + int eccbytes; + + if (chip->ecc.strength == 4) { + /* JZ4740 uses 9 bytes of ECC to correct maximum 4 errors */ + chip->ecc.bytes = 9; + } else { + chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) * + (chip->ecc.strength / 8); + } + + switch (chip->ecc.mode) { + case NAND_ECC_HW: + if (!nfc->ecc) { + dev_err(nfc->dev, "HW ECC selected, but ECC controller not found\n"); + return -ENODEV; + } + + chip->ecc.hwctl = ingenic_nand_ecc_hwctl; + chip->ecc.calculate = ingenic_nand_ecc_calculate; + chip->ecc.correct = ingenic_nand_ecc_correct; + /* fall through */ + case NAND_ECC_SOFT: + dev_info(nfc->dev, "using %s (strength %d, size %d, bytes %d)\n", + (nfc->ecc) ? "hardware ECC" : "software ECC", + chip->ecc.strength, chip->ecc.size, chip->ecc.bytes); + break; + case NAND_ECC_NONE: + dev_info(nfc->dev, "not using ECC\n"); + break; + default: + dev_err(nfc->dev, "ECC mode %d not supported\n", + chip->ecc.mode); + return -EINVAL; + } + + /* The NAND core will generate the ECC layout for SW ECC */ + if (chip->ecc.mode != NAND_ECC_HW) + return 0; + + /* Generate ECC layout. ECC codes are right aligned in the OOB area. */ + eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes; + + if (eccbytes > mtd->oobsize - 2) { + dev_err(nfc->dev, + "invalid ECC config: required %d ECC bytes, but only %d are available", + eccbytes, mtd->oobsize - 2); + return -EINVAL; + } + + /* + * The generic layout for BBT markers will most likely overlap with our + * ECC bytes in the OOB, so move the BBT markers outside the OOB area. + */ + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + /* For legacy reasons we use a different layout on the qi,lb60 board. */ + if (of_machine_is_compatible("qi,lb60")) + mtd_set_ooblayout(mtd, &qi_lb60_ooblayout_ops); + else + mtd_set_ooblayout(mtd, nfc->soc_info->oob_layout); + + return 0; +} + +static const struct nand_controller_ops ingenic_nand_controller_ops = { + .attach_chip = ingenic_nand_attach_chip, +}; + +static int ingenic_nand_init_chip(struct platform_device *pdev, + struct ingenic_nfc *nfc, + struct device_node *np, + unsigned int chipnr) +{ + struct device *dev = &pdev->dev; + struct ingenic_nand *nand; + struct ingenic_nand_cs *cs; + struct resource *res; + struct nand_chip *chip; + struct mtd_info *mtd; + const __be32 *reg; + int ret = 0; + + cs = &nfc->cs[chipnr]; + + reg = of_get_property(np, "reg", NULL); + if (!reg) + return -EINVAL; + + cs->bank = be32_to_cpu(*reg); + + jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND); + + res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr); + cs->base = devm_ioremap_resource(dev, res); + if (IS_ERR(cs->base)) + return PTR_ERR(cs->base); + + nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL); + if (!nand) + return -ENOMEM; + + nand->busy_gpio = devm_gpiod_get_optional(dev, "rb", GPIOD_IN); + + if (IS_ERR(nand->busy_gpio)) { + ret = PTR_ERR(nand->busy_gpio); + dev_err(dev, "failed to request busy GPIO: %d\n", ret); + return ret; + } else if (nand->busy_gpio) { + nand->chip.legacy.dev_ready = ingenic_nand_dev_ready; + } + + nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW); + + if (IS_ERR(nand->wp_gpio)) { + ret = PTR_ERR(nand->wp_gpio); + dev_err(dev, "failed to request WP GPIO: %d\n", ret); + return ret; + } + + chip = &nand->chip; + mtd = nand_to_mtd(chip); + mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%s.%d", dev_name(dev), + cs->bank); + if (!mtd->name) + return -ENOMEM; + mtd->dev.parent = dev; + + chip->legacy.IO_ADDR_R = cs->base + nfc->soc_info->data_offset; + chip->legacy.IO_ADDR_W = cs->base + nfc->soc_info->data_offset; + chip->legacy.chip_delay = RB_DELAY_US; + chip->options = NAND_NO_SUBPAGE_WRITE; + chip->legacy.select_chip = ingenic_nand_select_chip; + chip->legacy.cmd_ctrl = ingenic_nand_cmd_ctrl; + chip->ecc.mode = NAND_ECC_HW; + chip->controller = &nfc->controller; + nand_set_flash_node(chip, np); + + chip->controller->ops = &ingenic_nand_controller_ops; + ret = nand_scan(chip, 1); + if (ret) + return ret; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + nand_release(chip); + return ret; + } + + list_add_tail(&nand->chip_list, &nfc->chips); + + return 0; +} + +static void ingenic_nand_cleanup_chips(struct ingenic_nfc *nfc) +{ + struct ingenic_nand *chip; + + while (!list_empty(&nfc->chips)) { + chip = list_first_entry(&nfc->chips, + struct ingenic_nand, chip_list); + nand_release(&chip->chip); + list_del(&chip->chip_list); + } +} + +static int ingenic_nand_init_chips(struct ingenic_nfc *nfc, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np; + int i = 0; + int ret; + int num_chips = of_get_child_count(dev->of_node); + + if (num_chips > nfc->num_banks) { + dev_err(dev, "found %d chips but only %d banks\n", + num_chips, nfc->num_banks); + return -EINVAL; + } + + for_each_child_of_node(dev->of_node, np) { + ret = ingenic_nand_init_chip(pdev, nfc, np, i); + if (ret) { + ingenic_nand_cleanup_chips(nfc); + return ret; + } + + i++; + } + + return 0; +} + +static int ingenic_nand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + unsigned int num_banks; + struct ingenic_nfc *nfc; + int ret; + + num_banks = jz4780_nemc_num_banks(dev); + if (num_banks == 0) { + dev_err(dev, "no banks found\n"); + return -ENODEV; + } + + nfc = devm_kzalloc(dev, struct_size(nfc, cs, num_banks), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->soc_info = device_get_match_data(dev); + if (!nfc->soc_info) + return -EINVAL; + + /* + * Check for ECC HW before we call nand_scan_ident, to prevent us from + * having to call it again if the ECC driver returns -EPROBE_DEFER. + */ + nfc->ecc = of_ingenic_ecc_get(dev->of_node); + if (IS_ERR(nfc->ecc)) + return PTR_ERR(nfc->ecc); + + nfc->dev = dev; + nfc->num_banks = num_banks; + + nand_controller_init(&nfc->controller); + INIT_LIST_HEAD(&nfc->chips); + + ret = ingenic_nand_init_chips(nfc, pdev); + if (ret) { + if (nfc->ecc) + ingenic_ecc_release(nfc->ecc); + return ret; + } + + platform_set_drvdata(pdev, nfc); + return 0; +} + +static int ingenic_nand_remove(struct platform_device *pdev) +{ + struct ingenic_nfc *nfc = platform_get_drvdata(pdev); + + if (nfc->ecc) + ingenic_ecc_release(nfc->ecc); + + ingenic_nand_cleanup_chips(nfc); + + return 0; +} + +static const struct jz_soc_info jz4740_soc_info = { + .data_offset = 0x00000000, + .cmd_offset = 0x00008000, + .addr_offset = 0x00010000, + .oob_layout = &nand_ooblayout_lp_ops, +}; + +static const struct jz_soc_info jz4725b_soc_info = { + .data_offset = 0x00000000, + .cmd_offset = 0x00008000, + .addr_offset = 0x00010000, + .oob_layout = &jz4725b_ooblayout_ops, +}; + +static const struct jz_soc_info jz4780_soc_info = { + .data_offset = 0x00000000, + .cmd_offset = 0x00400000, + .addr_offset = 0x00800000, + .oob_layout = &nand_ooblayout_lp_ops, +}; + +static const struct of_device_id ingenic_nand_dt_match[] = { + { .compatible = "ingenic,jz4740-nand", .data = &jz4740_soc_info }, + { .compatible = "ingenic,jz4725b-nand", .data = &jz4725b_soc_info }, + { .compatible = "ingenic,jz4780-nand", .data = &jz4780_soc_info }, + {}, +}; +MODULE_DEVICE_TABLE(of, ingenic_nand_dt_match); + +static struct platform_driver ingenic_nand_driver = { + .probe = ingenic_nand_probe, + .remove = ingenic_nand_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(ingenic_nand_dt_match), + }, +}; +module_platform_driver(ingenic_nand_driver); + +MODULE_AUTHOR("Alex Smith "); +MODULE_AUTHOR("Harvey Hunt "); +MODULE_DESCRIPTION("Ingenic JZ47xx NAND driver"); +MODULE_LICENSE("GPL v2"); -- cgit From 4f032640bf5751ce792b69d2abdf18e0f379b3ce Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 5 Jul 2019 11:25:29 +0200 Subject: Revert "mtd: rawnand: sunxi: Add A23/A33 DMA support" This reverts commit c49836f05aa15282f7280e06ede3f6f8a6324833. The commit is wrong and its approach actually does not work. Let's revert it in order to add the feature with a clean patch. Fixes: c49836f05aa1 ("mtd: rawnand: sunxi: Add A23/A33 DMA support") Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/sunxi_nand.c | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index b021a5720b42..e93f39bc2bc5 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -43,7 +43,6 @@ #define NFC_REG_RCMD_SET 0x0028 #define NFC_REG_WCMD_SET 0x002C #define NFC_REG_A10_IO_DATA 0x0030 -#define NFC_REG_A23_IO_DATA 0x0300 #define NFC_REG_ECC_CTL 0x0034 #define NFC_REG_ECC_ST 0x0038 #define NFC_REG_DEBUG 0x003C @@ -205,14 +204,10 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * NAND Controller capabilities structure: stores NAND controller capabilities * for distinction between compatible strings. * - * @sram_through_ahb: On A23, we choose to access the internal RAM through AHB - * instead of MBUS (less configuration). A10, A10s, A13 and - * A20 use the MBUS but no extra configuration is needed. * @reg_io_data: I/O data register * @dma_maxburst: DMA maxburst */ struct sunxi_nfc_caps { - bool sram_through_ahb; unsigned int reg_io_data; unsigned int dma_maxburst; }; @@ -368,29 +363,10 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, goto err_unmap_buf; } - /* - * On A23, we suppose the "internal RAM" (p.12 of the NFC user manual) - * refers to the NAND controller's internal SRAM. This memory is mapped - * and so is accessible from the AHB. It seems that it can also be - * accessed by the MBUS. MBUS accesses are mandatory when using the - * internal DMA instead of the external DMA engine. - * - * During DMA I/O operation, either we access this memory from the AHB - * by clearing the NFC_RAM_METHOD bit, or we set the bit and use the - * MBUS. In this case, we should also configure the MBUS DMA length - * NFC_REG_MDMA_CNT(0xC4) to be chunksize * nchunks. NAND I/O over MBUS - * are also limited to 32kiB pages. - */ - if (nfc->caps->sram_through_ahb) - writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, - nfc->regs + NFC_REG_CTL); - else - writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, - nfc->regs + NFC_REG_CTL); - + writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, + nfc->regs + NFC_REG_CTL); writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); writel(chunksize, nfc->regs + NFC_REG_CNT); - dmat = dmaengine_submit(dmad); ret = dma_submit_error(dmat); @@ -2199,21 +2175,11 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .dma_maxburst = 4, }; -static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { - .sram_through_ahb = true, - .reg_io_data = NFC_REG_A23_IO_DATA, - .dma_maxburst = 8, -}; - static const struct of_device_id sunxi_nfc_ids[] = { { .compatible = "allwinner,sun4i-a10-nand", .data = &sunxi_nfc_a10_caps, }, - { - .compatible = "allwinner,sun8i-a23-nand-controller", - .data = &sunxi_nfc_a23_caps, - }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_nfc_ids); -- cgit From c7a87ceb17aee9222c069a97aee4647260c7b3a6 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 8 Apr 2019 09:41:46 +0200 Subject: mtd: rawnand: sunxi: Add A23/A33 DMA support with extra MBUS configuration Allwinner NAND controllers can make use of DMA to enhance the I/O throughput thanks to ECC pipelining. DMA handling with A23/A33 NAND IP is a bit different than with the older SoCs, hence the introduction of a new compatible to handle: * the differences between register offsets, * the burst length change from 4 to minimum 8, * manage SRAM accesses through MBUS with extra configuration. Fixes: c49836f05aa1 ("mtd: rawnand: sunxi: Add A23/A33 DMA support") Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/sunxi_nand.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index e93f39bc2bc5..89773293c64d 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -43,6 +43,7 @@ #define NFC_REG_RCMD_SET 0x0028 #define NFC_REG_WCMD_SET 0x002C #define NFC_REG_A10_IO_DATA 0x0030 +#define NFC_REG_A23_IO_DATA 0x0300 #define NFC_REG_ECC_CTL 0x0034 #define NFC_REG_ECC_ST 0x0038 #define NFC_REG_DEBUG 0x003C @@ -50,6 +51,7 @@ #define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) #define NFC_REG_SPARE_AREA 0x00A0 #define NFC_REG_PAT_ID 0x00A4 +#define NFC_REG_MDMA_CNT 0x00C4 #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -68,6 +70,7 @@ #define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) #define NFC_SAM BIT(12) #define NFC_RAM_METHOD BIT(14) +#define NFC_DMA_TYPE_NORMAL BIT(15) #define NFC_DEBUG_CTL BIT(31) /* define bit use in NFC_ST */ @@ -204,10 +207,13 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * NAND Controller capabilities structure: stores NAND controller capabilities * for distinction between compatible strings. * + * @extra_mbus_conf: Contrary to A10, A10s and A13, accessing internal RAM + * through MBUS on A23/A33 needs extra configuration. * @reg_io_data: I/O data register * @dma_maxburst: DMA maxburst */ struct sunxi_nfc_caps { + bool extra_mbus_conf; unsigned int reg_io_data; unsigned int dma_maxburst; }; @@ -367,6 +373,9 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, nfc->regs + NFC_REG_CTL); writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); writel(chunksize, nfc->regs + NFC_REG_CNT); + if (nfc->caps->extra_mbus_conf) + writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT); + dmat = dmaengine_submit(dmad); ret = dma_submit_error(dmat); @@ -2127,6 +2136,11 @@ static int sunxi_nfc_probe(struct platform_device *pdev) dmac_cfg.src_maxburst = nfc->caps->dma_maxburst; dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst; dmaengine_slave_config(nfc->dmac, &dmac_cfg); + + if (nfc->caps->extra_mbus_conf) + writel(readl(nfc->regs + NFC_REG_CTL) | + NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL); + } else { dev_warn(dev, "failed to request rxtx DMA channel\n"); } @@ -2175,11 +2189,21 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { .dma_maxburst = 4, }; +static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { + .extra_mbus_conf = true, + .reg_io_data = NFC_REG_A23_IO_DATA, + .dma_maxburst = 8, +}; + static const struct of_device_id sunxi_nfc_ids[] = { { .compatible = "allwinner,sun4i-a10-nand", .data = &sunxi_nfc_a10_caps, }, + { + .compatible = "allwinner,sun8i-a23-nand-controller", + .data = &sunxi_nfc_a23_caps, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_nfc_ids); -- cgit