From b156b7f2169bb4d963a2432ff03319fe7d64b823 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 17 Oct 2016 13:19:37 -0300 Subject: [PATCH 01/55] mtd: nand: omap2: Fix module autoload If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/mtd/nand/omap2_nand.ko | grep alias alias: platform:omap2-nand After this patch: $ modinfo drivers/mtd/nand/omap2_nand.ko | grep alias alias: platform:omap2-nand alias: of:N*T*Cti,omap2-nandC* alias: of:N*T*Cti,omap2-nand Signed-off-by: Javier Martinez Canillas Signed-off-by: Boris Brezillon --- drivers/mtd/nand/omap2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 5513bfd9cdc9..0934c3b3f70e 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -2197,6 +2197,7 @@ static const struct of_device_id omap_nand_ids[] = { { .compatible = "ti,omap2-nand", }, {}, }; +MODULE_DEVICE_TABLE(of, omap_nand_ids); static struct platform_driver omap_nand_driver = { .probe = omap_nand_probe, From f59dab8d9e8f37f7042ba05bfbab748b1312e73e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 20 Oct 2016 10:12:42 +0200 Subject: [PATCH 02/55] mtd: nand: sunxi: fix support for 512bytes ECC chunks The driver is incorrectly assuming that the ECC block size is always 1k which is not always true. Also take the other cases into account. Signed-off-by: Boris Brezillon Signed-off-by: Maxime Ripard --- drivers/mtd/nand/sunxi_nand.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 8b8470c4e6d0..e40482a65de6 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -145,6 +145,7 @@ #define NFC_ECC_PIPELINE BIT(3) #define NFC_ECC_EXCEPTION BIT(4) #define NFC_ECC_BLOCK_SIZE_MSK BIT(5) +#define NFC_ECC_BLOCK_512 BIT(5) #define NFC_RANDOM_EN BIT(9) #define NFC_RANDOM_DIRECTION BIT(10) #define NFC_ECC_MODE_MSK GENMASK(15, 12) @@ -817,6 +818,9 @@ static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE; + if (nand->ecc.size == 512) + ecc_ctl |= NFC_ECC_BLOCK_512; + writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); } From 6ac18a4859b3a7d55874bed2ebe6d704b7926a10 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 20 Oct 2016 10:12:43 +0200 Subject: [PATCH 03/55] mtd: nand: add support for the TC58NVG2S0H chip Add the description of the Toshiba TC58NVG2S0H SLC nand to the nand_ids table so we can use the NAND ECC infos and the ONFI timings. Signed-off-by: Boris Brezillon Signed-off-by: Maxime Ripard --- drivers/mtd/nand/nand_ids.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 2af9869a115e..b3a332f37e14 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -36,6 +36,9 @@ struct nand_flash_dev nand_flash_ids[] = { {"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) }, + {"TC58NVG2S0H 4G 3.3V 8-bit", + { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} }, + SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) }, {"TC58NVG3S0F 8G 3.3V 8-bit", { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, From 668592492409498afc277da41e84799e1d2255c2 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 20 Oct 2016 10:49:01 +0200 Subject: [PATCH 04/55] mtd: nand: Add OX820 NAND Support Add NAND driver to support the Oxford Semiconductor OX820 NAND Controller. This is a simple memory mapped NAND controller with single chip select and software ECC. Acked-by: Rob Herring Signed-off-by: Neil Armstrong Signed-off-by: Boris Brezillon --- .../devicetree/bindings/mtd/oxnas-nand.txt | 41 ++++ drivers/mtd/nand/Kconfig | 5 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/oxnas_nand.c | 195 ++++++++++++++++++ 4 files changed, 242 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/oxnas-nand.txt create mode 100644 drivers/mtd/nand/oxnas_nand.c diff --git a/Documentation/devicetree/bindings/mtd/oxnas-nand.txt b/Documentation/devicetree/bindings/mtd/oxnas-nand.txt new file mode 100644 index 000000000000..56d5c19da41d --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/oxnas-nand.txt @@ -0,0 +1,41 @@ +* Oxford Semiconductor OXNAS NAND Controller + +Please refer to nand.txt for generic information regarding MTD NAND bindings. + +Required properties: + - compatible: "oxsemi,ox820-nand" + - reg: Base address and length for NAND mapped memory. + +Optional Properties: + - clocks: phandle to the NAND gate clock if needed. + - resets: phandle to the NAND reset control if needed. + +Example: + +nandc: nand-controller@41000000 { + compatible = "oxsemi,ox820-nand"; + reg = <0x41000000 0x100000>; + clocks = <&stdclk CLK_820_NAND>; + resets = <&reset RESET_NAND>; + #address-cells = <1>; + #size-cells = <0>; + + nand@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <1>; + nand-ecc-mode = "soft"; + nand-ecc-algo = "hamming"; + + partition@0 { + label = "boot"; + reg = <0x00000000 0x00e00000>; + read-only; + }; + + partition@e00000 { + label = "ubi"; + reg = <0x00e00000 0x07200000>; + }; + }; +}; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7b7a887b4709..c023125989cf 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -426,6 +426,11 @@ config MTD_NAND_ORION No board specific support is done by this driver, each board must advertise a platform_device for the driver to attach. +config MTD_NAND_OXNAS + tristate "NAND Flash support for Oxford Semiconductor SoC" + help + This enables the NAND flash controller on Oxford Semiconductor SoCs. + config MTD_NAND_FSL_ELBC tristate "NAND support for Freescale eLBC controllers" depends on FSL_SOC diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index cafde6f3d957..05fc054a9472 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o +obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o diff --git a/drivers/mtd/nand/oxnas_nand.c b/drivers/mtd/nand/oxnas_nand.c new file mode 100644 index 000000000000..3e3bf3b364d2 --- /dev/null +++ b/drivers/mtd/nand/oxnas_nand.c @@ -0,0 +1,195 @@ +/* + * Oxford Semiconductor OXNAS NAND driver + + * Copyright (C) 2016 Neil Armstrong + * Heavily based on plat_nand.c : + * Author: Vitaly Wool + * Copyright (C) 2013 Ma Haijun + * Copyright (C) 2012 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Nand commands */ +#define OXNAS_NAND_CMD_ALE BIT(18) +#define OXNAS_NAND_CMD_CLE BIT(19) + +#define OXNAS_NAND_MAX_CHIPS 1 + +struct oxnas_nand_ctrl { + struct nand_hw_control base; + void __iomem *io_base; + struct clk *clk; + struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS]; +}; + +static uint8_t oxnas_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); + + return readb(oxnas->io_base); +} + +static void oxnas_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); + + ioread8_rep(oxnas->io_base, buf, len); +} + +static void oxnas_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); + + iowrite8_rep(oxnas->io_base, buf, len); +} + +/* Single CS command control */ +static void oxnas_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); + + if (ctrl & NAND_CLE) + writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_CLE); + else if (ctrl & NAND_ALE) + writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_ALE); +} + +/* + * Probe for the NAND device. + */ +static int oxnas_nand_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *nand_np; + struct oxnas_nand_ctrl *oxnas; + struct nand_chip *chip; + struct mtd_info *mtd; + struct resource *res; + int nchips = 0; + int count = 0; + int err = 0; + + /* Allocate memory for the device structure (and zero it) */ + oxnas = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), + GFP_KERNEL); + if (!oxnas) + return -ENOMEM; + + nand_hw_control_init(&oxnas->base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + oxnas->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(oxnas->io_base)) + return PTR_ERR(oxnas->io_base); + + oxnas->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(oxnas->clk)) + oxnas->clk = NULL; + + /* Only a single chip node is supported */ + count = of_get_child_count(np); + if (count > 1) + return -EINVAL; + + clk_prepare_enable(oxnas->clk); + device_reset_optional(&pdev->dev); + + for_each_child_of_node(np, nand_np) { + chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), + GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->controller = &oxnas->base; + + nand_set_flash_node(chip, nand_np); + nand_set_controller_data(chip, oxnas); + + mtd = nand_to_mtd(chip); + mtd->dev.parent = &pdev->dev; + mtd->priv = chip; + + chip->cmd_ctrl = oxnas_nand_cmd_ctrl; + chip->read_buf = oxnas_nand_read_buf; + chip->read_byte = oxnas_nand_read_byte; + chip->write_buf = oxnas_nand_write_buf; + chip->chip_delay = 30; + + /* Scan to find existence of the device */ + err = nand_scan(mtd, 1); + if (err) + return err; + + err = mtd_device_register(mtd, NULL, 0); + if (err) { + nand_release(mtd); + return err; + } + + oxnas->chips[nchips] = chip; + ++nchips; + } + + /* Exit if no chips found */ + if (!nchips) + return -ENODEV; + + platform_set_drvdata(pdev, oxnas); + + return 0; +} + +static int oxnas_nand_remove(struct platform_device *pdev) +{ + struct oxnas_nand_ctrl *oxnas = platform_get_drvdata(pdev); + + if (oxnas->chips[0]) + nand_release(nand_to_mtd(oxnas->chips[0])); + + clk_disable_unprepare(oxnas->clk); + + return 0; +} + +static const struct of_device_id oxnas_nand_match[] = { + { .compatible = "oxsemi,ox820-nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, oxnas_nand_match); + +static struct platform_driver oxnas_nand_driver = { + .probe = oxnas_nand_probe, + .remove = oxnas_nand_remove, + .driver = { + .name = "oxnas_nand", + .of_match_table = oxnas_nand_match, + }, +}; + +module_platform_driver(oxnas_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Neil Armstrong "); +MODULE_DESCRIPTION("Oxnas NAND driver"); +MODULE_ALIAS("platform:oxnas_nand"); From e9f66ae23c209eec617130126a23bf547bf7a6d8 Mon Sep 17 00:00:00 2001 From: Sergio Prado Date: Thu, 20 Oct 2016 19:42:44 -0200 Subject: [PATCH 05/55] mtd: s3c2410: make ecc mode configurable via platform data Removing CONFIG_MTD_NAND_S3C2410_HWECC option and adding a ecc_mode field in the drivers's platform data structure so it can be selectable via platform data. Also setting this field to NAND_ECC_SOFT in all boards using this driver since none of them had CONFIG_MTD_NAND_S3C2410_HWECC enabled. Signed-off-by: Sergio Prado Acked-by: Krzysztof Kozlowski Signed-off-by: Boris Brezillon --- arch/arm/mach-s3c24xx/common-smdk.c | 1 + arch/arm/mach-s3c24xx/mach-anubis.c | 1 + arch/arm/mach-s3c24xx/mach-at2440evb.c | 1 + arch/arm/mach-s3c24xx/mach-bast.c | 1 + arch/arm/mach-s3c24xx/mach-gta02.c | 1 + arch/arm/mach-s3c24xx/mach-jive.c | 1 + arch/arm/mach-s3c24xx/mach-mini2440.c | 1 + arch/arm/mach-s3c24xx/mach-osiris.c | 1 + arch/arm/mach-s3c24xx/mach-qt2410.c | 1 + arch/arm/mach-s3c24xx/mach-rx1950.c | 1 + arch/arm/mach-s3c24xx/mach-rx3715.c | 1 + arch/arm/mach-s3c24xx/mach-vstms.c | 1 + arch/arm/mach-s3c64xx/mach-hmt.c | 1 + arch/arm/mach-s3c64xx/mach-mini6410.c | 1 + arch/arm/mach-s3c64xx/mach-real6410.c | 1 + drivers/mtd/nand/Kconfig | 9 -- drivers/mtd/nand/s3c2410.c | 123 +++++++++--------- .../linux/platform_data/mtd-nand-s3c2410.h | 6 +- 18 files changed, 82 insertions(+), 71 deletions(-) diff --git a/arch/arm/mach-s3c24xx/common-smdk.c b/arch/arm/mach-s3c24xx/common-smdk.c index e9fbcc91c5c0..9e0bc46e90ec 100644 --- a/arch/arm/mach-s3c24xx/common-smdk.c +++ b/arch/arm/mach-s3c24xx/common-smdk.c @@ -171,6 +171,7 @@ static struct s3c2410_platform_nand smdk_nand_info = { .twrph1 = 20, .nr_sets = ARRAY_SIZE(smdk_nand_sets), .sets = smdk_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; /* devices we initialise */ diff --git a/arch/arm/mach-s3c24xx/mach-anubis.c b/arch/arm/mach-s3c24xx/mach-anubis.c index d03df0df01fa..029ef1b58925 100644 --- a/arch/arm/mach-s3c24xx/mach-anubis.c +++ b/arch/arm/mach-s3c24xx/mach-anubis.c @@ -223,6 +223,7 @@ static struct s3c2410_platform_nand __initdata anubis_nand_info = { .nr_sets = ARRAY_SIZE(anubis_nand_sets), .sets = anubis_nand_sets, .select_chip = anubis_nand_select, + .ecc_mode = NAND_ECC_SOFT, }; /* IDE channels */ diff --git a/arch/arm/mach-s3c24xx/mach-at2440evb.c b/arch/arm/mach-s3c24xx/mach-at2440evb.c index 9ae170fef2a7..7b28eb623fc1 100644 --- a/arch/arm/mach-s3c24xx/mach-at2440evb.c +++ b/arch/arm/mach-s3c24xx/mach-at2440evb.c @@ -114,6 +114,7 @@ static struct s3c2410_platform_nand __initdata at2440evb_nand_info = { .twrph1 = 40, .nr_sets = ARRAY_SIZE(at2440evb_nand_sets), .sets = at2440evb_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; /* DM9000AEP 10/100 ethernet controller */ diff --git a/arch/arm/mach-s3c24xx/mach-bast.c b/arch/arm/mach-s3c24xx/mach-bast.c index ed07cf392d4b..5185036765db 100644 --- a/arch/arm/mach-s3c24xx/mach-bast.c +++ b/arch/arm/mach-s3c24xx/mach-bast.c @@ -299,6 +299,7 @@ static struct s3c2410_platform_nand __initdata bast_nand_info = { .nr_sets = ARRAY_SIZE(bast_nand_sets), .sets = bast_nand_sets, .select_chip = bast_nand_select, + .ecc_mode = NAND_ECC_SOFT, }; /* DM9000 */ diff --git a/arch/arm/mach-s3c24xx/mach-gta02.c b/arch/arm/mach-s3c24xx/mach-gta02.c index 27ae6877550f..b0ed401da3a3 100644 --- a/arch/arm/mach-s3c24xx/mach-gta02.c +++ b/arch/arm/mach-s3c24xx/mach-gta02.c @@ -443,6 +443,7 @@ static struct s3c2410_platform_nand __initdata gta02_nand_info = { .twrph1 = 15, .nr_sets = ARRAY_SIZE(gta02_nand_sets), .sets = gta02_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; diff --git a/arch/arm/mach-s3c24xx/mach-jive.c b/arch/arm/mach-s3c24xx/mach-jive.c index 7d99fe8f6157..895aca225952 100644 --- a/arch/arm/mach-s3c24xx/mach-jive.c +++ b/arch/arm/mach-s3c24xx/mach-jive.c @@ -232,6 +232,7 @@ static struct s3c2410_platform_nand __initdata jive_nand_info = { .twrph1 = 40, .sets = jive_nand_sets, .nr_sets = ARRAY_SIZE(jive_nand_sets), + .ecc_mode = NAND_ECC_SOFT, }; static int __init jive_mtdset(char *options) diff --git a/arch/arm/mach-s3c24xx/mach-mini2440.c b/arch/arm/mach-s3c24xx/mach-mini2440.c index ec60bd4a1646..71af8d2fd320 100644 --- a/arch/arm/mach-s3c24xx/mach-mini2440.c +++ b/arch/arm/mach-s3c24xx/mach-mini2440.c @@ -287,6 +287,7 @@ static struct s3c2410_platform_nand mini2440_nand_info __initdata = { .nr_sets = ARRAY_SIZE(mini2440_nand_sets), .sets = mini2440_nand_sets, .ignore_unset_ecc = 1, + .ecc_mode = NAND_ECC_SOFT, }; /* DM9000AEP 10/100 ethernet controller */ diff --git a/arch/arm/mach-s3c24xx/mach-osiris.c b/arch/arm/mach-s3c24xx/mach-osiris.c index 2f6fdc326835..70b0eb7d3134 100644 --- a/arch/arm/mach-s3c24xx/mach-osiris.c +++ b/arch/arm/mach-s3c24xx/mach-osiris.c @@ -238,6 +238,7 @@ static struct s3c2410_platform_nand __initdata osiris_nand_info = { .nr_sets = ARRAY_SIZE(osiris_nand_sets), .sets = osiris_nand_sets, .select_chip = osiris_nand_select, + .ecc_mode = NAND_ECC_SOFT, }; /* PCMCIA control and configuration */ diff --git a/arch/arm/mach-s3c24xx/mach-qt2410.c b/arch/arm/mach-s3c24xx/mach-qt2410.c index 984516e8307a..868c82087403 100644 --- a/arch/arm/mach-s3c24xx/mach-qt2410.c +++ b/arch/arm/mach-s3c24xx/mach-qt2410.c @@ -284,6 +284,7 @@ static struct s3c2410_platform_nand __initdata qt2410_nand_info = { .twrph1 = 20, .nr_sets = ARRAY_SIZE(qt2410_nand_sets), .sets = qt2410_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; /* UDC */ diff --git a/arch/arm/mach-s3c24xx/mach-rx1950.c b/arch/arm/mach-s3c24xx/mach-rx1950.c index 25a139bb9826..e86ad6a68a0b 100644 --- a/arch/arm/mach-s3c24xx/mach-rx1950.c +++ b/arch/arm/mach-s3c24xx/mach-rx1950.c @@ -611,6 +611,7 @@ static struct s3c2410_platform_nand rx1950_nand_info = { .twrph1 = 15, .nr_sets = ARRAY_SIZE(rx1950_nand_sets), .sets = rx1950_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; static struct s3c2410_udc_mach_info rx1950_udc_cfg __initdata = { diff --git a/arch/arm/mach-s3c24xx/mach-rx3715.c b/arch/arm/mach-s3c24xx/mach-rx3715.c index cf55196f89ca..a39fb9780dd3 100644 --- a/arch/arm/mach-s3c24xx/mach-rx3715.c +++ b/arch/arm/mach-s3c24xx/mach-rx3715.c @@ -164,6 +164,7 @@ static struct s3c2410_platform_nand __initdata rx3715_nand_info = { .twrph1 = 15, .nr_sets = ARRAY_SIZE(rx3715_nand_sets), .sets = rx3715_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; static struct platform_device *rx3715_devices[] __initdata = { diff --git a/arch/arm/mach-s3c24xx/mach-vstms.c b/arch/arm/mach-s3c24xx/mach-vstms.c index b4460d5f7011..f5e6322145fa 100644 --- a/arch/arm/mach-s3c24xx/mach-vstms.c +++ b/arch/arm/mach-s3c24xx/mach-vstms.c @@ -117,6 +117,7 @@ static struct s3c2410_platform_nand __initdata vstms_nand_info = { .twrph1 = 20, .nr_sets = ARRAY_SIZE(vstms_nand_sets), .sets = vstms_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; static struct platform_device *vstms_devices[] __initdata = { diff --git a/arch/arm/mach-s3c64xx/mach-hmt.c b/arch/arm/mach-s3c64xx/mach-hmt.c index bc7dc1fcbf7d..59b5531f1987 100644 --- a/arch/arm/mach-s3c64xx/mach-hmt.c +++ b/arch/arm/mach-s3c64xx/mach-hmt.c @@ -204,6 +204,7 @@ static struct s3c2410_platform_nand hmt_nand_info = { .twrph1 = 40, .nr_sets = ARRAY_SIZE(hmt_nand_sets), .sets = hmt_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; static struct gpio_led hmt_leds[] = { diff --git a/arch/arm/mach-s3c64xx/mach-mini6410.c b/arch/arm/mach-s3c64xx/mach-mini6410.c index ae999fb3fe6d..a3e3e25728b4 100644 --- a/arch/arm/mach-s3c64xx/mach-mini6410.c +++ b/arch/arm/mach-s3c64xx/mach-mini6410.c @@ -142,6 +142,7 @@ static struct s3c2410_platform_nand mini6410_nand_info = { .twrph1 = 40, .nr_sets = ARRAY_SIZE(mini6410_nand_sets), .sets = mini6410_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; static struct s3c_fb_pd_win mini6410_lcd_type0_fb_win = { diff --git a/arch/arm/mach-s3c64xx/mach-real6410.c b/arch/arm/mach-s3c64xx/mach-real6410.c index 4e240ffa7ac7..d6b3ffd7704b 100644 --- a/arch/arm/mach-s3c64xx/mach-real6410.c +++ b/arch/arm/mach-s3c64xx/mach-real6410.c @@ -194,6 +194,7 @@ static struct s3c2410_platform_nand real6410_nand_info = { .twrph1 = 40, .nr_sets = ARRAY_SIZE(real6410_nand_sets), .sets = real6410_nand_sets, + .ecc_mode = NAND_ECC_SOFT, }; static struct platform_device *real6410_devices[] __initdata = { diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index c023125989cf..60757af314d3 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -179,15 +179,6 @@ config MTD_NAND_S3C2410_DEBUG help Enable debugging of the S3C NAND driver -config MTD_NAND_S3C2410_HWECC - bool "Samsung S3C NAND Hardware ECC" - depends on MTD_NAND_S3C2410 - help - Enable the use of the controller's internal ECC generator when - using NAND. Early versions of the chips have had problems with - incorrect ECC generation, and if using these, the default of - software ECC is preferable. - config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" depends on 4xx diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d459c19d78de..bb4ac3179d17 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -497,7 +497,6 @@ static int s3c2412_nand_devready(struct mtd_info *mtd) /* ECC handling functions */ -#ifdef CONFIG_MTD_NAND_S3C2410_HWECC static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { @@ -649,7 +648,6 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, return 0; } -#endif /* over-ride the standard functions for a little more speed. We can * use read/write block to move the data buffers to/from the controller @@ -858,50 +856,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, nmtd->info = info; nmtd->set = set; -#ifdef CONFIG_MTD_NAND_S3C2410_HWECC - chip->ecc.calculate = s3c2410_nand_calculate_ecc; - chip->ecc.correct = s3c2410_nand_correct_data; - chip->ecc.mode = NAND_ECC_HW; - chip->ecc.strength = 1; - - switch (info->cpu_type) { - case TYPE_S3C2410: - chip->ecc.hwctl = s3c2410_nand_enable_hwecc; - chip->ecc.calculate = s3c2410_nand_calculate_ecc; - break; - - case TYPE_S3C2412: - chip->ecc.hwctl = s3c2412_nand_enable_hwecc; - chip->ecc.calculate = s3c2412_nand_calculate_ecc; - break; - - case TYPE_S3C2440: - chip->ecc.hwctl = s3c2440_nand_enable_hwecc; - chip->ecc.calculate = s3c2440_nand_calculate_ecc; - break; - } -#else - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; -#endif - - if (set->disable_ecc) - chip->ecc.mode = NAND_ECC_NONE; - - switch (chip->ecc.mode) { - case NAND_ECC_NONE: - dev_info(info->device, "NAND ECC disabled\n"); - break; - case NAND_ECC_SOFT: - dev_info(info->device, "NAND soft ECC\n"); - break; - case NAND_ECC_HW: - dev_info(info->device, "NAND hardware ECC\n"); - break; - default: - dev_info(info->device, "NAND ECC UNKNOWN\n"); - break; - } + chip->ecc.mode = info->platform->ecc_mode; /* If you use u-boot BBT creation code, specifying this flag will * let the kernel fish out the BBT from the NAND, and also skip the @@ -923,28 +878,74 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, * * The internal state is currently limited to the ECC state information. */ -static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, - struct s3c2410_nand_mtd *nmtd) +static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, + struct s3c2410_nand_mtd *nmtd) { struct nand_chip *chip = &nmtd->chip; - dev_dbg(info->device, "chip %p => page shift %d\n", - chip, chip->page_shift); + switch (chip->ecc.mode) { - if (chip->ecc.mode != NAND_ECC_HW) - return; + case NAND_ECC_NONE: + dev_info(info->device, "ECC disabled\n"); + break; + + case NAND_ECC_SOFT: + /* + * This driver expects Hamming based ECC when ecc_mode is set + * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to + * avoid adding an extra ecc_algo field to + * s3c2410_platform_nand. + */ + chip->ecc.algo = NAND_ECC_HAMMING; + dev_info(info->device, "soft ECC\n"); + break; + + case NAND_ECC_HW: + chip->ecc.calculate = s3c2410_nand_calculate_ecc; + chip->ecc.correct = s3c2410_nand_correct_data; + chip->ecc.strength = 1; + + switch (info->cpu_type) { + case TYPE_S3C2410: + chip->ecc.hwctl = s3c2410_nand_enable_hwecc; + chip->ecc.calculate = s3c2410_nand_calculate_ecc; + break; + + case TYPE_S3C2412: + chip->ecc.hwctl = s3c2412_nand_enable_hwecc; + chip->ecc.calculate = s3c2412_nand_calculate_ecc; + break; + + case TYPE_S3C2440: + chip->ecc.hwctl = s3c2440_nand_enable_hwecc; + chip->ecc.calculate = s3c2440_nand_calculate_ecc; + break; + } + + dev_dbg(info->device, "chip %p => page shift %d\n", + chip, chip->page_shift); /* change the behaviour depending on whether we are using * the large or small page nand device */ + if (chip->page_shift > 10) { + chip->ecc.size = 256; + chip->ecc.bytes = 3; + } else { + chip->ecc.size = 512; + chip->ecc.bytes = 3; + mtd_set_ooblayout(nand_to_mtd(chip), + &s3c2410_ooblayout_ops); + } - if (chip->page_shift > 10) { - chip->ecc.size = 256; - chip->ecc.bytes = 3; - } else { - chip->ecc.size = 512; - chip->ecc.bytes = 3; - mtd_set_ooblayout(nand_to_mtd(chip), &s3c2410_ooblayout_ops); + dev_info(info->device, "hardware ECC\n"); + break; + + default: + dev_err(info->device, "invalid ECC mode!\n"); + return -EINVAL; } + + return 0; } /* s3c24xx_nand_probe @@ -1046,7 +1047,9 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) NULL); if (nmtd->scan_res == 0) { - s3c2410_nand_update_chip(info, nmtd); + err = s3c2410_nand_update_chip(info, nmtd); + if (err < 0) + goto exit_error; nand_scan_tail(mtd); s3c2410_nand_add_partition(info, nmtd, sets); } diff --git a/include/linux/platform_data/mtd-nand-s3c2410.h b/include/linux/platform_data/mtd-nand-s3c2410.h index c55e42ee57fa..729af13d1773 100644 --- a/include/linux/platform_data/mtd-nand-s3c2410.h +++ b/include/linux/platform_data/mtd-nand-s3c2410.h @@ -12,9 +12,10 @@ #ifndef __MTD_NAND_S3C2410_H #define __MTD_NAND_S3C2410_H +#include + /** * struct s3c2410_nand_set - define a set of one or more nand chips - * @disable_ecc: Entirely disable ECC - Dangerous * @flash_bbt: Openmoko u-boot can create a Bad Block Table * Setting this flag will allow the kernel to * look for it at boot time and also skip the NAND @@ -31,7 +32,6 @@ * a warning at boot time. */ struct s3c2410_nand_set { - unsigned int disable_ecc:1; unsigned int flash_bbt:1; unsigned int options; @@ -51,6 +51,8 @@ struct s3c2410_platform_nand { unsigned int ignore_unset_ecc:1; + nand_ecc_modes_t ecc_mode; + int nr_sets; struct s3c2410_nand_set *sets; From 7baf9a04e58ab6a79172dac7612c3c4d18dadcca Mon Sep 17 00:00:00 2001 From: Sergio Prado Date: Wed, 26 Oct 2016 21:59:54 -0200 Subject: [PATCH 06/55] dt-bindings: mtd: add DT binding for s3c2410 flash controller Adds the device tree bindings description for Samsung S3C2410 and compatible NAND flash controller. Acked-by: Rob Herring Acked-by: Krzysztof Kozlowski Signed-off-by: Sergio Prado Signed-off-by: Boris Brezillon --- .../bindings/mtd/samsung-s3c2410.txt | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/samsung-s3c2410.txt diff --git a/Documentation/devicetree/bindings/mtd/samsung-s3c2410.txt b/Documentation/devicetree/bindings/mtd/samsung-s3c2410.txt new file mode 100644 index 000000000000..0040eb8895e0 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/samsung-s3c2410.txt @@ -0,0 +1,56 @@ +* Samsung S3C2410 and compatible NAND flash controller + +Required properties: +- compatible : The possible values are: + "samsung,s3c2410-nand" + "samsung,s3c2412-nand" + "samsung,s3c2440-nand" +- reg : register's location and length. +- #address-cells, #size-cells : see nand.txt +- clocks : phandle to the nand controller clock +- clock-names : must contain "nand" + +Optional child nodes: +Child nodes representing the available nand chips. + +Optional child properties: +- nand-ecc-mode : see nand.txt +- nand-on-flash-bbt : see nand.txt + +Each child device node may optionally contain a 'partitions' sub-node, +which further contains sub-nodes describing the flash partition mapping. +See partition.txt for more detail. + +Example: + +nand-controller@4e000000 { + compatible = "samsung,s3c2440-nand"; + reg = <0x4e000000 0x40>; + + #address-cells = <1>; + #size-cells = <0>; + + clocks = <&clocks HCLK_NAND>; + clock-names = "nand"; + + nand { + nand-ecc-mode = "soft"; + nand-on-flash-bbt; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0 0x040000>; + }; + + partition@40000 { + label = "kernel"; + reg = <0x040000 0x500000>; + }; + }; + }; +}; From 1c825ad1b8cfe12ccc145dcdba360c52c0272c04 Mon Sep 17 00:00:00 2001 From: Sergio Prado Date: Wed, 26 Oct 2016 21:59:55 -0200 Subject: [PATCH 07/55] mtd: s3c2410: parse the device configuration from OF node Allows configuring Samsung's s3c2410 memory controller using a devicetree. Signed-off-by: Sergio Prado Acked-by: Krzysztof Kozlowski Signed-off-by: Boris Brezillon --- drivers/mtd/nand/s3c2410.c | 163 ++++++++++++++++-- .../linux/platform_data/mtd-nand-s3c2410.h | 1 + 2 files changed, 147 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index bb4ac3179d17..f0b030d44f71 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -185,6 +187,22 @@ struct s3c2410_nand_info { #endif }; +struct s3c24XX_nand_devtype_data { + enum s3c_cpu_type type; +}; + +static const struct s3c24XX_nand_devtype_data s3c2410_nand_devtype_data = { + .type = TYPE_S3C2410, +}; + +static const struct s3c24XX_nand_devtype_data s3c2412_nand_devtype_data = { + .type = TYPE_S3C2412, +}; + +static const struct s3c24XX_nand_devtype_data s3c2440_nand_devtype_data = { + .type = TYPE_S3C2440, +}; + /* conversion functions */ static struct s3c2410_nand_mtd *s3c2410_nand_mtd_toours(struct mtd_info *mtd) @@ -794,6 +812,30 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, return -ENODEV; } +static int s3c2410_nand_setup_data_interface(struct mtd_info *mtd, + const struct nand_data_interface *conf, + bool check_only) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + struct s3c2410_platform_nand *pdata = info->platform; + const struct nand_sdr_timings *timings; + int tacls; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + + tacls = timings->tCLS_min - timings->tWP_min; + if (tacls < 0) + tacls = 0; + + pdata->tacls = DIV_ROUND_UP(tacls, 1000); + pdata->twrph0 = DIV_ROUND_UP(timings->tWP_min, 1000); + pdata->twrph1 = DIV_ROUND_UP(timings->tCLH_min, 1000); + + return s3c2410_nand_setrate(info); +} + /** * s3c2410_nand_init_chip - initialise a single instance of an chip * @info: The base NAND controller the chip is on. @@ -808,9 +850,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *nmtd, struct s3c2410_nand_set *set) { + struct device_node *np = info->device->of_node; struct nand_chip *chip = &nmtd->chip; void __iomem *regs = info->regs; + nand_set_flash_node(chip, set->of_node); + chip->write_buf = s3c2410_nand_write_buf; chip->read_buf = s3c2410_nand_read_buf; chip->select_chip = s3c2410_nand_select_chip; @@ -819,6 +864,13 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->options = set->options; chip->controller = &info->controller; + /* + * let's keep behavior unchanged for legacy boards booting via pdata and + * auto-detect timings only when booting with a device tree. + */ + if (np) + chip->setup_data_interface = s3c2410_nand_setup_data_interface; + switch (info->cpu_type) { case TYPE_S3C2410: chip->IO_ADDR_W = regs + S3C2410_NFDATA; @@ -858,13 +910,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->ecc.mode = info->platform->ecc_mode; - /* If you use u-boot BBT creation code, specifying this flag will - * let the kernel fish out the BBT from the NAND, and also skip the - * full NAND scan that can take 1/2s or so. Little things... */ - if (set->flash_bbt) { + /* + * If you use u-boot BBT creation code, specifying this flag will + * let the kernel fish out the BBT from the NAND. + */ + if (set->flash_bbt) chip->bbt_options |= NAND_BBT_USE_FLASH; - chip->options |= NAND_SKIP_BBTSCAN; - } } /** @@ -945,6 +996,78 @@ static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, return -EINVAL; } + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->options |= NAND_SKIP_BBTSCAN; + + return 0; +} + +static const struct of_device_id s3c24xx_nand_dt_ids[] = { + { + .compatible = "samsung,s3c2410-nand", + .data = &s3c2410_nand_devtype_data, + }, { + /* also compatible with s3c6400 */ + .compatible = "samsung,s3c2412-nand", + .data = &s3c2412_nand_devtype_data, + }, { + .compatible = "samsung,s3c2440-nand", + .data = &s3c2440_nand_devtype_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s3c24xx_nand_dt_ids); + +static int s3c24xx_nand_probe_dt(struct platform_device *pdev) +{ + const struct s3c24XX_nand_devtype_data *devtype_data; + struct s3c2410_platform_nand *pdata; + struct s3c2410_nand_info *info = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node, *child; + struct s3c2410_nand_set *sets; + + devtype_data = of_device_get_match_data(&pdev->dev); + if (!devtype_data) + return -ENODEV; + + info->cpu_type = devtype_data->type; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdev->dev.platform_data = pdata; + + pdata->nr_sets = of_get_child_count(np); + if (!pdata->nr_sets) + return 0; + + sets = devm_kzalloc(&pdev->dev, sizeof(*sets) * pdata->nr_sets, + GFP_KERNEL); + if (!sets) + return -ENOMEM; + + pdata->sets = sets; + + for_each_available_child_of_node(np, child) { + sets->name = (char *)child->name; + sets->of_node = child; + sets->nr_chips = 1; + + of_node_get(child); + + sets++; + } + + return 0; +} + +static int s3c24xx_nand_probe_pdata(struct platform_device *pdev) +{ + struct s3c2410_nand_info *info = platform_get_drvdata(pdev); + + info->cpu_type = platform_get_device_id(pdev)->driver_data; + return 0; } @@ -957,8 +1080,7 @@ static int s3c2410_nand_update_chip(struct s3c2410_nand_info *info, */ static int s3c24xx_nand_probe(struct platform_device *pdev) { - struct s3c2410_platform_nand *plat = to_nand_plat(pdev); - enum s3c_cpu_type cpu_type; + struct s3c2410_platform_nand *plat; struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_set *sets; @@ -968,8 +1090,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) int nr_sets; int setno; - cpu_type = platform_get_device_id(pdev)->driver_data; - info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (info == NULL) { err = -ENOMEM; @@ -991,6 +1111,16 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) s3c2410_nand_clk_set_state(info, CLOCK_ENABLE); + if (pdev->dev.of_node) + err = s3c24xx_nand_probe_dt(pdev); + else + err = s3c24xx_nand_probe_pdata(pdev); + + if (err) + goto exit_error; + + plat = to_nand_plat(pdev); + /* allocate and map the resource */ /* currently we assume we have the one resource */ @@ -999,7 +1129,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) info->device = &pdev->dev; info->platform = plat; - info->cpu_type = cpu_type; info->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(info->regs)) { @@ -1009,12 +1138,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs); - /* initialise the hardware */ - - err = s3c2410_nand_inithw(info); - if (err != 0) - goto exit_error; - sets = (plat != NULL) ? plat->sets : NULL; nr_sets = (plat != NULL) ? plat->nr_sets : 1; @@ -1058,6 +1181,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) sets++; } + /* initialise the hardware */ + err = s3c2410_nand_inithw(info); + if (err != 0) + goto exit_error; + err = s3c2410_nand_cpufreq_register(info); if (err < 0) { dev_err(&pdev->dev, "failed to init cpufreq support\n"); @@ -1158,6 +1286,7 @@ static struct platform_driver s3c24xx_nand_driver = { .id_table = s3c24xx_driver_ids, .driver = { .name = "s3c24xx-nand", + .of_match_table = s3c24xx_nand_dt_ids, }, }; diff --git a/include/linux/platform_data/mtd-nand-s3c2410.h b/include/linux/platform_data/mtd-nand-s3c2410.h index 729af13d1773..f01659026b26 100644 --- a/include/linux/platform_data/mtd-nand-s3c2410.h +++ b/include/linux/platform_data/mtd-nand-s3c2410.h @@ -40,6 +40,7 @@ struct s3c2410_nand_set { char *name; int *nr_map; struct mtd_partition *partitions; + struct device_node *of_node; }; struct s3c2410_platform_nand { From 204e7ecd47e26cc12d9e8e8a7e7a2eeb9573f0ba Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sat, 1 Oct 2016 10:24:02 +0200 Subject: [PATCH 08/55] mtd: nand: Add a few more timings to nand_sdr_timings Add the tR_max, tBERS_max, tPROG_max and tCCS_min timings to the nand_sdr_timings struct. Assign default/safe values for the statically defined timings, and extract them from the ONFI parameter table if the NAND is ONFI compliant. Signed-off-by: Boris Brezillon Tested-by: Marc Gonzalez --- drivers/mtd/nand/nand_timings.c | 26 +++++++++++++++++++++++++- include/linux/mtd/nand.h | 8 ++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c index 13a587407be3..f06312df3669 100644 --- a/drivers/mtd/nand/nand_timings.c +++ b/drivers/mtd/nand/nand_timings.c @@ -18,6 +18,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 20000, .tALS_min = 50000, @@ -58,6 +60,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 10000, .tALS_min = 25000, @@ -98,6 +102,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 10000, .tALS_min = 15000, @@ -138,6 +144,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 5000, .tALS_min = 10000, @@ -178,6 +186,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 5000, .tALS_min = 10000, @@ -218,6 +228,8 @@ static const struct nand_data_interface onfi_sdr_timings[] = { { .type = NAND_SDR_IFACE, .timings.sdr = { + .tCCS_min = 500000, + .tR_max = 200000000, .tADL_min = 400000, .tALH_min = 5000, .tALS_min = 10000, @@ -290,10 +302,22 @@ int onfi_init_data_interface(struct nand_chip *chip, *iface = onfi_sdr_timings[timing_mode]; /* - * TODO: initialize timings that cannot be deduced from timing mode: + * Initialize timings that cannot be deduced from timing mode: * tR, tPROG, tCCS, ... * These information are part of the ONFI parameter page. */ + if (chip->onfi_version) { + struct nand_onfi_params *params = &chip->onfi_params; + struct nand_sdr_timings *timings = &iface->timings.sdr; + + /* microseconds -> picoseconds */ + timings->tPROG_max = 1000000UL * le16_to_cpu(params->t_prog); + timings->tBERS_max = 1000000UL * le16_to_cpu(params->t_bers); + timings->tR_max = 1000000UL * le16_to_cpu(params->t_r); + + /* nanoseconds -> picoseconds */ + timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); + } return 0; } diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index c5d3d5024fc8..6fe83bce83a6 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -584,6 +584,10 @@ struct nand_buffers { * * All these timings are expressed in picoseconds. * + * @tBERS_max: Block erase time + * @tCCS_min: Change column setup time + * @tPROG_max: Page program time + * @tR_max: Page read time * @tALH_min: ALE hold time * @tADL_min: ALE to data loading time * @tALS_min: ALE setup time @@ -621,6 +625,10 @@ struct nand_buffers { * @tWW_min: WP# transition to WE# low */ struct nand_sdr_timings { + u32 tBERS_max; + u32 tCCS_min; + u32 tPROG_max; + u32 tR_max; u32 tALH_min; u32 tADL_min; u32 tALS_min; From 6ea40a3ba93e1b14ffb349e276f9dfefc4334b99 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Sat, 1 Oct 2016 10:24:03 +0200 Subject: [PATCH 09/55] mtd: nand: Wait tCCS after a column change Drivers implementing ->cmd_ctrl() and relying on the default ->cmdfunc() implementation usually don't wait tCCS when a column change (RNDIN or RNDOUT) is requested. Add an option flag to ask the core to do so (note that we keep this as an opt-in to avoid breaking existing implementations), and make use of the ->data_interface information is available (otherwise, wait 500ns). Signed-off-by: Boris Brezillon Tested-by: Marc Gonzalez --- drivers/mtd/nand/nand_base.c | 26 +++++++++++++++++++++++++- include/linux/mtd/nand.h | 10 ++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e5718e5ecf92..0acb0070280a 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -709,6 +709,25 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, nand_wait_ready(mtd); } +static void nand_ccs_delay(struct nand_chip *chip) +{ + /* + * The controller already takes care of waiting for tCCS when the RNDIN + * or RNDOUT command is sent, return directly. + */ + if (!(chip->options & NAND_WAIT_TCCS)) + return; + + /* + * Wait tCCS_min if it is correctly defined, otherwise wait 500ns + * (which should be safe for all NANDs). + */ + if (chip->data_interface && chip->data_interface->timings.sdr.tCCS_min) + ndelay(chip->data_interface->timings.sdr.tCCS_min / 1000); + else + ndelay(500); +} + /** * nand_command_lp - [DEFAULT] Send command to NAND large page device * @mtd: MTD device structure @@ -773,10 +792,13 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: case NAND_CMD_STATUS: return; + case NAND_CMD_RNDIN: + nand_ccs_delay(chip); + return; + case NAND_CMD_RESET: if (chip->dev_ready) break; @@ -795,6 +817,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + nand_ccs_delay(chip); return; case NAND_CMD_READ0: diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 6fe83bce83a6..970ceb948835 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -210,6 +210,16 @@ enum nand_ecc_algo { */ #define NAND_USE_BOUNCE_BUFFER 0x00100000 +/* + * In case your controller is implementing ->cmd_ctrl() and is relying on the + * default ->cmdfunc() implementation, you may want to let the core handle the + * tCCS delay which is required when a column change (RNDIN or RNDOUT) is + * requested. + * If your controller already takes care of this delay, you don't need to set + * this flag. + */ +#define NAND_WAIT_TCCS 0x00200000 + /* Options set by nand scan */ /* Nand scan has allocated controller struct */ #define NAND_CONTROLLER_ALLOC 0x80000000 From 8bd8fbd8bccf6e32198000792ee8d1e9189f441f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 19 Sep 2016 11:21:18 +0200 Subject: [PATCH 10/55] mtd: nand: pxa3xx_nand: write exactly one message on probe failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For some error paths alloc_nand_resource() emitted an error message, for others it didn't. Make it consistently print a message including the error code where it's not constant and drop the hardly helpful additional message printed by the caller of alloc_nand_resource. Signed-off-by: Uwe Kleine-König Reviewed-by: Robert Jarzmik Signed-off-by: Boris Brezillon --- drivers/mtd/nand/pxa3xx_nand.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index b121bf4ed73a..4c749b030301 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1774,8 +1774,11 @@ static int alloc_nand_resource(struct platform_device *pdev) int ret, irq, cs; pdata = dev_get_platdata(&pdev->dev); - if (pdata->num_cs <= 0) + if (pdata->num_cs <= 0) { + dev_err(&pdev->dev, "invalid number of chip selects\n"); return -ENODEV; + } + info = devm_kzalloc(&pdev->dev, sizeof(*info) + sizeof(*host) * pdata->num_cs, GFP_KERNEL); @@ -1813,8 +1816,9 @@ static int alloc_nand_resource(struct platform_device *pdev) nand_hw_control_init(chip->controller); info->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "failed to get nand clock\n"); - return PTR_ERR(info->clk); + ret = PTR_ERR(info->clk); + dev_err(&pdev->dev, "failed to get nand clock: %d\n", ret); + return ret; } ret = clk_prepare_enable(info->clk); if (ret < 0) @@ -1842,6 +1846,7 @@ static int alloc_nand_resource(struct platform_device *pdev) info->mmio_base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(info->mmio_base)) { ret = PTR_ERR(info->mmio_base); + dev_err(&pdev->dev, "failed to map register space: %d\n", ret); goto fail_disable_clk; } info->mmio_phys = r->start; @@ -1861,7 +1866,7 @@ static int alloc_nand_resource(struct platform_device *pdev) pxa3xx_nand_irq_thread, IRQF_ONESHOT, pdev->name, info); if (ret < 0) { - dev_err(&pdev->dev, "failed to request IRQ\n"); + dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); goto fail_free_buf; } @@ -1960,10 +1965,8 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) } ret = alloc_nand_resource(pdev); - if (ret) { - dev_err(&pdev->dev, "alloc nand resource failed\n"); + if (ret) return ret; - } info = platform_get_drvdata(pdev); probe_success = 0; From c3d03de3962163855d2007b8c2243b9bf005bec7 Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 25 Oct 2016 15:15:50 +0200 Subject: [PATCH 11/55] mtd: nand: add tango NFC dt bindings doc Add the tango NAND Flash Controller dt bindings documentation. Signed-off-by: Marc Gonzalez Acked-by: Rob Herring Signed-off-by: Boris Brezillon --- .../devicetree/bindings/mtd/tango-nand.txt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/tango-nand.txt diff --git a/Documentation/devicetree/bindings/mtd/tango-nand.txt b/Documentation/devicetree/bindings/mtd/tango-nand.txt new file mode 100644 index 000000000000..ad5a02f2ac8c --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/tango-nand.txt @@ -0,0 +1,38 @@ +Sigma Designs Tango4 NAND Flash Controller (NFC) + +Required properties: + +- compatible: "sigma,smp8758-nand" +- reg: address/size of nfc_reg, nfc_mem, and pbus_reg +- dmas: reference to the DMA channel used by the controller +- dma-names: "nfc_sbox" +- clocks: reference to the system clock +- #address-cells: <1> +- #size-cells: <0> + +Children nodes represent the available NAND chips. +See Documentation/devicetree/bindings/mtd/nand.txt for generic bindings. + +Example: + + nandc: nand-controller@2c000 { + compatible = "sigma,smp8758-nand"; + reg = <0x2c000 0x30 0x2d000 0x800 0x20000 0x1000>; + dmas = <&dma0 3>; + dma-names = "nfc_sbox"; + clocks = <&clkgen SYS_CLK>; + #address-cells = <1>; + #size-cells = <0>; + + nand@0 { + reg = <0>; /* CS0 */ + nand-ecc-strength = <14>; + nand-ecc-step-size = <1024>; + }; + + nand@1 { + reg = <1>; /* CS1 */ + nand-ecc-strength = <14>; + nand-ecc-step-size = <1024>; + }; + }; From 6956e2385a16175ca350e199f9c760eb1644d5bf Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 25 Oct 2016 18:10:47 +0200 Subject: [PATCH 12/55] mtd: nand: add tango NAND flash controller support This driver supports the NAND Flash controller embedded in recent Tango chips, such as SMP8758 and SMP8759. Signed-off-by: Marc Gonzalez Signed-off-by: Boris Brezillon --- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/tango_nand.c | 658 ++++++++++++++++++++++++++++++++++ 3 files changed, 666 insertions(+) create mode 100644 drivers/mtd/nand/tango_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 60757af314d3..353a9ddf6b97 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -196,6 +196,13 @@ config MTD_NAND_S3C2410_CLKSTOP when the is NAND chip selected or released, but will save approximately 5mA of power when there is nothing happening. +config MTD_NAND_TANGO + tristate "NAND Flash support for Tango chips" + depends on ARCH_TANGO || COMPILE_TEST + depends on HAS_DMA + help + Enables the NAND Flash controller on Tango chips. + config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation)" depends on HAS_IOMEM diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 05fc054a9472..19a66e404d5b 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o +obj-$(CONFIG_MTD_NAND_TANGO) += tango_nand.o obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_DOCG4) += docg4.o diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c new file mode 100644 index 000000000000..fd8cf414cb2b --- /dev/null +++ b/drivers/mtd/nand/tango_nand.c @@ -0,0 +1,658 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Offsets relative to chip->base */ +#define PBUS_CMD 0 +#define PBUS_ADDR 4 +#define PBUS_DATA 8 + +/* Offsets relative to reg_base */ +#define NFC_STATUS 0x00 +#define NFC_FLASH_CMD 0x04 +#define NFC_DEVICE_CFG 0x08 +#define NFC_TIMING1 0x0c +#define NFC_TIMING2 0x10 +#define NFC_XFER_CFG 0x14 +#define NFC_PKT_0_CFG 0x18 +#define NFC_PKT_N_CFG 0x1c +#define NFC_BB_CFG 0x20 +#define NFC_ADDR_PAGE 0x24 +#define NFC_ADDR_OFFSET 0x28 +#define NFC_XFER_STATUS 0x2c + +/* NFC_STATUS values */ +#define CMD_READY BIT(31) + +/* NFC_FLASH_CMD values */ +#define NFC_READ 1 +#define NFC_WRITE 2 + +/* NFC_XFER_STATUS values */ +#define PAGE_IS_EMPTY BIT(16) + +/* Offsets relative to mem_base */ +#define METADATA 0x000 +#define ERROR_REPORT 0x1c0 + +/* + * Error reports are split in two bytes: + * byte 0 for the first packet in the page (PKT_0) + * byte 1 for other packets in the page (PKT_N, for N > 0) + * ERR_COUNT_PKT_N is the max error count over all but the first packet. + */ +#define DECODE_OK_PKT_0(v) ((v) & BIT(7)) +#define DECODE_OK_PKT_N(v) ((v) & BIT(15)) +#define ERR_COUNT_PKT_0(v) (((v) >> 0) & 0x3f) +#define ERR_COUNT_PKT_N(v) (((v) >> 8) & 0x3f) + +/* Offsets relative to pbus_base */ +#define PBUS_CS_CTRL 0x83c +#define PBUS_PAD_MODE 0x8f0 + +/* PBUS_CS_CTRL values */ +#define PBUS_IORDY BIT(31) + +/* + * PBUS_PAD_MODE values + * In raw mode, the driver communicates directly with the NAND chips. + * In NFC mode, the NAND Flash controller manages the communication. + * We use NFC mode for read and write; raw mode for everything else. + */ +#define MODE_RAW 0 +#define MODE_NFC BIT(31) + +#define METADATA_SIZE 4 +#define BBM_SIZE 6 +#define FIELD_ORDER 15 + +#define MAX_CS 4 + +struct tango_nfc { + struct nand_hw_control hw; + void __iomem *reg_base; + void __iomem *mem_base; + void __iomem *pbus_base; + struct tango_chip *chips[MAX_CS]; + struct dma_chan *chan; + int freq_kHz; +}; + +#define to_tango_nfc(ptr) container_of(ptr, struct tango_nfc, hw) + +struct tango_chip { + struct nand_chip nand_chip; + void __iomem *base; + u32 timing1; + u32 timing2; + u32 xfer_cfg; + u32 pkt_0_cfg; + u32 pkt_n_cfg; + u32 bb_cfg; +}; + +#define to_tango_chip(ptr) container_of(ptr, struct tango_chip, nand_chip) + +#define XFER_CFG(cs, page_count, steps, metadata_size) \ + ((cs) << 24 | (page_count) << 16 | (steps) << 8 | (metadata_size)) + +#define PKT_CFG(size, strength) ((size) << 16 | (strength)) + +#define BB_CFG(bb_offset, bb_size) ((bb_offset) << 16 | (bb_size)) + +#define TIMING(t0, t1, t2, t3) ((t0) << 24 | (t1) << 16 | (t2) << 8 | (t3)) + +static void tango_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); + + if (ctrl & NAND_CLE) + writeb_relaxed(dat, tchip->base + PBUS_CMD); + + if (ctrl & NAND_ALE) + writeb_relaxed(dat, tchip->base + PBUS_ADDR); +} + +static int tango_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + + return readl_relaxed(nfc->pbus_base + PBUS_CS_CTRL) & PBUS_IORDY; +} + +static u8 tango_read_byte(struct mtd_info *mtd) +{ + struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); + + return readb_relaxed(tchip->base + PBUS_DATA); +} + +static void tango_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); + + ioread8_rep(tchip->base + PBUS_DATA, buf, len); +} + +static void tango_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct tango_chip *tchip = to_tango_chip(mtd_to_nand(mtd)); + + iowrite8_rep(tchip->base + PBUS_DATA, buf, len); +} + +static void tango_select_chip(struct mtd_info *mtd, int idx) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + struct tango_chip *tchip = to_tango_chip(chip); + + if (idx < 0) + return; /* No "chip unselect" function */ + + writel_relaxed(tchip->timing1, nfc->reg_base + NFC_TIMING1); + writel_relaxed(tchip->timing2, nfc->reg_base + NFC_TIMING2); + writel_relaxed(tchip->xfer_cfg, nfc->reg_base + NFC_XFER_CFG); + writel_relaxed(tchip->pkt_0_cfg, nfc->reg_base + NFC_PKT_0_CFG); + writel_relaxed(tchip->pkt_n_cfg, nfc->reg_base + NFC_PKT_N_CFG); + writel_relaxed(tchip->bb_cfg, nfc->reg_base + NFC_BB_CFG); +} + +/* + * The controller does not check for bitflips in erased pages, + * therefore software must check instead. + */ +static int check_erased_page(struct nand_chip *chip, u8 *buf) +{ + u8 *meta = chip->oob_poi + BBM_SIZE; + u8 *ecc = chip->oob_poi + BBM_SIZE + METADATA_SIZE; + const int ecc_size = chip->ecc.bytes; + const int pkt_size = chip->ecc.size; + int i, res, meta_len, bitflips = 0; + + for (i = 0; i < chip->ecc.steps; ++i) { + meta_len = i ? 0 : METADATA_SIZE; + res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, + meta, meta_len, + chip->ecc.strength); + if (res < 0) + chip->mtd.ecc_stats.failed++; + + bitflips = max(res, bitflips); + buf += pkt_size; + ecc += ecc_size; + } + + return bitflips; +} + +static int decode_error_report(struct tango_nfc *nfc) +{ + u32 status, res; + + status = readl_relaxed(nfc->reg_base + NFC_XFER_STATUS); + if (status & PAGE_IS_EMPTY) + return 0; + + res = readl_relaxed(nfc->mem_base + ERROR_REPORT); + + if (DECODE_OK_PKT_0(res) && DECODE_OK_PKT_N(res)) + return max(ERR_COUNT_PKT_0(res), ERR_COUNT_PKT_N(res)); + + return -EBADMSG; +} + +static void tango_dma_callback(void *arg) +{ + complete(arg); +} + +static int do_dma(struct tango_nfc *nfc, int dir, int cmd, const void *buf, + int len, int page) +{ + void __iomem *addr = nfc->reg_base + NFC_STATUS; + struct dma_chan *chan = nfc->chan; + struct dma_async_tx_descriptor *desc; + struct scatterlist sg; + struct completion tx_done; + int err = -EIO; + u32 res, val; + + sg_init_one(&sg, buf, len); + if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1) + return -EIO; + + desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir, DMA_PREP_INTERRUPT); + if (!desc) + goto dma_unmap; + + desc->callback = tango_dma_callback; + desc->callback_param = &tx_done; + init_completion(&tx_done); + + writel_relaxed(MODE_NFC, nfc->pbus_base + PBUS_PAD_MODE); + + writel_relaxed(page, nfc->reg_base + NFC_ADDR_PAGE); + writel_relaxed(0, nfc->reg_base + NFC_ADDR_OFFSET); + writel_relaxed(cmd, nfc->reg_base + NFC_FLASH_CMD); + + dmaengine_submit(desc); + dma_async_issue_pending(chan); + + res = wait_for_completion_timeout(&tx_done, HZ); + if (res > 0) + err = readl_poll_timeout(addr, val, val & CMD_READY, 0, 1000); + + writel_relaxed(MODE_RAW, nfc->pbus_base + PBUS_PAD_MODE); + +dma_unmap: + dma_unmap_sg(chan->device->dev, &sg, 1, dir); + + return err; +} + +static int tango_read_page(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + int err, res, len = mtd->writesize; + + if (oob_required) + chip->ecc.read_oob(mtd, chip, page); + + err = do_dma(nfc, DMA_FROM_DEVICE, NFC_READ, buf, len, page); + if (err) + return err; + + res = decode_error_report(nfc); + if (res < 0) { + chip->ecc.read_oob_raw(mtd, chip, page); + res = check_erased_page(chip, buf); + } + + return res; +} + +static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + int err, len = mtd->writesize; + + /* Calling tango_write_oob() would send PAGEPROG twice */ + if (oob_required) + return -ENOTSUPP; + + writel_relaxed(0xffffffff, nfc->mem_base + METADATA); + err = do_dma(nfc, DMA_TO_DEVICE, NFC_WRITE, buf, len, page); + if (err) + return err; + + return 0; +} + +static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos) +{ + *pos += len; + + if (!*buf) { + /* skip over "len" bytes */ + chip->cmdfunc(&chip->mtd, NAND_CMD_RNDOUT, *pos, -1); + } else { + tango_read_buf(&chip->mtd, *buf, len); + *buf += len; + } +} + +static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos) +{ + *pos += len; + + if (!*buf) { + /* skip over "len" bytes */ + chip->cmdfunc(&chip->mtd, NAND_CMD_SEQIN, *pos, -1); + } else { + tango_write_buf(&chip->mtd, *buf, len); + *buf += len; + } +} + +/* + * Physical page layout (not drawn to scale) + * + * NB: Bad Block Marker area splits PKT_N in two (N1, N2). + * + * +---+-----------------+-------+-----+-----------+-----+----+-------+ + * | M | PKT_0 | ECC_0 | ... | N1 | BBM | N2 | ECC_N | + * +---+-----------------+-------+-----+-----------+-----+----+-------+ + * + * Logical page layout: + * + * +-----+---+-------+-----+-------+ + * oob = | BBM | M | ECC_0 | ... | ECC_N | + * +-----+---+-------+-----+-------+ + * + * +-----------------+-----+-----------------+ + * buf = | PKT_0 | ... | PKT_N | + * +-----------------+-----+-----------------+ + */ +static int raw_read(struct nand_chip *chip, u8 *buf, u8 *oob) +{ + u8 *oob_orig = oob; + const int page_size = chip->mtd.writesize; + const int ecc_size = chip->ecc.bytes; + const int pkt_size = chip->ecc.size; + int pos = 0; /* position within physical page */ + int rem = page_size; /* bytes remaining until BBM area */ + + if (oob) + oob += BBM_SIZE; + + aux_read(chip, &oob, METADATA_SIZE, &pos); + + while (rem > pkt_size) { + aux_read(chip, &buf, pkt_size, &pos); + aux_read(chip, &oob, ecc_size, &pos); + rem = page_size - pos; + } + + aux_read(chip, &buf, rem, &pos); + aux_read(chip, &oob_orig, BBM_SIZE, &pos); + aux_read(chip, &buf, pkt_size - rem, &pos); + aux_read(chip, &oob, ecc_size, &pos); + + return 0; +} + +static int raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) +{ + const u8 *oob_orig = oob; + const int page_size = chip->mtd.writesize; + const int ecc_size = chip->ecc.bytes; + const int pkt_size = chip->ecc.size; + int pos = 0; /* position within physical page */ + int rem = page_size; /* bytes remaining until BBM area */ + + if (oob) + oob += BBM_SIZE; + + aux_write(chip, &oob, METADATA_SIZE, &pos); + + while (rem > pkt_size) { + aux_write(chip, &buf, pkt_size, &pos); + aux_write(chip, &oob, ecc_size, &pos); + rem = page_size - pos; + } + + aux_write(chip, &buf, rem, &pos); + aux_write(chip, &oob_orig, BBM_SIZE, &pos); + aux_write(chip, &buf, pkt_size - rem, &pos); + aux_write(chip, &oob, ecc_size, &pos); + + return 0; +} + +static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + return raw_read(chip, buf, chip->oob_poi); +} + +static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + return raw_write(chip, buf, chip->oob_poi); +} + +static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + return raw_read(chip, NULL, chip->oob_poi); +} + +static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page); + raw_write(chip, NULL, chip->oob_poi); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + chip->waitfunc(mtd, chip); + return 0; +} + +static int oob_ecc(struct mtd_info *mtd, int idx, struct mtd_oob_region *res) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (idx >= ecc->steps) + return -ERANGE; + + res->offset = BBM_SIZE + METADATA_SIZE + ecc->bytes * idx; + res->length = ecc->bytes; + + return 0; +} + +static int oob_free(struct mtd_info *mtd, int idx, struct mtd_oob_region *res) +{ + return -ERANGE; /* no free space in spare area */ +} + +static const struct mtd_ooblayout_ops tango_nand_ooblayout_ops = { + .ecc = oob_ecc, + .free = oob_free, +}; + +static u32 to_ticks(int kHz, int ps) +{ + return DIV_ROUND_UP_ULL((u64)kHz * ps, NSEC_PER_SEC); +} + +static int tango_set_timings(struct mtd_info *mtd, + const struct nand_data_interface *conf, + bool check_only) +{ + const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf); + struct nand_chip *chip = mtd_to_nand(mtd); + struct tango_nfc *nfc = to_tango_nfc(chip->controller); + struct tango_chip *tchip = to_tango_chip(chip); + u32 Trdy, Textw, Twc, Twpw, Tacc, Thold, Trpw, Textr; + int kHz = nfc->freq_kHz; + + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + if (check_only) + return 0; + + Trdy = to_ticks(kHz, sdr->tCEA_max - sdr->tREA_max); + Textw = to_ticks(kHz, sdr->tWB_max); + Twc = to_ticks(kHz, sdr->tWC_min); + Twpw = to_ticks(kHz, sdr->tWC_min - sdr->tWP_min); + + Tacc = to_ticks(kHz, sdr->tREA_max); + Thold = to_ticks(kHz, sdr->tREH_min); + Trpw = to_ticks(kHz, sdr->tRC_min - sdr->tREH_min); + Textr = to_ticks(kHz, sdr->tRHZ_max); + + tchip->timing1 = TIMING(Trdy, Textw, Twc, Twpw); + tchip->timing2 = TIMING(Tacc, Thold, Trpw, Textr); + + return 0; +} + +static int chip_init(struct device *dev, struct device_node *np) +{ + u32 cs; + int err, res; + struct mtd_info *mtd; + struct nand_chip *chip; + struct tango_chip *tchip; + struct nand_ecc_ctrl *ecc; + struct tango_nfc *nfc = dev_get_drvdata(dev); + + tchip = devm_kzalloc(dev, sizeof(*tchip), GFP_KERNEL); + if (!tchip) + return -ENOMEM; + + res = of_property_count_u32_elems(np, "reg"); + if (res < 0) + return res; + + if (res != 1) + return -ENOTSUPP; /* Multi-CS chips are not supported */ + + err = of_property_read_u32_index(np, "reg", 0, &cs); + if (err) + return err; + + if (cs >= MAX_CS) + return -EINVAL; + + chip = &tchip->nand_chip; + ecc = &chip->ecc; + mtd = &chip->mtd; + + chip->read_byte = tango_read_byte; + chip->write_buf = tango_write_buf; + chip->read_buf = tango_read_buf; + chip->select_chip = tango_select_chip; + chip->cmd_ctrl = tango_cmd_ctrl; + chip->dev_ready = tango_dev_ready; + chip->setup_data_interface = tango_set_timings; + chip->options = NAND_USE_BOUNCE_BUFFER | + NAND_NO_SUBPAGE_WRITE | + NAND_WAIT_TCCS; + chip->controller = &nfc->hw; + tchip->base = nfc->pbus_base + (cs * 256); + + nand_set_flash_node(chip, np); + mtd_set_ooblayout(mtd, &tango_nand_ooblayout_ops); + mtd->dev.parent = dev; + + err = nand_scan_ident(mtd, 1, NULL); + if (err) + return err; + + ecc->mode = NAND_ECC_HW; + ecc->algo = NAND_ECC_BCH; + ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE); + + ecc->read_page_raw = tango_read_page_raw; + ecc->write_page_raw = tango_write_page_raw; + ecc->read_page = tango_read_page; + ecc->write_page = tango_write_page; + ecc->read_oob = tango_read_oob; + ecc->write_oob = tango_write_oob; + + err = nand_scan_tail(mtd); + if (err) + return err; + + tchip->xfer_cfg = XFER_CFG(cs, 1, ecc->steps, METADATA_SIZE); + tchip->pkt_0_cfg = PKT_CFG(ecc->size + METADATA_SIZE, ecc->strength); + tchip->pkt_n_cfg = PKT_CFG(ecc->size, ecc->strength); + tchip->bb_cfg = BB_CFG(mtd->writesize, BBM_SIZE); + + err = mtd_device_register(mtd, NULL, 0); + if (err) + return err; + + nfc->chips[cs] = tchip; + + return 0; +} + +static int tango_nand_remove(struct platform_device *pdev) +{ + int cs; + struct tango_nfc *nfc = platform_get_drvdata(pdev); + + dma_release_channel(nfc->chan); + + for (cs = 0; cs < MAX_CS; ++cs) { + if (nfc->chips[cs]) + nand_release(&nfc->chips[cs]->nand_chip.mtd); + } + + return 0; +} + +static int tango_nand_probe(struct platform_device *pdev) +{ + int err; + struct clk *clk; + struct resource *res; + struct tango_nfc *nfc; + struct device_node *np; + + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nfc->reg_base)) + return PTR_ERR(nfc->reg_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + nfc->mem_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nfc->mem_base)) + return PTR_ERR(nfc->mem_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + nfc->pbus_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nfc->pbus_base)) + return PTR_ERR(nfc->pbus_base); + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + nfc->chan = dma_request_chan(&pdev->dev, "nfc_sbox"); + if (IS_ERR(nfc->chan)) + return PTR_ERR(nfc->chan); + + platform_set_drvdata(pdev, nfc); + nand_hw_control_init(&nfc->hw); + nfc->freq_kHz = clk_get_rate(clk) / 1000; + + for_each_child_of_node(pdev->dev.of_node, np) { + err = chip_init(&pdev->dev, np); + if (err) { + tango_nand_remove(pdev); + return err; + } + } + + return 0; +} + +static const struct of_device_id tango_nand_ids[] = { + { .compatible = "sigma,smp8758-nand" }, + { /* sentinel */ } +}; + +static struct platform_driver tango_nand_driver = { + .probe = tango_nand_probe, + .remove = tango_nand_remove, + .driver = { + .name = "tango-nand", + .of_match_table = tango_nand_ids, + }, +}; + +module_platform_driver(tango_nand_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sigma Designs"); +MODULE_DESCRIPTION("Tango4 NAND Flash controller driver"); From a1a261707b51d728d0c2b4c32630bc43a8eb33b2 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 3 Nov 2016 02:21:04 +0900 Subject: [PATCH 13/55] mtd: nand: denali_dt: replace clk_disable() with clk_disable_unprepare() The denali_dt_probe() calls clk_disable_unprepare() in the bailout path, whereas denali_dt_remove calls clk_disable(), inconsistently. Replace the latter with clk_disable_unprepare() to make sure to unprepare the clock. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali_dt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c index 0cb1e8d9fbfc..f821dc1c5739 100644 --- a/drivers/mtd/nand/denali_dt.c +++ b/drivers/mtd/nand/denali_dt.c @@ -110,7 +110,7 @@ static int denali_dt_remove(struct platform_device *ofdev) struct denali_dt *dt = platform_get_drvdata(ofdev); denali_remove(&dt->denali); - clk_disable(dt->clk); + clk_disable_unprepare(dt->clk); return 0; } From 4722c0e958e636226100f37cd6d17ac09b695827 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 17:49:08 +0900 Subject: [PATCH 14/55] mtd: nand: change return type of nand_get_flash_type() to int Since commit d1e1f4e42b5d ("mtd: nand: add support for reading ONFI parameters from NAND device"), the returned "type" is never used in nand_scan_ident(). Make nand_get_flash_type() simply return an integer value in order to avoid unnecessary ERR_PTR/PTR_ERR dance. Signed-off-by: Masahiro Yamada Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0acb0070280a..4d099d4f442c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3999,10 +3999,9 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, /* * Get the flash and manufacturer id and lookup if the type is supported. */ -static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, - struct nand_chip *chip, - int *maf_id, int *dev_id, - struct nand_flash_dev *type) +static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, + int *maf_id, int *dev_id, + struct nand_flash_dev *type) { int busw; int i, maf_idx; @@ -4040,7 +4039,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (id_data[0] != *maf_id || id_data[1] != *dev_id) { pr_info("second ID read did not match %02x,%02x against %02x,%02x\n", *maf_id, *dev_id, id_data[0], id_data[1]); - return ERR_PTR(-ENODEV); + return -ENODEV; } if (!type) @@ -4067,7 +4066,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, } if (!type->name) - return ERR_PTR(-ENODEV); + return -ENODEV; if (!mtd->name) mtd->name = type->name; @@ -4112,7 +4111,7 @@ ident_done: pr_warn("bus width %d instead %d bit\n", (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, busw ? 16 : 8); - return ERR_PTR(-EINVAL); + return -EINVAL; } nand_decode_bbm_options(mtd, chip, id_data); @@ -4154,7 +4153,7 @@ ident_done: pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n", (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); - return type; + return 0; } static const char * const nand_ecc_modes[] = { @@ -4320,7 +4319,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, { int i, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_flash_dev *type; int ret; ret = nand_dt_init(chip); @@ -4343,14 +4341,12 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16); /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, &nand_maf_id, - &nand_dev_id, table); - - if (IS_ERR(type)) { + ret = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, table); + if (ret) { if (!(chip->options & NAND_SCAN_SILENT_NODEV)) pr_warn("No NAND device found\n"); chip->select_chip(mtd, -1); - return PTR_ERR(type); + return ret; } ret = nand_init_data_interface(chip); From 0d0aa866ffa45471b12c9b9c5c48cbc02dcca59b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:49 +0900 Subject: [PATCH 15/55] mtd: nand: ams-delta: return error code of nand_scan() on error The nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/ams-delta.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 78e12cc8bac2..5d6c26f3cf7f 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -234,10 +234,9 @@ static int ams_delta_init(struct platform_device *pdev) goto out_gpio; /* Scan to find existence of the device */ - if (nand_scan(ams_delta_mtd, 1)) { - err = -ENXIO; + err = nand_scan(ams_delta_mtd, 1); + if (err) goto out_mtd; - } /* Register the partitions */ mtd_device_register(ams_delta_mtd, partition_info, From 546fe03fe349fd79df28ed8fed5efb1357110e28 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:50 +0900 Subject: [PATCH 16/55] mtd: nand: cmx270: return error code of nand_scan() on error The nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/cmx270_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 49133783ca53..226ac0bcafc6 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -195,9 +195,9 @@ static int __init cmx270_init(void) this->write_buf = cmx270_write_buf; /* Scan to find existence of the device */ - if (nand_scan (cmx270_nand_mtd, 1)) { + ret = nand_scan(cmx270_nand_mtd, 1); + if (ret) { pr_notice("No NAND device\n"); - ret = -ENXIO; goto err_scan; } From 29453ba40bb5e91dcaa9b571208da1c1e75e63f1 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:51 +0900 Subject: [PATCH 17/55] mtd: nand: cs553x: return error code of nand_scan() on error The nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/cs553x_nand.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index a65e4e0f57a1..594b28684138 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -242,10 +242,9 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) } /* Scan to find existence of the device */ - if (nand_scan(new_mtd, 1)) { - err = -ENXIO; + err = nand_scan(new_mtd, 1); + if (err) goto out_free; - } cs553x_mtd[cs] = new_mtd; goto out; From 408bf51e92752d084d82d21e2c1f838d0ecc5eea Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:52 +0900 Subject: [PATCH 18/55] mtd: nand: gpio: return error code of nand_scan() on error The nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/gpio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 6317f6836022..0d24857469ab 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -286,10 +286,9 @@ static int gpio_nand_probe(struct platform_device *pdev) if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); - if (nand_scan(mtd, 1)) { - ret = -ENXIO; + ret = nand_scan(mtd, 1); + if (ret) goto err_wp; - } if (gpiomtd->plat.adjust_parts) gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size); From bbd4d03c56083069cf0808681bd301cf31701fbd Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:53 +0900 Subject: [PATCH 19/55] mtd: nand: mpc5121: return error code of nand_scan() on error The nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mpc5121_nfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 7eacb2f545f5..6d6eaed2d20c 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -777,9 +777,9 @@ static int mpc5121_nfc_probe(struct platform_device *op) } /* Detect NAND chips */ - if (nand_scan(mtd, be32_to_cpup(chips_no))) { + retval = nand_scan(mtd, be32_to_cpup(chips_no)); + if (retval) { dev_err(dev, "NAND Flash not found !\n"); - retval = -ENXIO; goto error; } From 43358c173d02446b9f8da25fb0de8cfc22eb1ae6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:54 +0900 Subject: [PATCH 20/55] mtd: nand: tmio: return error code of nand_scan() on error The nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENODEV. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/tmio_nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 08b30549ec0a..fc5e773f8b60 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -435,10 +435,10 @@ static int tmio_probe(struct platform_device *dev) nand_chip->waitfunc = tmio_nand_wait; /* Scan to find existence of the device */ - if (nand_scan(mtd, 1)) { - retval = -ENODEV; + retval = nand_scan(mtd, 1); + if (retval) goto err_irq; - } + /* Register the partitions */ retval = mtd_device_parse_register(mtd, NULL, NULL, data ? data->partition : NULL, From 6c34ad7d17849bbfaf362d7c5bed11d80c1cdab4 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:55 +0900 Subject: [PATCH 21/55] mtd: nand: orion: return error code of nand_scan() on error The nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/orion_nand.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 40a7c4a2cf0d..4a91c5d000be 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -155,10 +155,9 @@ static int __init orion_nand_probe(struct platform_device *pdev) clk_put(clk); } - if (nand_scan(mtd, 1)) { - ret = -ENXIO; + ret = nand_scan(mtd, 1); + if (ret) goto no_dev; - } mtd->name = "orion_nand"; ret = mtd_device_register(mtd, board->parts, board->nr_parts); From 4e5af27e2a3a393558936b82fece1942ec49f534 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:56 +0900 Subject: [PATCH 22/55] mtd: nand: pasemi: return error code of nand_scan() on error The nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/pasemi_nand.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 5de7591b0510..074b8b01289e 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -156,10 +156,9 @@ static int pasemi_nand_probe(struct platform_device *ofdev) chip->bbt_options = NAND_BBT_USE_FLASH; /* Scan to find existence of the device */ - if (nand_scan(pasemi_nand_mtd, 1)) { - err = -ENXIO; + err = nand_scan(pasemi_nand_mtd, 1); + if (err) goto out_lpc; - } if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) { dev_err(dev, "Unable to register MTD device\n"); From ce2eaca7ebf13006cb992ed2f8ce46aaf8724c71 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:57 +0900 Subject: [PATCH 23/55] mtd: nand: plat_nand: return error code of nand_scan() on error The nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/plat_nand.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 415a53a0deeb..791de3e4bbb6 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -86,10 +86,9 @@ static int plat_nand_probe(struct platform_device *pdev) } /* Scan to find existence of the device */ - if (nand_scan(mtd, pdata->chip.nr_chips)) { - err = -ENXIO; + err = nand_scan(mtd, pdata->chip.nr_chips); + if (err) goto out; - } part_types = pdata->chip.part_probe_types; From b5169d35ed585c0d5fb49cad38f3ae1d6963a641 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:58 +0900 Subject: [PATCH 24/55] mtd: nand: atmel: return error code of nand_scan_ident/tail() on error The nand_scan_ident/tail() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/atmel_nand.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 68b9160108c9..9ebd5ecefea6 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -2267,10 +2267,9 @@ static int atmel_nand_probe(struct platform_device *pdev) dev_info(host->dev, "No DMA support for NAND access.\n"); /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1, NULL)) { - res = -ENXIO; + res = nand_scan_ident(mtd, 1, NULL); + if (res) goto err_scan_ident; - } if (host->board.on_flash_bbt || on_flash_bbt) nand_chip->bbt_options |= NAND_BBT_USE_FLASH; @@ -2304,10 +2303,9 @@ static int atmel_nand_probe(struct platform_device *pdev) } /* second phase scan */ - if (nand_scan_tail(mtd)) { - res = -ENXIO; + res = nand_scan_tail(mtd); + if (res) goto err_scan_tail; - } mtd->name = "atmel_nand"; res = mtd_device_register(mtd, host->board.parts, From c25cca0336a2f748779fd4a0f08177bde984e7be Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:42:59 +0900 Subject: [PATCH 25/55] mtd: nand: brcmnand: return error code of nand_scan_ident/tail() on error The nand_scan_ident/tail() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/brcmnand/brcmnand.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index 9d2424bfdbf5..42ebd73f821d 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -2209,8 +2209,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) nand_writereg(ctrl, cfg_offs, nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH); - if (nand_scan_ident(mtd, 1, NULL)) - return -ENXIO; + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; chip->options |= NAND_NO_SUBPAGE_WRITE; /* @@ -2234,8 +2235,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) if (ret) return ret; - if (nand_scan_tail(mtd)) - return -ENXIO; + ret = nand_scan_tail(mtd); + if (ret) + return ret; return mtd_device_register(mtd, NULL, 0); } From ad5678ec40764ccd38dd688f5ec5c1c835241abd Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:00 +0900 Subject: [PATCH 26/55] mtd: nand: fsmc: return error code of nand_scan_ident/tail() on error The nand_scan_ident/tail() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsmc_nand.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index d4f454a4b35e..4924b43977ef 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -926,8 +926,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) /* * Scan to find existence of the device */ - if (nand_scan_ident(mtd, 1, NULL)) { - ret = -ENXIO; + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) { dev_err(&pdev->dev, "No NAND Device found!\n"); goto err_scan_ident; } @@ -992,10 +992,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) } /* Second stage of scan to fill MTD data-structures */ - if (nand_scan_tail(mtd)) { - ret = -ENXIO; + ret = nand_scan_tail(mtd); + if (ret) goto err_probe; - } /* * The partition information can is accessed by (in the same precedence) From b04bafca67dac3c390041bbcd694973e3d3f3b7d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:01 +0900 Subject: [PATCH 27/55] mtd: nand: lpc32xx: return error code of nand_scan_ident/tail() on error The nand_scan_ident/tail() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Acked-by: Vladimir Zapolskiy Signed-off-by: Boris Brezillon --- drivers/mtd/nand/lpc32xx_mlc.c | 10 ++++------ drivers/mtd/nand/lpc32xx_slc.c | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 852388171f20..5553a5d9efd1 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -747,10 +747,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) * Scan to find existance of the device and * Get the type of NAND device SMALL block or LARGE block */ - if (nand_scan_ident(mtd, 1, NULL)) { - res = -ENXIO; + res = nand_scan_ident(mtd, 1, NULL); + if (res) goto err_exit3; - } host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); if (!host->dma_buf) { @@ -793,10 +792,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) * Fills out all the uninitialized function pointers with the defaults * And scans for a bad block table if appropriate. */ - if (nand_scan_tail(mtd)) { - res = -ENXIO; + res = nand_scan_tail(mtd); + if (res) goto err_exit4; - } mtd->name = DRV_NAME; diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index 8d3edc34958e..53bafe23ab39 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -894,10 +894,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } /* Find NAND device */ - if (nand_scan_ident(mtd, 1, NULL)) { - res = -ENXIO; + res = nand_scan_ident(mtd, 1, NULL); + if (res) goto err_exit3; - } /* OOB and ECC CPU and DMA work areas */ host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); @@ -929,10 +928,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) /* * Fills out all the uninitialized function pointers with the defaults */ - if (nand_scan_tail(mtd)) { - res = -ENXIO; + res = nand_scan_tail(mtd); + if (res) goto err_exit3; - } mtd->name = "nxp_lpc3220_slc"; res = mtd_device_register(mtd, host->ncfg->parts, From f0dbe4aa35e73e4282d9446abb8900ab0636d483 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:02 +0900 Subject: [PATCH 28/55] mtd: nand: mediatek: return error code of nand_scan_ident/tail() on error The nand_scan_ident/tail() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENODEV. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mtk_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c index 5223a2182ee4..6c3eed3c2094 100644 --- a/drivers/mtd/nand/mtk_nand.c +++ b/drivers/mtd/nand/mtk_nand.c @@ -1297,7 +1297,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, ret = nand_scan_ident(mtd, nsels, NULL); if (ret) - return -ENODEV; + return ret; /* store bbt magic in page, cause OOB is not protected */ if (nand->bbt_options & NAND_BBT_USE_FLASH) @@ -1323,7 +1323,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, ret = nand_scan_tail(mtd); if (ret) - return -ENODEV; + return ret; ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0); if (ret) { From bc83c78871037b67d41d6fe70c7937ea89868d3b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:03 +0900 Subject: [PATCH 29/55] mtd: nand: mxc: return error code of nand_scan_ident/tail() on error The nand_scan_ident/tail() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index d7f724b24fd7..61ca020c5272 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1747,10 +1747,9 @@ static int mxcnd_probe(struct platform_device *pdev) } /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL)) { - err = -ENXIO; + err = nand_scan_ident(mtd, is_imx25_nfc(host) ? 4 : 1, NULL); + if (err) goto escan; - } switch (this->ecc.mode) { case NAND_ECC_HW: @@ -1808,10 +1807,9 @@ static int mxcnd_probe(struct platform_device *pdev) } /* second phase scan */ - if (nand_scan_tail(mtd)) { - err = -ENXIO; + err = nand_scan_tail(mtd); + if (err) goto escan; - } /* Register the partitions */ mtd_device_parse_register(mtd, part_probes, From bd93a3af82d5175a86badf7bc0e30d17cc6469ce Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:04 +0900 Subject: [PATCH 30/55] mtd: nand: omap2: return error code of nand_scan_ident/tail() on error The nand_scan_ident/tail() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/omap2.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 0934c3b3f70e..2a52101120d4 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1895,10 +1895,10 @@ static int omap_nand_probe(struct platform_device *pdev) /* scan NAND device connected to chip controller */ nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; - if (nand_scan_ident(mtd, 1, NULL)) { + err = nand_scan_ident(mtd, 1, NULL); + if (err) { dev_err(&info->pdev->dev, "scan failed, may be bus-width mismatch\n"); - err = -ENXIO; goto return_error; } @@ -2154,10 +2154,9 @@ static int omap_nand_probe(struct platform_device *pdev) scan_tail: /* second phase scan */ - if (nand_scan_tail(mtd)) { - err = -ENXIO; + err = nand_scan_tail(mtd); + if (err) goto return_error; - } if (dev->of_node) mtd_device_register(mtd, NULL, 0); From e9d354b4b26aa8fe18b3a6858f2161a43a040e1f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:05 +0900 Subject: [PATCH 31/55] mtd: nand: vf610: return error code of nand_scan_ident/tail() on error The nand_scan_ident/tail() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/vf610_nfc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 3ad514c44dcb..3ea4bb19e12d 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -717,10 +717,9 @@ static int vf610_nfc_probe(struct platform_device *pdev) vf610_nfc_preinit_controller(nfc); /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1, NULL)) { - err = -ENXIO; + err = nand_scan_ident(mtd, 1, NULL); + if (err) goto error; - } vf610_nfc_init_controller(nfc); @@ -775,10 +774,9 @@ static int vf610_nfc_probe(struct platform_device *pdev) } /* second phase scan */ - if (nand_scan_tail(mtd)) { - err = -ENXIO; + err = nand_scan_tail(mtd); + if (err) goto error; - } platform_set_drvdata(pdev, mtd); From 72480e4e9537fe7c8b9ccc980873665676002808 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:06 +0900 Subject: [PATCH 32/55] mtd: nand: cafe: return error code of nand_scan_ident() on error The nand_scan_ident() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. (This driver is already doing so for nand_scan_tail().) Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/cafe_nand.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 0b0c93702abb..d40c32d311d8 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -725,10 +725,9 @@ static int cafe_nand_probe(struct pci_dev *pdev, usedma = 0; /* Scan to find existence of the device */ - if (nand_scan_ident(mtd, 2, NULL)) { - err = -ENXIO; + err = nand_scan_ident(mtd, 2, NULL); + if (err) goto out_irq; - } cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers) + From c8cae35572b6132e7c3fbb708ebee4b84e29bbed Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:07 +0900 Subject: [PATCH 33/55] mtd: nand: hisi504: return error code of nand_scan_ident() on error The nand_scan_ident() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENODEV. (This driver is already doing so for nand_scan_tail().) Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/hisi504_nand.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 9432546f4cd4..e40364eeb556 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -774,10 +774,8 @@ static int hisi_nfc_probe(struct platform_device *pdev) } ret = nand_scan_ident(mtd, max_chips, NULL); - if (ret) { - ret = -ENODEV; + if (ret) goto err_res; - } host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize, &host->dma_buffer, GFP_KERNEL); From 133fe8fa614175eb7e59599dbfe357c7bc7da851 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:08 +0900 Subject: [PATCH 34/55] mtd: nand: pxa3xx: return error code of nand_scan_ident() on error The nand_scan_ident() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENODEV. (This driver is already doing so for nand_scan_tail().) Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/pxa3xx_nand.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 4c749b030301..649ba8200832 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1680,8 +1680,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) chip->ecc.strength = pdata->ecc_strength; chip->ecc.size = pdata->ecc_step_size; - if (nand_scan_ident(mtd, 1, NULL)) - return -ENODEV; + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; if (!pdata->keep_config) { ret = pxa3xx_nand_init(host); From c75183020fca0229fce1fd6ba21ae840b41d79e9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:09 +0900 Subject: [PATCH 35/55] mtd: nand: nandsim: remove unneeded checks for nand_scan_ident/tail() The nand_scan_ident/tail() never returns a positive value when it fails. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nandsim.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 1eb934414eb5..c76287a69663 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -2313,8 +2313,6 @@ static int __init ns_init_module(void) retval = nand_scan_ident(nsmtd, 1, NULL); if (retval) { NS_ERR("cannot scan NAND Simulator device\n"); - if (retval > 0) - retval = -ENXIO; goto error; } @@ -2350,8 +2348,6 @@ static int __init ns_init_module(void) retval = nand_scan_tail(nsmtd); if (retval) { NS_ERR("can't register NAND Simulator\n"); - if (retval > 0) - retval = -ENXIO; goto error; } From 83f48f80de8a03c053ff0cac1521c1eaeaa107f2 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 4 Nov 2016 19:43:10 +0900 Subject: [PATCH 36/55] mtd: nand: socrates: use nand_scan() for nand_scan_ident/tail() combo For this driver, there is nothing between nand_scan_ident() and nand_scan_tail(). They can be merged into nand_scan(). Also, nand_scan() returns an appropriate error value when it fails. Use it instead of the fixed error code -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/socrates_nand.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index 888fd314c62a..72369bd079af 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -187,17 +187,9 @@ static int socrates_nand_probe(struct platform_device *ofdev) dev_set_drvdata(&ofdev->dev, host); - /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1, NULL)) { - res = -ENXIO; + res = nand_scan(mtd, 1); + if (res) goto out; - } - - /* second phase scan */ - if (nand_scan_tail(mtd)) { - res = -ENXIO; - goto out; - } res = mtd_device_register(mtd, NULL, 0); if (!res) From ef5327dc2597bbf806393d5edca54b2f059e0e69 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:20 +0900 Subject: [PATCH 37/55] mtd: nand: denali: remove unneeded includes The driver calls devm_kzalloc()/devm_kfree() to allocate/free memory. They are declared in , not in . Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 1 - drivers/mtd/nand/denali_dt.c | 1 - drivers/mtd/nand/denali_pci.c | 1 - 3 files changed, 3 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 0476ae8776d9..36a0454911fe 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c index f821dc1c5739..5607fcd3b8ed 100644 --- a/drivers/mtd/nand/denali_dt.c +++ b/drivers/mtd/nand/denali_dt.c @@ -21,7 +21,6 @@ #include #include #include -#include #include "denali.h" diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c index de31514df282..ac843238b77e 100644 --- a/drivers/mtd/nand/denali_pci.c +++ b/drivers/mtd/nand/denali_pci.c @@ -14,7 +14,6 @@ #include #include #include -#include #include "denali.h" From c4ae0977f57deff490f0232bf8f2a7689cbdcbee Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:21 +0900 Subject: [PATCH 38/55] mtd: nand: denali: remove unused struct member denali_nand_info::idx The struct member "idx" was used as an index for debug_array long ago, but the DEBUG_DENALI feature was removed by commit 7cfffac06ca0 ("nand/denali: use dev_xx debug function to replace nand_dbg_print and some printk"). Since then, this has been only initialized, but never referenced. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 2 -- drivers/mtd/nand/denali.h | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 36a0454911fe..bf83a506177f 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1436,8 +1436,6 @@ static struct nand_bbt_descr bbt_mirror_descr = { /* initialize driver data structures */ static void denali_drv_init(struct denali_nand_info *denali) { - denali->idx = 0; - /* setup interrupt handler */ /* * the completion object will be used to notify diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index e7ab4866a5da..0ce73447ab3b 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -467,7 +467,6 @@ struct denali_nand_info { spinlock_t irq_lock; uint32_t irq_status; int irq_debug_array[32]; - int idx; int irq; uint32_t devnum; /* represent how many nands connected */ From 50d5f2a00aeb9778f9219f8df3fceb7a2071dc73 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:22 +0900 Subject: [PATCH 39/55] mtd: nand: denali: remove bogus comment about interrupt handler setup The interrupt handler is setup in denali_init(), not in denali_drv_init(). This comment is false. Such a comment adds no value, so just delete it instead of move. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index bf83a506177f..fde9413aed0b 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1436,7 +1436,6 @@ static struct nand_bbt_descr bbt_mirror_descr = { /* initialize driver data structures */ static void denali_drv_init(struct denali_nand_info *denali) { - /* setup interrupt handler */ /* * the completion object will be used to notify * the callee that the interrupt is done From c9e02584324285d5fbe3912a00a2383e0378f420 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:23 +0900 Subject: [PATCH 40/55] mtd: nand: denali: remove detect_partition_feature() The denali->fwblks is set by detect_partition_feature(), but it is not referenced from anywhere. That means the struct member fwblks and the whole of detect_partition_feature() are unneeded. The comment block implies this function is only for Intel platforms. I found drivers/staging/spectra used to exist, but it was deleted by commit be7f39c5ecf5 ("Staging: delete spectra driver") 5 years ago. So, I guess nobody would need this function any more. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 29 ----------------------------- drivers/mtd/nand/denali.h | 9 --------- 2 files changed, 38 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index fde9413aed0b..4699fff6940b 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -473,33 +473,6 @@ static void detect_max_banks(struct denali_nand_info *denali) denali->max_banks = 1 << (features & FEATURES__N_BANKS); } -static void detect_partition_feature(struct denali_nand_info *denali) -{ - /* - * For MRST platform, denali->fwblks represent the - * number of blocks firmware is taken, - * FW is in protect partition and MTD driver has no - * permission to access it. So let driver know how many - * blocks it can't touch. - */ - if (ioread32(denali->flash_reg + FEATURES) & FEATURES__PARTITION) { - if ((ioread32(denali->flash_reg + PERM_SRC_ID(1)) & - PERM_SRC_ID__SRCID) == SPECTRA_PARTITION_ID) { - denali->fwblks = - ((ioread32(denali->flash_reg + MIN_MAX_BANK(1)) & - MIN_MAX_BANK__MIN_VALUE) * - denali->blksperchip) - + - (ioread32(denali->flash_reg + MIN_BLK_ADDR(1)) & - MIN_BLK_ADDR__VALUE); - } else { - denali->fwblks = SPECTRA_START_BLOCK; - } - } else { - denali->fwblks = SPECTRA_START_BLOCK; - } -} - static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) { uint16_t status = PASS; @@ -551,8 +524,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) find_valid_banks(denali); - detect_partition_feature(denali); - /* * If the user specified to override the default timings * with a specific ONFI mode, we apply those changes here. diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 0ce73447ab3b..7c0800d9dd4c 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -383,14 +383,6 @@ #define CLK_X 5 #define CLK_MULTI 4 -/* spectraswconfig.h */ -#define CMD_DMA 0 - -#define SPECTRA_PARTITION_ID 0 -/**** Block Table and Reserved Block Parameters *****/ -#define SPECTRA_START_BLOCK 3 -#define NUM_FREE_BLOCKS_GATE 30 - /* KBV - Updated to LNW scratch register address */ #define SCRATCH_REG_ADDR CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR #define SCRATCH_REG_SIZE 64 @@ -470,7 +462,6 @@ struct denali_nand_info { int irq; uint32_t devnum; /* represent how many nands connected */ - uint32_t fwblks; /* represent how many blocks FW used */ uint32_t totalblks; uint32_t blksperchip; uint32_t bbtskipbytes; From 789ccf17754fe95cdd4de3cae7afb09a9bb43c94 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:24 +0900 Subject: [PATCH 41/55] mtd: nand: denali: remove "Spectra:" prefix from printk strings As far as I understood from the Kconfig menu deleted by commit be7f39c5ecf5 ("Staging: delete spectra driver"), the "Spectra" is specific to Intel Moorestown Platform. The Denali NAND controller IP is used for various SoCs such as Altera SOCFPGA, Socionext UniPhier, etc. The platform specific strings are not preferred in this driver. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 4699fff6940b..c525995f927c 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -402,7 +402,7 @@ static void get_hynix_nand_para(struct denali_nand_info *denali, break; default: dev_warn(denali->dev, - "Spectra: Unknown Hynix NAND (Device ID: 0x%x).\n" + "Unknown Hynix NAND (Device ID: 0x%x).\n" "Will use default parameter values instead.\n", device_id); } @@ -1458,7 +1458,7 @@ int denali_init(struct denali_nand_info *denali) */ if (request_irq(denali->irq, denali_isr, IRQF_SHARED, DENALI_NAND_NAME, denali)) { - pr_err("Spectra: Unable to allocate IRQ\n"); + dev_err(denali->dev, "Unable to request IRQ\n"); return -ENODEV; } @@ -1495,7 +1495,7 @@ int denali_init(struct denali_nand_info *denali) /* Is 32-bit DMA supported? */ ret = dma_set_mask(denali->dev, DMA_BIT_MASK(32)); if (ret) { - pr_err("Spectra: no usable DMA configuration\n"); + dev_err(denali->dev, "No usable DMA configuration\n"); goto failed_req_irq; } @@ -1503,7 +1503,7 @@ int denali_init(struct denali_nand_info *denali) mtd->writesize + mtd->oobsize, DMA_BIDIRECTIONAL); if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) { - dev_err(denali->dev, "Spectra: failed to map DMA buffer\n"); + dev_err(denali->dev, "Failed to map DMA buffer\n"); ret = -EIO; goto failed_req_irq; } @@ -1598,8 +1598,7 @@ int denali_init(struct denali_nand_info *denali) ret = mtd_device_register(mtd, NULL, 0); if (ret) { - dev_err(denali->dev, "Spectra: Failed to register MTD: %d\n", - ret); + dev_err(denali->dev, "Failed to register MTD: %d\n", ret); goto failed_req_irq; } return 0; From 376563917c23ec8e1d30475233f1111b307fd18b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:25 +0900 Subject: [PATCH 42/55] mtd: nand: denali: remove unused struct member totalblks, blksperchip The denali->blksperchip is set, but not referenced any more. The denali->totalblks is used only for calculating denali->blksperchip. Both of them are unneeded. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 8 -------- drivers/mtd/nand/denali.h | 2 -- 2 files changed, 10 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index c525995f927c..98b21d230e62 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1573,14 +1573,6 @@ int denali_init(struct denali_nand_info *denali) denali->nand.ecc.bytes *= denali->devnum; denali->nand.ecc.strength *= denali->devnum; - /* - * Let driver know the total blocks number and how many blocks - * contained by each nand chip. blksperchip will help driver to - * know how many blocks is taken by FW. - */ - denali->totalblks = mtd->size >> denali->nand.phys_erase_shift; - denali->blksperchip = denali->totalblks / denali->nand.numchips; - /* override the default read operations */ denali->nand.ecc.size = ECC_SECTOR_SIZE * denali->devnum; denali->nand.ecc.read_page = denali_read_page; diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 7c0800d9dd4c..ea22191e8515 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -462,8 +462,6 @@ struct denali_nand_info { int irq; uint32_t devnum; /* represent how many nands connected */ - uint32_t totalblks; - uint32_t blksperchip; uint32_t bbtskipbytes; uint32_t max_banks; }; From c1d0df192c3adb0a92a984f7f5ae1124027549dd Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:26 +0900 Subject: [PATCH 43/55] mtd: nand: denali: use managed devm_irq_request() Use the managed variant instead of request_irq() and free_irq(). Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 98b21d230e62..5b34d4a6dfa4 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -575,7 +575,6 @@ static void denali_irq_init(struct denali_nand_info *denali) static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali) { denali_set_intr_modes(denali, false); - free_irq(irqnum, denali); } static void denali_irq_enable(struct denali_nand_info *denali, @@ -1456,8 +1455,8 @@ int denali_init(struct denali_nand_info *denali) * denali_isr register is done after all the hardware * initilization is finished */ - if (request_irq(denali->irq, denali_isr, IRQF_SHARED, - DENALI_NAND_NAME, denali)) { + if (devm_request_irq(denali->dev, denali->irq, denali_isr, IRQF_SHARED, + DENALI_NAND_NAME, denali)) { dev_err(denali->dev, "Unable to request IRQ\n"); return -ENODEV; } From 7ebb8d06781f87f5c6c6e6bae42ab44ba0fe9012 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:27 +0900 Subject: [PATCH 44/55] mtd: nand: denali: return error code from devm_request_irq() on error The devm_request_irq() returns an appropriate error value when it fails. Use it instead of the fixed -ENODEV. While we are here, reword the comment to make it fit in a single line, fixing the misspelling of "initialization". Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 5b34d4a6dfa4..f636ac4222d0 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1451,14 +1451,12 @@ int denali_init(struct denali_nand_info *denali) denali_hw_init(denali); denali_drv_init(denali); - /* - * denali_isr register is done after all the hardware - * initilization is finished - */ - if (devm_request_irq(denali->dev, denali->irq, denali_isr, IRQF_SHARED, - DENALI_NAND_NAME, denali)) { + /* Request IRQ after all the hardware initialization is finished */ + ret = devm_request_irq(denali->dev, denali->irq, denali_isr, + IRQF_SHARED, DENALI_NAND_NAME, denali); + if (ret) { dev_err(denali->dev, "Unable to request IRQ\n"); - return -ENODEV; + return ret; } /* now that our ISR is registered, we can enable interrupts */ From a227d4e4c356c9be84daba80be282ec56ba46c6c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:28 +0900 Subject: [PATCH 45/55] mtd: nand: denali: return error code from nand_scan_ident/tail on error The nand_scan_ident/tail() returns an appropriate error value when it fails. Use it instead of the fixed -ENXIO. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index f636ac4222d0..bcb2bc79f0a1 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1474,10 +1474,9 @@ int denali_init(struct denali_nand_info *denali) * this is the first stage in a two step process to register * with the nand subsystem */ - if (nand_scan_ident(mtd, denali->max_banks, NULL)) { - ret = -ENXIO; + ret = nand_scan_ident(mtd, denali->max_banks, NULL); + if (ret) goto failed_req_irq; - } /* allocate the right size buffer now */ devm_kfree(denali->dev, denali->buf.buf); @@ -1580,10 +1579,9 @@ int denali_init(struct denali_nand_info *denali) denali->nand.ecc.write_oob = denali_write_oob; denali->nand.erase = denali_erase; - if (nand_scan_tail(mtd)) { - ret = -ENXIO; + ret = nand_scan_tail(mtd); + if (ret) goto failed_req_irq; - } ret = mtd_device_register(mtd, NULL, 0); if (ret) { From 01a1d524eb68c0395074dd1d661b119ac96edba1 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:29 +0900 Subject: [PATCH 46/55] mtd: nand: denali: remove unneeded parentheses Remove parentheses surrounding the whole right side of an assignment. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index bcb2bc79f0a1..4b0b51b987d4 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1510,16 +1510,16 @@ int denali_init(struct denali_nand_info *denali) * the real pagesize and anything necessery */ denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED); - denali->nand.chipsize <<= (denali->devnum - 1); - denali->nand.page_shift += (denali->devnum - 1); + denali->nand.chipsize <<= denali->devnum - 1; + denali->nand.page_shift += denali->devnum - 1; denali->nand.pagemask = (denali->nand.chipsize >> denali->nand.page_shift) - 1; - denali->nand.bbt_erase_shift += (denali->devnum - 1); + denali->nand.bbt_erase_shift += denali->devnum - 1; denali->nand.phys_erase_shift = denali->nand.bbt_erase_shift; - denali->nand.chip_shift += (denali->devnum - 1); - mtd->writesize <<= (denali->devnum - 1); - mtd->oobsize <<= (denali->devnum - 1); - mtd->erasesize <<= (denali->devnum - 1); + denali->nand.chip_shift += denali->devnum - 1; + mtd->writesize <<= denali->devnum - 1; + mtd->oobsize <<= denali->devnum - 1; + mtd->erasesize <<= denali->devnum - 1; mtd->size = denali->nand.numchips * denali->nand.chipsize; denali->bbtskipbytes *= denali->devnum; From fc80f21a11156dd01c5ef14153e6f9e1291ccb82 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 13:35:30 +0900 Subject: [PATCH 47/55] mtd: nand: denali: remove debug lines of __FILE__, __LINE__, __func__ Such debug lines might be useful when debugging the driver first, but should be deleted from the upstream code. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 4b0b51b987d4..73b9d4e2dca0 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -181,9 +181,6 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali) { int i; - dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", - __FILE__, __LINE__, __func__); - for (i = 0; i < denali->max_banks; i++) iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT, denali->flash_reg + INTR_STATUS(i)); @@ -233,9 +230,6 @@ static void nand_onfi_timing_set(struct denali_nand_info *denali, uint16_t acc_clks; uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt; - dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", - __FILE__, __LINE__, __func__); - en_lo = CEIL_DIV(Trp[mode], CLK_X); en_hi = CEIL_DIV(Treh[mode], CLK_X); #if ONFI_BLOOM_TIME @@ -480,9 +474,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) uint8_t maf_id, device_id; int i; - dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", - __FILE__, __LINE__, __func__); - /* * Use read id method to get device ID and other params. * For some NAND chips, controller can't report the correct @@ -537,9 +528,6 @@ static uint16_t denali_nand_timing_set(struct denali_nand_info *denali) static void denali_set_intr_modes(struct denali_nand_info *denali, uint16_t INT_ENABLE) { - dev_dbg(denali->dev, "%s, Line %d, Function: %s\n", - __FILE__, __LINE__, __func__); - if (INT_ENABLE) iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE); else From 3371d663bb4579f1b2003a92162edd6d90edd089 Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 15 Nov 2016 10:56:20 +0100 Subject: [PATCH 48/55] mtd: nand: Support controllers with custom page If your controller already sends the required NAND commands when reading or writing a page, then the framework is not supposed to send READ0 and SEQIN/PAGEPROG respectively. Signed-off-by: Marc Gonzalez Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 34 +++++++++++++++++++++++++++++++--- include/linux/mtd/nand.h | 12 ++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 4d099d4f442c..96d242e2fe34 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1961,7 +1961,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, __func__, buf); read_retry: - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); + if (nand_standard_page_accessors(&chip->ecc)) + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); /* * Now read the page into the buffer. Absent an error, @@ -2649,7 +2650,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, else subpage = 0; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + if (nand_standard_page_accessors(&chip->ecc)) + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) status = chip->ecc.write_page_raw(mtd, chip, buf, @@ -2672,7 +2674,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (!cached || !NAND_HAS_CACHEPROG(chip)) { - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + if (nand_standard_page_accessors(&chip->ecc)) + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are @@ -4511,6 +4514,26 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd) return corr >= ds_corr && ecc->strength >= chip->ecc_strength_ds; } +static bool invalid_ecc_page_accessors(struct nand_chip *chip) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (nand_standard_page_accessors(ecc)) + return false; + + /* + * NAND_ECC_CUSTOM_PAGE_ACCESS flag is set, make sure the NAND + * controller driver implements all the page accessors because + * default helpers are not suitable when the core does not + * send the READ0/PAGEPROG commands. + */ + return (!ecc->read_page || !ecc->write_page || + !ecc->read_page_raw || !ecc->write_page_raw || + (NAND_HAS_SUBPAGE_READ(chip) && !ecc->read_subpage) || + (NAND_HAS_SUBPAGE_WRITE(chip) && !ecc->write_subpage && + ecc->hwctl && ecc->calculate)); +} + /** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure @@ -4531,6 +4554,11 @@ int nand_scan_tail(struct mtd_info *mtd) !(chip->bbt_options & NAND_BBT_USE_FLASH))) return -EINVAL; + if (invalid_ecc_page_accessors(chip)) { + pr_err("Invalid ECC page accessors setup\n"); + return -EINVAL; + } + if (!(chip->options & NAND_OWN_BUFFERS)) { nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize + mtd->oobsize * 3, GFP_KERNEL); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 970ceb948835..ed6fd1993be1 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -142,6 +142,12 @@ enum nand_ecc_algo { */ #define NAND_ECC_GENERIC_ERASED_CHECK BIT(0) #define NAND_ECC_MAXIMIZE BIT(1) +/* + * If your controller already sends the required NAND commands when + * reading or writing a page, then the framework is not supposed to + * send READ0 and SEQIN/PAGEPROG respectively. + */ +#define NAND_ECC_CUSTOM_PAGE_ACCESS BIT(2) /* Bit mask for flags passed to do_nand_read_ecc */ #define NAND_GET_DEVICE 0x80 @@ -186,6 +192,7 @@ enum nand_ecc_algo { /* Macros to identify the above */ #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) #define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ)) +#define NAND_HAS_SUBPAGE_WRITE(chip) !((chip)->options & NAND_NO_SUBPAGE_WRITE) /* Non chip related options */ /* This option skips the bbt scan during initialization. */ @@ -568,6 +575,11 @@ struct nand_ecc_ctrl { int page); }; +static inline int nand_standard_page_accessors(struct nand_ecc_ctrl *ecc) +{ + return !(ecc->options & NAND_ECC_CUSTOM_PAGE_ACCESS); +} + /** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize. From ff9e9eae0f50ab3f7d1763677b62db4a2ed24a88 Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 15 Nov 2016 11:05:39 +0100 Subject: [PATCH 49/55] mtd: nand: tango: Enable custom page accessors Enable NAND_ECC_CUSTOM_PAGE_ACCESS in the tango NFC driver. Fixup the "raw" page accessors to send the proper NAND commands. Signed-off-by: Marc Gonzalez Signed-off-by: Boris Brezillon --- drivers/mtd/nand/tango_nand.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c index fd8cf414cb2b..bacc2a02b7fe 100644 --- a/drivers/mtd/nand/tango_nand.c +++ b/drivers/mtd/nand/tango_nand.c @@ -402,13 +402,17 @@ static int raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, u8 *buf, int oob_required, int page) { + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); return raw_read(chip, buf, chip->oob_poi); } static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const u8 *buf, int oob_required, int page) { - return raw_write(chip, buf, chip->oob_poi); + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page); + raw_write(chip, buf, chip->oob_poi); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + return 0; } static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip, @@ -553,6 +557,7 @@ static int chip_init(struct device *dev, struct device_node *np) ecc->write_page = tango_write_page; ecc->read_oob = tango_read_oob; ecc->write_oob = tango_write_oob; + ecc->options = NAND_ECC_CUSTOM_PAGE_ACCESS; err = nand_scan_tail(mtd); if (err) From 37871abdd5ea73bde645c8f3d2e9fc04b5ffaf6b Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 15 Nov 2016 11:08:19 +0100 Subject: [PATCH 50/55] mtd: nand: tango: Cleanup raw_write and raw_read Drop raw_write return value (no longer used). Drop raw_read return value (for symmetry). Signed-off-by: Marc Gonzalez Signed-off-by: Boris Brezillon --- drivers/mtd/nand/tango_nand.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c index bacc2a02b7fe..7ed35348993e 100644 --- a/drivers/mtd/nand/tango_nand.c +++ b/drivers/mtd/nand/tango_nand.c @@ -343,7 +343,7 @@ static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos) * buf = | PKT_0 | ... | PKT_N | * +-----------------+-----+-----------------+ */ -static int raw_read(struct nand_chip *chip, u8 *buf, u8 *oob) +static void raw_read(struct nand_chip *chip, u8 *buf, u8 *oob) { u8 *oob_orig = oob; const int page_size = chip->mtd.writesize; @@ -367,11 +367,9 @@ static int raw_read(struct nand_chip *chip, u8 *buf, u8 *oob) aux_read(chip, &oob_orig, BBM_SIZE, &pos); aux_read(chip, &buf, pkt_size - rem, &pos); aux_read(chip, &oob, ecc_size, &pos); - - return 0; } -static int raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) +static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) { const u8 *oob_orig = oob; const int page_size = chip->mtd.writesize; @@ -395,15 +393,14 @@ static int raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) aux_write(chip, &oob_orig, BBM_SIZE, &pos); aux_write(chip, &buf, pkt_size - rem, &pos); aux_write(chip, &oob, ecc_size, &pos); - - return 0; } static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, u8 *buf, int oob_required, int page) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - return raw_read(chip, buf, chip->oob_poi); + raw_read(chip, buf, chip->oob_poi); + return 0; } static int tango_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, @@ -419,7 +416,8 @@ static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - return raw_read(chip, NULL, chip->oob_poi); + raw_read(chip, NULL, chip->oob_poi); + return 0; } static int tango_write_oob(struct mtd_info *mtd, struct nand_chip *chip, From 442168273ecf57cf4494e492a59f29991089ff42 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Wed, 16 Nov 2016 08:02:55 +0000 Subject: [PATCH 51/55] mtd: nand: nandsim: fix error check debugfs_create_dir() and debugfs_create_file() returns NULL on error or a pointer on success. They do not return the error value with ERR_PTR. So we should not check the return with IS_ERR_OR_NULL, instead we should just check for NULL. Signed-off-by: Sudip Mukherjee Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nandsim.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index c76287a69663..c84742671a5f 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -525,24 +525,20 @@ static int nandsim_debugfs_create(struct nandsim *dev) { struct nandsim_debug_info *dbg = &dev->dbg; struct dentry *dent; - int err; if (!IS_ENABLED(CONFIG_DEBUG_FS)) return 0; dent = debugfs_create_dir("nandsim", NULL); - if (IS_ERR_OR_NULL(dent)) { - int err = dent ? -ENODEV : PTR_ERR(dent); - - NS_ERR("cannot create \"nandsim\" debugfs directory, err %d\n", - err); - return err; + if (!dent) { + NS_ERR("cannot create \"nandsim\" debugfs directory\n"); + return -ENODEV; } dbg->dfs_root = dent; dent = debugfs_create_file("wear_report", S_IRUSR, dbg->dfs_root, dev, &dfs_fops); - if (IS_ERR_OR_NULL(dent)) + if (!dent) goto out_remove; dbg->dfs_wear_report = dent; @@ -550,8 +546,7 @@ static int nandsim_debugfs_create(struct nandsim *dev) out_remove: debugfs_remove_recursive(dbg->dfs_root); - err = dent ? PTR_ERR(dent) : -ENODEV; - return err; + return -ENODEV; } /** From 8e8fd4d1e83ef7d64f260b4d8d928ab44cc1ce07 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 11:08:08 +0900 Subject: [PATCH 52/55] mtd: remove unneeded initializer in mtd_ooblayout_{get, set}_bytes() There is no need to initialize oobregion and section since they will be filled by mtd_ooblayout_find_region(). Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index d46e4adf6d2b..cf85f2b2ca2c 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1274,8 +1274,8 @@ static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf, int section, struct mtd_oob_region *oobregion)) { - struct mtd_oob_region oobregion = { }; - int section = 0, ret; + struct mtd_oob_region oobregion; + int section, ret; ret = mtd_ooblayout_find_region(mtd, start, §ion, &oobregion, iter); @@ -1317,8 +1317,8 @@ static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf, int section, struct mtd_oob_region *oobregion)) { - struct mtd_oob_region oobregion = { }; - int section = 0, ret; + struct mtd_oob_region oobregion; + int section, ret; ret = mtd_ooblayout_find_region(mtd, start, §ion, &oobregion, iter); From 7c295ef9642c107203a485843853b12d6f5cdb39 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 11:08:09 +0900 Subject: [PATCH 53/55] mtd: use min_t() to refactor mtd_ooblayout_{get, set}_bytes() I hope this will make the code a little more readable. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index cf85f2b2ca2c..ca6a89ab4663 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1283,7 +1283,7 @@ static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf, while (!ret) { int cnt; - cnt = oobregion.length > nbytes ? nbytes : oobregion.length; + cnt = min_t(int, nbytes, oobregion.length); memcpy(buf, oobbuf + oobregion.offset, cnt); buf += cnt; nbytes -= cnt; @@ -1326,7 +1326,7 @@ static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf, while (!ret) { int cnt; - cnt = oobregion.length > nbytes ? nbytes : oobregion.length; + cnt = min_t(int, nbytes, oobregion.length); memcpy(oobbuf + oobregion.offset, buf, cnt); buf += cnt; nbytes -= cnt; From 4d6aecfb7e26d2e8746e2eb1eab5e0fe378065ab Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 9 Nov 2016 11:08:10 +0900 Subject: [PATCH 54/55] mtd: remove unneeded initializer in mtd_ooblayout_count_bytes() There is no need to initialize oobregion since it will be filled by the iterator. This function is called with mtd_ooblayout_free or mtd_ooblayout_ecc for the iterator; both of them calls memset() to clear the oobregion. Signed-off-by: Masahiro Yamada Reviewed-by: Marek Vasut Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index ca6a89ab4663..ca661cee9b77 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1354,7 +1354,7 @@ static int mtd_ooblayout_count_bytes(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion)) { - struct mtd_oob_region oobregion = { }; + struct mtd_oob_region oobregion; int section = 0, ret, nbytes = 0; while (1) { From 8fcfba072420518fda4b2fe00a3f3c9c3e2774e2 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 21 Nov 2016 10:03:04 +0100 Subject: [PATCH 55/55] mtd: nand: tango: Use nand_to_mtd() instead of directly accessing chip->mtd The nand_to_mtd() helper is here to hide internal mtd_info <-> nand_chip association and ease future refactors. Make use of this helper instead of directly accessing chip->mtd. Signed-off-by: Boris Brezillon Acked-by: Marc Gonzalez --- drivers/mtd/nand/tango_nand.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/tango_nand.c b/drivers/mtd/nand/tango_nand.c index 7ed35348993e..ec87516b87f5 100644 --- a/drivers/mtd/nand/tango_nand.c +++ b/drivers/mtd/nand/tango_nand.c @@ -171,6 +171,7 @@ static void tango_select_chip(struct mtd_info *mtd, int idx) */ static int check_erased_page(struct nand_chip *chip, u8 *buf) { + struct mtd_info *mtd = nand_to_mtd(chip); u8 *meta = chip->oob_poi + BBM_SIZE; u8 *ecc = chip->oob_poi + BBM_SIZE + METADATA_SIZE; const int ecc_size = chip->ecc.bytes; @@ -183,7 +184,7 @@ static int check_erased_page(struct nand_chip *chip, u8 *buf) meta, meta_len, chip->ecc.strength); if (res < 0) - chip->mtd.ecc_stats.failed++; + mtd->ecc_stats.failed++; bitflips = max(res, bitflips); buf += pkt_size; @@ -300,26 +301,30 @@ static int tango_write_page(struct mtd_info *mtd, struct nand_chip *chip, static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos) { + struct mtd_info *mtd = nand_to_mtd(chip); + *pos += len; if (!*buf) { /* skip over "len" bytes */ - chip->cmdfunc(&chip->mtd, NAND_CMD_RNDOUT, *pos, -1); + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, *pos, -1); } else { - tango_read_buf(&chip->mtd, *buf, len); + tango_read_buf(mtd, *buf, len); *buf += len; } } static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos) { + struct mtd_info *mtd = nand_to_mtd(chip); + *pos += len; if (!*buf) { /* skip over "len" bytes */ - chip->cmdfunc(&chip->mtd, NAND_CMD_SEQIN, *pos, -1); + chip->cmdfunc(mtd, NAND_CMD_SEQIN, *pos, -1); } else { - tango_write_buf(&chip->mtd, *buf, len); + tango_write_buf(mtd, *buf, len); *buf += len; } } @@ -345,8 +350,9 @@ static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos) */ static void raw_read(struct nand_chip *chip, u8 *buf, u8 *oob) { + struct mtd_info *mtd = nand_to_mtd(chip); u8 *oob_orig = oob; - const int page_size = chip->mtd.writesize; + const int page_size = mtd->writesize; const int ecc_size = chip->ecc.bytes; const int pkt_size = chip->ecc.size; int pos = 0; /* position within physical page */ @@ -371,8 +377,9 @@ static void raw_read(struct nand_chip *chip, u8 *buf, u8 *oob) static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob) { + struct mtd_info *mtd = nand_to_mtd(chip); const u8 *oob_orig = oob; - const int page_size = chip->mtd.writesize; + const int page_size = mtd->writesize; const int ecc_size = chip->ecc.bytes; const int pkt_size = chip->ecc.size; int pos = 0; /* position within physical page */ @@ -522,7 +529,7 @@ static int chip_init(struct device *dev, struct device_node *np) chip = &tchip->nand_chip; ecc = &chip->ecc; - mtd = &chip->mtd; + mtd = nand_to_mtd(chip); chip->read_byte = tango_read_byte; chip->write_buf = tango_write_buf; @@ -584,7 +591,7 @@ static int tango_nand_remove(struct platform_device *pdev) for (cs = 0; cs < MAX_CS; ++cs) { if (nfc->chips[cs]) - nand_release(&nfc->chips[cs]->nand_chip.mtd); + nand_release(nand_to_mtd(&nfc->chips[cs]->nand_chip)); } return 0;