From f2e5a24480023db86ebd455c807aa30dc224ba9a Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 28 Mar 2012 11:13:05 -0700 Subject: mtd: plat_nand: Add default partition parser to driver Use cmdlinepart as the default partition parser and allow the arch setup code to still use their own partition parsers. Signed-off-by: H Hartley Sweeten Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 6404e6e81b1..585639947f5 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -23,6 +23,8 @@ struct plat_nand_data { void __iomem *io_base; }; +static const char *part_probe_types[] = { "cmdlinepart", NULL }; + /* * Probe for the NAND device. */ @@ -31,6 +33,7 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) struct platform_nand_data *pdata = pdev->dev.platform_data; struct plat_nand_data *data; struct resource *res; + const char **part_types; int err = 0; if (pdata->chip.nr_chips < 1) { @@ -98,8 +101,9 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) goto out; } - err = mtd_device_parse_register(&data->mtd, - pdata->chip.part_probe_types, NULL, + part_types = pdata->chip.part_probe_types ? : part_probe_types; + + err = mtd_device_parse_register(&data->mtd, part_types, NULL, pdata->chip.partitions, pdata->chip.nr_partitions); -- cgit v1.2.3 From 60f8291003844a42b6edd4d8f37b2995c9431b57 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 28 Mar 2012 11:13:09 -0700 Subject: arm: Use the plat_nand default partition parser Use the default partition parser, cmdlinepart, provided by the plat_nand driver. Signed-off-by: H Hartley Sweeten Acked-by: Tony Lindgren Acked-by: Alexander Clouter Acked-by: Marek Vasut Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/arm/mach-ep93xx/snappercl15.c | 3 --- arch/arm/mach-ep93xx/ts72xx.c | 3 --- arch/arm/mach-ixp4xx/ixdp425-setup.c | 3 --- arch/arm/mach-omap1/board-fsample.c | 3 --- arch/arm/mach-omap1/board-h2.c | 3 --- arch/arm/mach-omap1/board-h3.c | 3 --- arch/arm/mach-omap1/board-perseus2.c | 3 --- arch/arm/mach-orion5x/ts78xx-setup.c | 3 --- arch/arm/mach-pxa/balloon3.c | 3 --- arch/arm/mach-pxa/em-x270.c | 3 --- arch/arm/mach-pxa/palmtx.c | 3 --- 11 files changed, 33 deletions(-) diff --git a/arch/arm/mach-ep93xx/snappercl15.c b/arch/arm/mach-ep93xx/snappercl15.c index 0c00852ef16..5df9092f1fc 100644 --- a/arch/arm/mach-ep93xx/snappercl15.c +++ b/arch/arm/mach-ep93xx/snappercl15.c @@ -82,8 +82,6 @@ static int snappercl15_nand_dev_ready(struct mtd_info *mtd) return !!(__raw_readw(NAND_CTRL_ADDR(chip)) & SNAPPERCL15_NAND_RDY); } -static const char *snappercl15_nand_part_probes[] = {"cmdlinepart", NULL}; - static struct mtd_partition snappercl15_nand_parts[] = { { .name = "Kernel", @@ -100,7 +98,6 @@ static struct mtd_partition snappercl15_nand_parts[] = { static struct platform_nand_data snappercl15_nand_data = { .chip = { .nr_chips = 1, - .part_probe_types = snappercl15_nand_part_probes, .partitions = snappercl15_nand_parts, .nr_partitions = ARRAY_SIZE(snappercl15_nand_parts), .options = NAND_NO_AUTOINCR, diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c index 5ea790942e9..14d71217519 100644 --- a/arch/arm/mach-ep93xx/ts72xx.c +++ b/arch/arm/mach-ep93xx/ts72xx.c @@ -105,8 +105,6 @@ static int ts72xx_nand_device_ready(struct mtd_info *mtd) return !!(__raw_readb(addr) & 0x20); } -static const char *ts72xx_nand_part_probes[] = { "cmdlinepart", NULL }; - #define TS72XX_BOOTROM_PART_SIZE (SZ_16K) #define TS72XX_REDBOOT_PART_SIZE (SZ_2M + SZ_1M) @@ -134,7 +132,6 @@ static struct platform_nand_data ts72xx_nand_data = { .nr_chips = 1, .chip_offset = 0, .chip_delay = 15, - .part_probe_types = ts72xx_nand_part_probes, .partitions = ts72xx_nand_parts, .nr_partitions = ARRAY_SIZE(ts72xx_nand_parts), }, diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c index 3d742aee177..fccfc73e69f 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -60,8 +60,6 @@ static struct platform_device ixdp425_flash = { #if defined(CONFIG_MTD_NAND_PLATFORM) || \ defined(CONFIG_MTD_NAND_PLATFORM_MODULE) -const char *part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition ixdp425_partitions[] = { { .name = "ixp400 NAND FS 0", @@ -101,7 +99,6 @@ static struct platform_nand_data ixdp425_flash_nand_data = { .nr_chips = 1, .chip_delay = 30, .options = NAND_NO_AUTOINCR, - .part_probe_types = part_probes, .partitions = ixdp425_partitions, .nr_partitions = ARRAY_SIZE(ixdp425_partitions), }, diff --git a/arch/arm/mach-omap1/board-fsample.c b/arch/arm/mach-omap1/board-fsample.c index 80bd43c7f4e..62a1e1122c3 100644 --- a/arch/arm/mach-omap1/board-fsample.c +++ b/arch/arm/mach-omap1/board-fsample.c @@ -206,14 +206,11 @@ static int nand_dev_ready(struct mtd_info *mtd) return gpio_get_value(FSAMPLE_NAND_RB_GPIO_PIN); } -static const char *part_probes[] = { "cmdlinepart", NULL }; - static struct platform_nand_data nand_data = { .chip = { .nr_chips = 1, .chip_offset = 0, .options = NAND_SAMSUNG_LP_OPTIONS, - .part_probe_types = part_probes, }, .ctrl = { .cmd_ctrl = nand_cmd_ctl, diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index 553a2e53576..9811b27ea16 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -200,8 +200,6 @@ static int h2_nand_dev_ready(struct mtd_info *mtd) return gpio_get_value(H2_NAND_RB_GPIO_PIN); } -static const char *h2_part_probes[] = { "cmdlinepart", NULL }; - static struct platform_nand_data h2_nand_platdata = { .chip = { .nr_chips = 1, @@ -209,7 +207,6 @@ static struct platform_nand_data h2_nand_platdata = { .nr_partitions = ARRAY_SIZE(h2_nand_partitions), .partitions = h2_nand_partitions, .options = NAND_SAMSUNG_LP_OPTIONS, - .part_probe_types = h2_part_probes, }, .ctrl = { .cmd_ctrl = h2_nand_cmd_ctl, diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index 4c19f4c0685..94858dc8c84 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -202,8 +202,6 @@ static int nand_dev_ready(struct mtd_info *mtd) return gpio_get_value(H3_NAND_RB_GPIO_PIN); } -static const char *part_probes[] = { "cmdlinepart", NULL }; - static struct platform_nand_data nand_platdata = { .chip = { .nr_chips = 1, @@ -211,7 +209,6 @@ static struct platform_nand_data nand_platdata = { .nr_partitions = ARRAY_SIZE(nand_partitions), .partitions = nand_partitions, .options = NAND_SAMSUNG_LP_OPTIONS, - .part_probe_types = part_probes, }, .ctrl = { .cmd_ctrl = nand_cmd_ctl, diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index 76d4ee05a81..eb8b2c7d871 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -164,14 +164,11 @@ static int nand_dev_ready(struct mtd_info *mtd) return gpio_get_value(P2_NAND_RB_GPIO_PIN); } -static const char *part_probes[] = { "cmdlinepart", NULL }; - static struct platform_nand_data nand_data = { .chip = { .nr_chips = 1, .chip_offset = 0, .options = NAND_SAMSUNG_LP_OPTIONS, - .part_probe_types = part_probes, }, .ctrl = { .cmd_ctrl = nand_cmd_ctl, diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c index c96f37472ed..53e595ed0bb 100644 --- a/arch/arm/mach-orion5x/ts78xx-setup.c +++ b/arch/arm/mach-orion5x/ts78xx-setup.c @@ -245,8 +245,6 @@ static void ts78xx_ts_nand_read_buf(struct mtd_info *mtd, readsb(io_base, buf, len); } -const char *ts_nand_part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition ts78xx_ts_nand_parts[] = { { .name = "mbr", @@ -271,7 +269,6 @@ static struct mtd_partition ts78xx_ts_nand_parts[] = { static struct platform_nand_data ts78xx_ts_nand_data = { .chip = { .nr_chips = 1, - .part_probe_types = ts_nand_part_probes, .partitions = ts78xx_ts_nand_parts, .nr_partitions = ARRAY_SIZE(ts78xx_ts_nand_parts), .chip_delay = 15, diff --git a/arch/arm/mach-pxa/balloon3.c b/arch/arm/mach-pxa/balloon3.c index c35456f02ac..7ece2625914 100644 --- a/arch/arm/mach-pxa/balloon3.c +++ b/arch/arm/mach-pxa/balloon3.c @@ -679,8 +679,6 @@ static struct mtd_partition balloon3_partition_info[] = { }, }; -static const char *balloon3_part_probes[] = { "cmdlinepart", NULL }; - struct platform_nand_data balloon3_nand_pdata = { .chip = { .nr_chips = 4, @@ -688,7 +686,6 @@ struct platform_nand_data balloon3_nand_pdata = { .nr_partitions = ARRAY_SIZE(balloon3_partition_info), .partitions = balloon3_partition_info, .chip_delay = 50, - .part_probe_types = balloon3_part_probes, }, .ctrl = { .hwcontrol = 0, diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c index 16ec557b8e4..2771ad43e25 100644 --- a/arch/arm/mach-pxa/em-x270.c +++ b/arch/arm/mach-pxa/em-x270.c @@ -338,8 +338,6 @@ static struct mtd_partition em_x270_partition_info[] = { }, }; -static const char *em_x270_part_probes[] = { "cmdlinepart", NULL }; - struct platform_nand_data em_x270_nand_platdata = { .chip = { .nr_chips = 1, @@ -347,7 +345,6 @@ struct platform_nand_data em_x270_nand_platdata = { .nr_partitions = ARRAY_SIZE(em_x270_partition_info), .partitions = em_x270_partition_info, .chip_delay = 20, - .part_probe_types = em_x270_part_probes, }, .ctrl = { .hwcontrol = 0, diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c index 9507605ed54..0da35dccfd8 100644 --- a/arch/arm/mach-pxa/palmtx.c +++ b/arch/arm/mach-pxa/palmtx.c @@ -268,8 +268,6 @@ static struct mtd_partition palmtx_partition_info[] = { }, }; -static const char *palmtx_part_probes[] = { "cmdlinepart", NULL }; - struct platform_nand_data palmtx_nand_platdata = { .chip = { .nr_chips = 1, @@ -277,7 +275,6 @@ struct platform_nand_data palmtx_nand_platdata = { .nr_partitions = ARRAY_SIZE(palmtx_partition_info), .partitions = palmtx_partition_info, .chip_delay = 20, - .part_probe_types = palmtx_part_probes, }, .ctrl = { .cmd_ctrl = palmtx_nand_cmd_ctl, -- cgit v1.2.3 From 3203907e6308a34f208538e5ba4a8f48c0413946 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 28 Mar 2012 11:13:19 -0700 Subject: blackfin: Use the plat_nand default partition parser Use the default partition parser, cmdlinepart, provided by the plat_nand driver. Signed-off-by: H Hartley Sweeten Acked-by: Mike Frysinger Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/blackfin/mach-bf561/boards/acvilon.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/blackfin/mach-bf561/boards/acvilon.c b/arch/blackfin/mach-bf561/boards/acvilon.c index f6ffd6f054c..0b74218fdd3 100644 --- a/arch/blackfin/mach-bf561/boards/acvilon.c +++ b/arch/blackfin/mach-bf561/boards/acvilon.c @@ -248,8 +248,6 @@ static struct platform_device bfin_uart0_device = { #if defined(CONFIG_MTD_NAND_PLATFORM) || defined(CONFIG_MTD_NAND_PLATFORM_MODULE) -const char *part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition bfin_plat_nand_partitions[] = { { .name = "params(nand)", @@ -289,7 +287,6 @@ static struct platform_nand_data bfin_plat_nand_data = { .chip = { .nr_chips = 1, .chip_delay = 30, - .part_probe_types = part_probes, .partitions = bfin_plat_nand_partitions, .nr_partitions = ARRAY_SIZE(bfin_plat_nand_partitions), }, -- cgit v1.2.3 From 8b62b0877b4e08017341e591fa4c218a791e7a04 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 28 Mar 2012 11:14:00 -0700 Subject: mips: Use the plat_nand default partition parser Use the default partition parser, cmdlinepart, provided by the plat_nand driver. Signed-off-by: H Hartley Sweeten Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/mips/alchemy/devboards/db1200.c | 3 --- arch/mips/alchemy/devboards/db1300.c | 3 --- arch/mips/alchemy/devboards/db1550.c | 3 --- arch/mips/pnx833x/common/platform.c | 6 ------ 4 files changed, 15 deletions(-) diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c index a83302b96c0..48bc4226e0c 100644 --- a/arch/mips/alchemy/devboards/db1200.c +++ b/arch/mips/alchemy/devboards/db1200.c @@ -212,8 +212,6 @@ static int au1200_nand_device_ready(struct mtd_info *mtd) return __raw_readl((void __iomem *)MEM_STSTAT) & 1; } -static const char *db1200_part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition db1200_nand_parts[] = { { .name = "NAND FS 0", @@ -234,7 +232,6 @@ struct platform_nand_data db1200_nand_platdata = { .nr_partitions = ARRAY_SIZE(db1200_nand_parts), .partitions = db1200_nand_parts, .chip_delay = 20, - .part_probe_types = db1200_part_probes, }, .ctrl = { .dev_ready = au1200_nand_device_ready, diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c index 0893f2af0d0..c56e0246694 100644 --- a/arch/mips/alchemy/devboards/db1300.c +++ b/arch/mips/alchemy/devboards/db1300.c @@ -145,8 +145,6 @@ static int au1300_nand_device_ready(struct mtd_info *mtd) return __raw_readl((void __iomem *)MEM_STSTAT) & 1; } -static const char *db1300_part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition db1300_nand_parts[] = { { .name = "NAND FS 0", @@ -167,7 +165,6 @@ struct platform_nand_data db1300_nand_platdata = { .nr_partitions = ARRAY_SIZE(db1300_nand_parts), .partitions = db1300_nand_parts, .chip_delay = 20, - .part_probe_types = db1300_part_probes, }, .ctrl = { .dev_ready = au1300_nand_device_ready, diff --git a/arch/mips/alchemy/devboards/db1550.c b/arch/mips/alchemy/devboards/db1550.c index 6815d0783cd..9eb79062f46 100644 --- a/arch/mips/alchemy/devboards/db1550.c +++ b/arch/mips/alchemy/devboards/db1550.c @@ -149,8 +149,6 @@ static int au1550_nand_device_ready(struct mtd_info *mtd) return __raw_readl((void __iomem *)MEM_STSTAT) & 1; } -static const char *db1550_part_probes[] = { "cmdlinepart", NULL }; - static struct mtd_partition db1550_nand_parts[] = { { .name = "NAND FS 0", @@ -171,7 +169,6 @@ struct platform_nand_data db1550_nand_platdata = { .nr_partitions = ARRAY_SIZE(db1550_nand_parts), .partitions = db1550_nand_parts, .chip_delay = 20, - .part_probe_types = db1550_part_probes, }, .ctrl = { .dev_ready = au1550_nand_device_ready, diff --git a/arch/mips/pnx833x/common/platform.c b/arch/mips/pnx833x/common/platform.c index 87167dcc79f..05a1d922cd6 100644 --- a/arch/mips/pnx833x/common/platform.c +++ b/arch/mips/pnx833x/common/platform.c @@ -244,11 +244,6 @@ static struct platform_device pnx833x_sata_device = { .resource = pnx833x_sata_resources, }; -static const char *part_probes[] = { - "cmdlinepart", - NULL -}; - static void pnx833x_flash_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { @@ -268,7 +263,6 @@ static struct platform_nand_data pnx833x_flash_nand_data = { .chip = { .nr_chips = 1, .chip_delay = 25, - .part_probe_types = part_probes, }, .ctrl = { .cmd_ctrl = pnx833x_flash_nand_cmd_ctrl -- cgit v1.2.3 From ce4bbeeddb1dfc710554cc69fc21599311975077 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 28 Mar 2012 11:14:04 -0700 Subject: sh: Use the plat_nand default partition parser Use the default partition parser, cmdlinepart, provided by the plat_nand driver. Signed-off-by: H Hartley Sweeten Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/sh/boards/mach-migor/setup.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index ff6f69c6906..de3e0f670f3 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -187,7 +187,6 @@ static struct platform_nand_data migor_nand_flash_data = { .partitions = migor_nand_flash_partitions, .nr_partitions = ARRAY_SIZE(migor_nand_flash_partitions), .chip_delay = 20, - .part_probe_types = (const char *[]) { "cmdlinepart", NULL }, }, .ctrl = { .dev_ready = migor_nand_flash_ready, -- cgit v1.2.3 From 6d56b9d65813dd6a184efc5e7955d7e181a853c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 29 Mar 2012 23:12:32 +0200 Subject: mtd: mark const init data with __initconst instead of __initdata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As long as there is no other non-const variable marked __initdata in the same compilation unit it doesn't hurt. If there were one however compilation would fail with error: $variablename causes a section type conflict because a section containing const variables is marked read only and so cannot contain non-const variables. Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/wr_sbc82xx_flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c index 71b0ba79791..e7534c82f93 100644 --- a/drivers/mtd/maps/wr_sbc82xx_flash.c +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c @@ -59,7 +59,7 @@ static struct mtd_partition bigflash_parts[] = { } }; -static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; +static const char *part_probes[] __initconst = {"cmdlinepart", "RedBoot", NULL}; #define init_sbc82xx_one_flash(map, br, or) \ do { \ -- cgit v1.2.3 From 4aa6ae3ecca04d7956817170418c74861ce071de Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Sat, 31 Mar 2012 22:36:57 -0400 Subject: mtd: gpmi: do not include the mxs.h The mxs.h does not exit in the mx6q. So rewrite the __mxs_clrl()/__mxs_setl() and remove the mxs.h. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index e8ea7107932..fa5200b8c89 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -21,7 +21,6 @@ #include #include #include -#include #include "gpmi-nand.h" #include "gpmi-regs.h" @@ -37,6 +36,8 @@ struct timing_threshod timing_default_threshold = { .max_dll_delay_in_ns = 16, }; +#define MXS_SET_ADDR 0x4 +#define MXS_CLR_ADDR 0x8 /* * Clear the bit and poll it cleared. This is usually called with * a reset address and mask being either SFTRST(bit 31) or CLKGATE @@ -47,7 +48,7 @@ static int clear_poll_bit(void __iomem *addr, u32 mask) int timeout = 0x400; /* clear the bit */ - __mxs_clrl(mask, addr); + writel(mask, addr + MXS_CLR_ADDR); /* * SFTRST needs 3 GPMI clocks to settle, the reference manual @@ -92,11 +93,11 @@ static int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) goto error; /* clear CLKGATE */ - __mxs_clrl(MODULE_CLKGATE, reset_addr); + writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); if (!just_enable) { /* set SFTRST to reset the block */ - __mxs_setl(MODULE_SFTRST, reset_addr); + writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); udelay(1); /* poll CLKGATE becoming set */ -- cgit v1.2.3 From 4d16cd658700a36af8788a09b4789d09da355a8c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Apr 2012 09:59:44 +0800 Subject: mtd: use module_pci_driver This patch converts the drivers in drivers/mtd/* to use module_pci_driver() macro which makes the code smaller and a bit simpler. Signed-off-by: Axel Lin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/intel_vr_nor.c | 13 +------------ drivers/mtd/maps/pci.c | 13 +------------ drivers/mtd/maps/scb2_flash.c | 15 +-------------- drivers/mtd/nand/cafe_nand.c | 12 +----------- drivers/mtd/nand/denali.c | 15 +-------------- drivers/mtd/nand/r852.c | 13 +------------ 6 files changed, 6 insertions(+), 75 deletions(-) diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c index 92e1f41634c..93f03175c82 100644 --- a/drivers/mtd/maps/intel_vr_nor.c +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -260,18 +260,7 @@ static struct pci_driver vr_nor_pci_driver = { .id_table = vr_nor_pci_ids, }; -static int __init vr_nor_mtd_init(void) -{ - return pci_register_driver(&vr_nor_pci_driver); -} - -static void __exit vr_nor_mtd_exit(void) -{ - pci_unregister_driver(&vr_nor_pci_driver); -} - -module_init(vr_nor_mtd_init); -module_exit(vr_nor_mtd_exit); +module_pci_driver(vr_nor_pci_driver); MODULE_AUTHOR("Andy Lowe"); MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range"); diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 1d005a3e9b4..f14ce0af763 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -352,18 +352,7 @@ static struct pci_driver mtd_pci_driver = { .id_table = mtd_pci_ids, }; -static int __init mtd_pci_maps_init(void) -{ - return pci_register_driver(&mtd_pci_driver); -} - -static void __exit mtd_pci_maps_exit(void) -{ - pci_unregister_driver(&mtd_pci_driver); -} - -module_init(mtd_pci_maps_init); -module_exit(mtd_pci_maps_exit); +module_pci_driver(mtd_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Russell King "); diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 934a72c8007..9dcbc684abd 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -234,20 +234,7 @@ static struct pci_driver scb2_flash_driver = { .remove = __devexit_p(scb2_flash_remove), }; -static int __init -scb2_flash_init(void) -{ - return pci_register_driver(&scb2_flash_driver); -} - -static void __exit -scb2_flash_exit(void) -{ - pci_unregister_driver(&scb2_flash_driver); -} - -module_init(scb2_flash_init); -module_exit(scb2_flash_exit); +module_pci_driver(scb2_flash_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Tim Hockin "); diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 2a96e1a1206..2973d97bd52 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -888,17 +888,7 @@ static struct pci_driver cafe_nand_pci_driver = { .resume = cafe_nand_resume, }; -static int __init cafe_nand_init(void) -{ - return pci_register_driver(&cafe_nand_pci_driver); -} - -static void __exit cafe_nand_exit(void) -{ - pci_unregister_driver(&cafe_nand_pci_driver); -} -module_init(cafe_nand_init); -module_exit(cafe_nand_exit); +module_pci_driver(cafe_nand_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse "); diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index a9e57d68629..a1048c7c5d2 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1702,17 +1702,4 @@ static struct pci_driver denali_pci_driver = { .remove = denali_pci_remove, }; -static int __devinit denali_init(void) -{ - printk(KERN_INFO "Spectra MTD driver\n"); - return pci_register_driver(&denali_pci_driver); -} - -/* Free memory */ -static void __devexit denali_exit(void) -{ - pci_unregister_driver(&denali_pci_driver); -} - -module_init(denali_init); -module_exit(denali_exit); +module_pci_driver(denali_pci_driver); diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index c2040187c81..859ef318e5d 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -1104,18 +1104,7 @@ static struct pci_driver r852_pci_driver = { .driver.pm = &r852_pm_ops, }; -static __init int r852_module_init(void) -{ - return pci_register_driver(&r852_pci_driver); -} - -static void __exit r852_module_exit(void) -{ - pci_unregister_driver(&r852_pci_driver); -} - -module_init(r852_module_init); -module_exit(r852_module_exit); +module_pci_driver(r852_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Maxim Levitsky "); -- cgit v1.2.3 From 287558fe0e06a85c32863ec9204ed39fc218c5e3 Mon Sep 17 00:00:00 2001 From: Jim Cromie Date: Tue, 10 Apr 2012 18:55:56 -0600 Subject: mtd: lpddr: replace open-coded ARRAY_SIZE with macro Signed-off-by: Jim Cromie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/lpddr/qinfo_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c index dbfe17baf04..45abed67f1e 100644 --- a/drivers/mtd/lpddr/qinfo_probe.c +++ b/drivers/mtd/lpddr/qinfo_probe.c @@ -57,7 +57,7 @@ static struct qinfo_query_info qinfo_array[] = { static long lpddr_get_qinforec_pos(struct map_info *map, char *id_str) { - int qinfo_lines = sizeof(qinfo_array)/sizeof(struct qinfo_query_info); + int qinfo_lines = ARRAY_SIZE(qinfo_array); int i; int bankwidth = map_bankwidth(map) * 8; int major, minor; -- cgit v1.2.3 From 59fdd5b96aa633ffd40e8ce314f126206dfff86f Mon Sep 17 00:00:00 2001 From: Prabhakar Kushwaha Date: Mon, 9 Apr 2012 10:55:22 +0530 Subject: mtd: IFC NAND: Add support of ONFI NAND flash - Fix NAND_CMD_READID command for ONFI detect. - Add NAND_CMD_PARAM command to read the ONFI parameter page. Signed-off-by: Prabhakar Kushwaha Acked-by: Scott Wood Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_ifc_nand.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index c30ac7b83d2..5387cec53c9 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -375,21 +375,31 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, return; - /* READID must read all 8 possible bytes */ case NAND_CMD_READID: + case NAND_CMD_PARAM: { + int timing = IFC_FIR_OP_RB; + if (command == NAND_CMD_PARAM) + timing = IFC_FIR_OP_RBCD; + out_be32(&ifc->ifc_nand.nand_fir0, (IFC_FIR_OP_CMD0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT)); + (timing << IFC_NAND_FIR0_OP2_SHIFT)); out_be32(&ifc->ifc_nand.nand_fcr0, - NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT); - /* 8 bytes for manuf, device and exts */ - out_be32(&ifc->ifc_nand.nand_fbcr, 8); - ifc_nand_ctrl->read_bytes = 8; + command << IFC_NAND_FCR0_CMD0_SHIFT); + out_be32(&ifc->ifc_nand.row3, column); + + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + out_be32(&ifc->ifc_nand.nand_fbcr, 256); + ifc_nand_ctrl->read_bytes = 256; set_addr(mtd, 0, 0, 0); fsl_ifc_run_command(mtd); return; + } /* ERASE1 stores the block and page address */ case NAND_CMD_ERASE1: -- cgit v1.2.3 From 886bd33da7cfb1f6c7f3396f7146278ed02e76a7 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 9 Apr 2012 11:41:37 +0800 Subject: mtd: print out the page size and oob size after parsing out the nand Some not-supported nand chips may pass the current parsing code, and get the wrong page size and oob size. Sometimes, it's hard to notice that you get the wrong values, because there is no warning or error. So it's useful to print out the page size and oob size in the end of the parsing function. We can check these values with the datasheet of the nand chip as soon as possible. Artem: amend the print a bit Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 47b19c0bb07..eb88d8b0426 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3154,10 +3154,11 @@ ident_done: if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - pr_info("NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, - nand_manuf_ids[maf_idx].name, - chip->onfi_version ? chip->onfi_params.model : type->name); + pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)," + " page size: %d, OOB size: %d\n", + *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, + chip->onfi_version ? chip->onfi_params.model : type->name, + mtd->writesize, mtd->oobsize); return type; } -- cgit v1.2.3 From 8da8ba2ea6ad52ea8558384f38586b0c1a516e9d Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 10 Apr 2012 22:22:35 +0100 Subject: JFFS2: Add parameter to reserve disk space for root Add a new rp_size= parameter which creates a "reserved pool" of disk space which can only be used by root. Other users are not permitted to write to disk when the available space is less than the pool size. Based on original code by Artem Bityutskiy in https://dev.laptop.org/ticket/5317 [dwmw2: use capable(CAP_SYS_RESOURCE) not uid/gid check, fix debug prints] Signed-off-by: Daniel Drake Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/jffs2_fs_sb.h | 7 +++++++ fs/jffs2/nodemgmt.c | 42 ++++++++++++++++++++++++++++++++++++++++++ fs/jffs2/super.c | 17 +++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 55a0c1dcead..0d00bf26923 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -32,6 +32,13 @@ struct jffs2_inodirty; struct jffs2_mount_opts { bool override_compr; unsigned int compr; + + /* The size of the reserved pool. The reserved pool is the JFFS2 flash + * space which may only be used by root cannot be used by the other + * users. This is implemented simply by means of not allowing the + * latter users to write to the file system if the amount if the + * available space is less then 'rp_size'. */ + unsigned int rp_size; }; /* A struct for the overall file system control. Pointers to diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 6784d1e7a7e..0c96eb52c79 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -18,6 +18,37 @@ #include "nodelist.h" #include "debug.h" +/* + * Check whether the user is allowed to write. + */ +static int jffs2_rp_can_write(struct jffs2_sb_info *c) +{ + uint32_t avail; + struct jffs2_mount_opts *opts = &c->mount_opts; + + avail = c->dirty_size + c->free_size + c->unchecked_size + + c->erasing_size - c->resv_blocks_write * c->sector_size + - c->nospc_dirty_size; + + if (avail < 2 * opts->rp_size) + jffs2_dbg(1, "rpsize %u, dirty_size %u, free_size %u, " + "erasing_size %u, unchecked_size %u, " + "nr_erasing_blocks %u, avail %u, resrv %u\n", + opts->rp_size, c->dirty_size, c->free_size, + c->erasing_size, c->unchecked_size, + c->nr_erasing_blocks, avail, c->nospc_dirty_size); + + if (avail > opts->rp_size) + return 1; + + /* Always allow root */ + if (capable(CAP_SYS_RESOURCE)) + return 1; + + jffs2_dbg(1, "forbid writing\n"); + return 0; +} + /** * jffs2_reserve_space - request physical space to write nodes to flash * @c: superblock info @@ -55,6 +86,15 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, spin_lock(&c->erase_completion_lock); + /* + * Check if the free space is greater then size of the reserved pool. + * If not, only allow root to proceed with writing. + */ + if (prio != ALLOC_DELETION && !jffs2_rp_can_write(c)) { + ret = -ENOSPC; + goto out; + } + /* this needs a little more thought (true :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { @@ -158,6 +198,8 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, jffs2_dbg(1, "%s(): ret is %d\n", __func__, ret); } } + +out: spin_unlock(&c->erase_completion_lock); if (!ret) ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index f9916f312bd..66d44560f75 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -105,6 +105,8 @@ static int jffs2_show_options(struct seq_file *s, struct dentry *root) if (opts->override_compr) seq_printf(s, ",compr=%s", jffs2_compr_name(opts->compr)); + if (opts->rp_size) + seq_printf(s, ",rp_size=%u", opts->rp_size / 1024); return 0; } @@ -171,15 +173,18 @@ static const struct export_operations jffs2_export_ops = { * JFFS2 mount options. * * Opt_override_compr: override default compressor + * Opt_rp_size: size of reserved pool in KiB * Opt_err: just end of array marker */ enum { Opt_override_compr, + Opt_rp_size, Opt_err, }; static const match_table_t tokens = { {Opt_override_compr, "compr=%s"}, + {Opt_rp_size, "rp_size=%u"}, {Opt_err, NULL}, }; @@ -187,6 +192,7 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data) { substring_t args[MAX_OPT_ARGS]; char *p, *name; + unsigned int opt; if (!data) return 0; @@ -224,6 +230,17 @@ static int jffs2_parse_options(struct jffs2_sb_info *c, char *data) kfree(name); c->mount_opts.override_compr = true; break; + case Opt_rp_size: + if (match_int(&args[0], &opt)) + return -EINVAL; + opt *= 1024; + if (opt > c->mtd->size) { + pr_warn("Too large reserve pool specified, max " + "is %llu KB\n", c->mtd->size / 1024); + return -EINVAL; + } + c->mount_opts.rp_size = opt; + break; default: pr_err("Error: unrecognized mount option '%s' or missing value\n", p); -- cgit v1.2.3 From d7efe2281db53544400ac83160a79cf8925598d9 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Wed, 11 Apr 2012 04:04:34 +0300 Subject: mtd: omap2: fix resource leak in prefetch-busy path If prefetch engine is busy, current code "forgets" to call dma_unmap_single(), which results in a deadlock later, so add it. Signed-off-by: Grazvydas Ignotas Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/omap2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index c2b0bba9d8b..ab56069fa31 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -402,7 +402,7 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write); if (ret) /* PFPW engine is busy, use cpu copy method */ - goto out_copy; + goto out_copy_unmap; init_completion(&info->comp); @@ -421,6 +421,8 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); return 0; +out_copy_unmap: + dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); out_copy: if (info->nand.options & NAND_BUSWIDTH_16) is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) -- cgit v1.2.3 From 47d0505bdba39f3bf247b5d57d94b4a5fa8ae475 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 16 Apr 2012 12:50:37 -0700 Subject: mtd: fix scb2_flash build when PCI is not enabled scb2_flash.c is a PCI driver, but its build fails when CONFIG_PCI is not enabled, so make it depend on PCI. drivers/mtd/maps/scb2_flash.c:237:1: warning: data definition has no type or storage class drivers/mtd/maps/scb2_flash.c:237:1: warning: type defaults to 'int' in declaration of 'module_pci_driver' drivers/mtd/maps/scb2_flash.c:237:1: warning: parameter names (without types) in function declaration Signed-off-by: Randy Dunlap Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 8af67cfd671..5ba2458e799 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -224,7 +224,7 @@ config MTD_CK804XROM config MTD_SCB2_FLASH tristate "BIOS flash chip on Intel SCB2 boards" - depends on X86 && MTD_JEDECPROBE + depends on X86 && MTD_JEDECPROBE && PCI help Support for treating the BIOS flash chip on Intel SCB2 boards as an MTD device - with this you can reprogram your BIOS. -- cgit v1.2.3 From c0010eb5c9f1587a8831e3e7444d0d472643bf78 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 17 Apr 2012 17:07:56 +0530 Subject: mtd: spear_smi: Add clk_{un}prepare() support clk_{un}prepare is mandatory for platforms using common clock framework. Since this driver is used by SPEAr platform, which supports common clock framework, add clk_{un}prepare() support for it. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/spear_smi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 797d43cd355..67960362681 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -990,9 +990,9 @@ static int __devinit spear_smi_probe(struct platform_device *pdev) goto err_clk; } - ret = clk_enable(dev->clk); + ret = clk_prepare_enable(dev->clk); if (ret) - goto err_clk_enable; + goto err_clk_prepare_enable; ret = request_irq(irq, spear_smi_int_handler, 0, pdev->name, dev); if (ret) { @@ -1020,8 +1020,8 @@ err_bank_setup: free_irq(irq, dev); platform_set_drvdata(pdev, NULL); err_irq: - clk_disable(dev->clk); -err_clk_enable: + clk_disable_unprepare(dev->clk); +err_clk_prepare_enable: clk_put(dev->clk); err_clk: iounmap(dev->io_base); @@ -1074,7 +1074,7 @@ static int __devexit spear_smi_remove(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); free_irq(irq, dev); - clk_disable(dev->clk); + clk_disable_unprepare(dev->clk); clk_put(dev->clk); iounmap(dev->io_base); kfree(dev); @@ -1091,7 +1091,7 @@ int spear_smi_suspend(struct platform_device *pdev, pm_message_t state) struct spear_smi *dev = platform_get_drvdata(pdev); if (dev && dev->clk) - clk_disable(dev->clk); + clk_disable_unprepare(dev->clk); return 0; } @@ -1102,7 +1102,7 @@ int spear_smi_resume(struct platform_device *pdev) int ret = -EPERM; if (dev && dev->clk) - ret = clk_enable(dev->clk); + ret = clk_prepare_enable(dev->clk); if (!ret) spear_smi_hw_init(dev); -- cgit v1.2.3 From e25da1c07dfb318628a8a86f0967914dc4fd135e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 17 Apr 2012 17:07:57 +0530 Subject: mtd: fsmc_nand: Add clk_{un}prepare() support clk_{un}prepare is mandatory for platforms using common clock framework. Since this driver is used by SPEAr platform, which supports common clock framework, add clk_{un}prepare() support for it. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsmc_nand.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 1b8330e1155..9d7f4171e07 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -994,9 +994,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) return PTR_ERR(host->clk); } - ret = clk_enable(host->clk); + ret = clk_prepare_enable(host->clk); if (ret) - goto err_clk_enable; + goto err_clk_prepare_enable; /* * This device ID is actually a common AMBA ID as used on the @@ -1176,8 +1176,8 @@ err_req_write_chnl: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->read_dma_chan); err_req_read_chnl: - clk_disable(host->clk); -err_clk_enable: + clk_disable_unprepare(host->clk); +err_clk_prepare_enable: clk_put(host->clk); return ret; } @@ -1198,7 +1198,7 @@ static int fsmc_nand_remove(struct platform_device *pdev) dma_release_channel(host->write_dma_chan); dma_release_channel(host->read_dma_chan); } - clk_disable(host->clk); + clk_disable_unprepare(host->clk); clk_put(host->clk); } @@ -1210,7 +1210,7 @@ static int fsmc_nand_suspend(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); if (host) - clk_disable(host->clk); + clk_disable_unprepare(host->clk); return 0; } @@ -1218,7 +1218,7 @@ static int fsmc_nand_resume(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); if (host) { - clk_enable(host->clk); + clk_prepare_enable(host->clk); fsmc_nand_setup(host->regs_va, host->bank, host->nand.options & NAND_BUSWIDTH_16, host->dev_timings); -- cgit v1.2.3 From a9c465f07c2dcd515d20b96f93470762f9ae08b6 Mon Sep 17 00:00:00 2001 From: Ivan Djelic Date: Tue, 17 Apr 2012 13:11:53 +0200 Subject: mtd: nand: omap: fix race condition in omap_wait() If a context switch occurs in function omap_wait() just before the while loop is entered, then upon return from context switch the timeout may already have elapsed: in that case, status is never read from NAND device, and omap_wait() returns an error. This failure has been experimentally observed during stress tests. This patch ensures a NAND status read is always performed before returning, as in the generic nand_wait() function. Signed-off-by: Ivan Djelic Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/omap2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index ab56069fa31..05d3562ec74 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -881,7 +881,7 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); unsigned long timeo = jiffies; - int status = NAND_STATUS_FAIL, state = this->state; + int status, state = this->state; if (state == FL_ERASING) timeo += (HZ * 400) / 1000; @@ -896,6 +896,8 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) break; cond_resched(); } + + status = gpmc_nand_read(info->gpmc_cs, GPMC_NAND_DATA); return status; } -- cgit v1.2.3 From 5329d67cc32c0306426dc4bc4b5e66a36cb87ce0 Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Thu, 19 Apr 2012 13:15:57 +0200 Subject: mtd: bcm63xxpart: handle Broadcom partition order The original Broadcom partition order has the root fs in front of the kernel, which resulted in miscalculated partition sizes. Detect when such an image is on the flash and also reorder the partitions accordingly. Signed-off-by: Jonas Gorski Acked-by: Florian Fainelli Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/bcm63xxpart.c | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c index 608321ee056..63d2a64331f 100644 --- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/bcm63xxpart.c @@ -4,7 +4,7 @@ * Copyright © 2006-2008 Florian Fainelli * Mike Albon * Copyright © 2009-2010 Daniel Dickinson - * Copyright © 2011 Jonas Gorski + * Copyright © 2011-2012 Jonas Gorski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -82,6 +82,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, int namelen = 0; int i; u32 computed_crc; + bool rootfs_first = false; if (bcm63xx_detect_cfe(master)) return -EINVAL; @@ -109,6 +110,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, char *boardid = &(buf->board_id[0]); char *tagversion = &(buf->tag_version[0]); + sscanf(buf->flash_image_start, "%u", &rootfsaddr); sscanf(buf->kernel_address, "%u", &kerneladdr); sscanf(buf->kernel_length, "%u", &kernellen); sscanf(buf->total_length, "%u", &totallen); @@ -117,10 +119,19 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, tagversion, boardid); kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE; - rootfsaddr = kerneladdr + kernellen; + rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE; spareaddr = roundup(totallen, master->erasesize) + cfelen; sparelen = master->size - spareaddr - nvramlen; - rootfslen = spareaddr - rootfsaddr; + + if (rootfsaddr < kerneladdr) { + /* default Broadcom layout */ + rootfslen = kerneladdr - rootfsaddr; + rootfs_first = true; + } else { + /* OpenWrt layout */ + rootfsaddr = kerneladdr + kernellen; + rootfslen = spareaddr - rootfsaddr; + } } else { pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n", buf->header_crc, computed_crc); @@ -156,18 +167,26 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, curpart++; if (kernellen > 0) { - parts[curpart].name = "kernel"; - parts[curpart].offset = kerneladdr; - parts[curpart].size = kernellen; + int kernelpart = curpart; + + if (rootfslen > 0 && rootfs_first) + kernelpart++; + parts[kernelpart].name = "kernel"; + parts[kernelpart].offset = kerneladdr; + parts[kernelpart].size = kernellen; curpart++; } if (rootfslen > 0) { - parts[curpart].name = "rootfs"; - parts[curpart].offset = rootfsaddr; - parts[curpart].size = rootfslen; - if (sparelen > 0) - parts[curpart].size += sparelen; + int rootfspart = curpart; + + if (kernellen > 0 && rootfs_first) + rootfspart--; + parts[rootfspart].name = "rootfs"; + parts[rootfspart].offset = rootfsaddr; + parts[rootfspart].size = rootfslen; + if (sparelen > 0 && !rootfs_first) + parts[rootfspart].size += sparelen; curpart++; } -- cgit v1.2.3 From 39a5d0b93c244f40ee2e1502e4870685b7dcc1fa Mon Sep 17 00:00:00 2001 From: Ryosuke Saito Date: Mon, 23 Apr 2012 19:47:17 +0900 Subject: mtd: block2mtd: page_read() never returns NULL page_read() never returns NULL, so we can remove the NULL check here. Signed-off-by: Ryosuke Saito Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/block2mtd.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index a4a80b742e6..300aeb837ee 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -52,8 +52,6 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) while (pages) { page = page_read(mapping, index); - if (!page) - return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); @@ -112,8 +110,6 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, len = len - cpylen; page = page_read(dev->blkdev->bd_inode->i_mapping, index); - if (!page) - return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); @@ -148,8 +144,6 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, len = len - cpylen; page = page_read(mapping, index); - if (!page) - return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); -- cgit v1.2.3 From 8d1fd16df9e14b29965ed64e120015e481d1fe0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:33 +0200 Subject: mtd: mxc_nand: set owner field to prevent module unloading when in use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index cc0678a967c..7d82cc44cb2 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1275,6 +1275,7 @@ static int __devexit mxcnd_remove(struct platform_device *pdev) static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, + .owner = THIS_MODULE, }, .remove = __devexit_p(mxcnd_remove), }; -- cgit v1.2.3 From 8556958af4ef58147c11f772e6171c149efee53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:34 +0200 Subject: mtd: mxc_nand: use a flag to detect if the mx21 quirk is necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gets rid of several instances of cpu_is_mx21() in the driver. Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 98 ++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 7d82cc44cb2..6f6068043fa 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -173,6 +173,13 @@ struct mxc_nand_host { uint16_t (*get_dev_status)(struct mxc_nand_host *); int (*check_int)(struct mxc_nand_host *); void (*irq_control)(struct mxc_nand_host *, int); + + /* + * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked + * (CONFIG1:INT_MSK is set). To handle this the driver uses + * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK + */ + int irqpending_quirk; }; /* OOB placement block for use with hardware ecc generation */ @@ -244,20 +251,6 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = { static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; -static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) -{ - struct mxc_nand_host *host = dev_id; - - if (!host->check_int(host)) - return IRQ_NONE; - - host->irq_control(host, 0); - - complete(&host->op_completion); - - return IRQ_HANDLED; -} - static int check_int_v3(struct mxc_nand_host *host) { uint32_t tmp; @@ -280,26 +273,12 @@ static int check_int_v1_v2(struct mxc_nand_host *host) if (!(tmp & NFC_V1_V2_CONFIG2_INT)) return 0; - if (!cpu_is_mx21()) + if (!host->irqpending_quirk) writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); return 1; } -/* - * It has been observed that the i.MX21 cannot read the CONFIG2:INT bit - * if interrupts are masked (CONFIG1:INT_MSK is set). To handle this, the - * driver can enable/disable the irq line rather than simply masking the - * interrupts. - */ -static void irq_control_mx21(struct mxc_nand_host *host, int activate) -{ - if (activate) - enable_irq(host->irq); - else - disable_irq_nosync(host->irq); -} - static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) { uint16_t tmp; @@ -328,6 +307,32 @@ static void irq_control_v3(struct mxc_nand_host *host, int activate) writel(tmp, NFC_V3_CONFIG2); } +static void irq_control(struct mxc_nand_host *host, int activate) +{ + if (host->irqpending_quirk) { + if (activate) + enable_irq(host->irq); + else + disable_irq_nosync(host->irq); + } else { + host->irq_control(host, activate); + } +} + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + struct mxc_nand_host *host = dev_id; + + if (!host->check_int(host)) + return IRQ_NONE; + + irq_control(host, 0); + + complete(&host->op_completion); + + return IRQ_HANDLED; +} + /* This function polls the NANDFC to wait for the basic operation to * complete by checking the INT bit of config2 register. */ @@ -338,7 +343,7 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq) if (useirq) { if (!host->check_int(host)) { INIT_COMPLETION(host->op_completion); - host->irq_control(host, 1); + irq_control(host, 1); wait_for_completion(&host->op_completion); } } else { @@ -374,7 +379,7 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(cmd, NFC_V1_V2_FLASH_CMD); writew(NFC_CMD, NFC_V1_V2_CONFIG2); - if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) { + if (host->irqpending_quirk && (cmd == NAND_CMD_RESET)) { int max_retries = 100; /* Reset completion is indicated by NFC_CONFIG2 */ /* being set to 0 */ @@ -812,7 +817,7 @@ static void preset_v1_v2(struct mtd_info *mtd) if (nfc_is_v21()) config1 |= NFC_V2_CONFIG1_FP_INT; - if (!cpu_is_mx21()) + if (!host->irqpending_quirk) config1 |= NFC_V1_V2_CONFIG1_INT_MSK; if (nfc_is_v21() && mtd->writesize) { @@ -1103,10 +1108,9 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->send_read_id = send_read_id_v1_v2; host->get_dev_status = get_dev_status_v1_v2; host->check_int = check_int_v1_v2; + host->irq_control = irq_control_v1_v2; if (cpu_is_mx21()) - host->irq_control = irq_control_mx21; - else - host->irq_control = irq_control_v1_v2; + host->irqpending_quirk = 1; } if (nfc_is_v21()) { @@ -1182,28 +1186,24 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); /* - * mask the interrupt. For i.MX21 explicitely call - * irq_control_v1_v2 to use the mask bit. We can't call - * disable_irq_nosync() for an interrupt we do not own yet. + * Use host->irq_control here instead of irq_control because we must not + * disable_irq_nosync without having requested the irq */ - if (cpu_is_mx21()) - irq_control_v1_v2(host, 0); - else - host->irq_control(host, 0); + host->irq_control(host, 0); err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); if (err) goto eirq; - host->irq_control(host, 0); - /* - * Now that the interrupt is disabled make sure the interrupt - * mask bit is cleared on i.MX21. Otherwise we can't read - * the interrupt status bit on this machine. + * Now that we "own" the interrupt make sure the interrupt mask bit is + * cleared on i.MX21. Otherwise we can't read the interrupt status bit + * on this machine. */ - if (cpu_is_mx21()) - irq_control_v1_v2(host, 1); + if (host->irqpending_quirk) { + disable_irq_nosync(host->irq); + host->irq_control(host, 1); + } /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, nfc_is_v21() ? 4 : 1, NULL)) { -- cgit v1.2.3 From e4303b25f4452dd6a0a49c318e5e93d0c5f8b1ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:35 +0200 Subject: mtd: mxc_nand: move function pointers to a per-SOC struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares switching to platform ids and of-tree probing. Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 170 +++++++++++++++++++++++++++----------------- 1 file changed, 104 insertions(+), 66 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 6f6068043fa..b6e4618bb04 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -140,6 +140,19 @@ #define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) +struct mxc_nand_host; + +struct mxc_nand_devtype_data { + void (*preset)(struct mtd_info *); + void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); + void (*send_addr)(struct mxc_nand_host *, uint16_t, int); + void (*send_page)(struct mtd_info *, unsigned int); + void (*send_read_id)(struct mxc_nand_host *); + uint16_t (*get_dev_status)(struct mxc_nand_host *); + int (*check_int)(struct mxc_nand_host *); + void (*irq_control)(struct mxc_nand_host *, int); +}; + struct mxc_nand_host { struct mtd_info mtd; struct nand_chip nand; @@ -165,14 +178,7 @@ struct mxc_nand_host { unsigned int buf_start; int spare_len; - void (*preset)(struct mtd_info *); - void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); - void (*send_addr)(struct mxc_nand_host *, uint16_t, int); - void (*send_page)(struct mtd_info *, unsigned int); - void (*send_read_id)(struct mxc_nand_host *); - uint16_t (*get_dev_status)(struct mxc_nand_host *); - int (*check_int)(struct mxc_nand_host *); - void (*irq_control)(struct mxc_nand_host *, int); + const struct mxc_nand_devtype_data *devtype_data; /* * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked @@ -315,7 +321,7 @@ static void irq_control(struct mxc_nand_host *host, int activate) else disable_irq_nosync(host->irq); } else { - host->irq_control(host, activate); + host->devtype_data->irq_control(host, activate); } } @@ -323,7 +329,7 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) { struct mxc_nand_host *host = dev_id; - if (!host->check_int(host)) + if (!host->devtype_data->check_int(host)) return IRQ_NONE; irq_control(host, 0); @@ -341,14 +347,14 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq) int max_retries = 8000; if (useirq) { - if (!host->check_int(host)) { + if (!host->devtype_data->check_int(host)) { INIT_COMPLETION(host->op_completion); irq_control(host, 1); wait_for_completion(&host->op_completion); } } else { while (max_retries-- > 0) { - if (host->check_int(host)) + if (host->devtype_data->check_int(host)) break; udelay(1); @@ -621,7 +627,7 @@ static u_char mxc_nand_read_byte(struct mtd_info *mtd) /* Check for status request */ if (host->status_request) - return host->get_dev_status(host) & 0xFF; + return host->devtype_data->get_dev_status(host) & 0xFF; ret = *(uint8_t *)(host->data_buf + host->buf_start); host->buf_start++; @@ -756,34 +762,44 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) * perform a read/write buf operation, the saved column * address is used to index into the full page. */ - host->send_addr(host, 0, page_addr == -1); + host->devtype_data->send_addr(host, 0, page_addr == -1); if (mtd->writesize > 512) /* another col addr cycle for 2k page */ - host->send_addr(host, 0, false); + host->devtype_data->send_addr(host, 0, false); } /* Write out page address, if necessary */ if (page_addr != -1) { /* paddr_0 - p_addr_7 */ - host->send_addr(host, (page_addr & 0xff), false); + host->devtype_data->send_addr(host, (page_addr & 0xff), false); if (mtd->writesize > 512) { if (mtd->size >= 0x10000000) { /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, false); - host->send_addr(host, (page_addr >> 16) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); } else /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); } else { /* One more address cycle for higher density devices */ if (mtd->size >= 0x4000000) { /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, false); - host->send_addr(host, (page_addr >> 16) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, + false); + host->devtype_data->send_addr(host, + (page_addr >> 16) & 0xff, + true); } else /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff, true); + host->devtype_data->send_addr(host, + (page_addr >> 8) & 0xff, true); } } } @@ -942,15 +958,15 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Command pre-processing step */ switch (command) { case NAND_CMD_RESET: - host->preset(mtd); - host->send_cmd(host, command, false); + host->devtype_data->preset(mtd); + host->devtype_data->send_cmd(host, command, false); break; case NAND_CMD_STATUS: host->buf_start = 0; host->status_request = true; - host->send_cmd(host, command, true); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -963,13 +979,14 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, command = NAND_CMD_READ0; /* only READ0 is valid */ - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); if (mtd->writesize > 512) - host->send_cmd(host, NAND_CMD_READSTART, true); + host->devtype_data->send_cmd(host, + NAND_CMD_READSTART, true); - host->send_page(mtd, NFC_OUTPUT); + host->devtype_data->send_page(mtd, NFC_OUTPUT); memcpy(host->data_buf, host->main_area0, mtd->writesize); copy_spare(mtd, true); @@ -982,28 +999,28 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, host->buf_start = column; - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_PAGEPROG: memcpy(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); - host->send_page(mtd, NFC_INPUT); - host->send_cmd(host, command, true); + host->devtype_data->send_page(mtd, NFC_INPUT); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); break; case NAND_CMD_READID: - host->send_cmd(host, command, true); + host->devtype_data->send_cmd(host, command, true); mxc_do_addr_cycle(mtd, column, page_addr); - host->send_read_id(host); + host->devtype_data->send_read_id(host); host->buf_start = column; break; case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: - host->send_cmd(host, command, false); + host->devtype_data->send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); break; @@ -1037,6 +1054,42 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern, }; +/* v1: i.MX21, i.MX27, i.MX31 */ +static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { + .preset = preset_v1_v2, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1_v2, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, +}; + +/* v21: i.MX25, i.MX35 */ +static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { + .preset = preset_v1_v2, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1_v2, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, +}; + +/* v3: i.MX51, i.MX53 */ +static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { + .preset = preset_v3, + .send_cmd = send_cmd_v3, + .send_addr = send_addr_v3, + .send_page = send_page_v3, + .send_read_id = send_read_id_v3, + .get_dev_status = get_dev_status_v3, + .check_int = check_int_v3, + .irq_control = irq_control_v3, +}; + static int __init mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; @@ -1100,27 +1153,10 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->main_area0 = host->base; - if (nfc_is_v1() || nfc_is_v21()) { - host->preset = preset_v1_v2; - host->send_cmd = send_cmd_v1_v2; - host->send_addr = send_addr_v1_v2; - host->send_page = send_page_v1_v2; - host->send_read_id = send_read_id_v1_v2; - host->get_dev_status = get_dev_status_v1_v2; - host->check_int = check_int_v1_v2; - host->irq_control = irq_control_v1_v2; + if (nfc_is_v1()) { + host->devtype_data = &imx21_nand_devtype_data; if (cpu_is_mx21()) host->irqpending_quirk = 1; - } - - if (nfc_is_v21()) { - host->regs = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; - this->ecc.bytes = 9; - } else if (nfc_is_v1()) { host->regs = host->base + 0xe00; host->spare0 = host->base + 0x800; host->spare_len = 16; @@ -1128,7 +1164,16 @@ static int __init mxcnd_probe(struct platform_device *pdev) oob_largepage = &nandv1_hw_eccoob_largepage; this->ecc.bytes = 3; host->eccsize = 1; + } else if (nfc_is_v21()) { + host->devtype_data = &imx25_nand_devtype_data; + host->regs = host->base + 0x1e00; + host->spare0 = host->base + 0x1000; + host->spare_len = 64; + oob_smallpage = &nandv2_hw_eccoob_smallpage; + oob_largepage = &nandv2_hw_eccoob_largepage; + this->ecc.bytes = 9; } else if (nfc_is_v3_2()) { + host->devtype_data = &imx51_nand_devtype_data; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { err = -ENODEV; @@ -1142,14 +1187,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->regs_axi = host->base + 0x1e00; host->spare0 = host->base + 0x1000; host->spare_len = 64; - host->preset = preset_v3; - host->send_cmd = send_cmd_v3; - host->send_addr = send_addr_v3; - host->send_page = send_page_v3; - host->send_read_id = send_read_id_v3; - host->check_int = check_int_v3; - host->get_dev_status = get_dev_status_v3; - host->irq_control = irq_control_v3; oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_largepage = &nandv2_hw_eccoob_largepage; } else @@ -1186,10 +1223,11 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); /* - * Use host->irq_control here instead of irq_control because we must not - * disable_irq_nosync without having requested the irq + * Use host->devtype_data->irq_control() here instead of irq_control() + * because we must not disable_irq_nosync without having requested the + * irq. */ - host->irq_control(host, 0); + host->devtype_data->irq_control(host, 0); err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); if (err) @@ -1202,7 +1240,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) */ if (host->irqpending_quirk) { disable_irq_nosync(host->irq); - host->irq_control(host, 1); + host->devtype_data->irq_control(host, 1); } /* first scan to find the device and get the page size */ @@ -1212,7 +1250,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) } /* Call preset again, with correct writesize this time */ - host->preset(mtd); + host->devtype_data->preset(mtd); if (mtd->writesize == 2048) this->ecc.layout = oob_largepage; -- cgit v1.2.3 From 6d38af255a106fe921762c5c5de32dac92647af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:36 +0200 Subject: mtd: mxc_nand: split some functions to get rid of more nfc_is_vX() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 109 ++++++++++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 29 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index b6e4618bb04..fbd94f00e0a 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -151,6 +151,7 @@ struct mxc_nand_devtype_data { uint16_t (*get_dev_status)(struct mxc_nand_host *); int (*check_int)(struct mxc_nand_host *); void (*irq_control)(struct mxc_nand_host *, int); + u32 (*get_ecc_status)(struct mxc_nand_host *); }; struct mxc_nand_host { @@ -325,6 +326,21 @@ static void irq_control(struct mxc_nand_host *host, int activate) } } +static u32 get_ecc_status_v1(struct mxc_nand_host *host) +{ + return readw(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v2(struct mxc_nand_host *host) +{ + return readl(NFC_V1_V2_ECC_STATUS_RESULT); +} + +static u32 get_ecc_status_v3(struct mxc_nand_host *host) +{ + return readl(NFC_V3_ECC_STATUS_RESULT); +} + static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) { struct mxc_nand_host *host = dev_id; @@ -444,13 +460,27 @@ static void send_page_v3(struct mtd_info *mtd, unsigned int ops) wait_op_done(host, false); } -static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops) +static void send_page_v2(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + /* NANDFC buffer 0 is used for page read/write */ + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + writew(ops, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); +} + +static void send_page_v1(struct mtd_info *mtd, unsigned int ops) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; int bufs, i; - if (nfc_is_v1() && mtd->writesize > 512) + if (mtd->writesize > 512) bufs = 4; else bufs = 1; @@ -566,7 +596,7 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, * additional correction. 2-Bit errors cannot be corrected by * HW ECC, so we need to return failure */ - uint16_t ecc_status = readw(NFC_V1_V2_ECC_STATUS_RESULT); + uint16_t ecc_status = get_ecc_status_v1(host); if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); @@ -591,10 +621,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, no_subpages = mtd->writesize >> 9; - if (nfc_is_v21()) - ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT); - else - ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT); + ecc_stat = host->devtype_data->get_ecc_status(host); do { err = ecc_stat & ecc_bit_mask; @@ -821,7 +848,7 @@ static int get_eccsize(struct mtd_info *mtd) return 8; } -static void preset_v1_v2(struct mtd_info *mtd) +static void preset_v1(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; @@ -830,13 +857,40 @@ static void preset_v1_v2(struct mtd_info *mtd) if (nand_chip->ecc.mode == NAND_ECC_HW) config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - if (nfc_is_v21()) - config1 |= NFC_V2_CONFIG1_FP_INT; + if (!host->irqpending_quirk) + config1 |= NFC_V1_V2_CONFIG1_INT_MSK; + + host->eccsize = 1; + + writew(config1, NFC_V1_V2_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, NFC_V1_V2_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); + writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + writew(0x4, NFC_V1_V2_WRPROT); +} + +static void preset_v2(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t config1 = 0; + + if (nand_chip->ecc.mode == NAND_ECC_HW) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + + config1 |= NFC_V2_CONFIG1_FP_INT; if (!host->irqpending_quirk) config1 |= NFC_V1_V2_CONFIG1_INT_MSK; - if (nfc_is_v21() && mtd->writesize) { + if (mtd->writesize) { uint16_t pages_per_block = mtd->erasesize / mtd->writesize; host->eccsize = get_eccsize(mtd); @@ -855,20 +909,14 @@ static void preset_v1_v2(struct mtd_info *mtd) writew(0x2, NFC_V1_V2_CONFIG); /* Blocks to be unlocked */ - if (nfc_is_v21()) { - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); - writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); - writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); - } else if (nfc_is_v1()) { - writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); - writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); - } else - BUG(); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); /* Unlock Block Command for given address range */ writew(0x4, NFC_V1_V2_WRPROT); @@ -1056,26 +1104,28 @@ static struct nand_bbt_descr bbt_mirror_descr = { /* v1: i.MX21, i.MX27, i.MX31 */ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { - .preset = preset_v1_v2, + .preset = preset_v1, .send_cmd = send_cmd_v1_v2, .send_addr = send_addr_v1_v2, - .send_page = send_page_v1_v2, + .send_page = send_page_v1, .send_read_id = send_read_id_v1_v2, .get_dev_status = get_dev_status_v1_v2, .check_int = check_int_v1_v2, .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, }; /* v21: i.MX25, i.MX35 */ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { - .preset = preset_v1_v2, + .preset = preset_v2, .send_cmd = send_cmd_v1_v2, .send_addr = send_addr_v1_v2, - .send_page = send_page_v1_v2, + .send_page = send_page_v2, .send_read_id = send_read_id_v1_v2, .get_dev_status = get_dev_status_v1_v2, .check_int = check_int_v1_v2, .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v2, }; /* v3: i.MX51, i.MX53 */ @@ -1088,6 +1138,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .get_dev_status = get_dev_status_v3, .check_int = check_int_v3, .irq_control = irq_control_v3, + .get_ecc_status = get_ecc_status_v3, }; static int __init mxcnd_probe(struct platform_device *pdev) -- cgit v1.2.3 From 6dcdf99dfab15bebf71ae66ed04cdce05b20779c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:37 +0200 Subject: mtd: mxc_nand: put ecc layout into devtype structs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit makes problems on v1 and v2 regarding 4KiBpages more obvious. As I don't have a 4KiB flash handy I just keep the status quo. Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index fbd94f00e0a..e02d27a937c 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -152,6 +152,7 @@ struct mxc_nand_devtype_data { int (*check_int)(struct mxc_nand_host *); void (*irq_control)(struct mxc_nand_host *, int); u32 (*get_ecc_status)(struct mxc_nand_host *); + struct nand_ecclayout *ecclayout_512, *ecclayout_2k, *ecclayout_4k; }; struct mxc_nand_host { @@ -1113,6 +1114,9 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .check_int = check_int_v1_v2, .irq_control = irq_control_v1_v2, .get_ecc_status = get_ecc_status_v1, + .ecclayout_512 = &nandv1_hw_eccoob_smallpage, + .ecclayout_2k = &nandv1_hw_eccoob_largepage, + .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ }; /* v21: i.MX25, i.MX35 */ @@ -1126,6 +1130,9 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .check_int = check_int_v1_v2, .irq_control = irq_control_v1_v2, .get_ecc_status = get_ecc_status_v2, + .ecclayout_512 = &nandv2_hw_eccoob_smallpage, + .ecclayout_2k = &nandv2_hw_eccoob_largepage, + .ecclayout_4k = &nandv2_hw_eccoob_4k, }; /* v3: i.MX51, i.MX53 */ @@ -1139,6 +1146,9 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .check_int = check_int_v3, .irq_control = irq_control_v3, .get_ecc_status = get_ecc_status_v3, + .ecclayout_512 = &nandv2_hw_eccoob_smallpage, + .ecclayout_2k = &nandv2_hw_eccoob_largepage, + .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */ }; static int __init mxcnd_probe(struct platform_device *pdev) @@ -1149,7 +1159,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) struct mxc_nand_host *host; struct resource *res; int err = 0; - struct nand_ecclayout *oob_smallpage, *oob_largepage; /* Allocate memory for MTD device structure and private data */ host = kzalloc(sizeof(struct mxc_nand_host) + NAND_MAX_PAGESIZE + @@ -1211,8 +1220,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->regs = host->base + 0xe00; host->spare0 = host->base + 0x800; host->spare_len = 16; - oob_smallpage = &nandv1_hw_eccoob_smallpage; - oob_largepage = &nandv1_hw_eccoob_largepage; this->ecc.bytes = 3; host->eccsize = 1; } else if (nfc_is_v21()) { @@ -1220,8 +1227,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->regs = host->base + 0x1e00; host->spare0 = host->base + 0x1000; host->spare_len = 64; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; this->ecc.bytes = 9; } else if (nfc_is_v3_2()) { host->devtype_data = &imx51_nand_devtype_data; @@ -1238,13 +1243,11 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->regs_axi = host->base + 0x1e00; host->spare0 = host->base + 0x1000; host->spare_len = 64; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; } else BUG(); this->ecc.size = 512; - this->ecc.layout = oob_smallpage; + this->ecc.layout = host->devtype_data->ecclayout_512; if (pdata->hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; @@ -1304,9 +1307,9 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->devtype_data->preset(mtd); if (mtd->writesize == 2048) - this->ecc.layout = oob_largepage; - if (nfc_is_v21() && mtd->writesize == 4096) - this->ecc.layout = &nandv2_hw_eccoob_4k; + this->ecc.layout = host->devtype_data->ecclayout_2k; + else if (mtd->writesize == 4096) + this->ecc.layout = host->devtype_data->ecclayout_4k; /* second phase scan */ if (nand_scan_tail(mtd)) { -- cgit v1.2.3 From 5e05a2d695f920209991b7a097c2e4ad4d1cdf9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:38 +0200 Subject: mtd: mxc_nand: split chip_select function and put it into devtype struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index e02d27a937c..03664d5742d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -153,6 +153,7 @@ struct mxc_nand_devtype_data { void (*irq_control)(struct mxc_nand_host *, int); u32 (*get_ecc_status)(struct mxc_nand_host *); struct nand_ecclayout *ecclayout_512, *ecclayout_2k, *ecclayout_4k; + void (*select_chip)(struct mtd_info *mtd, int chip); }; struct mxc_nand_host { @@ -721,7 +722,7 @@ static int mxc_nand_verify_buf(struct mtd_info *mtd, /* This function is used by upper layer for select and * deselect of the NAND chip */ -static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +static void mxc_nand_select_chip_v1_v3(struct mtd_info *mtd, int chip) { struct nand_chip *nand_chip = mtd->priv; struct mxc_nand_host *host = nand_chip->priv; @@ -740,11 +741,30 @@ static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) clk_enable(host->clk); host->clk_act = 1; } +} + +static void mxc_nand_select_chip_v2(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + if (chip == -1) { + /* Disable the NFC clock */ + if (host->clk_act) { + clk_disable(host->clk); + host->clk_act = 0; + } + return; + } - if (nfc_is_v21()) { - host->active_cs = chip; - writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + if (!host->clk_act) { + /* Enable the NFC clock */ + clk_enable(host->clk); + host->clk_act = 1; } + + host->active_cs = chip; + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); } /* @@ -1117,6 +1137,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .ecclayout_512 = &nandv1_hw_eccoob_smallpage, .ecclayout_2k = &nandv1_hw_eccoob_largepage, .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, }; /* v21: i.MX25, i.MX35 */ @@ -1133,6 +1154,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .ecclayout_512 = &nandv2_hw_eccoob_smallpage, .ecclayout_2k = &nandv2_hw_eccoob_largepage, .ecclayout_4k = &nandv2_hw_eccoob_4k, + .select_chip = mxc_nand_select_chip_v2, }; /* v3: i.MX51, i.MX53 */ @@ -1149,6 +1171,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .ecclayout_512 = &nandv2_hw_eccoob_smallpage, .ecclayout_2k = &nandv2_hw_eccoob_largepage, .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, }; static int __init mxcnd_probe(struct platform_device *pdev) @@ -1183,7 +1206,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->priv = host; this->dev_ready = mxc_nand_dev_ready; this->cmdfunc = mxc_nand_command; - this->select_chip = mxc_nand_select_chip; this->read_byte = mxc_nand_read_byte; this->read_word = mxc_nand_read_word; this->write_buf = mxc_nand_write_buf; @@ -1246,6 +1268,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) } else BUG(); + this->select_chip = host->devtype_data->select_chip; this->ecc.size = 512; this->ecc.layout = host->devtype_data->ecclayout_512; -- cgit v1.2.3 From 69d023be00e9bb5368ced679cead9fb25474ec69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:39 +0200 Subject: mtd: mxc_nand: put callback for data correction into devtype struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gets rid of one more nfc_is_vX(). Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 03664d5742d..4a6d763ab7f 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -154,6 +154,8 @@ struct mxc_nand_devtype_data { u32 (*get_ecc_status)(struct mxc_nand_host *); struct nand_ecclayout *ecclayout_512, *ecclayout_2k, *ecclayout_4k; void (*select_chip)(struct mtd_info *mtd, int chip); + int (*correct_data)(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc); }; struct mxc_nand_host { @@ -1138,6 +1140,7 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .ecclayout_2k = &nandv1_hw_eccoob_largepage, .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v1, }; /* v21: i.MX25, i.MX35 */ @@ -1155,6 +1158,7 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .ecclayout_2k = &nandv2_hw_eccoob_largepage, .ecclayout_4k = &nandv2_hw_eccoob_4k, .select_chip = mxc_nand_select_chip_v2, + .correct_data = mxc_nand_correct_data_v2_v3, }; /* v3: i.MX51, i.MX53 */ @@ -1172,6 +1176,7 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .ecclayout_2k = &nandv2_hw_eccoob_largepage, .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */ .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v2_v3, }; static int __init mxcnd_probe(struct platform_device *pdev) @@ -1275,10 +1280,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) if (pdata->hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; - if (nfc_is_v1()) - this->ecc.correct = mxc_nand_correct_data_v1; - else - this->ecc.correct = mxc_nand_correct_data_v2_v3; + this->ecc.correct = host->devtype_data->correct_data; this->ecc.mode = NAND_ECC_HW; } else { this->ecc.mode = NAND_ECC_SOFT; -- cgit v1.2.3 From f48d0f9aa93c8f1d38f21d2531d572cf96205d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:40 +0200 Subject: mtd: mxc_nand: put several more fields into devtype_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 130 ++++++++++++++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 36 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 4a6d763ab7f..35c928ac190 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -156,6 +156,22 @@ struct mxc_nand_devtype_data { void (*select_chip)(struct mtd_info *mtd, int chip); int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); + + /* + * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked + * (CONFIG1:INT_MSK is set). To handle this the driver uses + * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK + */ + int irqpending_quirk; + int needs_ip; + + size_t regs_offset; + size_t spare0_offset; + size_t axi_offset; + + int spare_len; + int eccbytes; + int eccsize; }; struct mxc_nand_host { @@ -181,16 +197,8 @@ struct mxc_nand_host { uint8_t *data_buf; unsigned int buf_start; - int spare_len; const struct mxc_nand_devtype_data *devtype_data; - - /* - * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked - * (CONFIG1:INT_MSK is set). To handle this the driver uses - * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK - */ - int irqpending_quirk; }; /* OOB placement block for use with hardware ecc generation */ @@ -284,7 +292,7 @@ static int check_int_v1_v2(struct mxc_nand_host *host) if (!(tmp & NFC_V1_V2_CONFIG2_INT)) return 0; - if (!host->irqpending_quirk) + if (!host->devtype_data->irqpending_quirk) writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); return 1; @@ -320,7 +328,7 @@ static void irq_control_v3(struct mxc_nand_host *host, int activate) static void irq_control(struct mxc_nand_host *host, int activate) { - if (host->irqpending_quirk) { + if (host->devtype_data->irqpending_quirk) { if (activate) enable_irq(host->irq); else @@ -405,7 +413,7 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(cmd, NFC_V1_V2_FLASH_CMD); writew(NFC_CMD, NFC_V1_V2_CONFIG2); - if (host->irqpending_quirk && (cmd == NAND_CMD_RESET)) { + if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) { int max_retries = 100; /* Reset completion is indicated by NFC_CONFIG2 */ /* being set to 0 */ @@ -780,7 +788,7 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom) u16 n = mtd->writesize >> 9; u8 *d = host->data_buf + mtd->writesize; u8 *s = host->spare0; - u16 t = host->spare_len; + u16 t = host->devtype_data->spare_len; j = (mtd->oobsize / n >> 1) << 1; @@ -880,7 +888,7 @@ static void preset_v1(struct mtd_info *mtd) if (nand_chip->ecc.mode == NAND_ECC_HW) config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - if (!host->irqpending_quirk) + if (!host->devtype_data->irqpending_quirk) config1 |= NFC_V1_V2_CONFIG1_INT_MSK; host->eccsize = 1; @@ -910,7 +918,7 @@ static void preset_v2(struct mtd_info *mtd) config1 |= NFC_V2_CONFIG1_FP_INT; - if (!host->irqpending_quirk) + if (!host->devtype_data->irqpending_quirk) config1 |= NFC_V1_V2_CONFIG1_INT_MSK; if (mtd->writesize) { @@ -1125,7 +1133,7 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern, }; -/* v1: i.MX21, i.MX27, i.MX31 */ +/* v1 + irqpending_quirk: i.MX21 */ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .preset = preset_v1, .send_cmd = send_cmd_v1_v2, @@ -1141,6 +1149,39 @@ static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v1, + .irqpending_quirk = 1, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v1 + !irqpending_quirk: i.MX27, i.MX31 */ +static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { + .preset = preset_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ecclayout_512 = &nandv1_hw_eccoob_smallpage, + .ecclayout_2k = &nandv1_hw_eccoob_largepage, + .ecclayout_4k = &nandv1_hw_eccoob_smallpage, /* XXX: needs fix */ + .select_chip = mxc_nand_select_chip_v1_v3, + .correct_data = mxc_nand_correct_data_v1, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .axi_offset = 0, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, }; /* v21: i.MX25, i.MX35 */ @@ -1159,6 +1200,14 @@ static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { .ecclayout_4k = &nandv2_hw_eccoob_4k, .select_chip = mxc_nand_select_chip_v2, .correct_data = mxc_nand_correct_data_v2_v3, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0x1e00, + .spare0_offset = 0x1000, + .axi_offset = 0, + .spare_len = 64, + .eccbytes = 9, + .eccsize = 0, }; /* v3: i.MX51, i.MX53 */ @@ -1177,6 +1226,14 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .ecclayout_4k = &nandv2_hw_eccoob_smallpage, /* XXX: needs fix */ .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v2_v3, + .irqpending_quirk = 0, + .needs_ip = 1, + .regs_offset = 0, + .spare0_offset = 0x1000, + .axi_offset = 0x1e00, + .spare_len = 64, + .eccbytes = 0, + .eccsize = 0, }; static int __init mxcnd_probe(struct platform_device *pdev) @@ -1241,22 +1298,31 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->main_area0 = host->base; if (nfc_is_v1()) { - host->devtype_data = &imx21_nand_devtype_data; if (cpu_is_mx21()) - host->irqpending_quirk = 1; - host->regs = host->base + 0xe00; - host->spare0 = host->base + 0x800; - host->spare_len = 16; - this->ecc.bytes = 3; - host->eccsize = 1; + host->devtype_data = &imx21_nand_devtype_data; + else + host->devtype_data = &imx27_nand_devtype_data; } else if (nfc_is_v21()) { host->devtype_data = &imx25_nand_devtype_data; - host->regs = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - this->ecc.bytes = 9; } else if (nfc_is_v3_2()) { host->devtype_data = &imx51_nand_devtype_data; + } else + BUG(); + + if (host->devtype_data->regs_offset) + host->regs = host->base + host->devtype_data->regs_offset; + host->spare0 = host->base + host->devtype_data->spare0_offset; + if (host->devtype_data->axi_offset) + host->regs_axi = host->base + host->devtype_data->axi_offset; + + this->ecc.bytes = host->devtype_data->eccbytes; + host->eccsize = host->devtype_data->eccsize; + + this->select_chip = host->devtype_data->select_chip; + this->ecc.size = 512; + this->ecc.layout = host->devtype_data->ecclayout_512; + + if (host->devtype_data->needs_ip) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { err = -ENODEV; @@ -1267,15 +1333,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) err = -ENOMEM; goto eirq; } - host->regs_axi = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - } else - BUG(); - - this->select_chip = host->devtype_data->select_chip; - this->ecc.size = 512; - this->ecc.layout = host->devtype_data->ecclayout_512; + } if (pdata->hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; @@ -1317,7 +1375,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) * cleared on i.MX21. Otherwise we can't read the interrupt status bit * on this machine. */ - if (host->irqpending_quirk) { + if (host->devtype_data->irqpending_quirk) { disable_irq_nosync(host->irq); host->devtype_data->irq_control(host, 1); } -- cgit v1.2.3 From 6436356ba156b909c34a72e2905d9a9a874ca8e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:41 +0200 Subject: mtd: mxc_nand: implement device tree probing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is tested on i.MX27. Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- Documentation/devicetree/bindings/mtd/mxc-nand.txt | 19 ++++ drivers/mtd/nand/mxc_nand.c | 114 +++++++++++++++++---- 2 files changed, 114 insertions(+), 19 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/mxc-nand.txt diff --git a/Documentation/devicetree/bindings/mtd/mxc-nand.txt b/Documentation/devicetree/bindings/mtd/mxc-nand.txt new file mode 100644 index 00000000000..b5833d11c7b --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/mxc-nand.txt @@ -0,0 +1,19 @@ +* Freescale's mxc_nand + +Required properties: +- compatible: "fsl,imxXX-nand" +- reg: address range of the nfc block +- interrupts: irq to be used +- nand-bus-width: see nand.txt +- nand-ecc-mode: see nand.txt +- nand-on-flash-bbt: see nand.txt + +Example: + + nand@d8000000 { + compatible = "fsl,imx27-nand"; + reg = <0xd8000000 0x1000>; + interrupts = <29>; + nand-bus-width = <8>; + nand-ecc-mode = "hw"; + }; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 35c928ac190..1041bb13462 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -199,6 +201,7 @@ struct mxc_nand_host { unsigned int buf_start; const struct mxc_nand_devtype_data *devtype_data; + struct mxc_nand_platform_data pdata; }; /* OOB placement block for use with hardware ecc generation */ @@ -268,7 +271,7 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = { } }; -static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; +static const char *part_probes[] = { "RedBoot", "cmdlinepart", "ofpart", NULL }; static int check_int_v3(struct mxc_nand_host *host) { @@ -1236,11 +1239,85 @@ static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { .eccsize = 0, }; +#ifdef CONFIG_OF_MTD +static const struct of_device_id mxcnd_dt_ids[] = { + { + .compatible = "fsl,imx21-nand", + .data = &imx21_nand_devtype_data, + }, { + .compatible = "fsl,imx27-nand", + .data = &imx27_nand_devtype_data, + }, { + .compatible = "fsl,imx25-nand", + .data = &imx25_nand_devtype_data, + }, { + .compatible = "fsl,imx51-nand", + .data = &imx51_nand_devtype_data, + }, + { /* sentinel */ } +}; + +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + struct device_node *np = host->dev->of_node; + struct mxc_nand_platform_data *pdata = &host->pdata; + const struct of_device_id *of_id = + of_match_device(mxcnd_dt_ids, host->dev); + int buswidth; + + if (!np) + return 1; + + if (of_get_nand_ecc_mode(np) >= 0) + pdata->hw_ecc = 1; + + pdata->flash_bbt = of_get_nand_on_flash_bbt(np); + + buswidth = of_get_nand_bus_width(np); + if (buswidth < 0) + return buswidth; + + pdata->width = buswidth / 8; + + host->devtype_data = of_id->data; + + return 0; +} +#else +static int __init mxcnd_probe_dt(struct mxc_nand_host *host) +{ + return 1; +} +#endif + +static int __init mxcnd_probe_pdata(struct mxc_nand_host *host) +{ + struct mxc_nand_platform_data *pdata = host->dev->platform_data; + + if (!pdata) + return -ENODEV; + + host->pdata = *pdata; + + if (nfc_is_v1()) { + if (cpu_is_mx21()) + host->devtype_data = &imx21_nand_devtype_data; + else + host->devtype_data = &imx27_nand_devtype_data; + } else if (nfc_is_v21()) { + host->devtype_data = &imx25_nand_devtype_data; + } else if (nfc_is_v3_2()) { + host->devtype_data = &imx51_nand_devtype_data; + } else + BUG(); + + return 0; +} + static int __init mxcnd_probe(struct platform_device *pdev) { struct nand_chip *this; struct mtd_info *mtd; - struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct mxc_nand_host *host; struct resource *res; int err = 0; @@ -1297,17 +1374,11 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->main_area0 = host->base; - if (nfc_is_v1()) { - if (cpu_is_mx21()) - host->devtype_data = &imx21_nand_devtype_data; - else - host->devtype_data = &imx27_nand_devtype_data; - } else if (nfc_is_v21()) { - host->devtype_data = &imx25_nand_devtype_data; - } else if (nfc_is_v3_2()) { - host->devtype_data = &imx51_nand_devtype_data; - } else - BUG(); + err = mxcnd_probe_dt(host); + if (err > 0) + err = mxcnd_probe_pdata(host); + if (err < 0) + goto eirq; if (host->devtype_data->regs_offset) host->regs = host->base + host->devtype_data->regs_offset; @@ -1335,7 +1406,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) } } - if (pdata->hw_ecc) { + if (host->pdata.hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = host->devtype_data->correct_data; @@ -1344,11 +1415,11 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.mode = NAND_ECC_SOFT; } - /* NAND bus width determines access funtions used by upper layer */ - if (pdata->width == 2) + /* NAND bus width determines access functions used by upper layer */ + if (host->pdata.width == 2) this->options |= NAND_BUSWIDTH_16; - if (pdata->flash_bbt) { + if (host->pdata.flash_bbt) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; /* update flash based bbt */ @@ -1408,8 +1479,12 @@ static int __init mxcnd_probe(struct platform_device *pdev) } /* Register the partitions */ - mtd_device_parse_register(mtd, part_probes, NULL, pdata->parts, - pdata->nr_parts); + mtd_device_parse_register(mtd, part_probes, + &(struct mtd_part_parser_data){ + .of_node = pdev->dev.of_node, + }, + host->pdata.parts, + host->pdata.nr_parts); platform_set_drvdata(pdev, host); @@ -1451,6 +1526,7 @@ static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mxcnd_dt_ids), }, .remove = __devexit_p(mxcnd_remove), }; -- cgit v1.2.3 From 377873600aea55d105c8e9ce14be4ca7bfdf362a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 23 Apr 2012 11:23:42 +0200 Subject: ARM: imx: add mxc_nand to imx27 device tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/arm/boot/dts/imx27.dtsi | 9 +++++++++ arch/arm/mach-imx/imx27-dt.c | 1 + 2 files changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi index bc5e7d5ddd5..5a41b8a0c4e 100644 --- a/arch/arm/boot/dts/imx27.dtsi +++ b/arch/arm/boot/dts/imx27.dtsi @@ -213,5 +213,14 @@ status = "disabled"; }; }; + nand@d8000000 { + #address-cells = <1>; + #size-cells = <1>; + + compatible = "fsl,imx27-nand"; + reg = <0xd8000000 0x1000>; + interrupts = <29>; + status = "disabled"; + }; }; }; diff --git a/arch/arm/mach-imx/imx27-dt.c b/arch/arm/mach-imx/imx27-dt.c index ed38d03c61f..eee0cc8d92a 100644 --- a/arch/arm/mach-imx/imx27-dt.c +++ b/arch/arm/mach-imx/imx27-dt.c @@ -29,6 +29,7 @@ static const struct of_dev_auxdata imx27_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI2_BASE_ADDR, "imx27-cspi.1", NULL), OF_DEV_AUXDATA("fsl,imx27-cspi", MX27_CSPI3_BASE_ADDR, "imx27-cspi.2", NULL), OF_DEV_AUXDATA("fsl,imx27-wdt", MX27_WDOG_BASE_ADDR, "imx2-wdt.0", NULL), + OF_DEV_AUXDATA("fsl,imx27-nand", MX27_NFC_BASE_ADDR, "mxc_nand.0", NULL), { /* sentinel */ } }; -- cgit v1.2.3 From 4b6f05e14df163e319bc85ca6e35d2720a91207d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Apr 2012 10:05:22 +0200 Subject: mtd: mxc_nand: fix several sparse warnings about incorrect address space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 1041bb13462..ec5ba8bac73 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -181,8 +181,8 @@ struct mxc_nand_host { struct nand_chip nand; struct device *dev; - void *spare0; - void *main_area0; + void __iomem *spare0; + void __iomem *main_area0; void __iomem *base; void __iomem *regs; @@ -519,7 +519,7 @@ static void send_read_id_v3(struct mxc_nand_host *host) wait_op_done(host, true); - memcpy(host->data_buf, host->main_area0, 16); + memcpy_fromio(host->data_buf, host->main_area0, 16); } /* Request the NANDFC to perform a read of the NAND device ID. */ @@ -535,7 +535,7 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host) /* Wait for operation to complete */ wait_op_done(host, true); - memcpy(host->data_buf, host->main_area0, 16); + memcpy_fromio(host->data_buf, host->main_area0, 16); if (this->options & NAND_BUSWIDTH_16) { /* compress the ID info */ @@ -790,23 +790,23 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom) u16 i, j; u16 n = mtd->writesize >> 9; u8 *d = host->data_buf + mtd->writesize; - u8 *s = host->spare0; + u8 __iomem *s = host->spare0; u16 t = host->devtype_data->spare_len; j = (mtd->oobsize / n >> 1) << 1; if (bfrom) { for (i = 0; i < n - 1; i++) - memcpy(d + i * j, s + i * t, j); + memcpy_fromio(d + i * j, s + i * t, j); /* the last section */ - memcpy(d + i * j, s + i * t, mtd->oobsize - i * j); + memcpy_fromio(d + i * j, s + i * t, mtd->oobsize - i * j); } else { for (i = 0; i < n - 1; i++) - memcpy(&s[i * t], &d[i * j], j); + memcpy_toio(&s[i * t], &d[i * j], j); /* the last section */ - memcpy(&s[i * t], &d[i * j], mtd->oobsize - i * j); + memcpy_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j); } } @@ -1070,7 +1070,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, host->devtype_data->send_page(mtd, NFC_OUTPUT); - memcpy(host->data_buf, host->main_area0, mtd->writesize); + memcpy_fromio(host->data_buf, host->main_area0, mtd->writesize); copy_spare(mtd, true); break; @@ -1086,7 +1086,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_PAGEPROG: - memcpy(host->main_area0, host->data_buf, mtd->writesize); + memcpy_toio(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); host->devtype_data->send_page(mtd, NFC_INPUT); host->devtype_data->send_cmd(host, command, true); -- cgit v1.2.3 From 8abc0d4a1181b44e0a42cadab4a15f8c6aa42451 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Wed, 25 Apr 2012 15:14:17 +0200 Subject: mtd: cmdlinepart: fix commentary Signed-off-by: Peter Meerwald Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/cmdlinepart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index ddf9ec6d916..4558e0f4d07 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -70,7 +70,7 @@ struct cmdline_mtd_partition { /* mtdpart_setup() parses into here */ static struct cmdline_mtd_partition *partitions; -/* the command line passed to mtdpart_setupd() */ +/* the command line passed to mtdpart_setup() */ static char *cmdline; static int cmdline_parsed = 0; -- cgit v1.2.3 From 7c80c352331a27cf0584f1701ed3a003984985f0 Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Wed, 25 Apr 2012 14:45:22 -0400 Subject: jffs2: validate symlink size in jffs2_do_read_inode_internal() `csize' is read from disk and thus needs validation. Otherwise a bogus value 0xffffffff would turn the subsequent kmalloc(csize + 1, ...) into kmalloc(0, ...), leading to out-of-bounds write. This patch limits `csize' to JFFS2_MAX_NAME_LEN, which is also used in jffs2_symlink(). Artem: we actually validate csize by checking CRC, so this 0xFFs cannot come from empty flash region. But I guess an attacker could feed JFFS2 an image with random csize value, including 0xFFs. Signed-off-by: Xi Wang Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index dc0437e8476..9897f38af70 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1266,6 +1266,12 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, /* Symlink's inode data is the target path. Read it and * keep in RAM to facilitate quick follow symlink * operation. */ + uint32_t csize = je32_to_cpu(latest_node->csize); + if (csize > JFFS2_MAX_NAME_LEN) { + mutex_unlock(&f->sem); + jffs2_do_clear_inode(c, f); + return -ENAMETOOLONG; + } f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); if (!f->target) { JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); -- cgit v1.2.3 From b6778fd7812d877abdbc78b9b2d4304150b2ce05 Mon Sep 17 00:00:00 2001 From: Xi Wang Date: Wed, 25 Apr 2012 14:45:23 -0400 Subject: jffs2: refactor csize usage in jffs2_do_read_inode_internal() Replace the verbose `je32_to_cpu(latest_node->csize)' with a shorter `csize'. Signed-off-by: Xi Wang Cc: Artem Bityutskiy Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 9897f38af70..3833d74f230 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1272,19 +1272,19 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, jffs2_do_clear_inode(c, f); return -ENAMETOOLONG; } - f->target = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL); + f->target = kmalloc(csize + 1, GFP_KERNEL); if (!f->target) { - JFFS2_ERROR("can't allocate %d bytes of memory for the symlink target path cache\n", je32_to_cpu(latest_node->csize)); + JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize); mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); return -ENOMEM; } ret = jffs2_flash_read(c, ref_offset(rii.latest_ref) + sizeof(*latest_node), - je32_to_cpu(latest_node->csize), &retlen, (char *)f->target); + csize, &retlen, (char *)f->target); - if (ret || retlen != je32_to_cpu(latest_node->csize)) { - if (retlen != je32_to_cpu(latest_node->csize)) + if (ret || retlen != csize) { + if (retlen != csize) ret = -EIO; kfree(f->target); f->target = NULL; @@ -1293,7 +1293,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, return ret; } - f->target[je32_to_cpu(latest_node->csize)] = '\0'; + f->target[csize] = '\0'; dbg_readinode("symlink's target '%s' cached\n", f->target); } -- cgit v1.2.3 From 09cbe581e346229e33c48da00439419ff2fe6af7 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Fri, 27 Apr 2012 12:19:31 +0200 Subject: mtd: nand: Add a NAND_CMD_STATUS when using write verification To make sure the NAND chip is properly programmed we need a status command before each page write. When CONFIG_MTD_NAND_VERIFY_WRITE=y this assumption is broken when writing multiple pages consecutively. This patch fixes this. Signed-off-by: Bastian Hecht Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index eb88d8b0426..512066e3d14 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2118,6 +2118,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, if (chip->verify_buf(mtd, buf, mtd->writesize)) return -EIO; + + /* Make sure the next page prog is preceded by a status read */ + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); #endif return 0; } -- cgit v1.2.3 From 86c2072be6f3c2150cc35f00233f2c31bdba2745 Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Wed, 25 Apr 2012 12:06:05 -0700 Subject: mtd: ecc_strength is at ecc step granularity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ecc_strength element of mtd_info will be the strength of one ecc step, not of the entire writesize, as was previously planned. This is the appropriate way because, as was pointed out¹, bit errors in excess of the strength of one step can cause a hard error if they all occur within the same ecc region. ¹ http://lists.infradead.org/pipermail/linux-mtd/2012-March/040313.html Signed-off-by: Mike Dunn Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 2 +- include/linux/mtd/mtd.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 512066e3d14..671f228d4c7 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3487,7 +3487,7 @@ int nand_scan_tail(struct mtd_info *mtd) /* propagate ecc info to mtd_info */ mtd->ecclayout = chip->ecc.layout; - mtd->ecc_strength = chip->ecc.strength * chip->ecc.steps; + mtd->ecc_strength = chip->ecc.strength; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index cf5ea8cdcf8..cd0119d19cd 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -164,7 +164,7 @@ struct mtd_info { /* ECC layout structure pointer - read only! */ struct nand_ecclayout *ecclayout; - /* max number of correctible bit errors per writesize */ + /* max number of correctible bit errors per ecc step */ unsigned int ecc_strength; /* Data for variable erase regions. If numeraseregions is zero, -- cgit v1.2.3 From 44df4d11b8f573e9efcba94d63e6a1ad148c76d0 Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Wed, 25 Apr 2012 12:06:06 -0700 Subject: mtd: nand: fix incorrect ecc strength values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a couple of ecc strength values for which I earlier made conservative guesses, but whose correct values were later determined¹ (thanks Ivan). Also sets strength for fsl_ifc_nand, which was merged to mainline after the original patch that set the strength for all drivers. ¹ http://lists.infradead.org/pipermail/linux-mtd/2012-March/040325.html Signed-off-by: Mike Dunn Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/bcm_umi_nand.c | 7 +------ drivers/mtd/nand/fsl_ifc_nand.c | 1 + drivers/mtd/nand/jz4740_nand.c | 6 +----- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index 6908cdde306..7134adfa108 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -476,12 +476,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) this->badblock_pattern = &largepage_bbt; } - /* - * FIXME: ecc strength value of 6 bits per 512 bytes of data is a - * conservative guess, given 13 ecc bytes and using bch alg. - * (Assume Galois field order m=15 to allow a margin of error.) - */ - this->ecc.strength = 6; + this->ecc.strength = 8; #endif diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 5387cec53c9..872cc960529 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -821,6 +821,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* Hardware generates ECC per 512 Bytes */ chip->ecc.size = 512; chip->ecc.bytes = 8; + chip->ecc.strength = 4; switch (csor & CSOR_NAND_PGS_MASK) { case CSOR_NAND_PGS_512: diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index e4147e8acb7..a6fa884ae49 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -332,11 +332,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev) chip->ecc.mode = NAND_ECC_HW_OOB_FIRST; chip->ecc.size = 512; chip->ecc.bytes = 9; - chip->ecc.strength = 2; - /* - * FIXME: ecc_strength value of 2 bits per 512 bytes of data is a - * conservative guess, given 9 ecc bytes and reed-solomon alg. - */ + chip->ecc.strength = 4; if (pdata) chip->ecc.layout = pdata->ecc_layout; -- cgit v1.2.3 From a9b672e82bca47bf2b37ee869b8095000cf3ca88 Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Wed, 25 Apr 2012 12:06:07 -0700 Subject: mtd: expose ecc_strength through sysfs ecc_strength element of struct mtd_info is exposed as a read-only variable in sysfs. Signed-off-by: Mike Dunn Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- Documentation/ABI/testing/sysfs-class-mtd | 12 ++++++++++++ drivers/mtd/mtdcore.c | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd index 4d55a188898..43d18180b46 100644 --- a/Documentation/ABI/testing/sysfs-class-mtd +++ b/Documentation/ABI/testing/sysfs-class-mtd @@ -123,3 +123,15 @@ Description: half page, or a quarter page). In the case of ECC NOR, it is the ECC block size. + +What: /sys/class/mtd/mtdX/ecc_strength +Date: April 2012 +KernelVersion: 3.4 +Contact: linux-mtd@lists.infradead.org +Description: + Maximum number of bit errors that the device is capable of + correcting within each region covering an ecc step. This will + always be a non-negative integer. Note that some devices will + have multiple ecc steps within each writesize region. + + In the case of devices lacking any ECC capability, it is 0. diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c837507dfb1..090e849d3dc 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -250,6 +250,15 @@ static ssize_t mtd_name_show(struct device *dev, } static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL); +static ssize_t mtd_ecc_strength_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_strength); +} +static DEVICE_ATTR(ecc_strength, S_IRUGO, mtd_ecc_strength_show, NULL); + static struct attribute *mtd_attrs[] = { &dev_attr_type.attr, &dev_attr_flags.attr, @@ -260,6 +269,7 @@ static struct attribute *mtd_attrs[] = { &dev_attr_oobsize.attr, &dev_attr_numeraseregions.attr, &dev_attr_name.attr, + &dev_attr_ecc_strength.attr, NULL, }; -- cgit v1.2.3 From d062d4ede877fcd2ecc4c6262abad09a6f32950a Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Wed, 25 Apr 2012 12:06:08 -0700 Subject: mtd: bitflip_threshold added to mtd_info and sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An element 'bitflip_threshold' is added to struct mtd_info, and also exposed as a read/write variable in sysfs. This will be used to determine whether or not mtd_read() returns -EUCLEAN or 0 (absent a hard error). If the driver leaves it as zero, mtd will set it to a default value of ecc_strength. This v2 adds the line that propagates bitflip_threshold from the master to the partitions - thanks Ivan¹. ¹ http://lists.infradead.org/pipermail/linux-mtd/2012-April/040900.html Signed-off-by: Mike Dunn Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- Documentation/ABI/testing/sysfs-class-mtd | 36 +++++++++++++++++++++++++++++++ drivers/mtd/mtdcore.c | 33 ++++++++++++++++++++++++++++ drivers/mtd/mtdpart.c | 2 ++ include/linux/mtd/mtd.h | 9 ++++++++ 4 files changed, 80 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd index 43d18180b46..78835080856 100644 --- a/Documentation/ABI/testing/sysfs-class-mtd +++ b/Documentation/ABI/testing/sysfs-class-mtd @@ -135,3 +135,39 @@ Description: have multiple ecc steps within each writesize region. In the case of devices lacking any ECC capability, it is 0. + +What: /sys/class/mtd/mtdX/bitflip_threshold +Date: April 2012 +KernelVersion: 3.4 +Contact: linux-mtd@lists.infradead.org +Description: + This allows the user to examine and adjust the criteria by which + mtd returns -EUCLEAN from mtd_read(). If the maximum number of + bit errors that were corrected on any single region comprising + an ecc step (as reported by the driver) equals or exceeds this + value, -EUCLEAN is returned. Otherwise, absent an error, 0 is + returned. Higher layers (e.g., UBI) use this return code as an + indication that an erase block may be degrading and should be + scrutinized as a candidate for being marked as bad. + + The initial value may be specified by the flash device driver. + If not, then the default value is ecc_strength. + + The introduction of this feature brings a subtle change to the + meaning of the -EUCLEAN return code. Previously, it was + interpreted to mean simply "one or more bit errors were + corrected". Its new interpretation can be phrased as "a + dangerously high number of bit errors were corrected on one or + more regions comprising an ecc step". The precise definition of + "dangerously high" can be adjusted by the user with + bitflip_threshold. Users are discouraged from doing this, + however, unless they know what they are doing and have intimate + knowledge of the properties of their device. Broadly speaking, + bitflip_threshold should be low enough to detect genuine erase + block degradation, but high enough to avoid the consequences of + a persistent return value of -EUCLEAN on devices where sticky + bitflips occur. Note that if bitflip_threshold exceeds + ecc_strength, -EUCLEAN is never returned by the read functions. + + This is generally applicable only to NAND flash devices with ECC + capability. It is ignored on devices lacking ECC capability. diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 090e849d3dc..6a7cba1e24e 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -259,6 +259,34 @@ static ssize_t mtd_ecc_strength_show(struct device *dev, } static DEVICE_ATTR(ecc_strength, S_IRUGO, mtd_ecc_strength_show, NULL); +static ssize_t mtd_bitflip_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->bitflip_threshold); +} + +static ssize_t mtd_bitflip_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + unsigned int bitflip_threshold; + int retval; + + retval = kstrtouint(buf, 0, &bitflip_threshold); + if (retval) + return retval; + + mtd->bitflip_threshold = bitflip_threshold; + return count; +} +static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR, + mtd_bitflip_threshold_show, + mtd_bitflip_threshold_store); + static struct attribute *mtd_attrs[] = { &dev_attr_type.attr, &dev_attr_flags.attr, @@ -270,6 +298,7 @@ static struct attribute *mtd_attrs[] = { &dev_attr_numeraseregions.attr, &dev_attr_name.attr, &dev_attr_ecc_strength.attr, + &dev_attr_bitflip_threshold.attr, NULL, }; @@ -332,6 +361,10 @@ int add_mtd_device(struct mtd_info *mtd) mtd->index = i; mtd->usecount = 0; + /* default value if not set by driver */ + if (mtd->bitflip_threshold == 0) + mtd->bitflip_threshold = mtd->ecc_strength; + if (is_power_of_2(mtd->erasesize)) mtd->erasesize_shift = ffs(mtd->erasesize) - 1; else diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 9651c06de0a..ec75d44e025 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -517,6 +517,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, slave->mtd.ecclayout = master->ecclayout; slave->mtd.ecc_strength = master->ecc_strength; + slave->mtd.bitflip_threshold = master->bitflip_threshold; + if (master->_block_isbad) { uint64_t offs = 0; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index cd0119d19cd..63dadc0dfb6 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -157,6 +157,15 @@ struct mtd_info { unsigned int erasesize_mask; unsigned int writesize_mask; + /* + * read ops return -EUCLEAN if max number of bitflips corrected on any + * one region comprising an ecc step equals or exceeds this value. + * Settable by driver, else defaults to ecc_strength. User can override + * in sysfs. N.B. The meaning of the -EUCLEAN return code has changed; + * see Documentation/ABI/testing/sysfs-class-mtd for more detail. + */ + unsigned int bitflip_threshold; + // Kernel-only stuff starts here. const char *name; int index; -- cgit v1.2.3 From 3f91e94f7f511de74c0d2abe08672ccdbdd1961c Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Wed, 25 Apr 2012 12:06:09 -0700 Subject: mtd: nand: read_page() returns max_bitflips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ecc.read_page() method for nand drivers is changed to return the maximum number of bitflips that were corrected on any one region covering an ecc step, This patch doesn't change what the nand code returns to mtd. This v2 includes the change to the fsl_ifc_nand driver requested by Scott¹. ¹ http://lists.infradead.org/pipermail/linux-mtd/2012-April/040883.html Signed-off-by: Mike Dunn Acked-by (freescale changes): Scott Wood Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/atmel_nand.c | 9 +++++--- drivers/mtd/nand/bcm_umi_bch.c | 4 +++- drivers/mtd/nand/cafe_nand.c | 4 +++- drivers/mtd/nand/denali.c | 10 ++++++--- drivers/mtd/nand/docg4.c | 5 +++-- drivers/mtd/nand/fsl_elbc_nand.c | 21 ++++++++++++------ drivers/mtd/nand/fsl_ifc_nand.c | 9 +++++++- drivers/mtd/nand/fsmc_nand.c | 9 +++++--- drivers/mtd/nand/nand_base.c | 47 ++++++++++++++++++++++++++-------------- drivers/mtd/nand/sh_flctl.c | 2 +- 10 files changed, 82 insertions(+), 38 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 2165576a1c6..9a7876e8de4 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -335,6 +335,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd, uint8_t *oob = chip->oob_poi; uint8_t *ecc_pos; int stat; + unsigned int max_bitflips = 0; /* * Errata: ALE is incorrectly wired up to the ECC controller @@ -371,10 +372,12 @@ static int atmel_nand_read_page(struct mtd_info *mtd, /* check if there's an error */ stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } /* get back to oob start (end of page) */ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); @@ -382,7 +385,7 @@ static int atmel_nand_read_page(struct mtd_info *mtd, /* read the oob */ chip->read_buf(mtd, oob, mtd->oobsize); - return 0; + return max_bitflips; } /* diff --git a/drivers/mtd/nand/bcm_umi_bch.c b/drivers/mtd/nand/bcm_umi_bch.c index a930666d068..f8472b49567 100644 --- a/drivers/mtd/nand/bcm_umi_bch.c +++ b/drivers/mtd/nand/bcm_umi_bch.c @@ -116,6 +116,7 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, uint8_t eccCalc[NAND_ECC_NUM_BYTES]; int sectorOobSize = mtd->oobsize / eccsteps; int stat; + unsigned int max_bitflips = 0; for (sectorIdx = 0; sectorIdx < eccsteps; sectorIdx++, datap += eccsize) { @@ -177,9 +178,10 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, } #endif mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); } } - return 0; + return max_bitflips; } /**************************************************************************** diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 2973d97bd52..886ff3a064c 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -383,6 +383,7 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { struct cafe_priv *cafe = mtd->priv; + unsigned int max_bitflips = 0; cafe_dev_dbg(&cafe->pdev->dev, "ECC result %08x SYN1,2 %08x\n", cafe_readl(cafe, NAND_ECC_RESULT), @@ -449,10 +450,11 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, } else { dev_dbg(&cafe->pdev->dev, "Corrected %d symbol errors\n", n); mtd->ecc_stats.corrected += n; + max_bitflips = max_t(unsigned int, max_bitflips, n); } } - return 0; + return max_bitflips; } static struct nand_ecclayout cafe_oobinfo_2048 = { diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index a1048c7c5d2..1b346474dba 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -924,9 +924,10 @@ bool is_erased(uint8_t *buf, int len) #define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, - uint32_t irq_status) + uint32_t irq_status, unsigned int *max_bitflips) { bool check_erased_page = false; + unsigned int bitflips = 0; if (irq_status & INTR_STATUS__ECC_ERR) { /* read the ECC errors. we'll ignore them for now */ @@ -965,6 +966,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, /* correct the ECC error */ buf[offset] ^= err_correction_value; denali->mtd.ecc_stats.corrected++; + bitflips++; } } else { /* if the error is not correctable, need to @@ -984,6 +986,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, clear_interrupts(denali); denali_set_intr_modes(denali, true); } + *max_bitflips = bitflips; return check_erased_page; } @@ -1121,6 +1124,7 @@ static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { + unsigned int max_bitflips; struct denali_nand_info *denali = mtd_to_denali(mtd); dma_addr_t addr = denali->buf.dma_buf; @@ -1153,7 +1157,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, memcpy(buf, denali->buf.buf, mtd->writesize); - check_erased_page = handle_ecc(denali, buf, irq_status); + check_erased_page = handle_ecc(denali, buf, irq_status, &max_bitflips); denali_enable_dma(denali, false); if (check_erased_page) { @@ -1167,7 +1171,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, denali->mtd.ecc_stats.failed++; } } - return 0; + return max_bitflips; } static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index b0820266454..8e7da0170a2 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -720,6 +720,7 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, struct docg4_priv *doc = nand->priv; void __iomem *docptr = doc->virtadr; uint16_t status, edc_err, *buf16; + int bits_corrected = 0; dev_dbg(doc->dev, "%s: page %08x\n", __func__, page); @@ -772,7 +773,7 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, /* If bitflips are reported, attempt to correct with ecc */ if (edc_err & DOC_ECCCONF1_BCH_SYNDROM_ERR) { - int bits_corrected = correct_data(mtd, buf, page); + bits_corrected = correct_data(mtd, buf, page); if (bits_corrected == -EBADMSG) mtd->ecc_stats.failed++; else @@ -781,7 +782,7 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, } writew(0, docptr + DOC_DATAEND); - return 0; + return bits_corrected; } diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 80b5264f0a3..8638b5e734c 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -75,6 +75,7 @@ struct fsl_elbc_fcm_ctrl { unsigned int use_mdr; /* Non zero if the MDR is to be set */ unsigned int oob; /* Non zero if operating on OOB data */ unsigned int counter; /* counter for the initializations */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ }; /* These map to the positions used by the FCM hardware ECC generator */ @@ -253,6 +254,8 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) if (chip->ecc.mode != NAND_ECC_HW) return 0; + elbc_fcm_ctrl->max_bitflips = 0; + if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) { uint32_t lteccr = in_be32(&lbc->lteccr); /* @@ -262,11 +265,16 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) * bits 28-31 are uncorrectable errors, marked elsewhere. * for small page nand only 1 bit is used. * if the ELBC doesn't have the lteccr register it reads 0 + * FIXME: 4 bits can be corrected on NANDs with 2k pages, so + * count the number of sub-pages with bitflips and update + * ecc_stats.corrected accordingly. */ if (lteccr & 0x000F000F) out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */ - if (lteccr & 0x000F0000) + if (lteccr & 0x000F0000) { mtd->ecc_stats.corrected++; + elbc_fcm_ctrl->max_bitflips = 1; + } } return 0; @@ -743,13 +751,17 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, uint8_t *buf, int page) { + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_lbc_ctrl *ctrl = priv->ctrl; + struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; + fsl_elbc_read_buf(mtd, buf, mtd->writesize); fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) mtd->ecc_stats.failed++; - return 0; + return elbc_fcm_ctrl->max_bitflips; } /* ECC will be calculated automatically, and errors will be detected in @@ -814,11 +826,6 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->ecc.size = 512; chip->ecc.bytes = 3; chip->ecc.strength = 1; - /* - * FIXME: can hardware ecc correct 4 bitflips if page size is - * 2k? Then does hardware report number of corrections for this - * case? If so, ecc_stats reporting needs to be fixed as well. - */ } else { /* otherwise fall back to default software ECC */ chip->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 872cc960529..0adde967041 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -63,6 +63,7 @@ struct fsl_ifc_nand_ctrl { unsigned int oob; /* Non zero if operating on OOB data */ unsigned int eccread; /* Non zero for a full-page ECC read */ unsigned int counter; /* counter for the initializations */ + unsigned int max_bitflips; /* Saved during READ0 cmd */ }; static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; @@ -262,6 +263,8 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) dev_err(priv->dev, "NAND Flash Write Protect Error\n"); + nctrl->max_bitflips = 0; + if (nctrl->eccread) { int errors; int bufnum = nctrl->page & priv->bufnum_mask; @@ -290,6 +293,9 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) } mtd->ecc_stats.corrected += errors; + nctrl->max_bitflips = max_t(unsigned int, + nctrl->max_bitflips, + errors); } nctrl->eccread = 0; @@ -698,6 +704,7 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, { struct fsl_ifc_mtd *priv = chip->priv; struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; fsl_ifc_read_buf(mtd, buf, mtd->writesize); fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -708,7 +715,7 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) mtd->ecc_stats.failed++; - return 0; + return nctrl->max_bitflips; } /* ECC will be calculated automatically, and errors will be detected in diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 9d7f4171e07..6bf59fdde26 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -720,6 +720,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, */ uint16_t ecc_oob[7]; uint8_t *oob = (uint8_t *)&ecc_oob[0]; + unsigned int max_bitflips = 0; for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); @@ -748,13 +749,15 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /* diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 671f228d4c7..8718eaf8269 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1138,6 +1138,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; chip->ecc.read_page_raw(mtd, chip, buf, page); @@ -1154,12 +1155,14 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1180,6 +1183,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int datafrag_len, eccfrag_len, aligned_len, aligned_pos; int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; int index = 0; + unsigned int max_bitflips = 0; /* Column address within the page aligned to ECC size (256bytes) */ start_step = data_offs / chip->ecc.size; @@ -1244,12 +1248,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1271,6 +1277,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); @@ -1289,12 +1296,14 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1320,6 +1329,7 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; + unsigned int max_bitflips = 0; /* Read the OOB area first */ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); @@ -1337,12 +1347,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } - return 0; + return max_bitflips; } /** @@ -1363,6 +1375,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int eccsteps = chip->ecc.steps; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; @@ -1379,10 +1392,12 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, oob, eccbytes); stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) + if (stat < 0) { mtd->ecc_stats.failed++; - else + } else { mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } oob += eccbytes; @@ -1397,7 +1412,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, if (i) chip->read_buf(mtd, oob, i); - return 0; + return max_bitflips; } /** @@ -1588,7 +1603,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (oob) ops->oobretlen = ops->ooblen - oobreadlen; - if (ret) + if (ret < 0) return ret; if (mtd->ecc_stats.failed - stats.failed) diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index e9b2b260de3..4ea3e20169b 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -359,7 +359,7 @@ static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, if (flctl->hwecc_cant_correct[i]) mtd->ecc_stats.failed++; else - mtd->ecc_stats.corrected += 0; + mtd->ecc_stats.corrected += 0; /* FIXME */ } return 0; -- cgit v1.2.3 From e2788c98b98269a3131bffd2b57599280d7abd73 Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Wed, 25 Apr 2012 12:06:10 -0700 Subject: mtd: nand: add sanity check of ecc strength to nand_scan_tail() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds sanity checks that ensure that drivers for controllers with hardware ECC set the 'strength' element in struct nand_ecc_ctrl. Also stylistic changes to the line that calculates strength for software ECC. This v2 simplifies the check. Thanks Brian!¹ ¹ http://lists.infradead.org/pipermail/linux-mtd/2012-April/040890.html Signed-off-by: Mike Dunn Acked-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8718eaf8269..9f5d339a361 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3348,8 +3348,13 @@ int nand_scan_tail(struct mtd_info *mtd) if (!chip->ecc.write_oob) chip->ecc.write_oob = nand_write_oob_syndrome; - if (mtd->writesize >= chip->ecc.size) + if (mtd->writesize >= chip->ecc.size) { + if (!chip->ecc.strength) { + pr_warn("Driver must set ecc.strength when using hardware ECC\n"); + BUG(); + } break; + } pr_warn("%d byte HW ECC not possible on " "%d byte page size, fallback to SW ECC\n", chip->ecc.size, mtd->writesize); @@ -3404,7 +3409,7 @@ int nand_scan_tail(struct mtd_info *mtd) BUG(); } chip->ecc.strength = - chip->ecc.bytes*8 / fls(8*chip->ecc.size); + chip->ecc.bytes * 8 / fls(8 * chip->ecc.size); break; case NAND_ECC_NONE: -- cgit v1.2.3 From edbc4540e02c201bdd4f4d498ebb6ed517fd36e2 Mon Sep 17 00:00:00 2001 From: Mike Dunn Date: Wed, 25 Apr 2012 12:06:11 -0700 Subject: mtd: driver _read() returns max_bitflips; mtd_read() returns -EUCLEAN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The drivers' _read() method, absent an error, returns a non-negative integer indicating the maximum number of bit errors that were corrected in any one region comprising an ecc step. MTD returns -EUCLEAN if this is >= bitflip_threshold, 0 otherwise. If bitflip_threshold is zero, the comparison is not made since these devices lack ECC and always return zero in the non-error case (thanks Brian)¹. Note that this is a subtle change to the driver interface. This and the preceding patches in this set were tested with ubi on top of the nandsim and docg4 devices, running the ubi test io_basic from mtd-utils. ¹ http://lists.infradead.org/pipermail/linux-mtd/2012-March/040468.html Signed-off-by: Mike Dunn Acked-by: Robert Jarzmik Acked-by: Brian Norris Ivan Djelic Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- Documentation/ABI/testing/sysfs-class-mtd | 7 +++++-- drivers/mtd/devices/docg3.c | 6 ++++-- drivers/mtd/mtdcore.c | 14 +++++++++++++- drivers/mtd/mtdpart.c | 12 ++++++------ drivers/mtd/nand/alauda.c | 4 ++-- drivers/mtd/nand/nand_base.c | 18 ++++++++++++++---- drivers/mtd/onenand/onenand_base.c | 6 ++++-- include/linux/mtd/nand.h | 3 +++ 8 files changed, 51 insertions(+), 19 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd index 78835080856..db1ad7e34fc 100644 --- a/Documentation/ABI/testing/sysfs-class-mtd +++ b/Documentation/ABI/testing/sysfs-class-mtd @@ -167,7 +167,10 @@ Description: block degradation, but high enough to avoid the consequences of a persistent return value of -EUCLEAN on devices where sticky bitflips occur. Note that if bitflip_threshold exceeds - ecc_strength, -EUCLEAN is never returned by the read functions. + ecc_strength, -EUCLEAN is never returned by mtd_read(). + Conversely, if bitflip_threshold is zero, -EUCLEAN is always + returned, absent a hard error. This is generally applicable only to NAND flash devices with ECC - capability. It is ignored on devices lacking ECC capability. + capability. It is ignored on devices lacking ECC capability; + i.e., devices for which ecc_strength is zero. diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 8272c02668d..65d22a0439c 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -850,6 +850,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, u8 *buf = ops->datbuf; size_t len, ooblen, nbdata, nboob; u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; + int max_bitflips = 0; if (buf) len = ops->len; @@ -876,7 +877,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ret = 0; skip = from % DOC_LAYOUT_PAGE_SIZE; mutex_lock(&docg3->cascade->lock); - while (!ret && (len > 0 || ooblen > 0)) { + while (ret >= 0 && (len > 0 || ooblen > 0)) { calc_block_sector(from - skip, &block0, &block1, &page, &ofs, docg3->reliable); nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); @@ -936,7 +937,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, } if (ret > 0) { mtd->ecc_stats.corrected += ret; - ret = -EUCLEAN; + max_bitflips = max(max_bitflips, ret); + ret = max_bitflips; } } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6a7cba1e24e..575730744fd 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -800,12 +800,24 @@ EXPORT_SYMBOL_GPL(mtd_get_unmapped_area); int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + int ret_code; *retlen = 0; if (from < 0 || from > mtd->size || len > mtd->size - from) return -EINVAL; if (!len) return 0; - return mtd->_read(mtd, from, len, retlen, buf); + + /* + * In the absence of an error, drivers return a non-negative integer + * representing the maximum number of bitflips that were corrected on + * any one ecc region (if applicable; zero otherwise). + */ + ret_code = mtd->_read(mtd, from, len, retlen, buf); + if (unlikely(ret_code < 0)) + return ret_code; + if (mtd->ecc_strength == 0) + return 0; /* device lacks ecc */ + return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; } EXPORT_SYMBOL_GPL(mtd_read); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index ec75d44e025..d518e4db8a0 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -67,12 +67,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, stats = part->master->ecc_stats; res = part->master->_read(part->master, from + part->offset, len, retlen, buf); - if (unlikely(res)) { - if (mtd_is_bitflip(res)) - mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; - if (mtd_is_eccerr(res)) - mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; - } + if (unlikely(mtd_is_eccerr(res))) + mtd->ecc_stats.failed += + part->master->ecc_stats.failed - stats.failed; + else + mtd->ecc_stats.corrected += + part->master->ecc_stats.corrected - stats.corrected; return res; } diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 4f20e1d8bef..60a0dfdb080 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -414,7 +414,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, } err = 0; if (corrected) - err = -EUCLEAN; + err = 1; /* return max_bitflips per ecc step */ if (uncorrected) err = -EBADMSG; out: @@ -446,7 +446,7 @@ static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, } err = 0; if (corrected) - err = -EUCLEAN; + err = 1; /* return max_bitflips per ecc step */ if (uncorrected) err = -EBADMSG; return err; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9f5d339a361..640f1f8159e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1486,6 +1486,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, mtd->oobavail : mtd->oobsize; uint8_t *bufpoi, *oob, *buf; + unsigned int max_bitflips = 0; stats = mtd->ecc_stats; @@ -1513,7 +1514,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, sndcmd = 0; } - /* Now read the page into the buffer */ + /* + * Now read the page into the buffer. Absent an error, + * the read methods return max bitflips per ecc step. + */ if (unlikely(ops->mode == MTD_OPS_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, page); @@ -1530,15 +1534,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, break; } + max_bitflips = max_t(unsigned int, max_bitflips, ret); + /* Transfer not aligned data */ if (!aligned) { if (!NAND_SUBPAGE_READ(chip) && !oob && !(mtd->ecc_stats.failed - stats.failed) && - (ops->mode != MTD_OPS_RAW)) + (ops->mode != MTD_OPS_RAW)) { chip->pagebuf = realpage; - else + chip->pagebuf_bitflips = ret; + } else { /* Invalidate page cache */ chip->pagebuf = -1; + } memcpy(buf, chip->buffers->databuf + col, bytes); } @@ -1571,6 +1579,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } else { memcpy(buf, chip->buffers->databuf + col, bytes); buf += bytes; + max_bitflips = max_t(unsigned int, max_bitflips, + chip->pagebuf_bitflips); } readlen -= bytes; @@ -1609,7 +1619,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + return max_bitflips; } /** diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index b3ce12ef359..7153e0d2710 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1201,7 +1201,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; } /** @@ -1333,7 +1334,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; - return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; + /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ + return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; } /** diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1482340d3d9..2829e8be3a6 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -459,6 +459,8 @@ struct nand_buffers { * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagebuf: [INTERN] holds the pagenumber which is currently in * data_buf. + * @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is + * currently in data_buf. * @subpagesize: [INTERN] holds the subpagesize * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), * non 0 if ONFI supported. @@ -519,6 +521,7 @@ struct nand_chip { uint64_t chipsize; int pagemask; int pagebuf; + unsigned int pagebuf_bitflips; int subpagesize; uint8_t cellinfo; int badblockpos; -- cgit v1.2.3 From 831d316b8b80b68dcdd2b3f5ede6d33c2bbf5835 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 May 2012 17:12:53 -0700 Subject: mtd: nandsim: remove autoincrement code The NAND layer always has NAND_NO_AUTOINCR set, so we will never utilize the AUTOINCR code in nandsim. We will be removing the NAND_NO_AUTOINCR option soon, and so kill this code as well. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nandsim.c | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 261f478f8cc..6cc8fbfabb8 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -268,7 +268,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " #define OPT_PAGE512 0x00000002 /* 512-byte page chips */ #define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ #define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ -#define OPT_AUTOINCR 0x00000020 /* page number auto incrementation is possible */ #define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ #define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */ #define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */ @@ -594,7 +593,7 @@ static int init_nandsim(struct mtd_info *mtd) ns->options |= OPT_PAGE256; } else if (ns->geom.pgsz == 512) { - ns->options |= (OPT_PAGE512 | OPT_AUTOINCR); + ns->options |= OPT_PAGE512; if (ns->busw == 8) ns->options |= OPT_PAGE512_8BIT; } else if (ns->geom.pgsz == 2048) { @@ -663,8 +662,6 @@ static int init_nandsim(struct mtd_info *mtd) for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (second_id_byte != nand_flash_ids[i].id) continue; - if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR)) - ns->options |= OPT_AUTOINCR; } if (ns->busw == 16) @@ -1936,20 +1933,8 @@ static u_char ns_nand_read_byte(struct mtd_info *mtd) if (ns->regs.count == ns->regs.num) { NS_DBG("read_byte: all bytes were read\n"); - /* - * The OPT_AUTOINCR allows to read next consecutive pages without - * new read operation cycle. - */ - if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { - ns->regs.count = 0; - if (ns->regs.row + 1 < ns->geom.pgnum) - ns->regs.row += 1; - NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row); - do_state_action(ns, ACTION_CPY); - } - else if (NS_STATE(ns->nxstate) == STATE_READY) + if (NS_STATE(ns->nxstate) == STATE_READY) switch_state(ns); - } return outb; @@ -2203,14 +2188,7 @@ static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ns->regs.count += len; if (ns->regs.count == ns->regs.num) { - if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { - ns->regs.count = 0; - if (ns->regs.row + 1 < ns->geom.pgnum) - ns->regs.row += 1; - NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row); - do_state_action(ns, ACTION_CPY); - } - else if (NS_STATE(ns->nxstate) == STATE_READY) + if (NS_STATE(ns->nxstate) == STATE_READY) switch_state(ns); } -- cgit v1.2.3 From c00a0991d14dce8d714becc49ec8ae5db9c8ac98 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 May 2012 17:12:54 -0700 Subject: mtd: nand: remove autoincrement 'sndcmd' code The NAND_NO_AUTOINCR option is always set, so we will kill the option and make "no autoincrement" the default behavior for nand_base.c. Thus, we should remove the code which decides whether or not to send the NAND_CMD_READ0 command. Instead, we unconditionally send the command. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 43 ++++++------------------------------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 640f1f8159e..6246b0fc2fc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1477,8 +1477,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, int chipnr, page, realpage, col, bytes, aligned; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; - int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; - int sndcmd = 1; int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; @@ -1509,10 +1507,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (realpage != chip->pagebuf || oob) { bufpoi = aligned ? buf : chip->buffers->databuf; - if (likely(sndcmd)) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); /* * Now read the page into the buffer. Absent an error, @@ -1564,13 +1559,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, } if (!(chip->options & NAND_NO_READRDY)) { - /* - * Apply delay or wait for ready/busy pin. Do - * this before the AUTOINCR check, so no - * problems arise if a chip which does auto - * increment is marked as NOAUTOINCR by the - * board driver. - */ + /* Apply delay or wait for ready/busy pin */ if (!chip->dev_ready) udelay(chip->chip_delay); else @@ -1600,13 +1589,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } - - /* - * Check, if the chip supports auto page increment or if we - * have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) - sndcmd = 1; } ops->retlen = ops->len - (size_t) readlen; @@ -1800,10 +1782,9 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int page, realpage, chipnr, sndcmd = 1; + int page, realpage, chipnr; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; - int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1; int readlen = ops->ooblen; int len; uint8_t *buf = ops->oobbuf; @@ -1842,20 +1823,15 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, while (1) { if (ops->mode == MTD_OPS_RAW) - sndcmd = chip->ecc.read_oob_raw(mtd, chip, page, sndcmd); + chip->ecc.read_oob_raw(mtd, chip, page, 1); else - sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); + chip->ecc.read_oob(mtd, chip, page, 1); len = min(len, readlen); buf = nand_transfer_oob(chip, buf, ops, len); if (!(chip->options & NAND_NO_READRDY)) { - /* - * Apply delay or wait for ready/busy pin. Do this - * before the AUTOINCR check, so no problems arise if a - * chip which does auto increment is marked as - * NOAUTOINCR by the board driver. - */ + /* Apply delay or wait for ready/busy pin */ if (!chip->dev_ready) udelay(chip->chip_delay); else @@ -1876,13 +1852,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, chip->select_chip(mtd, -1); chip->select_chip(mtd, chipnr); } - - /* - * Check, if the chip supports auto page increment or if we - * have hit a block boundary. - */ - if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck)) - sndcmd = 1; } ops->oobretlen = ops->ooblen; -- cgit v1.2.3 From 1826dbccebc9a58a0b0c0a9b7c09e47b19d97398 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 May 2012 17:12:55 -0700 Subject: mtd: nand: kill NAND_NO_AUTOINCR option No drivers use auto-increment NAND, so kill the NO_AUTOINCR option entirely. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- Documentation/DocBook/mtdnand.tmpl | 2 -- arch/arm/mach-ep93xx/snappercl15.c | 1 - arch/arm/mach-ixp4xx/ixdp425-setup.c | 1 - arch/arm/mach-nomadik/board-nhk8815.c | 2 +- arch/mips/rb532/devices.c | 1 - drivers/mtd/nand/au1550nd.c | 2 -- drivers/mtd/nand/cafe_nand.c | 2 +- drivers/mtd/nand/cs553x_nand.c | 1 - drivers/mtd/nand/docg4.c | 3 +-- drivers/mtd/nand/fsl_elbc_nand.c | 2 +- drivers/mtd/nand/fsl_ifc_nand.c | 2 +- drivers/mtd/nand/h1910.c | 1 - drivers/mtd/nand/mpc5121_nfc.c | 1 - drivers/mtd/nand/nand_base.c | 8 +------- drivers/mtd/nand/nand_ids.c | 6 ++---- drivers/mtd/nand/pasemi_nand.c | 1 - drivers/mtd/nand/pxa3xx_nand.c | 1 - drivers/mtd/nand/sh_flctl.c | 2 -- drivers/mtd/nand/sm_common.c | 9 ++++----- include/linux/mtd/nand.h | 5 +---- 20 files changed, 13 insertions(+), 40 deletions(-) diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl index 0c674be0d3c..e0aedb7a782 100644 --- a/Documentation/DocBook/mtdnand.tmpl +++ b/Documentation/DocBook/mtdnand.tmpl @@ -1119,8 +1119,6 @@ in this page These constants are defined in nand.h. They are ored together to describe the chip functionality. -/* Chip can not auto increment pages */ -#define NAND_NO_AUTOINCR 0x00000001 /* Buswitdh is 16 bit */ #define NAND_BUSWIDTH_16 0x00000002 /* Device supports partial programming without padding */ diff --git a/arch/arm/mach-ep93xx/snappercl15.c b/arch/arm/mach-ep93xx/snappercl15.c index 5df9092f1fc..376b8f5ecfa 100644 --- a/arch/arm/mach-ep93xx/snappercl15.c +++ b/arch/arm/mach-ep93xx/snappercl15.c @@ -100,7 +100,6 @@ static struct platform_nand_data snappercl15_nand_data = { .nr_chips = 1, .partitions = snappercl15_nand_parts, .nr_partitions = ARRAY_SIZE(snappercl15_nand_parts), - .options = NAND_NO_AUTOINCR, .chip_delay = 25, }, .ctrl = { diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c index fccfc73e69f..108a9d3f382 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -98,7 +98,6 @@ static struct platform_nand_data ixdp425_flash_nand_data = { .chip = { .nr_chips = 1, .chip_delay = 30, - .options = NAND_NO_AUTOINCR, .partitions = ixdp425_partitions, .nr_partitions = ARRAY_SIZE(ixdp425_partitions), }, diff --git a/arch/arm/mach-nomadik/board-nhk8815.c b/arch/arm/mach-nomadik/board-nhk8815.c index 58cacafcf66..2e8d3e176bc 100644 --- a/arch/arm/mach-nomadik/board-nhk8815.c +++ b/arch/arm/mach-nomadik/board-nhk8815.c @@ -111,7 +111,7 @@ static struct nomadik_nand_platform_data nhk8815_nand_data = { .parts = nhk8815_partitions, .nparts = ARRAY_SIZE(nhk8815_partitions), .options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING \ - | NAND_NO_READRDY | NAND_NO_AUTOINCR, + | NAND_NO_READRDY, .init = nhk8815_nand_init, }; diff --git a/arch/mips/rb532/devices.c b/arch/mips/rb532/devices.c index a969eb82663..08c4f8e03ff 100644 --- a/arch/mips/rb532/devices.c +++ b/arch/mips/rb532/devices.c @@ -292,7 +292,6 @@ static void __init rb532_nand_setup(void) rb532_nand_data.chip.nr_partitions = ARRAY_SIZE(rb532_partition_info); rb532_nand_data.chip.partitions = rb532_partition_info; rb532_nand_data.chip.chip_delay = NAND_CHIP_DELAY; - rb532_nand_data.chip.options = NAND_NO_AUTOINCR; } diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 73abbc3e093..9f609d2dcf6 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -508,8 +508,6 @@ static int __devinit au1550nd_probe(struct platform_device *pdev) this->chip_delay = 30; this->ecc.mode = NAND_ECC_SOFT; - this->options = NAND_NO_AUTOINCR; - if (pd->devwidth) this->options |= NAND_BUSWIDTH_16; diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 886ff3a064c..75fb77b96ef 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -687,7 +687,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, /* Enable the following for a flash based bad block table */ cafe->nand.bbt_options = NAND_BBT_USE_FLASH; - cafe->nand.options = NAND_NO_AUTOINCR | NAND_OWN_BUFFERS; + cafe->nand.options = NAND_OWN_BUFFERS; if (skipbbt) { cafe->nand.options |= NAND_SKIP_BBTSCAN; diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 821c34c6250..adb6c3ef37f 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -240,7 +240,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) /* Enable the following for a flash based bad block table */ this->bbt_options = NAND_BBT_USE_FLASH; - this->options = NAND_NO_AUTOINCR; /* Scan to find existence of the device */ if (nand_scan(new_mtd, 1)) { diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 8e7da0170a2..1540de2ad39 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -1193,8 +1193,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->ecc.prepad = 8; nand->ecc.bytes = 8; nand->ecc.strength = DOCG4_T; - nand->options = - NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE | NAND_NO_AUTOINCR; + nand->options = NAND_BUSWIDTH_16 | NAND_NO_SUBPAGE_WRITE; nand->IO_ADDR_R = nand->IO_ADDR_W = doc->virtadr + DOC_IOSPACE_DATA; nand->controller = &nand->hwcontrol; spin_lock_init(&nand->controller->lock); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 8638b5e734c..4d995875d36 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -807,7 +807,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->bbt_md = &bbt_mirror_descr; /* set up nand options */ - chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->options = NAND_NO_READRDY; chip->bbt_options = NAND_BBT_USE_FLASH; chip->controller = &elbc_fcm_ctrl->controller; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 0adde967041..dffd2fa353a 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -806,7 +806,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) out_be32(&ifc->ifc_nand.ncfgr, 0x0); /* set up nand options */ - chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->options = NAND_NO_READRDY; chip->bbt_options = NAND_BBT_USE_FLASH; diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 11e48781342..85e55a5eb2f 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -124,7 +124,6 @@ static int __init h1910_init(void) /* 15 us command delay time */ this->chip_delay = 50; this->ecc.mode = NAND_ECC_SOFT; - this->options = NAND_NO_AUTOINCR; /* Scan to find existence of the device */ if (nand_scan(h1910_nand_mtd, 1)) { diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index c240cf1af96..c259c24d798 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -734,7 +734,6 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op) chip->write_buf = mpc5121_nfc_write_buf; chip->verify_buf = mpc5121_nfc_verify_buf; chip->select_chip = mpc5121_nfc_select_chip; - chip->options = NAND_NO_AUTOINCR; chip->bbt_options = NAND_BBT_USE_FLASH; chip->ecc.mode = NAND_ECC_SOFT; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6246b0fc2fc..5ec4d2c01b8 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2895,8 +2895,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, *busw = NAND_BUSWIDTH_16; chip->options &= ~NAND_CHIPOPTIONS_MSK; - chip->options |= (NAND_NO_READRDY | - NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK; + chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK; pr_info("ONFI flash detected\n"); return 1; @@ -3073,11 +3072,6 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; ident_done: - /* - * Set chip as a default. Board drivers can override it, if necessary. - */ - chip->options |= NAND_NO_AUTOINCR; - /* Try to identify manufacturer */ for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { if (nand_manuf_ids[maf_idx].id == *maf_id) diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index af4fe8ca7b5..621b70b7a15 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -70,7 +70,7 @@ struct nand_flash_dev nand_flash_ids[] = { * These are the new chips with large page size. The pagesize and the * erasesize is determined from the extended id bytes */ -#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) +#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY) #define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) /* 512 Megabit */ @@ -157,9 +157,7 @@ struct nand_flash_dev nand_flash_ids[] = { * writes possible, but not implemented now */ {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, - NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | - BBT_AUTO_REFRESH - }, + NAND_IS_AND | NAND_NO_READRDY | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, {NULL,} }; diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 974dbf8251c..1440e51cedc 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -155,7 +155,6 @@ static int __devinit pasemi_nand_probe(struct platform_device *ofdev) chip->ecc.mode = NAND_ECC_SOFT; /* Enable the following for a flash based bad block table */ - chip->options = NAND_NO_AUTOINCR; chip->bbt_options = NAND_BBT_USE_FLASH; /* Scan to find existence of the device */ diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index def50caa6f8..36a32a0d3b9 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1004,7 +1004,6 @@ KEEP_CONFIG: chip->ecc.size = host->page_size; chip->ecc.strength = 1; - chip->options = NAND_NO_AUTOINCR; chip->options |= NAND_NO_READRDY; if (host->reg_ndcr & NDCR_DWIDTH_M) chip->options |= NAND_BUSWIDTH_16; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 4ea3e20169b..3f0788fad66 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -881,8 +881,6 @@ static int __devinit flctl_probe(struct platform_device *pdev) flctl->hwecc = pdata->has_hwecc; flctl->holden = pdata->use_holden; - nand->options = NAND_NO_AUTOINCR; - /* Set address of hardware control function */ /* 20 us command delay time */ nand->chip_delay = 20; diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index 774c3c26671..082bcdcd6bc 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -94,17 +94,16 @@ static struct nand_flash_dev nand_smartmedia_flash_ids[] = { {NULL,} }; -#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD) static struct nand_flash_dev nand_xd_flash_ids[] = { {"xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, {"xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, {"xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, {"xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, - {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM}, - {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, XD_TYPEM}, - {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, XD_TYPEM}, - {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, XD_TYPEM}, + {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, NAND_BROKEN_XD}, + {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, NAND_BROKEN_XD}, + {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, NAND_BROKEN_XD}, + {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, NAND_BROKEN_XD}, {NULL,} }; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2829e8be3a6..627f0c575ac 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -161,8 +161,6 @@ typedef enum { * Option constants for bizarre disfunctionality and real * features. */ -/* Chip can not auto increment pages */ -#define NAND_NO_AUTOINCR 0x00000001 /* Buswidth is 16 bit */ #define NAND_BUSWIDTH_16 0x00000002 /* Device supports partial programming without padding */ @@ -207,7 +205,6 @@ typedef enum { (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK) /* Macros to identify the above */ -#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR)) #define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING)) #define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG)) #define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK)) @@ -216,7 +213,7 @@ typedef enum { && (chip->page_shift > 9)) /* Mask to zero out the chip options, which come from the id table */ -#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) +#define NAND_CHIPOPTIONS_MSK 0x0000ffff /* Non chip related options */ /* This option skips the bbt scan during initialization. */ -- cgit v1.2.3 From 5ca11ca7ad950c60d4a30bbf9eb18555d537b226 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 1 May 2012 04:04:00 +0200 Subject: mtd: m25p80: add support for Everspin MR25H256 MRAM chip This chip isn't JEDEC-compatible. Signed-off-by: Marek Vasut Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 1924d247c1c..85ce6fdc9e5 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -639,6 +639,9 @@ static const struct spi_device_id m25p_ids[] = { { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + /* Everspin */ + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, + /* Intel/Numonyx -- xxxs33b */ { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, -- cgit v1.2.3 From bb08bc108628493fb45d02da81cd201b6a42391c Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 30 Apr 2012 19:30:45 +0200 Subject: mtd: add m25p80 id for mx25l2005a Add the id and sector mappings for mx25l2005a flash chips. Signed-off-by: John Crispin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 85ce6fdc9e5..eb1cc9797da 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -648,6 +648,7 @@ static const struct spi_device_id m25p_ids[] = { { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, /* Macronix */ + { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, -- cgit v1.2.3 From a4f203512be974dbd7425f8f1d3d40720bf36997 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 30 Apr 2012 19:30:46 +0200 Subject: OF: MTD: make plat_nand loadable from DT This patch sets the of_match_table field inside plat_nand's platform_driver. We also add a struct mtd_part_parser_data pointer to make sure of_part parsing works. If an arch wants to support plat_nand via DT it needs to setup the platform_nand_data and hook it into the platform_device. Signed-off-by: John Crispin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 585639947f5..fd2d2a1b78b 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -31,6 +31,7 @@ static const char *part_probe_types[] = { "cmdlinepart", NULL }; static int __devinit plat_nand_probe(struct platform_device *pdev) { struct platform_nand_data *pdata = pdev->dev.platform_data; + struct mtd_part_parser_data ppdata; struct plat_nand_data *data; struct resource *res; const char **part_types; @@ -103,7 +104,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) part_types = pdata->chip.part_probe_types ? : part_probe_types; - err = mtd_device_parse_register(&data->mtd, part_types, NULL, + ppdata.of_node = pdev->dev.of_node; + err = mtd_device_parse_register(&data->mtd, part_types, &ppdata, pdata->chip.partitions, pdata->chip.nr_partitions); @@ -144,12 +146,19 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id plat_nand_match[] = { + { .compatible = "gen_nand" }, + {}, +}; +MODULE_DEVICE_TABLE(of, plat_nand_match); + static struct platform_driver plat_nand_driver = { - .probe = plat_nand_probe, - .remove = __devexit_p(plat_nand_remove), - .driver = { - .name = "gen_nand", - .owner = THIS_MODULE, + .probe = plat_nand_probe, + .remove = __devexit_p(plat_nand_remove), + .driver = { + .name = "gen_nand", + .owner = THIS_MODULE, + .of_match_table = plat_nand_match, }, }; -- cgit v1.2.3 From b4f7aa84d6ff44327ab91a2973ebf0c2a7797d24 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 30 Apr 2012 19:30:47 +0200 Subject: mtd: add read_byte support to plat_nand Lantiq SoCs have a External Bus Unit (EBU) that is used to attach MTD media. As we need to co-exist with PCI on the same bus, certain swapping settings must be applied. Similar to the NOR map driver we need to apply a fix to make NAND work. The easiest way is to use byte reads. Signed-off-by: John Crispin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 1 + include/linux/mtd/nand.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index fd2d2a1b78b..1bcb5204042 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -79,6 +79,7 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) data->chip.select_chip = pdata->ctrl.select_chip; data->chip.write_buf = pdata->ctrl.write_buf; data->chip.read_buf = pdata->ctrl.read_buf; + data->chip.read_byte = pdata->ctrl.read_byte; data->chip.chip_delay = pdata->chip.chip_delay; data->chip.options |= pdata->chip.options; data->chip.bbt_options |= pdata->chip.bbt_options; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 627f0c575ac..94a6679bfc2 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -654,6 +654,7 @@ struct platform_nand_ctrl { void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); + unsigned char (*read_byte)(struct mtd_info *mtd); void *priv; }; -- cgit v1.2.3 From 1fbb938dff5b6bb4514a4e7600276b03c7f08e25 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 2 May 2012 10:14:55 -0700 Subject: mtd: nand: add 'oob_required' argument to NAND {read,write}_page interfaces New NAND controllers can perform read/write via HW engines which don't expose OOB data in their DMA mode. To reflect this, we should rework the nand_chip / nand_ecc_ctrl interfaces that assume that drivers will always read/write OOB data in the nand_chip.oob_poi buffer. A better interface includes a boolean argument that explicitly tells the callee when OOB data is requested by the calling layer (for reading/writing to/from nand_chip.oob_poi). This patch adds the 'oob_required' parameter to each relevant {read,write}_page interface; all 'oob_required' parameters are left unused for now. The next patch will set the parameter properly in the nand_base.c callers, and follow-up patches will make use of 'oob_required' in some of the callee functions. Note that currently, there is no harm in ignoring the 'oob_required' parameter and *always* utilizing nand_chip.oob_poi, but there can be performance/complexity/design benefits from avoiding filling oob_poi in the common case. I will try to implement this for some drivers which can be ported easily. Note: I couldn't compile-test all of these easily, as some had ARCH dependencies. [dwmw2: Merge later 1/0 vs. true/false cleanup] Signed-off-by: Brian Norris Reviewed-by: Shmulik Ladkani Acked-by: Jiandong Zheng Acked-by: Mike Dunn Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/atmel_nand.c | 5 +-- drivers/mtd/nand/bcm_umi_bch.c | 10 +++--- drivers/mtd/nand/bcm_umi_nand.c | 2 +- drivers/mtd/nand/bf5xx_nand.c | 4 +-- drivers/mtd/nand/cafe_nand.c | 13 +++++--- drivers/mtd/nand/denali.c | 8 ++--- drivers/mtd/nand/docg4.c | 12 ++++---- drivers/mtd/nand/fsl_elbc_nand.c | 11 +++---- drivers/mtd/nand/fsl_ifc_nand.c | 10 +++--- drivers/mtd/nand/fsmc_nand.c | 3 +- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 8 ++--- drivers/mtd/nand/nand_base.c | 56 +++++++++++++++++++++------------- drivers/mtd/nand/pxa3xx_nand.c | 5 +-- drivers/mtd/nand/sh_flctl.c | 4 +-- include/linux/mtd/nand.h | 11 ++++--- 15 files changed, 90 insertions(+), 72 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 9a7876e8de4..97ac6712bb1 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -324,9 +324,10 @@ static int atmel_nand_calculate(struct mtd_info *mtd, * mtd: mtd info structure * chip: nand chip info structure * buf: buffer to store read data + * oob_required: caller expects OOB data read to chip->oob_poi */ -static int atmel_nand_read_page(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) +static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; diff --git a/drivers/mtd/nand/bcm_umi_bch.c b/drivers/mtd/nand/bcm_umi_bch.c index f8472b49567..5914bb32e00 100644 --- a/drivers/mtd/nand/bcm_umi_bch.c +++ b/drivers/mtd/nand/bcm_umi_bch.c @@ -22,9 +22,9 @@ /* ---- Private Function Prototypes -------------------------------------- */ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page); + struct nand_chip *chip, uint8_t *buf, int oob_required, int page); static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf); + struct nand_chip *chip, const uint8_t *buf, int oob_required); /* ---- Private Variables ------------------------------------------------ */ @@ -103,11 +103,12 @@ static struct nand_ecclayout nand_hw_eccoob_4096 = { * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data +* @oob_required: caller expects OOB data read to chip->oob_poi * ***************************************************************************/ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t * buf, - int page) + int oob_required, int page) { int sectorIdx = 0; int eccsize = chip->ecc.size; @@ -190,10 +191,11 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer +* @oob_required: must write chip->oob_poi to OOB * ***************************************************************************/ static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, const uint8_t *buf, int oob_required) { int sectorIdx = 0; int eccsize = chip->ecc.size; diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index 7134adfa108..c855e7cd337 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -341,7 +341,7 @@ static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf, * for MLC parts which may have permanently stuck bits. */ struct nand_chip *chip = mtd->priv; - int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0); + int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0, 0); if (ret < 0) return -EFAULT; else { diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index d7b86b925de..3f1c18599cb 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -558,7 +558,7 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, } static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { bf5xx_nand_read_buf(mtd, buf, mtd->writesize); bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -567,7 +567,7 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip } static void bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { bf5xx_nand_write_buf(mtd, buf, mtd->writesize); bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 75fb77b96ef..3a6c88d69bc 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -375,12 +375,13 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi * * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. */ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct cafe_priv *cafe = mtd->priv; unsigned int max_bitflips = 0; @@ -520,7 +521,8 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = { static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, + const uint8_t *buf, int oob_required) { struct cafe_priv *cafe = mtd->priv; @@ -532,16 +534,17 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd, } static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) + const uint8_t *buf, int oob_required, int page, + int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, oob_required); else - chip->ecc.write_page(mtd, chip, buf); + chip->ecc.write_page(mtd, chip, buf, oob_required); /* * Cached progamming disabled for now, Not sure if its worth the diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 1b346474dba..a54c1861194 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1087,7 +1087,7 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, * by write_page above. * */ static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { /* for regular page writes, we let HW handle all the ECC * data written to the device. */ @@ -1099,7 +1099,7 @@ static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, * write_page() function above. */ static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { /* for raw page writes, we want to disable ECC and simply write whatever data is in the buffer. */ @@ -1122,7 +1122,7 @@ static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, } static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { unsigned int max_bitflips; struct denali_nand_info *denali = mtd_to_denali(mtd); @@ -1175,7 +1175,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, } static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct denali_nand_info *denali = mtd_to_denali(mtd); diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 1540de2ad39..1f8485d7500 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -787,13 +787,13 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand, static int docg4_read_page_raw(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { return read_page(mtd, nand, buf, page, false); } static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { return read_page(mtd, nand, buf, page, true); } @@ -953,13 +953,13 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *nand, } static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { return write_page(mtd, nand, buf, false); } static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { return write_page(mtd, nand, buf, true); } @@ -1003,7 +1003,7 @@ static int __init read_factory_bbt(struct mtd_info *mtd) return -ENOMEM; read_page_prologue(mtd, g4_addr); - status = docg4_read_page(mtd, nand, buf, DOCG4_FACTORY_BBT_PAGE); + status = docg4_read_page(mtd, nand, buf, 0, DOCG4_FACTORY_BBT_PAGE); if (status) goto exit; @@ -1080,7 +1080,7 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) /* write first page of block */ write_page_prologue(mtd, g4_addr); - docg4_write_page(mtd, nand, buf); + docg4_write_page(mtd, nand, buf, 1); ret = pageprog(mtd); if (!ret) mtd->ecc_stats.badblocks++; diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 4d995875d36..11e34010272 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -746,10 +746,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) return 0; } -static int fsl_elbc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, - int page) +static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { struct fsl_elbc_mtd *priv = chip->priv; struct fsl_lbc_ctrl *ctrl = priv->ctrl; @@ -767,9 +765,8 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, /* ECC will be calculated automatically, and errors will be detected in * waitfunc. */ -static void fsl_elbc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf) +static void fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index dffd2fa353a..c085df3d816 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -698,9 +698,8 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) return nand_fsr | NAND_STATUS_WP; } -static int fsl_ifc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page) +static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { struct fsl_ifc_mtd *priv = chip->priv; struct fsl_ifc_ctrl *ctrl = priv->ctrl; @@ -721,9 +720,8 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, /* ECC will be calculated automatically, and errors will be detected in * waitfunc. */ -static void fsl_ifc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf) +static void fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { fsl_ifc_write_buf(mtd, buf, mtd->writesize); fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize); diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 6bf59fdde26..38d26240d8b 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -692,6 +692,7 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller expects OOB data read to chip->oob_poi * @page: page number to read * * This routine is needed for fsmc version 8 as reading from NAND chip has to be @@ -701,7 +702,7 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, * max of 8 bits) */ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct fsmc_nand_data *host = container_of(mtd, struct fsmc_nand_data, mtd); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 9ec51cec2e1..d85a2c1fad5 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -842,7 +842,7 @@ static void block_mark_swapping(struct gpmi_nand_data *this, } static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -928,8 +928,8 @@ exit_nfc: return ret; } -static void gpmi_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) +static void gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { struct gpmi_nand_data *this = chip->priv; struct bch_geometry *nfc_geo = &this->bch_geometry; @@ -1309,7 +1309,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Write the first page of the current stride. */ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); - chip->ecc.write_page_raw(mtd, chip, buffer); + chip->ecc.write_page_raw(mtd, chip, buffer, 0); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); /* Wait for the write to finish. */ diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5ec4d2c01b8..6d4894acb56 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1066,12 +1066,13 @@ EXPORT_SYMBOL(nand_lock); * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { chip->read_buf(mtd, buf, mtd->writesize); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -1083,13 +1084,14 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * We need a special oob layout and handling even when OOB isn't used. */ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1126,10 +1128,11 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read */ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1140,7 +1143,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint32_t *eccpos = chip->ecc.layout->eccpos; unsigned int max_bitflips = 0; - chip->ecc.read_page_raw(mtd, chip, buf, page); + chip->ecc.read_page_raw(mtd, chip, buf, 1, page); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -1263,12 +1266,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Not for syndrome calculating ECC controllers which need a special oob layout. */ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1311,6 +1315,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * Hardware ECC for large page chips, require OOB to be read first. For this @@ -1320,7 +1325,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * the data area, by overwriting the NAND manufacturer bad block markings. */ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1362,13 +1367,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi * @page: page number to read * * The hw generator calculates the error syndrome automatically. Therefore we * need a special oob layout and handling. */ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1514,14 +1520,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, * the read methods return max bitflips per ecc step. */ if (unlikely(ops->mode == MTD_OPS_RAW)) - ret = chip->ecc.read_page_raw(mtd, chip, - bufpoi, page); + ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, + 1, page); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi, - page); + 1, page); if (ret < 0) { if (!aligned) /* Invalidate page cache */ @@ -1913,11 +1919,12 @@ out: * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * Not for syndrome calculating ECC controllers, which use a special oob layout. */ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -1928,12 +1935,13 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * We need a special oob layout and handling even when ECC isn't checked. */ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1967,9 +1975,10 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -1985,7 +1994,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, 1); } /** @@ -1993,9 +2002,10 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB */ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2021,12 +2031,14 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * @oob_required: must write chip->oob_poi to OOB * * The hw generator calculates the error syndrome automatically. Therefore we * need a special oob layout and handling. */ static void nand_write_page_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -2065,21 +2077,23 @@ static void nand_write_page_syndrome(struct mtd_info *mtd, * @mtd: MTD device structure * @chip: NAND chip descriptor * @buf: the data to write + * @oob_required: must write chip->oob_poi to OOB * @page: page number to write * @cached: cached programming * @raw: use _raw version of write_page */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw) + const uint8_t *buf, int oob_required, int page, + int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) - chip->ecc.write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf, oob_required); else - chip->ecc.write_page(mtd, chip, buf); + chip->ecc.write_page(mtd, chip, buf, oob_required); /* * Cached progamming disabled for now. Not sure if it's worth the @@ -2261,7 +2275,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, memset(chip->oob_poi, 0xff, mtd->oobsize); } - ret = chip->write_page(mtd, chip, wbuf, page, cached, + ret = chip->write_page(mtd, chip, wbuf, 1, page, cached, (ops->mode == MTD_OPS_RAW)); if (ret) break; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 36a32a0d3b9..252aaefcacf 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -682,14 +682,15 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, } static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, const uint8_t *buf) + struct nand_chip *chip, const uint8_t *buf, int oob_required) { chip->write_buf(mtd, buf, mtd->writesize); chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, - struct nand_chip *chip, uint8_t *buf, int page) + struct nand_chip *chip, uint8_t *buf, int oob_required, + int page) { struct pxa3xx_nand_host *host = mtd->priv; struct pxa3xx_nand_info *info = host->info_data; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 3f0788fad66..aa9b8a5e0b8 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -344,7 +344,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va } static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, int page) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; @@ -366,7 +366,7 @@ static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, } static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf) + const uint8_t *buf, int oob_required) { int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 94a6679bfc2..c7755f455c8 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -360,15 +360,15 @@ struct nand_ecc_ctrl { int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc); int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page); + uint8_t *buf, int oob_required, int page); void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf); + const uint8_t *buf, int oob_required); int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, - uint8_t *buf, int page); + uint8_t *buf, int oob_required, int page); int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offs, uint32_t len, uint8_t *buf); void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf); + const uint8_t *buf, int oob_required); int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, int page); int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, @@ -504,7 +504,8 @@ struct nand_chip { int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, - const uint8_t *buf, int page, int cached, int raw); + const uint8_t *buf, int oob_required, int page, + int cached, int raw); int chip_delay; unsigned int options; -- cgit v1.2.3 From e47f3db4587c5486d3d5838fd4bef4f9c3b12205 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 2 May 2012 10:14:56 -0700 Subject: mtd: nand: pass proper 'oob_required' parameter We now have an interface for notifying the nand_ecc_ctrl functions when OOB data must be returned to the upper layers and when it may be left untouched. This patch fills in the 'oob_required' parameter properly from nand_do_{read,write}_ops. When utilized properly in the lower layers, this parameter can improve performance and/or reduce complexity for NAND HW and SW that can simply avoid transferring the OOB data. Signed-off-by: Brian Norris Reviewed-by: Shmulik Ladkani Acked-by: Jiandong Zheng Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6d4894acb56..464b742431e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1480,7 +1480,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - int chipnr, page, realpage, col, bytes, aligned; + int chipnr, page, realpage, col, bytes, aligned, oob_required; struct nand_chip *chip = mtd->priv; struct mtd_ecc_stats stats; int ret = 0; @@ -1504,6 +1504,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf = ops->datbuf; oob = ops->oobbuf; + oob_required = oob ? 1 : 0; while (1) { bytes = min(mtd->writesize - col, readlen); @@ -1521,13 +1522,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, */ if (unlikely(ops->mode == MTD_OPS_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, - 1, page); + oob_required, + page); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi, - 1, page); + oob_required, page); if (ret < 0) { if (!aligned) /* Invalidate page cache */ @@ -1554,7 +1556,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf += bytes; if (unlikely(oob)) { - int toread = min(oobreadlen, max_oobsize); if (toread) { @@ -2213,6 +2214,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; + int oob_required = oob ? 1 : 0; ops->retlen = 0; if (!writelen) @@ -2275,8 +2277,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, memset(chip->oob_poi, 0xff, mtd->oobsize); } - ret = chip->write_page(mtd, chip, wbuf, 1, page, cached, - (ops->mode == MTD_OPS_RAW)); + ret = chip->write_page(mtd, chip, wbuf, oob_required, page, + cached, (ops->mode == MTD_OPS_RAW)); if (ret) break; -- cgit v1.2.3 From d112dc7fa65882279434622a40cbe7677a780c46 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 2 May 2012 10:15:00 -0700 Subject: mtd: eLBC NAND: utilize oob_required parameter Don't read OOB if the caller doesn't require it. Signed-off-by: Brian Norris Reviewed-by: Scott Wood Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_elbc_nand.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 11e34010272..78429380611 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -754,7 +754,8 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; fsl_elbc_read_buf(mtd, buf, mtd->writesize); - fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) mtd->ecc_stats.failed++; -- cgit v1.2.3 From a6976cdfe69756436ed8c22b7a03e47a7efe36fb Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 2 May 2012 10:15:01 -0700 Subject: mtd: IFC NAND: utilize oob_required parameter Don't read OOB if the caller doesn't require it. Signed-off-by: Brian Norris Reviewed-by: Scott Wood Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_ifc_nand.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index c085df3d816..9602c1b7e27 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -706,7 +706,8 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip, struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; fsl_ifc_read_buf(mtd, buf, mtd->writesize); - fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + fsl_ifc_read_buf(mtd, chip->oob_poi, mtd->oobsize); if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) dev_err(priv->dev, "NAND Flash ECC Uncorrectable Error\n"); -- cgit v1.2.3 From 7725cc85932bd02dd12c23108e0ef748c551ccba Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 2 May 2012 10:15:02 -0700 Subject: mtd: gpmi-nand: utilize oob_requested parameter Don't read OOB if the caller didn't request it. Signed-off-by: Brian Norris Acked-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index d85a2c1fad5..7e8a035d444 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -908,22 +908,25 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, mtd->ecc_stats.corrected += corrected; } - /* - * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() for - * details about our policy for delivering the OOB. - * - * We fill the caller's buffer with set bits, and then copy the block - * mark to th caller's buffer. Note that, if block mark swapping was - * necessary, it has already been done, so we can rely on the first - * byte of the auxiliary buffer to contain the block mark. - */ - memset(chip->oob_poi, ~0, mtd->oobsize); - chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + if (oob_required) { + /* + * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() + * for details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the + * block mark to th caller's buffer. Note that, if block mark + * swapping was necessary, it has already been done, so we can + * rely on the first byte of the auxiliary buffer to contain + * the block mark. + */ + memset(chip->oob_poi, ~0, mtd->oobsize); + chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; - read_page_swap_end(this, buf, mtd->writesize, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); + read_page_swap_end(this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + } exit_nfc: return ret; } -- cgit v1.2.3 From 279f08d4ef6548be4e2fa638245adebc77436e54 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 2 May 2012 10:15:03 -0700 Subject: mtd: nand: utilize oob_required parameter Don't read/write OOB if the caller doesn't require it. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 464b742431e..14e9b32ed8c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1075,7 +1075,8 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { chip->read_buf(mtd, buf, mtd->writesize); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } @@ -1928,7 +1929,8 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { chip->write_buf(mtd, buf, mtd->writesize); - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + if (oob_required) + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } /** -- cgit v1.2.3 From e10db1f00a5e3c2ec04d7fe26c7444dc55a59b19 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 4 May 2012 21:42:05 -0400 Subject: mtd: gpmi: add device tree support to gpmi-nand This patch just adds the DT support to gpmi-nand. Signed-off-by: Huang Shijie Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- .../devicetree/bindings/mtd/gpmi-nand.txt | 33 ++++++ drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 7 +- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 126 ++++++++++----------- drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 4 +- include/linux/mtd/gpmi-nand.h | 8 +- 5 files changed, 105 insertions(+), 73 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/gpmi-nand.txt diff --git a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt new file mode 100644 index 00000000000..1a5bbd346d2 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt @@ -0,0 +1,33 @@ +* Freescale General-Purpose Media Interface (GPMI) + +The GPMI nand controller provides an interface to control the +NAND flash chips. We support only one NAND chip now. + +Required properties: + - compatible : should be "fsl,-gpmi-nand" + - reg : should contain registers location and length for gpmi and bch. + - reg-names: Should contain the reg names "gpmi-nand" and "bch" + - interrupts : The first is the DMA interrupt number for GPMI. + The second is the BCH interrupt number. + - interrupt-names : The interrupt names "gpmi-dma", "bch"; + - fsl,gpmi-dma-channel : Should contain the dma channel it uses. + +The device tree may optionally contain sub-nodes describing partitions of the +address space. See partition.txt for more detail. + +Examples: + +gpmi-nand@8000c000 { + compatible = "fsl,imx28-gpmi-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x8000c000 2000>, <0x8000a000 2000>; + reg-names = "gpmi-nand", "bch"; + interrupts = <88>, <41>; + interrupt-names = "gpmi-dma", "bch"; + fsl,gpmi-dma-channel = <4>; + + partition@0 { + ... + }; +}; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index fa5200b8c89..5c55c717fe1 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -256,11 +256,12 @@ static unsigned int ns_to_cycles(unsigned int time, return max(k, min); } +#define DEF_MIN_PROP_DELAY 5 +#define DEF_MAX_PROP_DELAY 9 /* Apply timing to current hardware conditions. */ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, struct gpmi_nfc_hardware_timing *hw) { - struct gpmi_nand_platform_data *pdata = this->pdata; struct timing_threshod *nfc = &timing_default_threshold; struct nand_chip *nand = &this->nand; struct nand_timing target = this->timing; @@ -277,8 +278,8 @@ static int gpmi_nfc_compute_hardware_timing(struct gpmi_nand_data *this, int ideal_sample_delay_in_ns; unsigned int sample_delay_factor; int tEYE; - unsigned int min_prop_delay_in_ns = pdata->min_prop_delay_in_ns; - unsigned int max_prop_delay_in_ns = pdata->max_prop_delay_in_ns; + unsigned int min_prop_delay_in_ns = DEF_MIN_PROP_DELAY; + unsigned int max_prop_delay_in_ns = DEF_MAX_PROP_DELAY; /* * If there are multiple chips, we need to relax the timings to allow diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 7e8a035d444..d9dc4c874ff 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "gpmi-nand.h" /* add our owner bbt descriptor */ @@ -386,7 +388,7 @@ static void release_bch_irq(struct gpmi_nand_data *this) static bool gpmi_dma_filter(struct dma_chan *chan, void *param) { struct gpmi_nand_data *this = param; - struct resource *r = this->private; + int dma_channel = (int)this->private; if (!mxs_dma_is_apbh(chan)) return false; @@ -398,7 +400,7 @@ static bool gpmi_dma_filter(struct dma_chan *chan, void *param) * for mx28 : MX28_DMA_GPMI0 ~ MX28_DMA_GPMI7 * (These eight channels share the same IRQ!) */ - if (r->start <= chan->chan_id && chan->chan_id <= r->end) { + if (dma_channel == chan->chan_id) { chan->private = &this->dma_data; return true; } @@ -418,57 +420,45 @@ static void release_dma_channels(struct gpmi_nand_data *this) static int __devinit acquire_dma_channels(struct gpmi_nand_data *this) { struct platform_device *pdev = this->pdev; - struct gpmi_nand_platform_data *pdata = this->pdata; - struct resources *res = &this->resources; - struct resource *r, *r_dma; - unsigned int i; + struct resource *r_dma; + struct device_node *dn; + int dma_channel; + unsigned int ret; + struct dma_chan *dma_chan; + dma_cap_mask_t mask; + + /* dma channel, we only use the first one. */ + dn = pdev->dev.of_node; + ret = of_property_read_u32(dn, "fsl,gpmi-dma-channel", &dma_channel); + if (ret) { + pr_err("unable to get DMA channel from dt.\n"); + goto acquire_err; + } + this->private = (void *)dma_channel; - r = platform_get_resource_byname(pdev, IORESOURCE_DMA, - GPMI_NAND_DMA_CHANNELS_RES_NAME); + /* gpmi dma interrupt */ r_dma = platform_get_resource_byname(pdev, IORESOURCE_IRQ, GPMI_NAND_DMA_INTERRUPT_RES_NAME); - if (!r || !r_dma) { + if (!r_dma) { pr_err("Can't get resource for DMA\n"); - return -ENXIO; + goto acquire_err; } + this->dma_data.chan_irq = r_dma->start; - /* used in gpmi_dma_filter() */ - this->private = r; - - for (i = r->start; i <= r->end; i++) { - struct dma_chan *dma_chan; - dma_cap_mask_t mask; + /* request dma channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); - if (i - r->start >= pdata->max_chip_count) - break; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - /* get the DMA interrupt */ - if (r_dma->start == r_dma->end) { - /* only register the first. */ - if (i == r->start) - this->dma_data.chan_irq = r_dma->start; - else - this->dma_data.chan_irq = NO_IRQ; - } else - this->dma_data.chan_irq = r_dma->start + (i - r->start); - - dma_chan = dma_request_channel(mask, gpmi_dma_filter, this); - if (!dma_chan) - goto acquire_err; - - /* fill the first empty item */ - this->dma_chans[i - r->start] = dma_chan; + dma_chan = dma_request_channel(mask, gpmi_dma_filter, this); + if (!dma_chan) { + pr_err("dma_request_channel failed.\n"); + goto acquire_err; } - res->dma_low_channel = r->start; - res->dma_high_channel = i; + this->dma_chans[0] = dma_chan; return 0; acquire_err: - pr_err("Can't acquire DMA channel %u\n", i); release_dma_channels(this); return -EINVAL; } @@ -1465,9 +1455,9 @@ void gpmi_nfc_exit(struct gpmi_nand_data *this) static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) { - struct gpmi_nand_platform_data *pdata = this->pdata; struct mtd_info *mtd = &this->mtd; struct nand_chip *chip = &this->nand; + struct mtd_part_parser_data ppdata = {}; int ret; /* init current chip */ @@ -1505,14 +1495,14 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) if (ret) goto err_out; - ret = nand_scan(mtd, pdata->max_chip_count); + ret = nand_scan(mtd, 1); if (ret) { pr_err("Chip scan failed\n"); goto err_out; } - ret = mtd_device_parse_register(mtd, NULL, NULL, - pdata->partitions, pdata->partition_count); + ppdata.of_node = this->pdev->dev.of_node; + ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); if (ret) goto err_out; return 0; @@ -1522,12 +1512,37 @@ err_out: return ret; } +static const struct platform_device_id gpmi_ids[] = { + { .name = "imx23-gpmi-nand", .driver_data = IS_MX23, }, + { .name = "imx28-gpmi-nand", .driver_data = IS_MX28, }, + {}, +}; + +static const struct of_device_id gpmi_nand_id_table[] = { + { + .compatible = "fsl,imx23-gpmi-nand", + .data = (void *)&gpmi_ids[IS_MX23] + }, { + .compatible = "fsl,imx28-gpmi-nand", + .data = (void *)&gpmi_ids[IS_MX28] + }, {} +}; +MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); + static int __devinit gpmi_nand_probe(struct platform_device *pdev) { - struct gpmi_nand_platform_data *pdata = pdev->dev.platform_data; struct gpmi_nand_data *this; + const struct of_device_id *of_id; int ret; + of_id = of_match_device(gpmi_nand_id_table, &pdev->dev); + if (of_id) { + pdev->id_entry = of_id->data; + } else { + pr_err("Failed to find the right device id.\n"); + return -ENOMEM; + } + this = kzalloc(sizeof(*this), GFP_KERNEL); if (!this) { pr_err("Failed to allocate per-device memory\n"); @@ -1537,13 +1552,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, this); this->pdev = pdev; this->dev = &pdev->dev; - this->pdata = pdata; - - if (pdata->platform_init) { - ret = pdata->platform_init(); - if (ret) - goto platform_init_error; - } ret = acquire_resources(this); if (ret) @@ -1561,7 +1569,6 @@ static int __devinit gpmi_nand_probe(struct platform_device *pdev) exit_nfc_init: release_resources(this); -platform_init_error: exit_acquire_resources: platform_set_drvdata(pdev, NULL); kfree(this); @@ -1579,19 +1586,10 @@ static int __exit gpmi_nand_remove(struct platform_device *pdev) return 0; } -static const struct platform_device_id gpmi_ids[] = { - { - .name = "imx23-gpmi-nand", - .driver_data = IS_MX23, - }, { - .name = "imx28-gpmi-nand", - .driver_data = IS_MX28, - }, {}, -}; - static struct platform_driver gpmi_nand_driver = { .driver = { .name = "gpmi-nand", + .of_match_table = gpmi_nand_id_table, }, .probe = gpmi_nand_probe, .remove = __exit_p(gpmi_nand_remove), diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index ec6180d4ff8..12fdd7767c1 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -266,8 +266,8 @@ extern int gpmi_read_page(struct gpmi_nand_data *, #define STATUS_UNCORRECTABLE 0xfe /* Use the platform_id to distinguish different Archs. */ -#define IS_MX23 0x1 -#define IS_MX28 0x2 +#define IS_MX23 0x0 +#define IS_MX28 0x1 #define GPMI_IS_MX23(x) ((x)->pdev->id_entry->driver_data == IS_MX23) #define GPMI_IS_MX28(x) ((x)->pdev->id_entry->driver_data == IS_MX28) #endif diff --git a/include/linux/mtd/gpmi-nand.h b/include/linux/mtd/gpmi-nand.h index 69b6dbf46b5..ed3c4e09f3d 100644 --- a/include/linux/mtd/gpmi-nand.h +++ b/include/linux/mtd/gpmi-nand.h @@ -23,12 +23,12 @@ #define GPMI_NAND_RES_SIZE 6 /* Resource names for the GPMI NAND driver. */ -#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "GPMI NAND GPMI Registers" +#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" #define GPMI_NAND_GPMI_INTERRUPT_RES_NAME "GPMI NAND GPMI Interrupt" -#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "GPMI NAND BCH Registers" -#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "GPMI NAND BCH Interrupt" +#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" +#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" #define GPMI_NAND_DMA_CHANNELS_RES_NAME "GPMI NAND DMA Channels" -#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "GPMI NAND DMA Interrupt" +#define GPMI_NAND_DMA_INTERRUPT_RES_NAME "gpmi-dma" /** * struct gpmi_nand_platform_data - GPMI NAND driver platform data. -- cgit v1.2.3 From 9013bb40ae8e97d579015a3dc96a10aa9c625757 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 4 May 2012 21:42:06 -0400 Subject: mtd: gpmi: add gpmi support for mx6q add gpmi support for mx6q. Signed-off-by: Huang Shijie Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 2 +- drivers/mtd/nand/gpmi-nand/bch-regs.h | 42 ++++++++++++++++++++++++++-------- drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 11 +++++---- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 4 ++++ drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 2 ++ 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7d17cecad69..bf0a28d513d 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -440,7 +440,7 @@ config MTD_NAND_NANDSIM config MTD_NAND_GPMI_NAND bool "GPMI NAND Flash Controller driver" - depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28) + depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q) help Enables NAND Flash support for IMX23 or IMX28. The GPMI controller is very powerful, with the help of BCH diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h index 4effb8c579d..a0924515c39 100644 --- a/drivers/mtd/nand/gpmi-nand/bch-regs.h +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h @@ -51,15 +51,26 @@ #define BP_BCH_FLASH0LAYOUT0_ECC0 12 #define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) -#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \ - (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0) +#define MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0 (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) +#define BF_BCH_FLASH0LAYOUT0_ECC0(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT0_ECC0) \ + & MX6Q_BM_BCH_FLASH0LAYOUT0_ECC0) \ + : (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) \ + & BM_BCH_FLASH0LAYOUT0_ECC0) \ + ) #define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 #define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) -#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \ - (((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\ - & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) \ + ) #define HW_BCH_FLASH0LAYOUT1 0x00000090 @@ -72,13 +83,24 @@ #define BP_BCH_FLASH0LAYOUT1_ECCN 12 #define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) -#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \ - (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN) +#define MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN 11 +#define MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN (0x1f << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) +#define BF_BCH_FLASH0LAYOUT1_ECCN(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) << MX6Q_BP_BCH_FLASH0LAYOUT1_ECCN) \ + & MX6Q_BM_BCH_FLASH0LAYOUT1_ECCN) \ + : (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) \ + & BM_BCH_FLASH0LAYOUT1_ECCN) \ + ) #define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 #define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) -#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \ - (((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ - & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0x3ff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v, x) \ + (GPMI_IS_MX6Q(x) \ + ? (((v) >> 2) & MX6Q_BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + : ((v) & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + ) #endif diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 5c55c717fe1..a1f43329ad4 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -224,13 +224,13 @@ int bch_set_geometry(struct gpmi_nand_data *this) /* Configure layout 0. */ writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) - | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) - | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size), + | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this), r->bch_regs + HW_BCH_FLASH0LAYOUT0); writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) - | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) - | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size), + | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) + | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this), r->bch_regs + HW_BCH_FLASH0LAYOUT1); /* Set *all* chip selects to use layout 0. */ @@ -805,7 +805,8 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) if (GPMI_IS_MX23(this)) { mask = MX23_BM_GPMI_DEBUG_READY0 << chip; reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); - } else if (GPMI_IS_MX28(this)) { + } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6Q(this)) { + /* MX28 shares the same R/B register as MX6Q. */ mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); reg = readl(r->gpmi_regs + HW_GPMI_STAT); } else diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index d9dc4c874ff..519e4306e35 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1515,6 +1515,7 @@ err_out: static const struct platform_device_id gpmi_ids[] = { { .name = "imx23-gpmi-nand", .driver_data = IS_MX23, }, { .name = "imx28-gpmi-nand", .driver_data = IS_MX28, }, + { .name = "imx6q-gpmi-nand", .driver_data = IS_MX6Q, }, {}, }; @@ -1525,6 +1526,9 @@ static const struct of_device_id gpmi_nand_id_table[] = { }, { .compatible = "fsl,imx28-gpmi-nand", .data = (void *)&gpmi_ids[IS_MX28] + }, { + .compatible = "fsl,imx6q-gpmi-nand", + .data = (void *)&gpmi_ids[IS_MX6Q] }, {} }; MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h index 12fdd7767c1..ce5daa16092 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h @@ -268,6 +268,8 @@ extern int gpmi_read_page(struct gpmi_nand_data *, /* Use the platform_id to distinguish different Archs. */ #define IS_MX23 0x0 #define IS_MX28 0x1 +#define IS_MX6Q 0x2 #define GPMI_IS_MX23(x) ((x)->pdev->id_entry->driver_data == IS_MX23) #define GPMI_IS_MX28(x) ((x)->pdev->id_entry->driver_data == IS_MX28) +#define GPMI_IS_MX6Q(x) ((x)->pdev->id_entry->driver_data == IS_MX6Q) #endif -- cgit v1.2.3 From f4d0b3557582808d5ce9f868e9dfd98f0e769e82 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 7 May 2012 18:25:00 +0300 Subject: jffs2: remove lock_super We do not need 'lock_super()'/'unlock_super()' in JFFS2 - kill them. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/super.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 66d44560f75..3f1c90c2bdf 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -67,15 +67,12 @@ static void jffs2_write_super(struct super_block *sb) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); - lock_super(sb); sb->s_dirt = 0; if (!(sb->s_flags & MS_RDONLY)) { jffs2_dbg(1, "%s()\n", __func__); jffs2_flush_wbuf_gc(c, 0); } - - unlock_super(sb); } static const char *jffs2_compr_name(unsigned int compr) -- cgit v1.2.3 From c3c4a36979c8b68cc35643497e83c7383cd07955 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 7 May 2012 18:45:39 +0300 Subject: jffs2: remove unnecessary GC pass on umount We do not need to call 'jffs2_write_super()' on unmount. This function causes a GC pass to make sure the current contents is pushed out with the data which we already have on the media. But this is not needed on unmount and only slows unmount down unnecessarily. It is enough to just sync the write-buffer. This call was added by one of the generic VFS rework patch-sets, see 8c85e125124a473d6f3e9bb187b0b84207f81d91. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/super.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 3f1c90c2bdf..1613cd28304 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -333,9 +333,6 @@ static void jffs2_put_super (struct super_block *sb) jffs2_dbg(2, "%s()\n", __func__); - if (sb->s_dirt) - jffs2_write_super(sb); - mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); mutex_unlock(&c->alloc_sem); -- cgit v1.2.3 From e832579fd100eb4f9658bdfefd61caf86a6cbff1 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 7 May 2012 18:51:54 +0300 Subject: jffs2: remove unnecessary GC pass on sync We do not need to call 'jffs2_write_super()' on sync. This function causes a GC pass to make sure the current contents is pushed out with the data which we already have on the media. But this is not needed on unmount and only slows sync down unnecessarily. It is enough to just sync the write-buffer. This call was added by one of the generic VFS rework patch-sets, see d579ed00aa96a7f7486978540a0d7cecaff742ae. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/super.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 1613cd28304..b36aa950d2a 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -112,8 +112,6 @@ static int jffs2_sync_fs(struct super_block *sb, int wait) { struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); - jffs2_write_super(sb); - mutex_lock(&c->alloc_sem); jffs2_flush_wbuf_pad(c); mutex_unlock(&c->alloc_sem); -- cgit v1.2.3 From 208b14e507c00ff7f108e1a388dd3d8cc805a443 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 7 May 2012 19:02:46 +0300 Subject: jffs2: get rid of jffs2_sync_super Currently JFFS2 file-system maps the VFS "superblock" abstraction to the write-buffer. Namely, it uses VFS services to synchronize the write-buffer periodically. The whole "superblock write-out" VFS infrastructure is served by the 'sync_supers()' kernel thread, which wakes up every 5 (by default) seconds and writes out all dirty superblock using the '->write_super()' call-back. But the problem with this thread is that it wastes power by waking up the system every 5 seconds no matter what. So we want to kill it completely and thus, we need to make file-systems to stop using the '->write_super' VFS service, and then remove it together with the kernel thread. This patch switches the JFFS2 write-buffer management from '->write_super()'/'->s_dirt' to a delayed work. Instead of setting the 's_dirt' flag we just schedule a delayed work for synchronizing the write-buffer. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/jffs2_fs_sb.h | 4 ++++ fs/jffs2/os-linux.h | 7 ++----- fs/jffs2/super.c | 13 ------------ fs/jffs2/wbuf.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 58 insertions(+), 21 deletions(-) diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 0d00bf26923..413ef89c2d1 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -133,6 +133,10 @@ struct jffs2_sb_info { struct jffs2_inodirty *wbuf_inodes; struct rw_semaphore wbuf_sem; /* Protects the write buffer */ + struct delayed_work wbuf_dwork; /* write-buffer write-out work */ + int wbuf_queued; /* non-zero delayed work is queued */ + spinlock_t wbuf_dwork_lock; /* protects wbuf_dwork and and wbuf_queued */ + unsigned char *oobbuf; int oobavail; /* How many bytes are available for JFFS2 in OOB */ #endif diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 1cd3aec9d9a..bcd983d7e7f 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -95,6 +95,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) #define jffs2_ubivol(c) (0) #define jffs2_ubivol_setup(c) (0) #define jffs2_ubivol_cleanup(c) do {} while (0) +#define jffs2_dirty_trigger(c) do {} while (0) #else /* NAND and/or ECC'd NOR support present */ @@ -135,14 +136,10 @@ void jffs2_ubivol_cleanup(struct jffs2_sb_info *c); #define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE)) int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c); void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c); +void jffs2_dirty_trigger(struct jffs2_sb_info *c); #endif /* WRITEBUFFER */ -static inline void jffs2_dirty_trigger(struct jffs2_sb_info *c) -{ - OFNI_BS_2SFFJ(c)->s_dirt = 1; -} - /* background.c */ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index b36aa950d2a..61ea41389f9 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -63,18 +63,6 @@ static void jffs2_i_init_once(void *foo) inode_init_once(&f->vfs_inode); } -static void jffs2_write_super(struct super_block *sb) -{ - struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); - - sb->s_dirt = 0; - - if (!(sb->s_flags & MS_RDONLY)) { - jffs2_dbg(1, "%s()\n", __func__); - jffs2_flush_wbuf_gc(c, 0); - } -} - static const char *jffs2_compr_name(unsigned int compr) { switch (compr) { @@ -263,7 +251,6 @@ static const struct super_operations jffs2_super_operations = .alloc_inode = jffs2_alloc_inode, .destroy_inode =jffs2_destroy_inode, .put_super = jffs2_put_super, - .write_super = jffs2_write_super, .statfs = jffs2_statfs, .remount_fs = jffs2_remount_fs, .evict_inode = jffs2_evict_inode, diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 74d9be19df3..6f4529d3697 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "nodelist.h" @@ -85,7 +86,7 @@ static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) { struct jffs2_inodirty *new; - /* Mark the superblock dirty so that kupdated will flush... */ + /* Schedule delayed write-buffer write-out */ jffs2_dirty_trigger(c); if (jffs2_wbuf_pending_for_ino(c, ino)) @@ -1148,6 +1149,47 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock * return 1; } +static struct jffs2_sb_info *work_to_sb(struct work_struct *work) +{ + struct delayed_work *dwork; + + dwork = container_of(work, struct delayed_work, work); + return container_of(dwork, struct jffs2_sb_info, wbuf_dwork); +} + +static void delayed_wbuf_sync(struct work_struct *work) +{ + struct jffs2_sb_info *c = work_to_sb(work); + struct super_block *sb = OFNI_BS_2SFFJ(c); + + spin_lock(&c->wbuf_dwork_lock); + c->wbuf_queued = 0; + spin_unlock(&c->wbuf_dwork_lock); + + if (!(sb->s_flags & MS_RDONLY)) { + jffs2_dbg(1, "%s()\n", __func__); + jffs2_flush_wbuf_gc(c, 0); + } +} + +void jffs2_dirty_trigger(struct jffs2_sb_info *c) +{ + struct super_block *sb = OFNI_BS_2SFFJ(c); + unsigned long delay; + + if (sb->s_flags & MS_RDONLY) + return; + + spin_lock(&c->wbuf_dwork_lock); + if (!c->wbuf_queued) { + jffs2_dbg(1, "%s()\n", __func__); + delay = msecs_to_jiffies(dirty_writeback_interval * 10); + queue_delayed_work(system_long_wq, &c->wbuf_dwork, delay); + c->wbuf_queued = 1; + } + spin_unlock(&c->wbuf_dwork_lock); +} + int jffs2_nand_flash_setup(struct jffs2_sb_info *c) { struct nand_ecclayout *oinfo = c->mtd->ecclayout; @@ -1169,6 +1211,8 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) /* Initialise write buffer */ init_rwsem(&c->wbuf_sem); + spin_lock_init(&c->wbuf_dwork_lock); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); c->wbuf_pagesize = c->mtd->writesize; c->wbuf_ofs = 0xFFFFFFFF; @@ -1207,8 +1251,8 @@ int jffs2_dataflash_setup(struct jffs2_sb_info *c) { /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); - - + spin_lock_init(&c->wbuf_dwork_lock); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); c->wbuf_pagesize = c->mtd->erasesize; /* Find a suitable c->sector_size @@ -1267,6 +1311,9 @@ int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) { /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); + spin_lock_init(&c->wbuf_dwork_lock); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); + c->wbuf_pagesize = c->mtd->writesize; c->wbuf_ofs = 0xFFFFFFFF; @@ -1299,6 +1346,8 @@ int jffs2_ubivol_setup(struct jffs2_sb_info *c) { return 0; init_rwsem(&c->wbuf_sem); + spin_lock_init(&c->wbuf_dwork_lock); + INIT_DELAYED_WORK(&c->wbuf_dwork, delayed_wbuf_sync); c->wbuf_pagesize = c->mtd->writesize; c->wbuf_ofs = 0xFFFFFFFF; -- cgit v1.2.3 From 4fba37aec7e43f7c80a89425cc820622fcf07464 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Wed, 9 May 2012 04:04:54 +0530 Subject: mtd: m25p80: Add support for Winbond W25Q80BW Winbond W25Q80BW is a 8Mbit serial flash memory device. Signed-off-by: Thomas Abraham Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index eb1cc9797da..5d0d68c3fe2 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -732,6 +732,7 @@ static const struct spi_device_id m25p_ids[] = { { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, /* Catalyst / On Semiconductor -- non-JEDEC */ { "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, -- cgit v1.2.3 From 5c2ffb11d40dd967eecb45b8570a871746ba124b Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Wed, 9 May 2012 13:06:35 +0300 Subject: mtd: nand: remove 'sndcmd' parameter of 'read_oob/read_oob_raw' As of [mtd: nand: remove autoincrement 'sndcmd' code], the NAND_CMD_READ0 command is issued unconditionally. Thus, read_oob/read_oob_raw's 'sndcmd' argument is no longer needed, as well as their return code. Remove the 'sndcmd' parameter, and set the return code to 0. Signed-off-by: Shmulik Ladkani Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 4 ++-- drivers/mtd/nand/denali.c | 5 ++--- drivers/mtd/nand/docg4.c | 2 +- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 8 ++------ drivers/mtd/nand/nand_base.c | 19 +++++++------------ drivers/mtd/nand/r852.c | 9 +++------ include/linux/mtd/nand.h | 5 ++--- 7 files changed, 19 insertions(+), 33 deletions(-) diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 3a6c88d69bc..41371ba1a81 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -364,11 +364,11 @@ static int cafe_nand_write_oob(struct mtd_info *mtd, /* Don't use -- use nand_read_oob_std for now */ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return 1; + return 0; } /** * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index a54c1861194..0650aafa0dd 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1113,12 +1113,11 @@ static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, } static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { read_oob_data(mtd, chip->oob_poi, page); - return 0; /* notify NAND core to send command to - NAND device. */ + return 0; } static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index 1f8485d7500..a225e49a562 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -799,7 +799,7 @@ static int docg4_read_page(struct mtd_info *mtd, struct nand_chip *nand, } static int docg4_read_oob(struct mtd_info *mtd, struct nand_chip *nand, - int page, int sndcmd) + int page) { struct docg4_priv *doc = nand->priv; void __iomem *docptr = doc->virtadr; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 519e4306e35..95f2af848ff 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1061,7 +1061,7 @@ exit_auxiliary: * this driver. */ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { struct gpmi_nand_data *this = chip->priv; @@ -1084,11 +1084,7 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, chip->oob_poi[0] = chip->read_byte(mtd); } - /* - * Return true, indicating that the next call to this function must send - * a command. - */ - return true; + return 0; } static int diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 14e9b32ed8c..4047d7c2fee 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1645,17 +1645,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to read - * @sndcmd: flag whether to issue read command or not */ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return sndcmd; + return 0; } /** @@ -1664,10 +1660,9 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, * @mtd: mtd info structure * @chip: nand chip info structure * @page: page number to read - * @sndcmd: flag whether to issue read command or not */ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { uint8_t *buf = chip->oob_poi; int length = mtd->oobsize; @@ -1694,7 +1689,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, if (length > 0) chip->read_buf(mtd, bufpoi, length); - return 1; + return 0; } /** @@ -1831,9 +1826,9 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, while (1) { if (ops->mode == MTD_OPS_RAW) - chip->ecc.read_oob_raw(mtd, chip, page, 1); + chip->ecc.read_oob_raw(mtd, chip, page); else - chip->ecc.read_oob(mtd, chip, page, 1); + chip->ecc.read_oob(mtd, chip, page); len = min(len, readlen); buf = nand_transfer_oob(chip, buf, ops, len); diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 859ef318e5d..8cb627751c9 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -539,14 +539,11 @@ exit: * nand_read_oob_syndrome assumes we can send column address - we can't */ static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd) + int page) { - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - sndcmd = 0; - } + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return sndcmd; + return 0; } /* diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index c7755f455c8..57977c64052 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -372,9 +372,8 @@ struct nand_ecc_ctrl { int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, int page); int (*read_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip, - int page, int sndcmd); - int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page, - int sndcmd); + int page); + int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page); int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page); }; -- cgit v1.2.3 From 1951f2f710a621ae0bc4268617046a6c02c634d0 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Wed, 9 May 2012 13:13:34 +0300 Subject: mtd: nand: check the return code of 'read_oob/read_oob_raw' Apparently, there is an implementor of 'read_oob' which may return an error inidication (e.g. docg4_read_oob may return -EIO). Test the return value of 'read_oob/read_oob_raw', and if negative, propagate the error, so it's returned by the '_read_oob' interface. Signed-off-by: Shmulik Ladkani Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 4047d7c2fee..d47586cf64c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1791,6 +1791,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, int readlen = ops->ooblen; int len; uint8_t *buf = ops->oobbuf; + int ret = 0; pr_debug("%s: from = 0x%08Lx, len = %i\n", __func__, (unsigned long long)from, readlen); @@ -1826,9 +1827,12 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, while (1) { if (ops->mode == MTD_OPS_RAW) - chip->ecc.read_oob_raw(mtd, chip, page); + ret = chip->ecc.read_oob_raw(mtd, chip, page); else - chip->ecc.read_oob(mtd, chip, page); + ret = chip->ecc.read_oob(mtd, chip, page); + + if (ret < 0) + break; len = min(len, readlen); buf = nand_transfer_oob(chip, buf, ops, len); @@ -1857,7 +1861,10 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, } } - ops->oobretlen = ops->ooblen; + ops->oobretlen = ops->ooblen - readlen; + + if (ret < 0) + return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; -- cgit v1.2.3 From 8d602cf50d3bba864bc1438f486b626df69c87b3 Mon Sep 17 00:00:00 2001 From: Ivan Djelic Date: Thu, 26 Apr 2012 14:17:49 +0200 Subject: ARM: OMAP3: gpmc: add BCH ecc api and modes This patch adds a simple BCH ecc computation api, similar to the existing Hamming ecc api. It is intended to be used by the MTD layer. It implements the following features: - support 4-bit and 8-bit ecc computation - do not protect user bytes in spare area, only data area is protected - ecc for an erased NAND page (0xFFs) is also a sequence of 0xFFs This last feature is obtained by adding a constant polynomial to the hardware computed ecc. It allows to correct bitflips in blank pages and is extremely useful to support filesystems such as UBIFS, which expect erased pages to contain only 0xFFs. This api has been tested on an OMAP3630 board. Artem: The OMAP maintainer Tony Lindgren gave us his blessing for merging this patch via the MTD tree. Signed-off-by: Ivan Djelic Acked-by: Tony Lindgren Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/arm/mach-omap2/gpmc.c | 184 +++++++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/gpmc.h | 11 ++ 2 files changed, 195 insertions(+) diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 00d510858e2..1ca8d7fd625 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c @@ -49,6 +49,7 @@ #define GPMC_ECC_CONTROL 0x1f8 #define GPMC_ECC_SIZE_CONFIG 0x1fc #define GPMC_ECC1_RESULT 0x200 +#define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */ #define GPMC_CS0_OFFSET 0x60 #define GPMC_CS_SIZE 0x30 @@ -920,3 +921,186 @@ int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code) return 0; } EXPORT_SYMBOL_GPL(gpmc_calculate_ecc); + +#ifdef CONFIG_ARCH_OMAP3 + +/** + * gpmc_init_hwecc_bch - initialize hardware BCH ecc functionality + * @cs: chip select number + * @nsectors: how many 512-byte sectors to process + * @nerrors: how many errors to correct per sector (4 or 8) + * + * This function must be executed before any call to gpmc_enable_hwecc_bch. + */ +int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors) +{ + /* check if ecc module is in use */ + if (gpmc_ecc_used != -EINVAL) + return -EINVAL; + + /* support only OMAP3 class */ + if (!cpu_is_omap34xx()) { + printk(KERN_ERR "BCH ecc is not supported on this CPU\n"); + return -EINVAL; + } + + /* + * For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1. + * Other chips may be added if confirmed to work. + */ + if ((nerrors == 4) && + (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) { + printk(KERN_ERR "BCH 4-bit mode is not supported on this CPU\n"); + return -EINVAL; + } + + /* sanity check */ + if (nsectors > 8) { + printk(KERN_ERR "BCH cannot process %d sectors (max is 8)\n", + nsectors); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(gpmc_init_hwecc_bch); + +/** + * gpmc_enable_hwecc_bch - enable hardware BCH ecc functionality + * @cs: chip select number + * @mode: read/write mode + * @dev_width: device bus width(1 for x16, 0 for x8) + * @nsectors: how many 512-byte sectors to process + * @nerrors: how many errors to correct per sector (4 or 8) + */ +int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors, + int nerrors) +{ + unsigned int val; + + /* check if ecc module is in use */ + if (gpmc_ecc_used != -EINVAL) + return -EINVAL; + + gpmc_ecc_used = cs; + + /* clear ecc and enable bits */ + gpmc_write_reg(GPMC_ECC_CONTROL, 0x1); + + /* + * When using BCH, sector size is hardcoded to 512 bytes. + * Here we are using wrapping mode 6 both for reading and writing, with: + * size0 = 0 (no additional protected byte in spare area) + * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) + */ + gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (32 << 22) | (0 << 12)); + + /* BCH configuration */ + val = ((1 << 16) | /* enable BCH */ + (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */ + (0x06 << 8) | /* wrap mode = 6 */ + (dev_width << 7) | /* bus width */ + (((nsectors-1) & 0x7) << 4) | /* number of sectors */ + (cs << 1) | /* ECC CS */ + (0x1)); /* enable ECC */ + + gpmc_write_reg(GPMC_ECC_CONFIG, val); + gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); + return 0; +} +EXPORT_SYMBOL_GPL(gpmc_enable_hwecc_bch); + +/** + * gpmc_calculate_ecc_bch4 - Generate 7 ecc bytes per sector of 512 data bytes + * @cs: chip select number + * @dat: The pointer to data on which ecc is computed + * @ecc: The ecc output buffer + */ +int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc) +{ + int i; + unsigned long nsectors, reg, val1, val2; + + if (gpmc_ecc_used != cs) + return -EINVAL; + + nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1; + + for (i = 0; i < nsectors; i++) { + + reg = GPMC_ECC_BCH_RESULT_0 + 16*i; + + /* Read hw-computed remainder */ + val1 = gpmc_read_reg(reg + 0); + val2 = gpmc_read_reg(reg + 4); + + /* + * Add constant polynomial to remainder, in order to get an ecc + * sequence of 0xFFs for a buffer filled with 0xFFs; and + * left-justify the resulting polynomial. + */ + *ecc++ = 0x28 ^ ((val2 >> 12) & 0xFF); + *ecc++ = 0x13 ^ ((val2 >> 4) & 0xFF); + *ecc++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF)); + *ecc++ = 0x39 ^ ((val1 >> 20) & 0xFF); + *ecc++ = 0x96 ^ ((val1 >> 12) & 0xFF); + *ecc++ = 0xac ^ ((val1 >> 4) & 0xFF); + *ecc++ = 0x7f ^ ((val1 & 0xF) << 4); + } + + gpmc_ecc_used = -EINVAL; + return 0; +} +EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch4); + +/** + * gpmc_calculate_ecc_bch8 - Generate 13 ecc bytes per block of 512 data bytes + * @cs: chip select number + * @dat: The pointer to data on which ecc is computed + * @ecc: The ecc output buffer + */ +int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc) +{ + int i; + unsigned long nsectors, reg, val1, val2, val3, val4; + + if (gpmc_ecc_used != cs) + return -EINVAL; + + nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1; + + for (i = 0; i < nsectors; i++) { + + reg = GPMC_ECC_BCH_RESULT_0 + 16*i; + + /* Read hw-computed remainder */ + val1 = gpmc_read_reg(reg + 0); + val2 = gpmc_read_reg(reg + 4); + val3 = gpmc_read_reg(reg + 8); + val4 = gpmc_read_reg(reg + 12); + + /* + * Add constant polynomial to remainder, in order to get an ecc + * sequence of 0xFFs for a buffer filled with 0xFFs. + */ + *ecc++ = 0xef ^ (val4 & 0xFF); + *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF); + *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF); + *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF); + *ecc++ = 0xed ^ (val3 & 0xFF); + *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF); + *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF); + *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF); + *ecc++ = 0x97 ^ (val2 & 0xFF); + *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF); + *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF); + *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF); + *ecc++ = 0xb5 ^ (val1 & 0xFF); + } + + gpmc_ecc_used = -EINVAL; + return 0; +} +EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch8); + +#endif /* CONFIG_ARCH_OMAP3 */ diff --git a/arch/arm/plat-omap/include/plat/gpmc.h b/arch/arm/plat-omap/include/plat/gpmc.h index 1527929b445..f37764a3607 100644 --- a/arch/arm/plat-omap/include/plat/gpmc.h +++ b/arch/arm/plat-omap/include/plat/gpmc.h @@ -92,6 +92,8 @@ enum omap_ecc { OMAP_ECC_HAMMING_CODE_HW, /* gpmc to detect the error */ /* 1-bit ecc: stored at beginning of spare area as romcode */ OMAP_ECC_HAMMING_CODE_HW_ROMCODE, /* gpmc method & romcode layout */ + OMAP_ECC_BCH4_CODE_HW, /* 4-bit BCH ecc code */ + OMAP_ECC_BCH8_CODE_HW, /* 8-bit BCH ecc code */ }; /* @@ -157,4 +159,13 @@ extern int gpmc_nand_write(int cs, int cmd, int wval); int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size); int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code); + +#ifdef CONFIG_ARCH_OMAP3 +int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors); +int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors, + int nerrors); +int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc); +int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc); +#endif /* CONFIG_ARCH_OMAP3 */ + #endif -- cgit v1.2.3 From 0e618ef0a6a33cf7ef96c2c824402088dd8ef48c Mon Sep 17 00:00:00 2001 From: Ivan Djelic Date: Mon, 30 Apr 2012 12:17:18 +0200 Subject: mtd: nand: omap: add support for hardware BCH ecc Two modes are supported: 4-bit and 8-bit error correction. Note that 4-bit mode is only confirmed to work on OMAP3630 ES 1.x, x >= 1. The OMAP3 GPMC hardware BCH engine computes remainder polynomials, it does not provide automatic error location and correction: this step is implemented using the BCH library. This implementation only protects page data, there is no support for protecting user-defined spare area bytes (this could be added with few modifications); therefore, it cannot be used with YAFFS2 or other similar filesystems that depend on oob storage. Before being stored to nand flash, hardware BCH ecc is adjusted so that an erased page has a valid ecc; thus allowing correction of bitflips in blank pages (also common on 4-bit devices). BCH correction mode is selected at runtime by setting platform data parameter 'ecc_opt' to value OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW. This code has been tested with mtd test modules, UBI and UBIFS on a BeagleBoard revC3 (OMAP3530 ES3.0 + Micron NAND 256MiB 1,8V 16-bit). Signed-off-by: Ivan Djelic Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 40 ++++++++ drivers/mtd/nand/omap2.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 285 insertions(+) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index bf0a28d513d..31bb7e5b504 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -115,6 +115,46 @@ config MTD_NAND_OMAP2 Support for NAND flash on Texas Instruments OMAP2, OMAP3 and OMAP4 platforms. +config MTD_NAND_OMAP_BCH + depends on MTD_NAND && MTD_NAND_OMAP2 && ARCH_OMAP3 + bool "Enable support for hardware BCH error correction" + default n + select BCH + select BCH_CONST_PARAMS + help + Support for hardware BCH error correction. + +choice + prompt "BCH error correction capability" + depends on MTD_NAND_OMAP_BCH + +config MTD_NAND_OMAP_BCH8 + bool "8 bits / 512 bytes (recommended)" + help + Support correcting up to 8 bitflips per 512-byte block. + This will use 13 bytes of spare area per 512 bytes of page data. + This is the recommended mode, as 4-bit mode does not work + on some OMAP3 revisions, due to a hardware bug. + +config MTD_NAND_OMAP_BCH4 + bool "4 bits / 512 bytes" + help + Support correcting up to 4 bitflips per 512-byte block. + This will use 7 bytes of spare area per 512 bytes of page data. + Note that this mode does not work on some OMAP3 revisions, due to a + hardware bug. Please check your OMAP datasheet before selecting this + mode. + +endchoice + +if MTD_NAND_OMAP_BCH +config BCH_CONST_M + default 13 +config BCH_CONST_T + default 4 if MTD_NAND_OMAP_BCH4 + default 8 if MTD_NAND_OMAP_BCH8 +endif + config MTD_NAND_IDS tristate diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 05d3562ec74..d7f681d0c9b 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -21,6 +21,10 @@ #include #include +#ifdef CONFIG_MTD_NAND_OMAP_BCH +#include +#endif + #include #include #include @@ -127,6 +131,11 @@ struct omap_nand_info { } iomode; u_char *buf; int buf_len; + +#ifdef CONFIG_MTD_NAND_OMAP_BCH + struct bch_control *bch; + struct nand_ecclayout ecclayout; +#endif }; /** @@ -929,6 +938,226 @@ static int omap_dev_ready(struct mtd_info *mtd) return 1; } +#ifdef CONFIG_MTD_NAND_OMAP_BCH + +/** + * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) +{ + int nerrors; + unsigned int dev_width; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_chip *chip = mtd->priv; + + nerrors = (info->nand.ecc.bytes == 13) ? 8 : 4; + dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; + /* + * Program GPMC to perform correction on one 512-byte sector at a time. + * Using 4 sectors at a time (i.e. ecc.size = 2048) is also possible and + * gives a slight (5%) performance gain (but requires additional code). + */ + (void)gpmc_enable_hwecc_bch(info->gpmc_cs, mode, dev_width, 1, nerrors); +} + +/** + * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + */ +static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + return gpmc_calculate_ecc_bch4(info->gpmc_cs, dat, ecc_code); +} + +/** + * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + */ +static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + return gpmc_calculate_ecc_bch8(info->gpmc_cs, dat, ecc_code); +} + +/** + * omap3_correct_data_bch - Decode received data and correct errors + * @mtd: MTD device structure + * @data: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + */ +static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data, + u_char *read_ecc, u_char *calc_ecc) +{ + int i, count; + /* cannot correct more than 8 errors */ + unsigned int errloc[8]; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + + count = decode_bch(info->bch, NULL, 512, read_ecc, calc_ecc, NULL, + errloc); + if (count > 0) { + /* correct errors */ + for (i = 0; i < count; i++) { + /* correct data only, not ecc bytes */ + if (errloc[i] < 8*512) + data[errloc[i]/8] ^= 1 << (errloc[i] & 7); + pr_debug("corrected bitflip %u\n", errloc[i]); + } + } else if (count < 0) { + pr_err("ecc unrecoverable error\n"); + } + return count; +} + +/** + * omap3_free_bch - Release BCH ecc resources + * @mtd: MTD device structure + */ +static void omap3_free_bch(struct mtd_info *mtd) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + if (info->bch) { + free_bch(info->bch); + info->bch = NULL; + } +} + +/** + * omap3_init_bch - Initialize BCH ECC + * @mtd: MTD device structure + * @ecc_opt: OMAP ECC mode (OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW) + */ +static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) +{ + int ret, max_errors; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); +#ifdef CONFIG_MTD_NAND_OMAP_BCH8 + const int hw_errors = 8; +#else + const int hw_errors = 4; +#endif + info->bch = NULL; + + max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? 8 : 4; + if (max_errors != hw_errors) { + pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported", + max_errors, hw_errors); + goto fail; + } + + /* initialize GPMC BCH engine */ + ret = gpmc_init_hwecc_bch(info->gpmc_cs, 1, max_errors); + if (ret) + goto fail; + + /* software bch library is only used to detect and locate errors */ + info->bch = init_bch(13, max_errors, 0x201b /* hw polynomial */); + if (!info->bch) + goto fail; + + info->nand.ecc.size = 512; + info->nand.ecc.hwctl = omap3_enable_hwecc_bch; + info->nand.ecc.correct = omap3_correct_data_bch; + info->nand.ecc.mode = NAND_ECC_HW; + + /* + * The number of corrected errors in an ecc block that will trigger + * block scrubbing defaults to the ecc strength (4 or 8). + * Set mtd->bitflip_threshold here to define a custom threshold. + */ + + if (max_errors == 8) { + info->nand.ecc.strength = 8; + info->nand.ecc.bytes = 13; + info->nand.ecc.calculate = omap3_calculate_ecc_bch8; + } else { + info->nand.ecc.strength = 4; + info->nand.ecc.bytes = 7; + info->nand.ecc.calculate = omap3_calculate_ecc_bch4; + } + + pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors); + return 0; +fail: + omap3_free_bch(mtd); + return -1; +} + +/** + * omap3_init_bch_tail - Build an oob layout for BCH ECC correction. + * @mtd: MTD device structure + */ +static int omap3_init_bch_tail(struct mtd_info *mtd) +{ + int i, steps; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_ecclayout *layout = &info->ecclayout; + + /* build oob layout */ + steps = mtd->writesize/info->nand.ecc.size; + layout->eccbytes = steps*info->nand.ecc.bytes; + + /* do not bother creating special oob layouts for small page devices */ + if (mtd->oobsize < 64) { + pr_err("BCH ecc is not supported on small page devices\n"); + goto fail; + } + + /* reserve 2 bytes for bad block marker */ + if (layout->eccbytes+2 > mtd->oobsize) { + pr_err("no oob layout available for oobsize %d eccbytes %u\n", + mtd->oobsize, layout->eccbytes); + goto fail; + } + + /* put ecc bytes at oob tail */ + for (i = 0; i < layout->eccbytes; i++) + layout->eccpos[i] = mtd->oobsize-layout->eccbytes+i; + + layout->oobfree[0].offset = 2; + layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; + info->nand.ecc.layout = layout; + + if (!(info->nand.options & NAND_BUSWIDTH_16)) + info->nand.badblock_pattern = &bb_descrip_flashbased; + return 0; +fail: + omap3_free_bch(mtd); + return -1; +} + +#else +static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) +{ + pr_err("CONFIG_MTD_NAND_OMAP_BCH is not enabled\n"); + return -1; +} +static int omap3_init_bch_tail(struct mtd_info *mtd) +{ + return -1; +} +static void omap3_free_bch(struct mtd_info *mtd) +{ +} +#endif /* CONFIG_MTD_NAND_OMAP_BCH */ + static int __devinit omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; @@ -1067,6 +1296,13 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->nand.ecc.hwctl = omap_enable_hwecc; info->nand.ecc.correct = omap_correct_data; info->nand.ecc.mode = NAND_ECC_HW; + } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || + (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { + err = omap3_init_bch(&info->mtd, pdata->ecc_opt); + if (err) { + err = -EINVAL; + goto out_release_mem_region; + } } /* DIP switches on some boards change between 8 and 16 bit @@ -1098,6 +1334,14 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) (offset + omap_oobinfo.eccbytes); info->nand.ecc.layout = &omap_oobinfo; + } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || + (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { + /* build OOB layout for BCH ECC correction */ + err = omap3_init_bch_tail(&info->mtd); + if (err) { + err = -EINVAL; + goto out_release_mem_region; + } } /* second phase scan */ @@ -1126,6 +1370,7 @@ static int omap_nand_remove(struct platform_device *pdev) struct mtd_info *mtd = platform_get_drvdata(pdev); struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); + omap3_free_bch(&info->mtd); platform_set_drvdata(pdev, NULL); if (info->dma_ch != -1) -- cgit v1.2.3 From 9824f75d56298e5fe4f9f57d9f3abd5fbf3d472c Mon Sep 17 00:00:00 2001 From: Jean-Christophe DUBOIS Date: Thu, 10 May 2012 17:13:44 +0200 Subject: jffs2: allow to discriminate between recoverable and non-recoverable errors This patch is basically a revert of commit f326966b3df47f4fa7e90425f60efdd30c31fe19. It allows JFFS2 to make the distinction between a potential transient error (reading or writing the media) and a non recoverable error like a bad CRC on the extended attribute data or some insconsitent parameters. In order to make clear that the error is indeed intended to report a corrupted attribute, a new local error code (JFFS2_XATTR_IS_CORRUPTED) is introduced rather than returning a confusing positive EIO, which is what led to the inappropriate "fix" last time. This error code is never reported to user space and only checked locally in this file. Signed-off-by: Jean-Christophe DUBOIS Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/xattr.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index b55b803eddc..c18c0ab70ea 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -11,6 +11,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#define JFFS2_XATTR_IS_CORRUPTED 1 + #include #include #include @@ -153,7 +155,7 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", offset, je32_to_cpu(rx.hdr_crc), crc); xd->flags |= JFFS2_XFLAGS_INVALID; - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len)); if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK @@ -169,7 +171,7 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat je32_to_cpu(rx.xid), xd->xid, je32_to_cpu(rx.version), xd->version); xd->flags |= JFFS2_XFLAGS_INVALID; - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } xd->xprefix = rx.xprefix; xd->name_len = rx.name_len; @@ -227,12 +229,12 @@ static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum data[xd->name_len] = '\0'; crc = crc32(0, data, length); if (crc != xd->data_crc) { - JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XREF)" + JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XATTR)" " at %#08x, read: 0x%08x calculated: 0x%08x\n", ref_offset(xd->node), xd->data_crc, crc); kfree(data); xd->flags |= JFFS2_XFLAGS_INVALID; - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } xd->flags |= JFFS2_XFLAGS_HOT; @@ -270,7 +272,7 @@ static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x if (xd->xname) return 0; if (xd->flags & JFFS2_XFLAGS_INVALID) - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; if (unlikely(is_xattr_datum_unchecked(c, xd))) rc = do_verify_xattr_datum(c, xd); if (!rc) @@ -462,7 +464,7 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref if (crc != je32_to_cpu(rr.node_crc)) { JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", offset, je32_to_cpu(rr.node_crc), crc); - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK || je16_to_cpu(rr.nodetype) != JFFS2_NODETYPE_XREF @@ -472,7 +474,7 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref offset, je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK, je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF, je32_to_cpu(rr.totlen), PAD(sizeof(rr))); - return -EIO; + return JFFS2_XATTR_IS_CORRUPTED; } ref->ino = je32_to_cpu(rr.ino); ref->xid = je32_to_cpu(rr.xid); -- cgit v1.2.3 From 8c5a0366403f5dbca9d4ccf50f95bafbaddc2844 Mon Sep 17 00:00:00 2001 From: Jean-Christophe DUBOIS Date: Thu, 10 May 2012 17:14:03 +0200 Subject: jffs2: allow to complete xattr integrity check on first GC scan Unlike file data integrity the xattr data integrity was not checked before some explicit access to the attribute was made. This could leave in the system a number of corrupted extended attributes which will be detected only at access time and possibly at a very late time compared to the time the corruption actually happened. This patch adds the ability to check for extended attribute integrity on first GC scan pass (similar to file data integrity check). This allows for all present attributes to be completly verified before any use of them. In order to work correctly this patch also needs the patch allowing JFFS2 to discriminate between recoverable and non recoverable errors on extended attributes. Signed-off-by: Jean-Christophe DUBOIS Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 1 + fs/jffs2/xattr.c | 7 +++++++ fs/jffs2/xattr.h | 2 ++ 3 files changed, 10 insertions(+) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 3833d74f230..1ea349fff68 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1421,6 +1421,7 @@ int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i mutex_unlock(&f->sem); jffs2_do_clear_inode(c, f); } + jffs2_xattr_do_crccheck_inode(c, ic); kfree (f); return ret; } diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index c18c0ab70ea..3034e970eb9 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -437,6 +437,8 @@ static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datu * is called to release xattr related objects when unmounting. * check_xattr_ref_inode(c, ic) * is used to confirm inode does not have duplicate xattr name/value pair. + * jffs2_xattr_do_crccheck_inode(c, ic) + * is used to force xattr data integrity check during the initial gc scan. * -------------------------------------------------- */ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) { @@ -684,6 +686,11 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac return rc; } +void jffs2_xattr_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + check_xattr_ref_inode(c, ic); +} + /* -------- xattr subsystem functions --------------- * jffs2_init_xattr_subsystem(c) * is used to initialize semaphore and list_head, and some variables. diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 7be4beb306f..467ff376ee2 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -77,6 +77,7 @@ extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c); extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, uint32_t xid, uint32_t version); +extern void jffs2_xattr_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); @@ -108,6 +109,7 @@ extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t); #define jffs2_build_xattr_subsystem(c) #define jffs2_clear_xattr_subsystem(c) +#define jffs2_xattr_do_crccheck_inode(c, ic) #define jffs2_xattr_delete_inode(c, ic) #define jffs2_xattr_free_inode(c, ic) #define jffs2_verify_xattr(c) (1) -- cgit v1.2.3 From 43dc03c7ba644620283523b7ae62da94bb81fdbb Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Fri, 11 May 2012 12:15:41 +0200 Subject: mtd: add fixup for S29NS512P NOR flash. Spansion S29NS512P flash uses a 16bit transfer to report number of sectors instead of two 8bit accesses as CFI specifies. Artem: remove warning message which said that we are applying the fixup - no need to scary the user unnecessarily. Signed-off-by: Javier Martin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index d02592e6a0f..49b8b035e8e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -332,6 +332,19 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd) } } +static void fixup_s29ns512p_sectors(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + /* + * S29NS512P flash uses more than 8bits to report number of sectors, + * which is not permitted by CFI. + */ + cfi->cfiq->EraseRegionInfo[0] = 0x020001ff; + pr_warning("%s: Bad S29NS512P CFI data, adjust to 512 sectors\n", mtd->name); +} + /* Used to fix CFI-Tables of chips without Extended Query Tables */ static struct cfi_fixup cfi_nopri_fixup_table[] = { { CFI_MFR_SST, 0x234a, fixup_sst39vf }, /* SST39VF1602 */ @@ -362,6 +375,7 @@ static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors }, { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors }, { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors }, + { CFI_MFR_AMD, 0x3f00, fixup_s29ns512p_sectors }, { CFI_MFR_SST, 0x536a, fixup_sst38vf640x_sectorsize }, /* SST38VF6402 */ { CFI_MFR_SST, 0x536b, fixup_sst38vf640x_sectorsize }, /* SST38VF6401 */ { CFI_MFR_SST, 0x536c, fixup_sst38vf640x_sectorsize }, /* SST38VF6404 */ -- cgit v1.2.3 From 5df41de5870e2184e75a8cb133ca81888006f097 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 13 May 2012 23:34:24 -0500 Subject: mtd: cfi_cmdset_0002: Slight cleanup of fixup messages These should be semicolons, not commas. Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 49b8b035e8e..22d0493a026 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -317,7 +317,7 @@ static void fixup_s29gl064n_sectors(struct mtd_info *mtd) if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) { cfi->cfiq->EraseRegionInfo[0] |= 0x0040; - pr_warning("%s: Bad S29GL064N CFI data, adjust from 64 to 128 sectors\n", mtd->name); + pr_warning("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n", mtd->name); } } @@ -328,7 +328,7 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd) if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) { cfi->cfiq->EraseRegionInfo[1] &= ~0x0040; - pr_warning("%s: Bad S29GL032N CFI data, adjust from 127 to 63 sectors\n", mtd->name); + pr_warning("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n", mtd->name); } } @@ -342,7 +342,7 @@ static void fixup_s29ns512p_sectors(struct mtd_info *mtd) * which is not permitted by CFI. */ cfi->cfiq->EraseRegionInfo[0] = 0x020001ff; - pr_warning("%s: Bad S29NS512P CFI data, adjust to 512 sectors\n", mtd->name); + pr_warning("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n", mtd->name); } /* Used to fix CFI-Tables of chips without Extended Query Tables */ -- cgit v1.2.3 From 52c2d9aad4923536c10d278e02da2e0254e46ee0 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Mon, 9 Apr 2012 13:19:08 +0200 Subject: mtd: docg3 fix in-middle of blocks reads Corner case reads do not work, and return false data and ECC. This case is typically seen in a ubifs usage, with a read of type: - docg3 docg3: doc_read_oob(from=14882415, mode=1, data=(c30eca40:12), oob=( (null):0)) This results in the following reads: - docg3 docg3: doc_read_data_area(buf= (null), len=111) - docg3 docg3: doc_read_data_area(buf=c30eca40, len=12) - docg3 docg3: doc_read_data_area(buf= (null), len=389) - docg3 docg3: doc_read_data_area(buf= (null), len=0) - docg3 docg3: doc_read_data_area(buf= (null), len=16) If we suppose that the pages content is : - bytes 0 .. 111 : 0x0a - bytes 112 .. 255 : 0x0f Then the returned bytes will be : - 111 times 0x0a (correct) - 0x0a 2 times and 0x0f 10 times (incorrect, should be 0x0a,0x0f) - 0x0f 389 times (correct) - nothing - correct OOB The reason seams that the first 111 bytes read ends between the 2 docg3 planes, and that the first following read (in the 12 bytes sequence, read of 16 bit word) returns the byte of the rightmost plane duplicated in high and lower byte of the word. Fix this behaviour by ensuring that if the previous read ended up in-between the 2 planes, there will be a first 1 byte read to get back to the beginning of leftmost plane. Signed-off-by: Robert Jarzmik Signed-off-by: Artem Bityutskiy --- drivers/mtd/devices/docg3.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 65d22a0439c..3dbbfa58251 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -227,7 +227,7 @@ static void doc_read_data_area(struct docg3 *docg3, void *buf, int len, u8 data8, *dst8; doc_dbg("doc_read_data_area(buf=%p, len=%d)\n", buf, len); - cdr = len & 0x3; + cdr = len & 0x1; len4 = len - cdr; if (first) @@ -732,12 +732,24 @@ err: * @len: the number of bytes to be read (must be a multiple of 4) * @buf: the buffer to be filled in (or NULL is forget bytes) * @first: 1 if first time read, DOC_READADDRESS should be set + * @last_odd: 1 if last read ended up on an odd byte + * + * Reads bytes from a prepared page. There is a trickery here : if the last read + * ended up on an odd offset in the 1024 bytes double page, ie. between the 2 + * planes, the first byte must be read apart. If a word (16bit) read was used, + * the read would return the byte of plane 2 as low *and* high endian, which + * will mess the read. * */ static int doc_read_page_getbytes(struct docg3 *docg3, int len, u_char *buf, - int first) + int first, int last_odd) { - doc_read_data_area(docg3, buf, len, first); + if (last_odd && len > 0) { + doc_read_data_area(docg3, buf, 1, first); + doc_read_data_area(docg3, buf ? buf + 1 : buf, len - 1, 0); + } else { + doc_read_data_area(docg3, buf, len, first); + } doc_delay(docg3, 2); return len; } @@ -888,20 +900,20 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, ret = doc_read_page_ecc_init(docg3, DOC_ECC_BCH_TOTAL_BYTES); if (ret < 0) goto err_in_read; - ret = doc_read_page_getbytes(docg3, skip, NULL, 1); + ret = doc_read_page_getbytes(docg3, skip, NULL, 1, 0); if (ret < skip) goto err_in_read; - ret = doc_read_page_getbytes(docg3, nbdata, buf, 0); + ret = doc_read_page_getbytes(docg3, nbdata, buf, 0, skip % 2); if (ret < nbdata) goto err_in_read; doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE - nbdata - skip, - NULL, 0); - ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0); + NULL, 0, (skip + nbdata) % 2); + ret = doc_read_page_getbytes(docg3, nboob, oobbuf, 0, 0); if (ret < nboob) goto err_in_read; doc_read_page_getbytes(docg3, DOC_LAYOUT_OOB_SIZE - nboob, - NULL, 0); + NULL, 0, nboob % 2); doc_get_bch_hw_ecc(docg3, hwecc); eccconf1 = doc_register_readb(docg3, DOC_ECCCONF1); @@ -1006,7 +1018,7 @@ static int doc_reload_bbt(struct docg3 *docg3) DOC_LAYOUT_PAGE_SIZE); if (!ret) doc_read_page_getbytes(docg3, DOC_LAYOUT_PAGE_SIZE, - buf, 1); + buf, 1, 0); buf += DOC_LAYOUT_PAGE_SIZE; } doc_read_page_finish(docg3); @@ -1066,10 +1078,10 @@ static int doc_get_erase_count(struct docg3 *docg3, loff_t from) ret = doc_reset_seq(docg3); if (!ret) ret = doc_read_page_prepare(docg3, block0, block1, page, - ofs + DOC_LAYOUT_WEAR_OFFSET); + ofs + DOC_LAYOUT_WEAR_OFFSET, 0); if (!ret) ret = doc_read_page_getbytes(docg3, DOC_LAYOUT_WEAR_SIZE, - buf, 1); + buf, 1, 0); doc_read_page_finish(docg3); if (ret || (buf[0] != DOC_ERASE_MARK) || (buf[2] != DOC_ERASE_MARK)) -- cgit v1.2.3 From 34a5704d91d6f8376a4c0a0143a1dd3eb3ccb37e Mon Sep 17 00:00:00 2001 From: Dmitry Maluka Date: Fri, 11 May 2012 20:51:51 +0300 Subject: mtd: nand: fix scan_read_raw_oob It seems there is a bug in scan_read_raw_oob() in nand_bbt.c which should cause wrong functioning of NAND_BBT_SCANALLPAGES option. Artem: the patch did not apply and I had to amend it a bit. Signed-off-by: Artem Bityutskiy Cc: stable@kernel.org --- drivers/mtd/nand/nand_bbt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 20a112f591f..30d1319ff06 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -324,6 +324,7 @@ static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs, buf += mtd->oobsize + mtd->writesize; len -= mtd->writesize; + offs += mtd->writesize; } return 0; } -- cgit v1.2.3 From 2e929d001e85126d9267de373d4b76014789661d Mon Sep 17 00:00:00 2001 From: Frank Svendsboe Date: Thu, 17 May 2012 22:43:09 +0200 Subject: mtd: of_parts: fix breakage in Kconfig MTD_OF_PARTS and the default setting is not working due to using 'Y' instead of 'y', introduced in commit d6137badeff1ef64b4e0092ec249ebdeaeb3ff37. This made our board, and possibly other boards using DTS defined partitions and not having CONFIG_MTD_OF_PARTS=y defined in the defconfig, fail to mount root. Signed-off-by: Frank Svendsboe Cc: stable@kernel.org [3.2+] Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5760c1a4b3f..27143e042af 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -128,7 +128,7 @@ config MTD_AFS_PARTS config MTD_OF_PARTS tristate "OpenFirmware partitioning information support" - default Y + default y depends on OF help This provides a partition parsing function which derives -- cgit v1.2.3 From 5636ce0f070dee85c1b1203fe436a89648d25d06 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 21 May 2012 22:59:27 +0200 Subject: mtd: gpmi-nand: define ecc.strength Fix an issue which was introduced by the recent addition of ecc.strength. The ecc.strength wasn't set in gpmi-nand, resulting in the following crash: [ 2.550000] kernel BUG at drivers/mtd/nand/nand_base.c:3347! ... [ 2.550000] [] (nand_scan_tail+0x328/0x650) from [] (gpmi_nand_probe+0x43c/0x5a4) [ 2.550000] [] (gpmi_nand_probe+0x43c/0x5a4) from [] (platform_drv_probe+0x14/0x18) [ 2.550000] [] (platform_drv_probe+0x14/0x18) from [] (driver_probe_device+0x74/0x1fc) [ 2.550000] [] (driver_probe_device+0x74/0x1fc) from [] (__driver_attach+0x94/0x98) [ 2.550000] [] (__driver_attach+0x94/0x98) from [] (bus_for_each_dev+0x50/0x80) [ 2.550000] [] (bus_for_each_dev+0x50/0x80) from [] (bus_add_driver+0x188/0x25c) [ 2.550000] [] (bus_add_driver+0x188/0x25c) from [] (driver_register+0x78/0x138) [ 2.550000] [] (driver_register+0x78/0x138) from [] (gpmi_nand_init+0xc/0x30) [ 2.550000] [] (gpmi_nand_init+0xc/0x30) from [] (do_one_initcall+0x108/0x17c) [ 2.550000] [] (do_one_initcall+0x108/0x17c) from [] (kernel_init+0xfc/0x1bc) [ 2.550000] [] (kernel_init+0xfc/0x1bc) from [] (kernel_thread_exit+0x0/0x8) Signed-off-by: Marek Vasut Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 95f2af848ff..3eafc68154e 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1424,6 +1424,10 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this) if (ret) return ret; + /* Adjust the ECC strength according to the chip. */ + this->nand.ecc.strength = this->bch_geometry.ecc_strength; + this->mtd.ecc_strength = this->bch_geometry.ecc_strength; + /* NAND boot init, depends on the gpmi_set_geometry(). */ return nand_boot_init(this); } @@ -1482,6 +1486,7 @@ static int __devinit gpmi_nfc_init(struct gpmi_nand_data *this) chip->options |= NAND_NO_SUBPAGE_WRITE; chip->ecc.mode = NAND_ECC_HW; chip->ecc.size = 1; + chip->ecc.strength = 8; chip->ecc.layout = &gpmi_hw_ecclayout; /* Allocate a temporary DMA buffer for reading ID in the nand_scan() */ -- cgit v1.2.3 From 2e24e32e2759348c9290404abad4f729f791bfad Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 24 May 2012 00:17:23 +0200 Subject: mtd: block2mtd: fix recursive call of mtd_writev The 'mtd_writev' interface calls the function assigned to the '_write' field of a given mtd device if that is not NULL. The block2mtd driver sets the '_writev' field to the 'mtd_writev' function itself and thus causes a endless loop. This is caused by 1dbebd32562b3c2caeca35960e5cb00bfcc12900 (mtd: harmonize mtd_writev usage). Remove the assignment from the block2mtd driver to fix the issue. Signed-off-by: Gabor Juhos Cc: stable@kernel.org [3.3+] Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/block2mtd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 300aeb837ee..681e2ee0f2d 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -265,7 +265,6 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) dev->mtd.flags = MTD_CAP_RAM; dev->mtd._erase = block2mtd_erase; dev->mtd._write = block2mtd_write; - dev->mtd._writev = mtd_writev; dev->mtd._sync = block2mtd_sync; dev->mtd._read = block2mtd_read; dev->mtd.priv = dev; -- cgit v1.2.3 From 4a43faf54e9173b6acce37cf7f053fc9515a2cdf Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 25 May 2012 16:22:42 +0200 Subject: mtd: mxc_nand: move ecc strengh setup before nand_scan_tail Since commit 6a918bade9dab40aaef80559bd1169c69e8d69cb, the mxc_nand driver fails with: Driver must set ecc.strength when using hardware ECC This is because nand_scan_tail checks for correct ecc strength settings, so we must set them up before nand_scan_tail. Signed-off-by: Sascha Hauer Cc: stable@vger.kernel.org [3.4+] Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index ec5ba8bac73..3310a48000d 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1465,12 +1465,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) else if (mtd->writesize == 4096) this->ecc.layout = host->devtype_data->ecclayout_4k; - /* second phase scan */ - if (nand_scan_tail(mtd)) { - err = -ENXIO; - goto escan; - } - if (this->ecc.mode == NAND_ECC_HW) { if (nfc_is_v1()) this->ecc.strength = 1; @@ -1478,6 +1472,12 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.strength = (host->eccsize == 4) ? 4 : 8; } + /* second phase scan */ + if (nand_scan_tail(mtd)) { + err = -ENXIO; + goto escan; + } + /* Register the partitions */ mtd_device_parse_register(mtd, part_probes, &(struct mtd_part_parser_data){ -- cgit v1.2.3