From fddcca5107051adf9e4481d2a79ae0616577fd2c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 29 Feb 2016 13:20:28 +0100 Subject: [PATCH 001/155] mtd: avoid stack overflow in MTD CFI code When map_word gets too large, we use a lot of kernel stack, and for MTD_MAP_BANK_WIDTH_32, this means we use more than the recommended 1024 bytes in a number of functions: drivers/mtd/chips/cfi_cmdset_0020.c: In function 'cfi_staa_write_buffers': drivers/mtd/chips/cfi_cmdset_0020.c:651:1: warning: the frame size of 1336 bytes is larger than 1024 bytes [-Wframe-larger-than=] drivers/mtd/chips/cfi_cmdset_0020.c: In function 'cfi_staa_erase_varsize': drivers/mtd/chips/cfi_cmdset_0020.c:972:1: warning: the frame size of 1208 bytes is larger than 1024 bytes [-Wframe-larger-than=] drivers/mtd/chips/cfi_cmdset_0001.c: In function 'do_write_buffer': drivers/mtd/chips/cfi_cmdset_0001.c:1835:1: warning: the frame size of 1240 bytes is larger than 1024 bytes [-Wframe-larger-than=] This can be avoided if all operations on the map word are done indirectly and the stack gets reused between the calls. We can mostly achieve this by selecting MTD_COMPLEX_MAPPINGS whenever MTD_MAP_BANK_WIDTH_32 is set, but for the case that no other bank width is enabled, we also need to use a non-constant map_bankwidth() to convince the compiler to use less stack. Signed-off-by: Arnd Bergmann [Brian: this patch mostly achieves its goal by forcing MTD_COMPLEX_MAPPINGS (and the accompanying indirection) for 256-bit mappings; the rest of the change is mostly a wash, though it helps reduce stack size slightly. If we really care about supporting 256-bit mappings though, we should consider rewriting some of this code to avoid keeping and assigning so many 256-bit objects on the stack.] Signed-off-by: Brian Norris --- drivers/mtd/chips/Kconfig | 1 + include/linux/mtd/map.h | 19 +++++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index 3b3dabce58de..bbfa1f129266 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -115,6 +115,7 @@ config MTD_MAP_BANK_WIDTH_16 config MTD_MAP_BANK_WIDTH_32 bool "Support 256-bit buswidth" if MTD_CFI_GEOMETRY + select MTD_COMPLEX_MAPPINGS if HAS_IOMEM default n help If you wish to support CFI devices on a physical bus which is diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 5e0eb7ccabd4..3aa56e3104bb 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -122,18 +122,13 @@ #endif #ifdef CONFIG_MTD_MAP_BANK_WIDTH_32 -# ifdef map_bankwidth -# undef map_bankwidth -# define map_bankwidth(map) ((map)->bankwidth) -# undef map_bankwidth_is_large -# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) -# undef map_words -# define map_words(map) map_calc_words(map) -# else -# define map_bankwidth(map) 32 -# define map_bankwidth_is_large(map) (1) -# define map_words(map) map_calc_words(map) -# endif +/* always use indirect access for 256-bit to preserve kernel stack */ +# undef map_bankwidth +# define map_bankwidth(map) ((map)->bankwidth) +# undef map_bankwidth_is_large +# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8) +# undef map_words +# define map_words(map) map_calc_words(map) #define map_bankwidth_is_32(map) (map_bankwidth(map) == 32) #undef MAX_MAP_BANKWIDTH #define MAX_MAP_BANKWIDTH 32 From 5651d6aaf489d1db48c253cf884b40214e91c2c5 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 26 Feb 2016 11:50:28 +0100 Subject: [PATCH 002/155] mtd: bcm47xxsflash: use ioremap_cache() instead of KSEG0ADDR() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using KSEG0ADDR makes code highly MIPS dependent and not portable. Thanks to the fix a68f376 ("MIPS: io.h: Define `ioremap_cache'") we can use ioremap_cache which is generic and supported on MIPS as well now. KSEG0ADDR was translating 0x1c000000 into 0x9c000000. With ioremap_cache we use MIPS's __ioremap (and then remap_area_pages). This results in different address (e.g. 0xc0080000) but it still should be cached as expected and it was successfully tested with BCM47186B0. Other than that drivers/bcma/driver_chipcommon_sflash.c nicely setups a struct resource for access window, but we wren't using it. Use it now and drop duplicated info. Signed-off-by: Brian Norris Signed-off-by: Rafał Miłecki --- drivers/bcma/driver_chipcommon_sflash.c | 1 - drivers/mtd/devices/bcm47xxsflash.c | 29 +++++++++++++++++---- drivers/mtd/devices/bcm47xxsflash.h | 3 ++- include/linux/bcma/bcma_driver_chipcommon.h | 1 - 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/bcma/driver_chipcommon_sflash.c b/drivers/bcma/driver_chipcommon_sflash.c index 04d706ca5f43..35b13a08ca3e 100644 --- a/drivers/bcma/driver_chipcommon_sflash.c +++ b/drivers/bcma/driver_chipcommon_sflash.c @@ -146,7 +146,6 @@ int bcma_sflash_init(struct bcma_drv_cc *cc) return -ENOTSUPP; } - sflash->window = BCMA_SOC_FLASH2; sflash->blocksize = e->blocksize; sflash->numblocks = e->numblocks; sflash->size = sflash->blocksize * sflash->numblocks; diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 347bb83db864..1c65c15b31a1 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -109,8 +110,7 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, if ((from + len) > mtd->size) return -EINVAL; - memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from), - len); + memcpy_fromio(buf, b47s->window + from, len); *retlen = len; return len; @@ -275,15 +275,33 @@ static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset, static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) { - struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; + struct bcma_sflash *sflash = dev_get_platdata(dev); struct bcm47xxsflash *b47s; + struct resource *res; int err; - b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL); + b47s = devm_kzalloc(dev, sizeof(*b47s), GFP_KERNEL); if (!b47s) return -ENOMEM; sflash->priv = b47s; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "invalid resource\n"); + return -EINVAL; + } + if (!devm_request_mem_region(dev, res->start, resource_size(res), + res->name)) { + dev_err(dev, "can't request region for resource %pR\n", res); + return -EBUSY; + } + b47s->window = ioremap_cache(res->start, resource_size(res)); + if (!b47s->window) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + return -ENOMEM; + } + b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash); b47s->cc_read = bcm47xxsflash_bcma_cc_read; b47s->cc_write = bcm47xxsflash_bcma_cc_write; @@ -297,7 +315,6 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) break; } - b47s->window = sflash->window; b47s->blocksize = sflash->blocksize; b47s->numblocks = sflash->numblocks; b47s->size = sflash->size; @@ -306,6 +323,7 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); if (err) { pr_err("Failed to register MTD device: %d\n", err); + iounmap(b47s->window); return err; } @@ -321,6 +339,7 @@ static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) struct bcm47xxsflash *b47s = sflash->priv; mtd_device_unregister(&b47s->mtd); + iounmap(b47s->window); return 0; } diff --git a/drivers/mtd/devices/bcm47xxsflash.h b/drivers/mtd/devices/bcm47xxsflash.h index fe93daf4f489..1564b62b412e 100644 --- a/drivers/mtd/devices/bcm47xxsflash.h +++ b/drivers/mtd/devices/bcm47xxsflash.h @@ -65,7 +65,8 @@ struct bcm47xxsflash { enum bcm47xxsflash_type type; - u32 window; + void __iomem *window; + u32 blocksize; u16 numblocks; u32 size; diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index 846513c73606..a5ac2cad5cb7 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -587,7 +587,6 @@ struct mtd_info; struct bcma_sflash { bool present; - u32 window; u32 blocksize; u16 numblocks; u32 size; From 08922f644878c9163ada8df3ef9def89be1d5e90 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Tue, 29 Mar 2016 11:16:17 +0530 Subject: [PATCH 003/155] mtd: devices: m25p80: add support for mmap read request Certain SPI controllers may provide accelerated hardware interface to read from m25p80 type flash devices in order to provide better read performance. SPI core supports such devices with spi_flash_read() API. Call spi_flash_read(), if supported, to make use of such interface. Signed-off-by: Vignesh R [Brian: add memset()] Signed-off-by: Brian Norris --- drivers/mtd/devices/m25p80.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index c9c3b7fa3051..9d6854467651 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -131,6 +131,28 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, /* convert the dummy cycles to the number of bytes */ dummy /= 8; + if (spi_flash_read_supported(spi)) { + struct spi_flash_read_message msg; + int ret; + + memset(&msg, 0, sizeof(msg)); + + msg.buf = buf; + msg.from = from; + msg.len = len; + msg.read_opcode = nor->read_opcode; + msg.addr_width = nor->addr_width; + msg.dummy_bytes = dummy; + /* TODO: Support other combinations */ + msg.opcode_nbits = SPI_NBITS_SINGLE; + msg.addr_nbits = SPI_NBITS_SINGLE; + msg.data_nbits = m25p80_rx_nbits(nor); + + ret = spi_flash_read(spi, &msg); + *retlen = msg.retlen; + return ret; + } + spi_message_init(&m); memset(t, 0, (sizeof t)); From 58bc67fc32b1c67fb045f4828a67134dc8fee631 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jul 2015 15:23:28 +0300 Subject: [PATCH 004/155] ARM: OMAP2+: gpmc: Add platform data Add a platform data structure for GPMC. It contains all the necessary platform information that needs to be passed from platform init code to GPMC driver. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- include/linux/omap-gpmc.h | 3 +-- include/linux/platform_data/gpmc-omap.h | 30 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 include/linux/platform_data/gpmc-omap.h diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index d833eb4dd446..45d9075be1e5 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -7,8 +7,7 @@ * option) any later version. */ -/* Maximum Number of Chip Selects */ -#define GPMC_CS_NUM 8 +#include #define GPMC_CONFIG_WP 0x00000005 diff --git a/include/linux/platform_data/gpmc-omap.h b/include/linux/platform_data/gpmc-omap.h new file mode 100644 index 000000000000..6804a8b387d7 --- /dev/null +++ b/include/linux/platform_data/gpmc-omap.h @@ -0,0 +1,30 @@ +/* + * OMAP GPMC Platform data + * + * Copyright (C) 2014 Texas Instruments, Inc. - http://www.ti.com + * Roger Quadros + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef _GPMC_OMAP_H_ +#define _GPMC_OMAP_H_ + +/* Maximum Number of Chip Selects */ +#define GPMC_CS_NUM 8 + +/* Data for each chip select */ +struct gpmc_omap_cs_data { + bool valid; /* data is valid */ + bool is_nand; /* device within this CS is NAND */ + struct platform_device *pdev; /* device within this CS region */ + unsigned int pdata_size; +}; + +struct gpmc_omap_platform_data { + struct gpmc_omap_cs_data cs[GPMC_CS_NUM]; +}; + +#endif /* _GPMC_OMAP_H */ From fabe7d7756d17f5da4bd80fa2373c4bd93ed39e5 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jul 2015 15:23:29 +0300 Subject: [PATCH 005/155] ARM: OMAP2+: gpmc: Add gpmc timings and settings to platform data Add device_timings, gpmc_timings and gpmc_setting to gpmc platform data. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- include/linux/omap-gpmc.h | 139 ----------------------- include/linux/platform_data/gpmc-omap.h | 142 ++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 139 deletions(-) diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index 45d9075be1e5..2dcef1c8c8d4 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -14,145 +14,6 @@ #define GPMC_IRQ_FIFOEVENTENABLE 0x01 #define GPMC_IRQ_COUNT_EVENT 0x02 -#define GPMC_BURST_4 4 /* 4 word burst */ -#define GPMC_BURST_8 8 /* 8 word burst */ -#define GPMC_BURST_16 16 /* 16 word burst */ -#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */ -#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */ -#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */ -#define GPMC_MUX_AD 2 /* Addr-Data multiplex */ - -/* bool type time settings */ -struct gpmc_bool_timings { - bool cycle2cyclediffcsen; - bool cycle2cyclesamecsen; - bool we_extra_delay; - bool oe_extra_delay; - bool adv_extra_delay; - bool cs_extra_delay; - bool time_para_granularity; -}; - -/* - * Note that all values in this struct are in nanoseconds except sync_clk - * (which is in picoseconds), while the register values are in gpmc_fck cycles. - */ -struct gpmc_timings { - /* Minimum clock period for synchronous mode (in picoseconds) */ - u32 sync_clk; - - /* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */ - u32 cs_on; /* Assertion time */ - u32 cs_rd_off; /* Read deassertion time */ - u32 cs_wr_off; /* Write deassertion time */ - - /* ADV signal timings corresponding to GPMC_CONFIG3 */ - u32 adv_on; /* Assertion time */ - u32 adv_rd_off; /* Read deassertion time */ - u32 adv_wr_off; /* Write deassertion time */ - u32 adv_aad_mux_on; /* ADV assertion time for AAD */ - u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */ - u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */ - - /* WE signals timings corresponding to GPMC_CONFIG4 */ - u32 we_on; /* WE assertion time */ - u32 we_off; /* WE deassertion time */ - - /* OE signals timings corresponding to GPMC_CONFIG4 */ - u32 oe_on; /* OE assertion time */ - u32 oe_off; /* OE deassertion time */ - u32 oe_aad_mux_on; /* OE assertion time for AAD */ - u32 oe_aad_mux_off; /* OE deassertion time for AAD */ - - /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */ - u32 page_burst_access; /* Multiple access word delay */ - u32 access; /* Start-cycle to first data valid delay */ - u32 rd_cycle; /* Total read cycle time */ - u32 wr_cycle; /* Total write cycle time */ - - u32 bus_turnaround; - u32 cycle2cycle_delay; - - u32 wait_monitoring; - u32 clk_activation; - - /* The following are only on OMAP3430 */ - u32 wr_access; /* WRACCESSTIME */ - u32 wr_data_mux_bus; /* WRDATAONADMUXBUS */ - - struct gpmc_bool_timings bool_timings; -}; - -/* Device timings in picoseconds */ -struct gpmc_device_timings { - u32 t_ceasu; /* address setup to CS valid */ - u32 t_avdasu; /* address setup to ADV valid */ - /* XXX: try to combine t_avdp_r & t_avdp_w. Issue is - * of tusb using these timings even for sync whilst - * ideally for adv_rd/(wr)_off it should have considered - * t_avdh instead. This indirectly necessitates r/w - * variations of t_avdp as it is possible to have one - * sync & other async - */ - u32 t_avdp_r; /* ADV low time (what about t_cer ?) */ - u32 t_avdp_w; - u32 t_aavdh; /* address hold time */ - u32 t_oeasu; /* address setup to OE valid */ - u32 t_aa; /* access time from ADV assertion */ - u32 t_iaa; /* initial access time */ - u32 t_oe; /* access time from OE assertion */ - u32 t_ce; /* access time from CS asertion */ - u32 t_rd_cycle; /* read cycle time */ - u32 t_cez_r; /* read CS deassertion to high Z */ - u32 t_cez_w; /* write CS deassertion to high Z */ - u32 t_oez; /* OE deassertion to high Z */ - u32 t_weasu; /* address setup to WE valid */ - u32 t_wpl; /* write assertion time */ - u32 t_wph; /* write deassertion time */ - u32 t_wr_cycle; /* write cycle time */ - - u32 clk; - u32 t_bacc; /* burst access valid clock to output delay */ - u32 t_ces; /* CS setup time to clk */ - u32 t_avds; /* ADV setup time to clk */ - u32 t_avdh; /* ADV hold time from clk */ - u32 t_ach; /* address hold time from clk */ - u32 t_rdyo; /* clk to ready valid */ - - u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */ - u32 t_ce_avd; /* CS on to ADV on delay */ - - /* XXX: check the possibility of combining - * cyc_aavhd_oe & cyc_aavdh_we - */ - u8 cyc_aavdh_oe;/* read address hold time in cycles */ - u8 cyc_aavdh_we;/* write address hold time in cycles */ - u8 cyc_oe; /* access time from OE assertion in cycles */ - u8 cyc_wpl; /* write deassertion time in cycles */ - u32 cyc_iaa; /* initial access time in cycles */ - - /* extra delays */ - bool ce_xdelay; - bool avd_xdelay; - bool oe_xdelay; - bool we_xdelay; -}; - -struct gpmc_settings { - bool burst_wrap; /* enables wrap bursting */ - bool burst_read; /* enables read page/burst mode */ - bool burst_write; /* enables write page/burst mode */ - bool device_nand; /* device is NAND */ - bool sync_read; /* enables synchronous reads */ - bool sync_write; /* enables synchronous writes */ - bool wait_on_read; /* monitor wait on reads */ - bool wait_on_write; /* monitor wait on writes */ - u32 burst_len; /* page/burst length */ - u32 device_width; /* device bus width (8 or 16 bit) */ - u32 mux_add_data; /* multiplex address & data */ - u32 wait_pin; /* wait-pin to be used */ -}; - extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t, struct gpmc_settings *gpmc_s, struct gpmc_device_timings *dev_t); diff --git a/include/linux/platform_data/gpmc-omap.h b/include/linux/platform_data/gpmc-omap.h index 6804a8b387d7..67ccdb0e1606 100644 --- a/include/linux/platform_data/gpmc-omap.h +++ b/include/linux/platform_data/gpmc-omap.h @@ -15,10 +15,152 @@ /* Maximum Number of Chip Selects */ #define GPMC_CS_NUM 8 +/* bool type time settings */ +struct gpmc_bool_timings { + bool cycle2cyclediffcsen; + bool cycle2cyclesamecsen; + bool we_extra_delay; + bool oe_extra_delay; + bool adv_extra_delay; + bool cs_extra_delay; + bool time_para_granularity; +}; + +/* + * Note that all values in this struct are in nanoseconds except sync_clk + * (which is in picoseconds), while the register values are in gpmc_fck cycles. + */ +struct gpmc_timings { + /* Minimum clock period for synchronous mode (in picoseconds) */ + u32 sync_clk; + + /* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */ + u32 cs_on; /* Assertion time */ + u32 cs_rd_off; /* Read deassertion time */ + u32 cs_wr_off; /* Write deassertion time */ + + /* ADV signal timings corresponding to GPMC_CONFIG3 */ + u32 adv_on; /* Assertion time */ + u32 adv_rd_off; /* Read deassertion time */ + u32 adv_wr_off; /* Write deassertion time */ + u32 adv_aad_mux_on; /* ADV assertion time for AAD */ + u32 adv_aad_mux_rd_off; /* ADV read deassertion time for AAD */ + u32 adv_aad_mux_wr_off; /* ADV write deassertion time for AAD */ + + /* WE signals timings corresponding to GPMC_CONFIG4 */ + u32 we_on; /* WE assertion time */ + u32 we_off; /* WE deassertion time */ + + /* OE signals timings corresponding to GPMC_CONFIG4 */ + u32 oe_on; /* OE assertion time */ + u32 oe_off; /* OE deassertion time */ + u32 oe_aad_mux_on; /* OE assertion time for AAD */ + u32 oe_aad_mux_off; /* OE deassertion time for AAD */ + + /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */ + u32 page_burst_access; /* Multiple access word delay */ + u32 access; /* Start-cycle to first data valid delay */ + u32 rd_cycle; /* Total read cycle time */ + u32 wr_cycle; /* Total write cycle time */ + + u32 bus_turnaround; + u32 cycle2cycle_delay; + + u32 wait_monitoring; + u32 clk_activation; + + /* The following are only on OMAP3430 */ + u32 wr_access; /* WRACCESSTIME */ + u32 wr_data_mux_bus; /* WRDATAONADMUXBUS */ + + struct gpmc_bool_timings bool_timings; +}; + +/* Device timings in picoseconds */ +struct gpmc_device_timings { + u32 t_ceasu; /* address setup to CS valid */ + u32 t_avdasu; /* address setup to ADV valid */ + /* XXX: try to combine t_avdp_r & t_avdp_w. Issue is + * of tusb using these timings even for sync whilst + * ideally for adv_rd/(wr)_off it should have considered + * t_avdh instead. This indirectly necessitates r/w + * variations of t_avdp as it is possible to have one + * sync & other async + */ + u32 t_avdp_r; /* ADV low time (what about t_cer ?) */ + u32 t_avdp_w; + u32 t_aavdh; /* address hold time */ + u32 t_oeasu; /* address setup to OE valid */ + u32 t_aa; /* access time from ADV assertion */ + u32 t_iaa; /* initial access time */ + u32 t_oe; /* access time from OE assertion */ + u32 t_ce; /* access time from CS asertion */ + u32 t_rd_cycle; /* read cycle time */ + u32 t_cez_r; /* read CS deassertion to high Z */ + u32 t_cez_w; /* write CS deassertion to high Z */ + u32 t_oez; /* OE deassertion to high Z */ + u32 t_weasu; /* address setup to WE valid */ + u32 t_wpl; /* write assertion time */ + u32 t_wph; /* write deassertion time */ + u32 t_wr_cycle; /* write cycle time */ + + u32 clk; + u32 t_bacc; /* burst access valid clock to output delay */ + u32 t_ces; /* CS setup time to clk */ + u32 t_avds; /* ADV setup time to clk */ + u32 t_avdh; /* ADV hold time from clk */ + u32 t_ach; /* address hold time from clk */ + u32 t_rdyo; /* clk to ready valid */ + + u32 t_ce_rdyz; /* XXX: description ?, or use t_cez instead */ + u32 t_ce_avd; /* CS on to ADV on delay */ + + /* XXX: check the possibility of combining + * cyc_aavhd_oe & cyc_aavdh_we + */ + u8 cyc_aavdh_oe;/* read address hold time in cycles */ + u8 cyc_aavdh_we;/* write address hold time in cycles */ + u8 cyc_oe; /* access time from OE assertion in cycles */ + u8 cyc_wpl; /* write deassertion time in cycles */ + u32 cyc_iaa; /* initial access time in cycles */ + + /* extra delays */ + bool ce_xdelay; + bool avd_xdelay; + bool oe_xdelay; + bool we_xdelay; +}; + +#define GPMC_BURST_4 4 /* 4 word burst */ +#define GPMC_BURST_8 8 /* 8 word burst */ +#define GPMC_BURST_16 16 /* 16 word burst */ +#define GPMC_DEVWIDTH_8BIT 1 /* 8-bit device width */ +#define GPMC_DEVWIDTH_16BIT 2 /* 16-bit device width */ +#define GPMC_MUX_AAD 1 /* Addr-Addr-Data multiplex */ +#define GPMC_MUX_AD 2 /* Addr-Data multiplex */ + +struct gpmc_settings { + bool burst_wrap; /* enables wrap bursting */ + bool burst_read; /* enables read page/burst mode */ + bool burst_write; /* enables write page/burst mode */ + bool device_nand; /* device is NAND */ + bool sync_read; /* enables synchronous reads */ + bool sync_write; /* enables synchronous writes */ + bool wait_on_read; /* monitor wait on reads */ + bool wait_on_write; /* monitor wait on writes */ + u32 burst_len; /* page/burst length */ + u32 device_width; /* device bus width (8 or 16 bit) */ + u32 mux_add_data; /* multiplex address & data */ + u32 wait_pin; /* wait-pin to be used */ +}; + /* Data for each chip select */ struct gpmc_omap_cs_data { bool valid; /* data is valid */ bool is_nand; /* device within this CS is NAND */ + struct gpmc_settings *settings; + struct gpmc_device_timings *device_timings; + struct gpmc_timings *gpmc_timings; struct platform_device *pdev; /* device within this CS region */ unsigned int pdata_size; }; From f47fcad63f6847ea677c6c7030f30fd6438e0052 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 5 Aug 2015 13:58:01 +0300 Subject: [PATCH 006/155] memory: omap-gpmc: Introduce GPMC to NAND interface The OMAP GPMC module has certain registers dedicated for NAND access and some NAND bits mixed with other GPMC functionality. For the NAND dedicated registers we have the struct gpmc_nand_regs. The NAND driver needs to access NAND specific bits from the following non-dedicated registers - EMPTYWRITEBUFFERSTATUS from GPMC_STATUS For accessing these bits we introduce the struct gpmc_nand_ops. Add gpmc_omap_get_nand_ops() that returns the gpmc_nand_ops along with updating the gpmc_nand_regs. This API will be called by the OMAP NAND driver to access the necessary bits in GPMC register space. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- drivers/memory/omap-gpmc.c | 21 +++++++++++++++++++++ include/linux/omap-gpmc.h | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 21825ddce4a3..0b62afd86f7e 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1118,6 +1118,27 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) } } +static struct gpmc_nand_ops nand_ops; + +/** + * gpmc_omap_get_nand_ops - Get the GPMC NAND interface + * @regs: the GPMC NAND register map exclusive for NAND use. + * @cs: GPMC chip select number on which the NAND sits. The + * register map returned will be specific to this chip select. + * + * Returns NULL on error e.g. invalid cs. + */ +struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs) +{ + if (cs >= gpmc_cs_num) + return NULL; + + gpmc_update_nand_reg(reg, cs); + + return &nand_ops; +} +EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops); + int gpmc_get_client_irq(unsigned irq_config) { int i; diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index 2dcef1c8c8d4..dc2ada6fb9b4 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -14,14 +14,45 @@ #define GPMC_IRQ_FIFOEVENTENABLE 0x01 #define GPMC_IRQ_COUNT_EVENT 0x02 +/** + * gpmc_nand_ops - Interface between NAND and GPMC + * @nand_write_buffer_empty: get the NAND write buffer empty status. + */ +struct gpmc_nand_ops { + bool (*nand_writebuffer_empty)(void); +}; + +struct gpmc_nand_regs; + +#if IS_ENABLED(CONFIG_OMAP_GPMC) +struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs, + int cs); +#else +static inline gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *regs, + int cs) +{ + return NULL; +} +#endif /* CONFIG_OMAP_GPMC */ + +/*--------------------------------*/ + +/* deprecated APIs */ +#if IS_ENABLED(CONFIG_OMAP_GPMC) +void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs); +#else +static inline void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) +{ +} +#endif /* CONFIG_OMAP_GPMC */ +/*--------------------------------*/ + extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t, struct gpmc_settings *gpmc_s, struct gpmc_device_timings *dev_t); -struct gpmc_nand_regs; struct device_node; -extern void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs); extern int gpmc_get_client_irq(unsigned irq_config); extern unsigned int gpmc_ticks_to_ns(unsigned int ticks); From 512d73d1c64f15da9cdcdcdfba3cd8db0d4d94cc Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 5 Aug 2015 13:34:50 +0300 Subject: [PATCH 007/155] memory: omap-gpmc: Add GPMC-NAND ops to get writebufferempty status This is needed by OMAP NAND driver to poll the empty status of the writebuffer. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- drivers/memory/omap-gpmc.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 0b62afd86f7e..90dfba5a8f55 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -81,6 +81,8 @@ #define GPMC_CONFIG_LIMITEDADDRESS BIT(1) +#define GPMC_STATUS_EMPTYWRITEBUFFERSTATUS BIT(0) + #define GPMC_CONFIG2_CSEXTRADELAY BIT(7) #define GPMC_CONFIG3_ADVEXTRADELAY BIT(7) #define GPMC_CONFIG4_OEEXTRADELAY BIT(7) @@ -1118,7 +1120,17 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) } } -static struct gpmc_nand_ops nand_ops; +static bool gpmc_nand_writebuffer_empty(void) +{ + if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS) + return true; + + return false; +} + +static struct gpmc_nand_ops nand_ops = { + .nand_writebuffer_empty = gpmc_nand_writebuffer_empty, +}; /** * gpmc_omap_get_nand_ops - Get the GPMC NAND interface From 384258f252727c67772bbd48dad3185a30ba50d3 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 30 Jul 2015 14:49:23 +0300 Subject: [PATCH 008/155] memory: omap-gpmc: Implement IRQ domain for NAND IRQs GPMC provides 2 interrupts for NAND use. i.e. fifoevent and termcount. Use IRQ domain for this. NAND device tree node can then get the necessary interrupts by using gpmc as the interrupt parent. Legacy boot uses gpmc_get_client_irq to get the NAND interrupts from the GPMC IRQ domain. Get rid of custom bitmasks and use IRQ domain for that as well. Signed-off-by: Roger Quadros Acked-by: Rob Herring Acked-by: Tony Lindgren --- .../devicetree/bindings/bus/ti-gpmc.txt | 8 + drivers/memory/omap-gpmc.c | 248 ++++++++++-------- include/linux/omap-gpmc.h | 5 +- 3 files changed, 145 insertions(+), 116 deletions(-) diff --git a/Documentation/devicetree/bindings/bus/ti-gpmc.txt b/Documentation/devicetree/bindings/bus/ti-gpmc.txt index 01683707060b..13f13786f992 100644 --- a/Documentation/devicetree/bindings/bus/ti-gpmc.txt +++ b/Documentation/devicetree/bindings/bus/ti-gpmc.txt @@ -32,6 +32,12 @@ Required properties: bootloader) are used for the physical address decoding. As this will change in the future, filling correct values here is a requirement. + - interrupt-controller: The GPMC driver implements and interrupt controller for + the NAND events "fifoevent" and "termcount". + The interrupt number mapping is as follows + 0 - NAND_fifoevent + 1 - NAND_termcount + - interrupt-cells: Must be set to 2 Timing properties for child nodes. All are optional and default to 0. @@ -130,6 +136,8 @@ Example for an AM33xx board: #address-cells = <2>; #size-cells = <1>; ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */ + interrupt-controller; + #interrupt-cells = <2>; /* child nodes go here */ }; diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 90dfba5a8f55..e28d6bc2500a 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -127,7 +128,6 @@ #define GPMC_CONFIG_RDY_BSY 0x00000001 #define GPMC_CONFIG_DEV_SIZE 0x00000002 #define GPMC_CONFIG_DEV_TYPE 0x00000003 -#define GPMC_SET_IRQ_STATUS 0x00000004 #define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31) #define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 30) @@ -176,8 +176,6 @@ #define GPMC_CONFIG_WRITEPROTECT 0x00000010 #define WR_RD_PIN_MONITORING 0x00600000 -#define GPMC_ENABLE_IRQ 0x0000000d - /* ECC commands */ #define GPMC_ECC_READ 0 /* Reset Hardware ECC for read */ #define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */ @@ -201,11 +199,6 @@ struct gpmc_cs_data { struct resource mem; }; -struct gpmc_client_irq { - unsigned irq; - u32 bitmask; -}; - /* Structure to save gpmc cs context */ struct gpmc_cs_config { u32 config1; @@ -233,9 +226,13 @@ struct omap3_gpmc_regs { struct gpmc_cs_config cs_context[GPMC_CS_NUM]; }; -static struct gpmc_client_irq gpmc_client_irq[GPMC_NR_IRQ]; -static struct irq_chip gpmc_irq_chip; -static int gpmc_irq_start; +struct gpmc_device { + struct device *dev; + int irq; + struct irq_chip irq_chip; +}; + +static struct irq_domain *gpmc_irq_domain; static struct resource gpmc_mem_root; static struct gpmc_cs_data gpmc_cs[GPMC_CS_NUM]; @@ -243,8 +240,6 @@ static DEFINE_SPINLOCK(gpmc_mem_lock); /* Define chip-selects as reserved by default until probe completes */ static unsigned int gpmc_cs_num = GPMC_CS_NUM; static unsigned int gpmc_nr_waitpins; -static struct device *gpmc_dev; -static int gpmc_irq; static resource_size_t phys_base, mem_size; static unsigned gpmc_capability; static void __iomem *gpmc_base; @@ -1056,14 +1051,6 @@ int gpmc_configure(int cmd, int wval) u32 regval; switch (cmd) { - case GPMC_ENABLE_IRQ: - gpmc_write_reg(GPMC_IRQENABLE, wval); - break; - - case GPMC_SET_IRQ_STATUS: - gpmc_write_reg(GPMC_IRQSTATUS, wval); - break; - case GPMC_CONFIG_WP: regval = gpmc_read_reg(GPMC_CONFIG); if (wval) @@ -1153,84 +1140,96 @@ EXPORT_SYMBOL_GPL(gpmc_omap_get_nand_ops); int gpmc_get_client_irq(unsigned irq_config) { - int i; + if (!gpmc_irq_domain) { + pr_warn("%s called before GPMC IRQ domain available\n", + __func__); + return 0; + } - if (hweight32(irq_config) > 1) + if (irq_config >= GPMC_NR_IRQ) return 0; - for (i = 0; i < GPMC_NR_IRQ; i++) - if (gpmc_client_irq[i].bitmask & irq_config) - return gpmc_client_irq[i].irq; - - return 0; + return irq_create_mapping(gpmc_irq_domain, irq_config); } -static int gpmc_irq_endis(unsigned irq, bool endis) +static int gpmc_irq_endis(unsigned long hwirq, bool endis) { - int i; u32 regval; - for (i = 0; i < GPMC_NR_IRQ; i++) - if (irq == gpmc_client_irq[i].irq) { - regval = gpmc_read_reg(GPMC_IRQENABLE); - if (endis) - regval |= gpmc_client_irq[i].bitmask; - else - regval &= ~gpmc_client_irq[i].bitmask; - gpmc_write_reg(GPMC_IRQENABLE, regval); - break; - } + regval = gpmc_read_reg(GPMC_IRQENABLE); + if (endis) + regval |= BIT(hwirq); + else + regval &= ~BIT(hwirq); + gpmc_write_reg(GPMC_IRQENABLE, regval); return 0; } static void gpmc_irq_disable(struct irq_data *p) { - gpmc_irq_endis(p->irq, false); + gpmc_irq_endis(p->hwirq, false); } static void gpmc_irq_enable(struct irq_data *p) { - gpmc_irq_endis(p->irq, true); + gpmc_irq_endis(p->hwirq, true); } static void gpmc_irq_noop(struct irq_data *data) { } static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; } -static int gpmc_setup_irq(void) +static int gpmc_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) { - int i; + struct gpmc_device *gpmc = d->host_data; + + irq_set_chip_data(virq, gpmc); + irq_set_chip_and_handler(virq, &gpmc->irq_chip, handle_simple_irq); + irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOAUTOEN); + + return 0; +} + +static const struct irq_domain_ops gpmc_irq_domain_ops = { + .map = gpmc_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static irqreturn_t gpmc_handle_irq(int irq, void *data) +{ + int hwirq, virq; u32 regval; + struct gpmc_device *gpmc = data; - if (!gpmc_irq) - return -EINVAL; + regval = gpmc_read_reg(GPMC_IRQSTATUS); - gpmc_irq_start = irq_alloc_descs(-1, 0, GPMC_NR_IRQ, 0); - if (gpmc_irq_start < 0) { - pr_err("irq_alloc_descs failed\n"); - return gpmc_irq_start; + if (!regval) + return IRQ_NONE; + + for (hwirq = 0; hwirq < GPMC_NR_IRQ; hwirq++) { + if (regval & BIT(hwirq)) { + virq = irq_find_mapping(gpmc_irq_domain, hwirq); + if (!virq) { + dev_warn(gpmc->dev, + "spurious irq detected hwirq %d, virq %d\n", + hwirq, virq); + } + + generic_handle_irq(virq); + } } - gpmc_irq_chip.name = "gpmc"; - gpmc_irq_chip.irq_startup = gpmc_irq_noop_ret; - gpmc_irq_chip.irq_enable = gpmc_irq_enable; - gpmc_irq_chip.irq_disable = gpmc_irq_disable; - gpmc_irq_chip.irq_shutdown = gpmc_irq_noop; - gpmc_irq_chip.irq_ack = gpmc_irq_noop; - gpmc_irq_chip.irq_mask = gpmc_irq_noop; - gpmc_irq_chip.irq_unmask = gpmc_irq_noop; + gpmc_write_reg(GPMC_IRQSTATUS, regval); - gpmc_client_irq[0].bitmask = GPMC_IRQ_FIFOEVENTENABLE; - gpmc_client_irq[1].bitmask = GPMC_IRQ_COUNT_EVENT; + return IRQ_HANDLED; +} - for (i = 0; i < GPMC_NR_IRQ; i++) { - gpmc_client_irq[i].irq = gpmc_irq_start + i; - irq_set_chip_and_handler(gpmc_client_irq[i].irq, - &gpmc_irq_chip, handle_simple_irq); - irq_modify_status(gpmc_client_irq[i].irq, IRQ_NOREQUEST, - IRQ_NOAUTOEN); - } +static int gpmc_setup_irq(struct gpmc_device *gpmc) +{ + u32 regval; + int rc; /* Disable interrupts */ gpmc_write_reg(GPMC_IRQENABLE, 0); @@ -1239,22 +1238,46 @@ static int gpmc_setup_irq(void) regval = gpmc_read_reg(GPMC_IRQSTATUS); gpmc_write_reg(GPMC_IRQSTATUS, regval); - return request_irq(gpmc_irq, gpmc_handle_irq, 0, "gpmc", NULL); -} + gpmc->irq_chip.name = "gpmc"; + gpmc->irq_chip.irq_startup = gpmc_irq_noop_ret; + gpmc->irq_chip.irq_enable = gpmc_irq_enable; + gpmc->irq_chip.irq_disable = gpmc_irq_disable; + gpmc->irq_chip.irq_shutdown = gpmc_irq_noop; + gpmc->irq_chip.irq_ack = gpmc_irq_noop; + gpmc->irq_chip.irq_mask = gpmc_irq_noop; + gpmc->irq_chip.irq_unmask = gpmc_irq_noop; -static int gpmc_free_irq(void) -{ - int i; - - if (gpmc_irq) - free_irq(gpmc_irq, NULL); - - for (i = 0; i < GPMC_NR_IRQ; i++) { - irq_set_handler(gpmc_client_irq[i].irq, NULL); - irq_set_chip(gpmc_client_irq[i].irq, &no_irq_chip); + gpmc_irq_domain = irq_domain_add_linear(gpmc->dev->of_node, + GPMC_NR_IRQ, + &gpmc_irq_domain_ops, + gpmc); + if (!gpmc_irq_domain) { + dev_err(gpmc->dev, "IRQ domain add failed\n"); + return -ENODEV; } - irq_free_descs(gpmc_irq_start, GPMC_NR_IRQ); + rc = request_irq(gpmc->irq, gpmc_handle_irq, 0, "gpmc", gpmc); + if (rc) { + dev_err(gpmc->dev, "failed to request irq %d: %d\n", + gpmc->irq, rc); + irq_domain_remove(gpmc_irq_domain); + gpmc_irq_domain = NULL; + } + + return rc; +} + +static int gpmc_free_irq(struct gpmc_device *gpmc) +{ + int hwirq; + + free_irq(gpmc->irq, gpmc); + + for (hwirq = 0; hwirq < GPMC_NR_IRQ; hwirq++) + irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq)); + + irq_domain_remove(gpmc_irq_domain); + gpmc_irq_domain = NULL; return 0; } @@ -2154,6 +2177,14 @@ static int gpmc_probe(struct platform_device *pdev) int rc; u32 l; struct resource *res; + struct gpmc_device *gpmc; + + gpmc = devm_kzalloc(&pdev->dev, sizeof(*gpmc), GFP_KERNEL); + if (!gpmc) + return -ENOMEM; + + gpmc->dev = &pdev->dev; + platform_set_drvdata(pdev, gpmc); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) @@ -2167,15 +2198,16 @@ static int gpmc_probe(struct platform_device *pdev) return PTR_ERR(gpmc_base); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) - dev_warn(&pdev->dev, "Failed to get resource: irq\n"); - else - gpmc_irq = res->start; + if (!res) { + dev_err(&pdev->dev, "Failed to get resource: irq\n"); + return -ENOENT; + } + + gpmc->irq = res->start; gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(gpmc_l3_clk)) { dev_err(&pdev->dev, "Failed to get GPMC fck\n"); - gpmc_irq = 0; return PTR_ERR(gpmc_l3_clk); } @@ -2187,8 +2219,6 @@ static int gpmc_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); - gpmc_dev = &pdev->dev; - l = gpmc_read_reg(GPMC_REVISION); /* @@ -2207,13 +2237,16 @@ static int gpmc_probe(struct platform_device *pdev) gpmc_capability = GPMC_HAS_WR_ACCESS | GPMC_HAS_WR_DATA_MUX_BUS; if (GPMC_REVISION_MAJOR(l) > 0x5) gpmc_capability |= GPMC_HAS_MUX_AAD; - dev_info(gpmc_dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l), + dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l), GPMC_REVISION_MINOR(l)); gpmc_mem_init(); - if (gpmc_setup_irq() < 0) - dev_warn(gpmc_dev, "gpmc_setup_irq failed\n"); + rc = gpmc_setup_irq(gpmc); + if (rc) { + dev_err(gpmc->dev, "gpmc_setup_irq failed\n"); + goto fail; + } if (!pdev->dev.of_node) { gpmc_cs_num = GPMC_CS_NUM; @@ -2222,21 +2255,27 @@ static int gpmc_probe(struct platform_device *pdev) rc = gpmc_probe_dt(pdev); if (rc < 0) { - pm_runtime_put_sync(&pdev->dev); - dev_err(gpmc_dev, "failed to probe DT parameters\n"); - return rc; + dev_err(gpmc->dev, "failed to probe DT parameters\n"); + gpmc_free_irq(gpmc); + goto fail; } return 0; + +fail: + pm_runtime_put_sync(&pdev->dev); + return rc; } static int gpmc_remove(struct platform_device *pdev) { - gpmc_free_irq(); + struct gpmc_device *gpmc = platform_get_drvdata(pdev); + + gpmc_free_irq(gpmc); gpmc_mem_exit(); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - gpmc_dev = NULL; + return 0; } @@ -2282,25 +2321,6 @@ static __exit void gpmc_exit(void) postcore_initcall(gpmc_init); module_exit(gpmc_exit); -static irqreturn_t gpmc_handle_irq(int irq, void *dev) -{ - int i; - u32 regval; - - regval = gpmc_read_reg(GPMC_IRQSTATUS); - - if (!regval) - return IRQ_NONE; - - for (i = 0; i < GPMC_NR_IRQ; i++) - if (regval & gpmc_client_irq[i].bitmask) - generic_handle_irq(gpmc_client_irq[i].irq); - - gpmc_write_reg(GPMC_IRQSTATUS, regval); - - return IRQ_HANDLED; -} - static struct omap3_gpmc_regs gpmc_context; void omap3_gpmc_save_context(void) diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index dc2ada6fb9b4..9e9d79e8efa5 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -11,8 +11,9 @@ #define GPMC_CONFIG_WP 0x00000005 -#define GPMC_IRQ_FIFOEVENTENABLE 0x01 -#define GPMC_IRQ_COUNT_EVENT 0x02 +/* IRQ numbers in GPMC IRQ domain for legacy boot use */ +#define GPMC_IRQ_FIFOEVENTENABLE 0 +#define GPMC_IRQ_COUNT_EVENT 1 /** * gpmc_nand_ops - Interface between NAND and GPMC From c509aefd75d026f4ef4aa306131d7a780c2eda7b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 5 Aug 2015 14:01:50 +0300 Subject: [PATCH 009/155] mtd: nand: omap: Use gpmc_omap_get_nand_ops() to get NAND registers Deprecate nand register passing via platform data and use gpmc_omap_get_nand_ops() instead. Signed-off-by: Roger Quadros Acked-by: Brian Norris Acked-by: Tony Lindgren --- arch/arm/mach-omap2/gpmc-nand.c | 2 -- drivers/mtd/nand/omap2.c | 9 ++++++++- include/linux/platform_data/mtd-nand-omap2.h | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index 72918c4973ea..04e6998c1529 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -121,8 +121,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, if (err < 0) goto out_free_cs; - gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs); - if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt)) { pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n"); err = -EINVAL; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 0749ca1a1456..cba9bf0adba1 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -28,6 +28,7 @@ #include #include +#include #include #define DRIVER_NAME "omap2-nand" @@ -168,7 +169,9 @@ struct omap_nand_info { } iomode; u_char *buf; int buf_len; + /* Interface to GPMC */ struct gpmc_nand_regs reg; + struct gpmc_nand_ops *ops; /* generated at runtime depending on ECC algorithm and layout selected */ struct nand_ecclayout oobinfo; /* fields specific for BCHx_HW ECC scheme */ @@ -1665,9 +1668,13 @@ static int omap_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); + info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); + if (!info->ops) { + dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); + return -ENODEV; + } info->pdev = pdev; info->gpmc_cs = pdata->cs; - info->reg = pdata->reg; info->of_node = pdata->of_node; info->ecc_opt = pdata->ecc_opt; nand_chip = &info->nand; diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index 090bbab0130a..a067f581e938 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -75,10 +75,12 @@ struct omap_nand_platform_data { enum nand_io xfer_type; int devsize; enum omap_ecc ecc_opt; - struct gpmc_nand_regs reg; /* for passing the partitions */ struct device_node *of_node; struct device_node *elm_of_node; + + /* deprecated */ + struct gpmc_nand_regs reg; }; #endif From d6e552168db59d627fd56074f2c588df1faf0c95 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 5 Aug 2015 13:36:43 +0300 Subject: [PATCH 010/155] mtd: nand: omap: Switch to using GPMC-NAND ops for writebuffer empty check Instead of accessing the gpmc_status register directly start using the gpmc_nand_ops->nand_writebuffer_empty() helper to check write buffer empty status. Signed-off-by: Roger Quadros Acked-by: Brian Norris Acked-by: Tony Lindgren --- drivers/mtd/nand/omap2.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index cba9bf0adba1..98023d5bfc35 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -291,14 +291,13 @@ static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) { struct omap_nand_info *info = mtd_to_omap(mtd); u_char *p = (u_char *)buf; - u32 status = 0; + bool status; while (len--) { iowrite8(*p++, info->nand.IO_ADDR_W); /* wait until buffer is available for write */ do { - status = readl(info->reg.gpmc_status) & - STATUS_BUFF_EMPTY; + status = info->ops->nand_writebuffer_empty(); } while (!status); } } @@ -326,7 +325,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) { struct omap_nand_info *info = mtd_to_omap(mtd); u16 *p = (u16 *) buf; - u32 status = 0; + bool status; /* FIXME try bursts of writesw() or DMA ... */ len >>= 1; @@ -334,8 +333,7 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) iowrite16(*p++, info->nand.IO_ADDR_W); /* wait until buffer is available for write */ do { - status = readl(info->reg.gpmc_status) & - STATUS_BUFF_EMPTY; + status = info->ops->nand_writebuffer_empty(); } while (!status); } } From 01b95fc6b299b99075b8980371fc19b980236c32 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 20 May 2014 22:29:28 +0300 Subject: [PATCH 011/155] mtd: nand: omap: Copy platform data parameters to omap_nand_info data Copy all the platform data parameters to the driver's local data structure 'omap_nand_info' and use it in the entire driver. This will make it easer for device tree migration. Signed-off-by: Roger Quadros Acked-by: Brian Norris Acked-by: Tony Lindgren --- drivers/mtd/nand/omap2.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 98023d5bfc35..7e4e263c7d9c 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -152,13 +152,17 @@ static struct nand_hw_control omap_gpmc_controller = { }; struct omap_nand_info { - struct omap_nand_platform_data *pdata; struct nand_chip nand; struct platform_device *pdev; int gpmc_cs; - unsigned long phys_base; + bool dev_ready; + enum nand_io xfer_type; + int devsize; enum omap_ecc ecc_opt; + struct device_node *elm_of_node; + + unsigned long phys_base; struct completion comp; struct dma_chan *dma; int gpmc_irq_fifo; @@ -1631,7 +1635,7 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info, "CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); return false; } - if (ecc_needs_elm && !is_elm_present(info, pdata->elm_of_node)) { + if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) { dev_err(&info->pdev->dev, "ELM not available\n"); return false; } @@ -1675,6 +1679,11 @@ static int omap_nand_probe(struct platform_device *pdev) info->gpmc_cs = pdata->cs; info->of_node = pdata->of_node; info->ecc_opt = pdata->ecc_opt; + info->dev_ready = pdata->dev_ready; + info->xfer_type = pdata->xfer_type; + info->devsize = pdata->devsize; + info->elm_of_node = pdata->elm_of_node; + nand_chip = &info->nand; mtd = nand_to_mtd(nand_chip); mtd->dev.parent = &pdev->dev; @@ -1700,7 +1709,7 @@ static int omap_nand_probe(struct platform_device *pdev) * chip delay which is slightly more than tR (AC Timing) of the NAND * device and read status register until you get a failure or success */ - if (pdata->dev_ready) { + if (info->dev_ready) { nand_chip->dev_ready = omap_dev_ready; nand_chip->chip_delay = 0; } else { @@ -1714,15 +1723,16 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->options |= NAND_SKIP_BBTSCAN; /* scan NAND device connected to chip controller */ - nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16; + nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; if (nand_scan_ident(mtd, 1, NULL)) { - dev_err(&info->pdev->dev, "scan failed, may be bus-width mismatch\n"); + dev_err(&info->pdev->dev, + "scan failed, may be bus-width mismatch\n"); err = -ENXIO; goto return_error; } /* re-populate low-level callbacks based on xfer modes */ - switch (pdata->xfer_type) { + switch (info->xfer_type) { case NAND_OMAP_PREFETCH_POLLED: nand_chip->read_buf = omap_read_buf_pref; nand_chip->write_buf = omap_write_buf_pref; @@ -1802,7 +1812,7 @@ static int omap_nand_probe(struct platform_device *pdev) default: dev_err(&pdev->dev, - "xfer_type(%d) not supported!\n", pdata->xfer_type); + "xfer_type(%d) not supported!\n", info->xfer_type); err = -EINVAL; goto return_error; } From c9711ec5250b22fd94e9b34c17c095e001a90e66 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 21 May 2014 07:29:03 +0300 Subject: [PATCH 012/155] mtd: nand: omap: Clean up device tree support Move NAND specific device tree parsing to NAND driver. The NAND controller node must have a compatible id, register space resource and interrupt resource. Signed-off-by: Roger Quadros Acked-by: Brian Norris Acked-by: Tony Lindgren --- arch/arm/mach-omap2/gpmc-nand.c | 5 +- drivers/memory/omap-gpmc.c | 143 +++++-------------- drivers/mtd/nand/omap2.c | 134 ++++++++++++++--- include/linux/platform_data/mtd-nand-omap2.h | 3 +- 4 files changed, 153 insertions(+), 132 deletions(-) diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index 04e6998c1529..f6ac027f3c3b 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -97,10 +97,7 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT); memset(&s, 0, sizeof(struct gpmc_settings)); - if (gpmc_nand_data->of_node) - gpmc_read_settings_dt(gpmc_nand_data->of_node, &s); - else - gpmc_set_legacy(gpmc_nand_data, &s); + gpmc_set_legacy(gpmc_nand_data, &s); s.device_nand = true; diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index e28d6bc2500a..8dc6e3b1c44a 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -1852,105 +1851,6 @@ static void __maybe_unused gpmc_read_timings_dt(struct device_node *np, of_property_read_bool(np, "gpmc,time-para-granularity"); } -#if IS_ENABLED(CONFIG_MTD_NAND) - -static const char * const nand_xfer_types[] = { - [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", - [NAND_OMAP_POLLED] = "polled", - [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", - [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", -}; - -static int gpmc_probe_nand_child(struct platform_device *pdev, - struct device_node *child) -{ - u32 val; - const char *s; - struct gpmc_timings gpmc_t; - struct omap_nand_platform_data *gpmc_nand_data; - - if (of_property_read_u32(child, "reg", &val) < 0) { - dev_err(&pdev->dev, "%s has no 'reg' property\n", - child->full_name); - return -ENODEV; - } - - gpmc_nand_data = devm_kzalloc(&pdev->dev, sizeof(*gpmc_nand_data), - GFP_KERNEL); - if (!gpmc_nand_data) - return -ENOMEM; - - gpmc_nand_data->cs = val; - gpmc_nand_data->of_node = child; - - /* Detect availability of ELM module */ - gpmc_nand_data->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); - if (gpmc_nand_data->elm_of_node == NULL) - gpmc_nand_data->elm_of_node = - of_parse_phandle(child, "elm_id", 0); - - /* select ecc-scheme for NAND */ - if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { - pr_err("%s: ti,nand-ecc-opt not found\n", __func__); - return -ENODEV; - } - - if (!strcmp(s, "sw")) - gpmc_nand_data->ecc_opt = OMAP_ECC_HAM1_CODE_SW; - else if (!strcmp(s, "ham1") || - !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) - gpmc_nand_data->ecc_opt = - OMAP_ECC_HAM1_CODE_HW; - else if (!strcmp(s, "bch4")) - if (gpmc_nand_data->elm_of_node) - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH4_CODE_HW; - else - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; - else if (!strcmp(s, "bch8")) - if (gpmc_nand_data->elm_of_node) - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH8_CODE_HW; - else - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; - else if (!strcmp(s, "bch16")) - if (gpmc_nand_data->elm_of_node) - gpmc_nand_data->ecc_opt = - OMAP_ECC_BCH16_CODE_HW; - else - pr_err("%s: BCH16 requires ELM support\n", __func__); - else - pr_err("%s: ti,nand-ecc-opt invalid value\n", __func__); - - /* select data transfer mode for NAND controller */ - if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) - for (val = 0; val < ARRAY_SIZE(nand_xfer_types); val++) - if (!strcasecmp(s, nand_xfer_types[val])) { - gpmc_nand_data->xfer_type = val; - break; - } - - gpmc_nand_data->flash_bbt = of_get_nand_on_flash_bbt(child); - - val = of_get_nand_bus_width(child); - if (val == 16) - gpmc_nand_data->devsize = NAND_BUSWIDTH_16; - - gpmc_read_timings_dt(child, &gpmc_t); - gpmc_nand_init(gpmc_nand_data, &gpmc_t); - - return 0; -} -#else -static int gpmc_probe_nand_child(struct platform_device *pdev, - struct device_node *child) -{ - return 0; -} -#endif - #if IS_ENABLED(CONFIG_MTD_ONENAND) static int gpmc_probe_onenand_child(struct platform_device *pdev, struct device_node *child) @@ -2069,9 +1969,42 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, goto err; } - ret = of_property_read_u32(child, "bank-width", &gpmc_s.device_width); - if (ret < 0) - goto err; + if (of_node_cmp(child->name, "nand") == 0) { + /* Warn about older DT blobs with no compatible property */ + if (!of_property_read_bool(child, "compatible")) { + dev_warn(&pdev->dev, + "Incompatible NAND node: missing compatible"); + ret = -EINVAL; + goto err; + } + } + + if (of_device_is_compatible(child, "ti,omap2-nand")) { + /* NAND specific setup */ + val = of_get_nand_bus_width(child); + switch (val) { + case 8: + gpmc_s.device_width = GPMC_DEVWIDTH_8BIT; + break; + case 16: + gpmc_s.device_width = GPMC_DEVWIDTH_16BIT; + break; + default: + dev_err(&pdev->dev, "%s: invalid 'nand-bus-width'\n", + child->name); + ret = -EINVAL; + goto err; + } + + /* disable write protect */ + gpmc_configure(GPMC_CONFIG_WP, 0); + gpmc_s.device_nand = true; + } else { + ret = of_property_read_u32(child, "bank-width", + &gpmc_s.device_width); + if (ret < 0) + goto err; + } gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings"); ret = gpmc_cs_program_settings(cs, &gpmc_s); @@ -2155,9 +2088,7 @@ static int gpmc_probe_dt(struct platform_device *pdev) if (!child->name) continue; - if (of_node_cmp(child->name, "nand") == 0) - ret = gpmc_probe_nand_child(pdev, child); - else if (of_node_cmp(child->name, "onenand") == 0) + if (of_node_cmp(child->name, "onenand") == 0) ret = gpmc_probe_onenand_child(pdev, child); else ret = gpmc_probe_generic_child(pdev, child); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 7e4e263c7d9c..35b8f3359c17 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -176,11 +177,11 @@ struct omap_nand_info { /* Interface to GPMC */ struct gpmc_nand_regs reg; struct gpmc_nand_ops *ops; + bool flash_bbt; /* generated at runtime depending on ECC algorithm and layout selected */ struct nand_ecclayout oobinfo; /* fields specific for BCHx_HW ECC scheme */ struct device *elm_dev; - struct device_node *of_node; }; static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) @@ -1643,10 +1644,86 @@ static bool omap2_nand_ecc_check(struct omap_nand_info *info, return true; } +static const char * const nand_xfer_types[] = { + [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", + [NAND_OMAP_POLLED] = "polled", + [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", + [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", +}; + +static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) +{ + struct device_node *child = dev->of_node; + int i; + const char *s; + u32 cs; + + if (of_property_read_u32(child, "reg", &cs) < 0) { + dev_err(dev, "reg not found in DT\n"); + return -EINVAL; + } + + info->gpmc_cs = cs; + + /* detect availability of ELM module. Won't be present pre-OMAP4 */ + info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); + if (!info->elm_of_node) + dev_dbg(dev, "ti,elm-id not in DT\n"); + + /* select ecc-scheme for NAND */ + if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { + dev_err(dev, "ti,nand-ecc-opt not found\n"); + return -EINVAL; + } + + if (!strcmp(s, "sw")) { + info->ecc_opt = OMAP_ECC_HAM1_CODE_SW; + } else if (!strcmp(s, "ham1") || + !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) { + info->ecc_opt = OMAP_ECC_HAM1_CODE_HW; + } else if (!strcmp(s, "bch4")) { + if (info->elm_of_node) + info->ecc_opt = OMAP_ECC_BCH4_CODE_HW; + else + info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; + } else if (!strcmp(s, "bch8")) { + if (info->elm_of_node) + info->ecc_opt = OMAP_ECC_BCH8_CODE_HW; + else + info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; + } else if (!strcmp(s, "bch16")) { + info->ecc_opt = OMAP_ECC_BCH16_CODE_HW; + } else { + dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n"); + return -EINVAL; + } + + /* select data transfer mode */ + if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) { + for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { + if (!strcasecmp(s, nand_xfer_types[i])) { + info->xfer_type = i; + goto next; + } + } + + dev_err(dev, "unrecognized value for ti,nand-xfer-type\n"); + return -EINVAL; + } + +next: + of_get_nand_on_flash_bbt(child); + + if (of_get_nand_bus_width(child) == 16) + info->devsize = NAND_BUSWIDTH_16; + + return 0; +} + static int omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; - struct omap_nand_platform_data *pdata; + struct omap_nand_platform_data *pdata = NULL; struct mtd_info *mtd; struct nand_chip *nand_chip; struct nand_ecclayout *ecclayout; @@ -1656,39 +1733,47 @@ static int omap_nand_probe(struct platform_device *pdev) unsigned sig; unsigned oob_index; struct resource *res; - - pdata = dev_get_platdata(&pdev->dev); - if (pdata == NULL) { - dev_err(&pdev->dev, "platform data missing\n"); - return -ENODEV; - } + struct device *dev = &pdev->dev; info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), GFP_KERNEL); if (!info) return -ENOMEM; - platform_set_drvdata(pdev, info); + info->pdev = pdev; + if (dev->of_node) { + if (omap_get_dt_info(dev, info)) + return -EINVAL; + } else { + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "platform data missing\n"); + return -EINVAL; + } + + info->gpmc_cs = pdata->cs; + info->reg = pdata->reg; + info->ecc_opt = pdata->ecc_opt; + info->dev_ready = pdata->dev_ready; + info->xfer_type = pdata->xfer_type; + info->devsize = pdata->devsize; + info->elm_of_node = pdata->elm_of_node; + info->flash_bbt = pdata->flash_bbt; + } + + platform_set_drvdata(pdev, info); info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); if (!info->ops) { dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); return -ENODEV; } - info->pdev = pdev; - info->gpmc_cs = pdata->cs; - info->of_node = pdata->of_node; - info->ecc_opt = pdata->ecc_opt; - info->dev_ready = pdata->dev_ready; - info->xfer_type = pdata->xfer_type; - info->devsize = pdata->devsize; - info->elm_of_node = pdata->elm_of_node; nand_chip = &info->nand; mtd = nand_to_mtd(nand_chip); mtd->dev.parent = &pdev->dev; nand_chip->ecc.priv = NULL; - nand_set_flash_node(nand_chip, pdata->of_node); + nand_set_flash_node(nand_chip, dev->of_node); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); @@ -1717,7 +1802,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->chip_delay = 50; } - if (pdata->flash_bbt) + if (info->flash_bbt) nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; else nand_chip->options |= NAND_SKIP_BBTSCAN; @@ -2035,7 +2120,10 @@ scan_tail: goto return_error; } - mtd_device_register(mtd, pdata->parts, pdata->nr_parts); + if (dev->of_node) + mtd_device_register(mtd, NULL, 0); + else + mtd_device_register(mtd, pdata->parts, pdata->nr_parts); platform_set_drvdata(pdev, mtd); @@ -2066,11 +2154,17 @@ static int omap_nand_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id omap_nand_ids[] = { + { .compatible = "ti,omap2-nand", }, + {}, +}; + static struct platform_driver omap_nand_driver = { .probe = omap_nand_probe, .remove = omap_nand_remove, .driver = { .name = DRIVER_NAME, + .of_match_table = of_match_ptr(omap_nand_ids), }, }; diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index a067f581e938..ff27e5a77e03 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -76,11 +76,10 @@ struct omap_nand_platform_data { int devsize; enum omap_ecc ecc_opt; - /* for passing the partitions */ - struct device_node *of_node; struct device_node *elm_of_node; /* deprecated */ struct gpmc_nand_regs reg; + struct device_node *of_node; }; #endif From 51735caad3db6237fa9d31a2ce8b54cbd42ff6f0 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 May 2014 11:54:45 +0300 Subject: [PATCH 013/155] mtd: nand: omap: Update DT binding documentation Add compatible id and interrupts. The NAND interrupts are provided by the GPMC controller node. Signed-off-by: Roger Quadros Acked-by: Rob Herring Acked-by: Brian Norris Acked-by: Tony Lindgren --- .../devicetree/bindings/mtd/gpmc-nand.txt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt index fb733c4e1c11..ff3215d20343 100644 --- a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt +++ b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt @@ -13,7 +13,11 @@ Documentation/devicetree/bindings/mtd/nand.txt Required properties: - - reg: The CS line the peripheral is connected to + - compatible: "ti,omap2-nand" + - reg: range id (CS number), base offset and length of the + NAND I/O space + - interrupt-parent: must point to gpmc node + - interrupts: Two interrupt specifiers, one for fifoevent, one for termcount. Optional properties: @@ -55,17 +59,22 @@ Example for an AM33xx board: gpmc: gpmc@50000000 { compatible = "ti,am3352-gpmc"; ti,hwmods = "gpmc"; - reg = <0x50000000 0x1000000>; + reg = <0x50000000 0x36c>; interrupts = <100>; gpmc,num-cs = <8>; gpmc,num-waitpins = <2>; #address-cells = <2>; #size-cells = <1>; - ranges = <0 0 0x08000000 0x2000>; /* CS0: NAND */ + ranges = <0 0 0x08000000 0x1000000>; /* CS0 space, 16MB */ elm_id = <&elm>; + interrupt-controller; + #interrupt-cells = <2>; nand@0,0 { - reg = <0 0 0>; /* CS0, offset 0 */ + compatible = "ti,omap2-nand"; + reg = <0 0 4>; /* CS0, offset 0, NAND I/O window 4 */ + interrupt-parent = <&gpmc>; + interrupts = <0 IRQ_TYPE_NONE>, <1 IRQ_TYPE NONE>; nand-bus-width = <16>; ti,nand-ecc-opt = "bch8"; ti,nand-xfer-type = "polled"; From bdd7e033fe4cc3836b141b75119a4a975a64d9bc Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 9 Jul 2015 17:31:45 +0300 Subject: [PATCH 014/155] memory: omap-gpmc: Prevent mapping into 1st 16MB We have been preventing mapping GPMC children in the first 1MB but really it has to be the first 16MB as the minimum GPMC partition size is 16MB. Also print an error message if CS mapping fails due to DT requesting address outside the GPMC map. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- drivers/memory/omap-gpmc.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 8dc6e3b1c44a..bfe4e8710973 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -94,6 +94,14 @@ #define GPMC_CS_SIZE 0x30 #define GPMC_BCH_SIZE 0x10 +/* + * The first 1MB of GPMC address space is typically mapped to + * the internal ROM. Never allocate the first page, to + * facilitate bug detection; even if we didn't boot from ROM. + * As GPMC minimum partition size is 16MB we can only start from + * there. + */ +#define GPMC_MEM_START 0x1000000 #define GPMC_MEM_END 0x3FFFFFFF #define GPMC_CHUNK_SHIFT 24 /* 16 MB */ @@ -1297,12 +1305,7 @@ static void gpmc_mem_init(void) { int cs; - /* - * The first 1MB of GPMC address space is typically mapped to - * the internal ROM. Never allocate the first page, to - * facilitate bug detection; even if we didn't boot from ROM. - */ - gpmc_mem_root.start = SZ_1M; + gpmc_mem_root.start = GPMC_MEM_START; gpmc_mem_root.end = GPMC_MEM_END; /* Reserve all regions that has been set up by bootloader */ @@ -1966,6 +1969,15 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, if (ret < 0) { dev_err(&pdev->dev, "cannot remap GPMC CS %d to %pa\n", cs, &res.start); + if (res.start < GPMC_MEM_START) { + dev_info(&pdev->dev, + "GPMC CS %d start cannot be lesser than 0x%x\n", + cs, GPMC_MEM_START); + } else if (res.end > GPMC_MEM_END) { + dev_info(&pdev->dev, + "GPMC CS %d end cannot be greater than 0x%x\n", + cs, GPMC_MEM_END); + } goto err; } From 3c76f6119a64eb8ff6d088ceb6ca03891e29a7ce Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 5 Aug 2015 17:01:54 +0300 Subject: [PATCH 015/155] memory: omap-gpmc: Move device tree binding to correct location omap-gpmc.c is a memory controller so move the binding to the right place. Signed-off-by: Roger Quadros Acked-by: Rob Herring Acked-by: Tony Lindgren --- .../{bus/ti-gpmc.txt => memory-controllers/omap-gpmc.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Documentation/devicetree/bindings/{bus/ti-gpmc.txt => memory-controllers/omap-gpmc.txt} (100%) diff --git a/Documentation/devicetree/bindings/bus/ti-gpmc.txt b/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt similarity index 100% rename from Documentation/devicetree/bindings/bus/ti-gpmc.txt rename to Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt From d2d00862dfbbd22d80ee67f816cb7eeaea71f03b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 7 Mar 2016 12:18:43 +0200 Subject: [PATCH 016/155] memory: omap-gpmc: Support general purpose input for WAITPINs OMAPs can have 2 to 4 WAITPINs that can be used as general purpose input if not used for memory wait state insertion. The first user will be the OMAP NAND chip to get the NAND read/busy status using gpiolib. Signed-off-by: Roger Quadros Acked-by: Rob Herring Acked-by: Tony Lindgren --- .../bindings/memory-controllers/omap-gpmc.txt | 6 + drivers/memory/Kconfig | 1 + drivers/memory/omap-gpmc.c | 117 ++++++++++++++++-- 3 files changed, 112 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt b/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt index 13f13786f992..97e71924dbbb 100644 --- a/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt +++ b/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt @@ -38,6 +38,10 @@ Required properties: 0 - NAND_fifoevent 1 - NAND_termcount - interrupt-cells: Must be set to 2 + - gpio-controller: The GPMC driver implements a GPIO controller for the + GPMC WAIT pins that can be used as general purpose inputs. + 0 maps to GPMC_WAIT0 pin. + - gpio-cells: Must be set to 2 Timing properties for child nodes. All are optional and default to 0. @@ -138,6 +142,8 @@ Example for an AM33xx board: ranges = <0 0 0x08000000 0x10000000>; /* CS0 @addr 0x8000000, size 0x10000000 */ interrupt-controller; #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; /* child nodes go here */ }; diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index 51d5cd20c26a..a9b1c1419bef 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -51,6 +51,7 @@ config TI_EMIF config OMAP_GPMC bool + select GPIOLIB help This driver is for the General Purpose Memory Controller (GPMC) present on Texas Instruments SoCs (e.g. OMAP2+). GPMC allows diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index bfe4e8710973..4dd1c65ee70c 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -237,6 +238,7 @@ struct gpmc_device { struct device *dev; int irq; struct irq_chip irq_chip; + struct gpio_chip gpio_chip; }; static struct irq_domain *gpmc_irq_domain; @@ -2064,10 +2066,71 @@ err: return ret; } +static int gpmc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + return 1; /* we're input only */ +} + +static int gpmc_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + return 0; /* we're input only */ +} + +static int gpmc_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + return -EINVAL; /* we're input only */ +} + +static void gpmc_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ +} + +static int gpmc_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + u32 reg; + + offset += 8; + + reg = gpmc_read_reg(GPMC_STATUS) & BIT(offset); + + return !!reg; +} + +static int gpmc_gpio_init(struct gpmc_device *gpmc) +{ + int ret; + + gpmc->gpio_chip.parent = gpmc->dev; + gpmc->gpio_chip.owner = THIS_MODULE; + gpmc->gpio_chip.label = DEVICE_NAME; + gpmc->gpio_chip.ngpio = gpmc_nr_waitpins; + gpmc->gpio_chip.get_direction = gpmc_gpio_get_direction; + gpmc->gpio_chip.direction_input = gpmc_gpio_direction_input; + gpmc->gpio_chip.direction_output = gpmc_gpio_direction_output; + gpmc->gpio_chip.set = gpmc_gpio_set; + gpmc->gpio_chip.get = gpmc_gpio_get; + gpmc->gpio_chip.base = -1; + + ret = gpiochip_add(&gpmc->gpio_chip); + if (ret < 0) { + dev_err(gpmc->dev, "could not register gpio chip: %d\n", ret); + return ret; + } + + return 0; +} + +static void gpmc_gpio_exit(struct gpmc_device *gpmc) +{ + gpiochip_remove(&gpmc->gpio_chip); +} + static int gpmc_probe_dt(struct platform_device *pdev) { int ret; - struct device_node *child; const struct of_device_id *of_id = of_match_device(gpmc_dt_ids, &pdev->dev); @@ -2095,6 +2158,14 @@ static int gpmc_probe_dt(struct platform_device *pdev) return ret; } + return 0; +} + +static int gpmc_probe_dt_children(struct platform_device *pdev) +{ + int ret; + struct device_node *child; + for_each_available_child_of_node(pdev->dev.of_node, child) { if (!child->name) @@ -2104,6 +2175,9 @@ static int gpmc_probe_dt(struct platform_device *pdev) ret = gpmc_probe_onenand_child(pdev, child); else ret = gpmc_probe_generic_child(pdev, child); + + if (ret) + return ret; } return 0; @@ -2113,6 +2187,11 @@ static int gpmc_probe_dt(struct platform_device *pdev) { return 0; } + +static int gpmc_probe_dt_children(struct platform_device *pdev) +{ + return 0; +} #endif static int gpmc_probe(struct platform_device *pdev) @@ -2159,6 +2238,15 @@ static int gpmc_probe(struct platform_device *pdev) return -EINVAL; } + if (pdev->dev.of_node) { + rc = gpmc_probe_dt(pdev); + if (rc) + return rc; + } else { + gpmc_cs_num = GPMC_CS_NUM; + gpmc_nr_waitpins = GPMC_NR_WAITPINS; + } + pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); @@ -2184,29 +2272,33 @@ static int gpmc_probe(struct platform_device *pdev) GPMC_REVISION_MINOR(l)); gpmc_mem_init(); + rc = gpmc_gpio_init(gpmc); + if (rc) + goto gpio_init_failed; rc = gpmc_setup_irq(gpmc); if (rc) { dev_err(gpmc->dev, "gpmc_setup_irq failed\n"); - goto fail; + goto setup_irq_failed; } - if (!pdev->dev.of_node) { - gpmc_cs_num = GPMC_CS_NUM; - gpmc_nr_waitpins = GPMC_NR_WAITPINS; - } - - rc = gpmc_probe_dt(pdev); + rc = gpmc_probe_dt_children(pdev); if (rc < 0) { - dev_err(gpmc->dev, "failed to probe DT parameters\n"); - gpmc_free_irq(gpmc); - goto fail; + dev_err(gpmc->dev, "failed to probe DT children\n"); + goto dt_children_failed; } return 0; -fail: +dt_children_failed: + gpmc_free_irq(gpmc); +setup_irq_failed: + gpmc_gpio_exit(gpmc); +gpio_init_failed: + gpmc_mem_exit(); pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return rc; } @@ -2215,6 +2307,7 @@ static int gpmc_remove(struct platform_device *pdev) struct gpmc_device *gpmc = platform_get_drvdata(pdev); gpmc_free_irq(gpmc); + gpmc_gpio_exit(gpmc); gpmc_mem_exit(); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); From 210325f0f4eb531f83ffb0b0f95612e2a8063983 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 6 Aug 2015 13:21:40 +0300 Subject: [PATCH 017/155] memory: omap-gpmc: Reserve WAITPIN if needed for WAIT monitoring If the device attached to GPMC wants to use the WAIT pin for WAIT monitoring then we reserve it internally for exclusive use. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- drivers/memory/omap-gpmc.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 4dd1c65ee70c..784a64f31a8b 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1911,6 +1911,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, const char *name; int ret, cs; u32 val; + struct gpio_desc *waitpin_desc = NULL; + struct gpmc_device *gpmc = platform_get_drvdata(pdev); if (of_property_read_u32(child, "reg", &cs) < 0) { dev_err(&pdev->dev, "%s has no 'reg' property\n", @@ -2020,16 +2022,30 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, goto err; } + /* Reserve wait pin if it is required and valid */ + if (gpmc_s.wait_on_read || gpmc_s.wait_on_write) { + unsigned int wait_pin = gpmc_s.wait_pin; + + waitpin_desc = gpiochip_request_own_desc(&gpmc->gpio_chip, + wait_pin, "WAITPIN"); + if (IS_ERR(waitpin_desc)) { + dev_err(&pdev->dev, "invalid wait-pin: %d\n", wait_pin); + ret = PTR_ERR(waitpin_desc); + goto err; + } + } + gpmc_cs_show_timings(cs, "before gpmc_cs_program_settings"); + ret = gpmc_cs_program_settings(cs, &gpmc_s); if (ret < 0) - goto err; + goto err_cs; ret = gpmc_cs_set_timings(cs, &gpmc_t, &gpmc_s); if (ret) { dev_err(&pdev->dev, "failed to set gpmc timings for: %s\n", child->name); - goto err; + goto err_cs; } /* Clear limited address i.e. enable A26-A11 */ @@ -2060,6 +2076,10 @@ err_child_fail: dev_err(&pdev->dev, "failed to create gpmc child %s\n", child->name); ret = -ENODEV; +err_cs: + if (waitpin_desc) + gpiochip_free_own_desc(waitpin_desc); + err: gpmc_cs_free(cs); From b2bac25a4d298309bb4b2649bb1107ddaa287c47 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 19 Feb 2016 11:01:02 +0200 Subject: [PATCH 018/155] memory: omap-gpmc: Support WAIT pin edge interrupts OMAPs can have 2 to 4 WAITPINs that can be used as edge triggered interrupts if not used for memory wait state insertion. Support these interrupts via the gpmc IRQ domain. The gpmc IRQ domain interrupt map is: 0 - NAND_fifoevent 1 - NAND_termcount 2 - GPMC_WAIT0 edge 3 - GPMC_WAIT1 edge, and so on Signed-off-by: Roger Quadros Acked-by: Rob Herring Acked-by: Tony Lindgren --- .../bindings/memory-controllers/omap-gpmc.txt | 5 +- drivers/memory/omap-gpmc.c | 106 +++++++++++++++--- 2 files changed, 92 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt b/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt index 97e71924dbbb..21055e210234 100644 --- a/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt +++ b/Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt @@ -33,10 +33,13 @@ Required properties: As this will change in the future, filling correct values here is a requirement. - interrupt-controller: The GPMC driver implements and interrupt controller for - the NAND events "fifoevent" and "termcount". + the NAND events "fifoevent" and "termcount" plus the + rising/falling edges on the GPMC_WAIT pins. The interrupt number mapping is as follows 0 - NAND_fifoevent 1 - NAND_termcount + 2 - GPMC_WAIT0 pin edge + 3 - GPMC_WAIT1 pin edge, and so on. - interrupt-cells: Must be set to 2 - gpio-controller: The GPMC driver implements a GPIO controller for the GPMC WAIT pins that can be used as general purpose inputs. diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 784a64f31a8b..ea9c89747950 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -189,9 +189,7 @@ #define GPMC_ECC_WRITE 1 /* Reset Hardware ECC for write */ #define GPMC_ECC_READSYN 2 /* Reset before syndrom is read back */ -/* XXX: Only NAND irq has been considered,currently these are the only ones used - */ -#define GPMC_NR_IRQ 2 +#define GPMC_NR_NAND_IRQS 2 /* number of NAND specific IRQs */ enum gpmc_clk_domain { GPMC_CD_FCLK, @@ -239,6 +237,7 @@ struct gpmc_device { int irq; struct irq_chip irq_chip; struct gpio_chip gpio_chip; + int nirqs; }; static struct irq_domain *gpmc_irq_domain; @@ -1155,7 +1154,8 @@ int gpmc_get_client_irq(unsigned irq_config) return 0; } - if (irq_config >= GPMC_NR_IRQ) + /* we restrict this to NAND IRQs only */ + if (irq_config >= GPMC_NR_NAND_IRQS) return 0; return irq_create_mapping(gpmc_irq_domain, irq_config); @@ -1165,6 +1165,10 @@ static int gpmc_irq_endis(unsigned long hwirq, bool endis) { u32 regval; + /* bits GPMC_NR_NAND_IRQS to 8 are reserved */ + if (hwirq >= GPMC_NR_NAND_IRQS) + hwirq += 8 - GPMC_NR_NAND_IRQS; + regval = gpmc_read_reg(GPMC_IRQENABLE); if (endis) regval |= BIT(hwirq); @@ -1185,9 +1189,64 @@ static void gpmc_irq_enable(struct irq_data *p) gpmc_irq_endis(p->hwirq, true); } -static void gpmc_irq_noop(struct irq_data *data) { } +static void gpmc_irq_mask(struct irq_data *d) +{ + gpmc_irq_endis(d->hwirq, false); +} -static unsigned int gpmc_irq_noop_ret(struct irq_data *data) { return 0; } +static void gpmc_irq_unmask(struct irq_data *d) +{ + gpmc_irq_endis(d->hwirq, true); +} + +static void gpmc_irq_edge_config(unsigned long hwirq, bool rising_edge) +{ + u32 regval; + + /* NAND IRQs polarity is not configurable */ + if (hwirq < GPMC_NR_NAND_IRQS) + return; + + /* WAITPIN starts at BIT 8 */ + hwirq += 8 - GPMC_NR_NAND_IRQS; + + regval = gpmc_read_reg(GPMC_CONFIG); + if (rising_edge) + regval &= ~BIT(hwirq); + else + regval |= BIT(hwirq); + + gpmc_write_reg(GPMC_CONFIG, regval); +} + +static void gpmc_irq_ack(struct irq_data *d) +{ + unsigned int hwirq = d->hwirq; + + /* skip reserved bits */ + if (hwirq >= GPMC_NR_NAND_IRQS) + hwirq += 8 - GPMC_NR_NAND_IRQS; + + /* Setting bit to 1 clears (or Acks) the interrupt */ + gpmc_write_reg(GPMC_IRQSTATUS, BIT(hwirq)); +} + +static int gpmc_irq_set_type(struct irq_data *d, unsigned int trigger) +{ + /* can't set type for NAND IRQs */ + if (d->hwirq < GPMC_NR_NAND_IRQS) + return -EINVAL; + + /* We can support either rising or falling edge at a time */ + if (trigger == IRQ_TYPE_EDGE_FALLING) + gpmc_irq_edge_config(d->hwirq, false); + else if (trigger == IRQ_TYPE_EDGE_RISING) + gpmc_irq_edge_config(d->hwirq, true); + else + return -EINVAL; + + return 0; +} static int gpmc_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw) @@ -1195,8 +1254,14 @@ static int gpmc_irq_map(struct irq_domain *d, unsigned int virq, struct gpmc_device *gpmc = d->host_data; irq_set_chip_data(virq, gpmc); - irq_set_chip_and_handler(virq, &gpmc->irq_chip, handle_simple_irq); - irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOAUTOEN); + if (hw < GPMC_NR_NAND_IRQS) { + irq_modify_status(virq, IRQ_NOREQUEST, IRQ_NOAUTOEN); + irq_set_chip_and_handler(virq, &gpmc->irq_chip, + handle_simple_irq); + } else { + irq_set_chip_and_handler(virq, &gpmc->irq_chip, + handle_edge_irq); + } return 0; } @@ -1209,16 +1274,21 @@ static const struct irq_domain_ops gpmc_irq_domain_ops = { static irqreturn_t gpmc_handle_irq(int irq, void *data) { int hwirq, virq; - u32 regval; + u32 regval, regvalx; struct gpmc_device *gpmc = data; regval = gpmc_read_reg(GPMC_IRQSTATUS); + regvalx = regval; if (!regval) return IRQ_NONE; - for (hwirq = 0; hwirq < GPMC_NR_IRQ; hwirq++) { - if (regval & BIT(hwirq)) { + for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++) { + /* skip reserved status bits */ + if (hwirq == GPMC_NR_NAND_IRQS) + regvalx >>= 8 - GPMC_NR_NAND_IRQS; + + if (regvalx & BIT(hwirq)) { virq = irq_find_mapping(gpmc_irq_domain, hwirq); if (!virq) { dev_warn(gpmc->dev, @@ -1248,16 +1318,15 @@ static int gpmc_setup_irq(struct gpmc_device *gpmc) gpmc_write_reg(GPMC_IRQSTATUS, regval); gpmc->irq_chip.name = "gpmc"; - gpmc->irq_chip.irq_startup = gpmc_irq_noop_ret; gpmc->irq_chip.irq_enable = gpmc_irq_enable; gpmc->irq_chip.irq_disable = gpmc_irq_disable; - gpmc->irq_chip.irq_shutdown = gpmc_irq_noop; - gpmc->irq_chip.irq_ack = gpmc_irq_noop; - gpmc->irq_chip.irq_mask = gpmc_irq_noop; - gpmc->irq_chip.irq_unmask = gpmc_irq_noop; + gpmc->irq_chip.irq_ack = gpmc_irq_ack; + gpmc->irq_chip.irq_mask = gpmc_irq_mask; + gpmc->irq_chip.irq_unmask = gpmc_irq_unmask; + gpmc->irq_chip.irq_set_type = gpmc_irq_set_type; gpmc_irq_domain = irq_domain_add_linear(gpmc->dev->of_node, - GPMC_NR_IRQ, + gpmc->nirqs, &gpmc_irq_domain_ops, gpmc); if (!gpmc_irq_domain) { @@ -1282,7 +1351,7 @@ static int gpmc_free_irq(struct gpmc_device *gpmc) free_irq(gpmc->irq, gpmc); - for (hwirq = 0; hwirq < GPMC_NR_IRQ; hwirq++) + for (hwirq = 0; hwirq < gpmc->nirqs; hwirq++) irq_dispose_mapping(irq_find_mapping(gpmc_irq_domain, hwirq)); irq_domain_remove(gpmc_irq_domain); @@ -2296,6 +2365,7 @@ static int gpmc_probe(struct platform_device *pdev) if (rc) goto gpio_init_failed; + gpmc->nirqs = GPMC_NR_NAND_IRQS + gpmc_nr_waitpins; rc = gpmc_setup_irq(gpmc); if (rc) { dev_err(gpmc->dev, "gpmc_setup_irq failed\n"); From 9e6946215dbd9803e8b511928c9f61f3a49e2c58 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 7 Aug 2015 10:38:13 +0300 Subject: [PATCH 019/155] memory: omap-gpmc: Prevent GPMC_STATUS from being accessed via gpmc_regs GPMC_STATUS register is private to the GPMC module and must not be accessed directly by NAND driver through the gpmc_regs. They must use gpmc_omap_get_nand_ops() instead. Signed-off-by: Roger Quadros Acked-by: Tony Lindgren --- drivers/memory/omap-gpmc.c | 2 +- include/linux/platform_data/mtd-nand-omap2.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index ea9c89747950..33d69b1e4c31 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1081,7 +1081,7 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) { int i; - reg->gpmc_status = gpmc_base + GPMC_STATUS; + reg->gpmc_status = NULL; /* deprecated */ reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET + GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs; reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET + diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index ff27e5a77e03..7f6de5377f80 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -45,7 +45,6 @@ enum omap_ecc { }; struct gpmc_nand_regs { - void __iomem *gpmc_status; void __iomem *gpmc_nand_command; void __iomem *gpmc_nand_address; void __iomem *gpmc_nand_data; @@ -64,6 +63,8 @@ struct gpmc_nand_regs { void __iomem *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER]; + /* Deprecated. Do not use */ + void __iomem *gpmc_status; }; struct omap_nand_platform_data { From 10f22ee367c4aff7841da6a83c10445d7d6328d9 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 6 Aug 2015 17:39:35 +0300 Subject: [PATCH 020/155] mtd: nand: omap2: Implement NAND ready using gpiolib The GPMC WAIT pin status are now available over gpiolib. Update the omap_dev_ready() function to use gpio instead of directly accessing GPMC register space. Signed-off-by: Roger Quadros Acked-by: Brian Norris Acked-by: Boris Brezillon Acked-by: Tony Lindgren --- .../devicetree/bindings/mtd/gpmc-nand.txt | 2 ++ drivers/mtd/nand/omap2.c | 29 ++++++++++++------- include/linux/platform_data/mtd-nand-omap2.h | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt index ff3215d20343..3ee7e202657c 100644 --- a/Documentation/devicetree/bindings/mtd/gpmc-nand.txt +++ b/Documentation/devicetree/bindings/mtd/gpmc-nand.txt @@ -48,6 +48,7 @@ Optional properties: locating ECC errors for BCHx algorithms. SoC devices which have ELM hardware engines should specify this device node in .dtsi Using ELM for ECC error correction frees some CPU cycles. + - rb-gpios: GPIO specifier for the ready/busy# pin. For inline partition table parsing (optional): @@ -78,6 +79,7 @@ Example for an AM33xx board: nand-bus-width = <16>; ti,nand-ecc-opt = "bch8"; ti,nand-xfer-type = "polled"; + rb-gpios = <&gpmc 0 GPIO_ACTIVE_HIGH>; /* gpmc_wait0 */ gpmc,sync-clk-ps = <0>; gpmc,cs-on-ns = <0>; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 35b8f3359c17..e0b2b2f0fbde 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -182,6 +183,8 @@ struct omap_nand_info { struct nand_ecclayout oobinfo; /* fields specific for BCHx_HW ECC scheme */ struct device *elm_dev; + /* NAND ready gpio */ + struct gpio_desc *ready_gpiod; }; static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) @@ -1023,21 +1026,16 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) } /** - * omap_dev_ready - calls the platform specific dev_ready function + * omap_dev_ready - checks the NAND Ready GPIO line * @mtd: MTD device structure + * + * Returns true if ready and false if busy. */ static int omap_dev_ready(struct mtd_info *mtd) { - unsigned int val = 0; struct omap_nand_info *info = mtd_to_omap(mtd); - val = readl(info->reg.gpmc_status); - - if ((val & 0x100) == 0x100) { - return 1; - } else { - return 0; - } + return gpiod_get_value(info->ready_gpiod); } /** @@ -1755,7 +1753,9 @@ static int omap_nand_probe(struct platform_device *pdev) info->gpmc_cs = pdata->cs; info->reg = pdata->reg; info->ecc_opt = pdata->ecc_opt; - info->dev_ready = pdata->dev_ready; + if (pdata->dev_ready) + dev_info(&pdev->dev, "pdata->dev_ready is deprecated\n"); + info->xfer_type = pdata->xfer_type; info->devsize = pdata->devsize; info->elm_of_node = pdata->elm_of_node; @@ -1787,6 +1787,13 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; nand_chip->cmd_ctrl = omap_hwcontrol; + info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", + GPIOD_IN); + if (IS_ERR(info->ready_gpiod)) { + dev_err(dev, "failed to get ready gpio\n"); + return PTR_ERR(info->ready_gpiod); + } + /* * If RDY/BSY line is connected to OMAP then use the omap ready * function and the generic nand_wait function which reads the status @@ -1794,7 +1801,7 @@ static int omap_nand_probe(struct platform_device *pdev) * chip delay which is slightly more than tR (AC Timing) of the NAND * device and read status register until you get a failure or success */ - if (info->dev_ready) { + if (info->ready_gpiod) { nand_chip->dev_ready = omap_dev_ready; nand_chip->chip_delay = 0; } else { diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index 7f6de5377f80..17d57a18bac5 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -71,7 +71,6 @@ struct omap_nand_platform_data { int cs; struct mtd_partition *parts; int nr_parts; - bool dev_ready; bool flash_bbt; enum nand_io xfer_type; int devsize; @@ -82,5 +81,6 @@ struct omap_nand_platform_data { /* deprecated */ struct gpmc_nand_regs reg; struct device_node *of_node; + bool dev_ready; }; #endif From b0fcd8ab7b3c89b5da7fff5224d06ed73e7a33cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 23 Mar 2016 11:19:00 +0100 Subject: [PATCH 021/155] mtd: nand: add new enum for storing ECC algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our nand_ecc_modes_t is already a bit abused by value NAND_ECC_SOFT_BCH. This enum should store ECC mode only and putting algorithm details there is a bad idea. It would result in too many values impossible to support in a sane way. To solve this problem let's add a new enum. We'll have to modify all drivers to set it properly but once it's done it'll be possible to drop NAND_ECC_SOFT_BCH. That will result in a cleaner design and more possibilities like setting ECC algorithm for hardware ECC mode. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- include/linux/mtd/nand.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 56574ba36555..1b673e19667c 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -119,6 +119,12 @@ typedef enum { NAND_ECC_SOFT_BCH, } nand_ecc_modes_t; +enum nand_ecc_algo { + NAND_ECC_UNKNOWN, + NAND_ECC_HAMMING, + NAND_ECC_BCH, +}; + /* * Constants for Hardware ECC */ @@ -458,6 +464,7 @@ struct nand_hw_control { /** * struct nand_ecc_ctrl - Control structure for ECC * @mode: ECC mode + * @algo: ECC algorithm * @steps: number of ECC steps per page * @size: data bytes per ECC step * @bytes: ECC bytes per step @@ -508,6 +515,7 @@ struct nand_hw_control { */ struct nand_ecc_ctrl { nand_ecc_modes_t mode; + enum nand_ecc_algo algo; int steps; int size; int bytes; From dd2dcc004230b9d8fa809102cd326e3ee4bbdb2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 23 Mar 2016 11:19:01 +0100 Subject: [PATCH 022/155] of: mtd: prepare helper reading NAND ECC algo from DT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NAND subsystem is being slightly reworked to store ECC details in separated fields. In future we'll want to add support for more DT properties as specifying every possible setup with a single "nand-ecc-mode" is a pretty bad idea. To allow this let's add a helper that will support something like "nand-ecc-algo" in future. Right now we use it for keeping backward compatibility. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/of/of_mtd.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/of_mtd.h | 6 ++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c index b7361ed70537..15d056e181d2 100644 --- a/drivers/of/of_mtd.c +++ b/drivers/of/of_mtd.c @@ -49,6 +49,42 @@ int of_get_nand_ecc_mode(struct device_node *np) } EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode); +/** + * of_get_nand_ecc_algo - Get nand ecc algorithm for given device_node + * @np: Pointer to the given device_node + * + * The function gets ecc algorithm and returns its enum value, or errno in error + * case. + */ +int of_get_nand_ecc_algo(struct device_node *np) +{ + const char *pm; + int err; + + /* + * TODO: Read ECC algo OF property and map it to enum nand_ecc_algo. + * It's not implemented yet as currently NAND subsystem ignores + * algorithm explicitly set this way. Once it's handled we should + * document & support new property. + */ + + /* + * For backward compatibility we also read "nand-ecc-mode" checking + * for some obsoleted values that were specifying ECC algorithm. + */ + err = of_property_read_string(np, "nand-ecc-mode", &pm); + if (err < 0) + return err; + + if (!strcasecmp(pm, "soft")) + return NAND_ECC_HAMMING; + else if (!strcasecmp(pm, "soft_bch")) + return NAND_ECC_BCH; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(of_get_nand_ecc_algo); + /** * of_get_nand_ecc_step_size - Get ECC step size associated to * the required ECC strength (see below). diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h index e266caa36402..0f6aca5c6f2f 100644 --- a/include/linux/of_mtd.h +++ b/include/linux/of_mtd.h @@ -13,6 +13,7 @@ #include int of_get_nand_ecc_mode(struct device_node *np); +int of_get_nand_ecc_algo(struct device_node *np); int of_get_nand_ecc_step_size(struct device_node *np); int of_get_nand_ecc_strength(struct device_node *np); int of_get_nand_bus_width(struct device_node *np); @@ -25,6 +26,11 @@ static inline int of_get_nand_ecc_mode(struct device_node *np) return -ENOSYS; } +static inline int of_get_nand_ecc_algo(struct device_node *np) +{ + return -ENOSYS; +} + static inline int of_get_nand_ecc_step_size(struct device_node *np) { return -ENOSYS; From 79082457d71af879922e15f9dedf85384e84c29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 23 Mar 2016 11:19:02 +0100 Subject: [PATCH 023/155] mtd: nand: set ECC algorithm in nand_dt_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use recently added of_get_nand_ecc_algo for that. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8d6287ef3926..728543b97dd8 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3954,7 +3954,7 @@ ident_done: static int nand_dt_init(struct nand_chip *chip) { struct device_node *dn = nand_get_flash_node(chip); - int ecc_mode, ecc_strength, ecc_step; + int ecc_mode, ecc_algo, ecc_strength, ecc_step; if (!dn) return 0; @@ -3966,6 +3966,7 @@ static int nand_dt_init(struct nand_chip *chip) chip->bbt_options |= NAND_BBT_USE_FLASH; ecc_mode = of_get_nand_ecc_mode(dn); + ecc_algo = of_get_nand_ecc_algo(dn); ecc_strength = of_get_nand_ecc_strength(dn); ecc_step = of_get_nand_ecc_step_size(dn); @@ -3978,6 +3979,9 @@ static int nand_dt_init(struct nand_chip *chip) if (ecc_mode >= 0) chip->ecc.mode = ecc_mode; + if (ecc_algo >= 0) + chip->ecc.algo = ecc_algo; + if (ecc_strength >= 0) chip->ecc.strength = ecc_strength; From 8ae6bcd1dcea249591b4165bd7f795c4868bc531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 23 Mar 2016 11:19:03 +0100 Subject: [PATCH 024/155] mtd: nand: nandsim: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This follows recent work on switching to enum nand_ecc_algo and deprecating NAND_ECC_SOFT_BCH. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nandsim.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 1fd519503bb1..6ff1d8d31ac2 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -2261,6 +2261,7 @@ static int __init ns_init_module(void) chip->read_buf = ns_nand_read_buf; chip->read_word = ns_nand_read_word; chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */ /* and 'badblocks' parameters to work */ chip->options |= NAND_SKIP_BBTSCAN; @@ -2339,6 +2340,7 @@ static int __init ns_init_module(void) goto error; } chip->ecc.mode = NAND_ECC_SOFT_BCH; + chip->ecc.algo = NAND_ECC_BCH; chip->ecc.size = 512; chip->ecc.strength = bch; chip->ecc.bytes = eccbytes; From ff6ee101584c17c4f30ca48d75e2910c49d0b0ff Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 1 Mar 2016 14:11:52 +0100 Subject: [PATCH 025/155] mtd: nand: atmel: correct bitflips in erased pages for pre-sama5d4 SoCs New atmel SoCs are able to fix bitflips in erased pages, but old ones are still impacted by this problem. Use nand_check_erased_ecc_chunk() to handle this case. Signed-off-by: Boris Brezillon Reported-by: Herve Codina Reviewed-by: Herve Codina Tested-by: Herve Codina --- drivers/mtd/nand/atmel_nand.c | 37 +++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 20cbaabb2959..0b5da7271440 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -863,17 +863,6 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, uint8_t *buf_pos; int max_bitflips = 0; - /* If can correct bitfilps from erased page, do the normal check */ - if (host->caps->pmecc_correct_erase_page) - goto normal_check; - - for (i = 0; i < nand_chip->ecc.total; i++) - if (ecc[i] != 0xff) - goto normal_check; - /* Erased page, return OK */ - return 0; - -normal_check: for (i = 0; i < nand_chip->ecc.steps; i++) { err_nbr = 0; if (pmecc_stat & 0x1) { @@ -884,16 +873,30 @@ normal_check: pmecc_get_sigma(mtd); err_nbr = pmecc_err_location(mtd); - if (err_nbr == -1) { + if (err_nbr >= 0) { + pmecc_correct_data(mtd, buf_pos, ecc, i, + nand_chip->ecc.bytes, + err_nbr); + } else if (!host->caps->pmecc_correct_erase_page) { + u8 *ecc_pos = ecc + (i * nand_chip->ecc.bytes); + + /* Try to detect erased pages */ + err_nbr = nand_check_erased_ecc_chunk(buf_pos, + host->pmecc_sector_size, + ecc_pos, + nand_chip->ecc.bytes, + NULL, 0, + nand_chip->ecc.strength); + } + + if (err_nbr < 0) { dev_err(host->dev, "PMECC: Too many errors\n"); mtd->ecc_stats.failed++; return -EIO; - } else { - pmecc_correct_data(mtd, buf_pos, ecc, i, - nand_chip->ecc.bytes, err_nbr); - mtd->ecc_stats.corrected += err_nbr; - max_bitflips = max_t(int, max_bitflips, err_nbr); } + + mtd->ecc_stats.corrected += err_nbr; + max_bitflips = max_t(int, max_bitflips, err_nbr); } pmecc_stat >>= 1; } From ce8716e97149d15379603890c4c7a2acfcf4a7ee Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 23 Mar 2016 21:53:27 -0400 Subject: [PATCH 026/155] mtd: nand: jz4780: fixup, device structure assigned at probe bch->dev is already assigned to &pdev->dev in the probe function. Remove the duplicate assignment done in jz4780_bch_get(). Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Boris Brezillon Acked-by: Acked-by: Harvey Hunt --- drivers/mtd/nand/jz4780_bch.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/nand/jz4780_bch.c b/drivers/mtd/nand/jz4780_bch.c index 755499c6650e..d74f4ba4a6f4 100644 --- a/drivers/mtd/nand/jz4780_bch.c +++ b/drivers/mtd/nand/jz4780_bch.c @@ -287,7 +287,6 @@ static struct jz4780_bch *jz4780_bch_get(struct device_node *np) bch = platform_get_drvdata(pdev); clk_prepare_enable(bch->clk); - bch->dev = &pdev->dev; return bch; } From 2d472aba15ff169a30dc3661f837d8535af9b9ee Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:26:35 +0200 Subject: [PATCH 027/155] mtd: nand: document the NAND controller/NAND chip DT representation Standardize the NAND controller/NAND chip DT representation. Now, all new NAND controller drivers should comply with this representation, even if they are only supporting a single NAND chip. Existing drivers can keep support for the old representation (where only the NAND chip was described), but are encouraged to also support the new one. Signed-off-by: Boris Brezillon Acked-by: Brian Norris Acked-by: Rob Herring --- .../devicetree/bindings/mtd/nand.txt | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt index b53f92e252d4..a17662b1dfcb 100644 --- a/Documentation/devicetree/bindings/mtd/nand.txt +++ b/Documentation/devicetree/bindings/mtd/nand.txt @@ -1,4 +1,23 @@ -* MTD generic binding +* NAND chip and NAND controller generic binding + +NAND controller/NAND chip representation: + +The NAND controller should be represented with its own DT node, and all +NAND chips attached to this controller should be defined as children nodes +of the NAND controller. This representation should be enforced even for +simple controllers supporting only one chip. + +Mandatory NAND controller properties: +- #address-cells: depends on your controller. Should at least be 1 to + encode the CS line id. +- #size-cells: depends on your controller. Put zero unless you need a + mapping between CS lines and dedicated memory regions + +Optional NAND controller properties +- ranges: only needed if you need to define a mapping between CS lines and + memory regions + +Optional NAND chip properties: - nand-ecc-mode : String, operation mode of the NAND ecc mode. Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", @@ -19,3 +38,19 @@ errors per {size} bytes". The interpretation of these parameters is implementation-defined, so not all implementations must support all possible combinations. However, implementations are encouraged to further specify the value(s) they support. + +Example: + + nand-controller { + #address-cells = <1>; + #size-cells = <0>; + + /* controller specific properties */ + + nand@0 { + reg = <0>; + nand-ecc-mode = "soft_bch"; + + /* controller specific properties */ + }; + }; From 269ecf03a5b32b0d1de1f60a4b86b75c0521053f Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 10 Mar 2016 15:34:16 -0800 Subject: [PATCH 028/155] mtd: brcmnand: Add support for v6.2 controllers Document and match the brcm,brcmnand-v6.2 compatible string, the controller has a register layout identical to the v6.0 version and supports prefetch. Update the command shift logic to account for v6.2 controller which are the first ones to use a shift of 0 (6.1 used a shift of 24). Signed-off-by: Kamal Dasu Signed-off-by: Florian Fainelli Acked-by: Rob Herring Acked-by: Brian Norris Signed-off-by: Boris Brezillon --- Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt | 1 + drivers/mtd/nand/brcmnand/brcmnand.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt b/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt index c2546ced9c02..deb24cbdda82 100644 --- a/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt +++ b/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt @@ -24,6 +24,7 @@ Required properties: brcm,brcmnand-v5.0 brcm,brcmnand-v6.0 brcm,brcmnand-v6.1 + brcm,brcmnand-v6.2 brcm,brcmnand-v7.0 brcm,brcmnand-v7.1 brcm,brcmnand diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index e0528397306a..0b7a698cc233 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -601,7 +601,7 @@ static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) { - if (ctrl->nand_version < 0x0700) + if (ctrl->nand_version < 0x0602) return 24; return 0; } @@ -2115,6 +2115,7 @@ static const struct of_device_id brcmnand_of_match[] = { { .compatible = "brcm,brcmnand-v5.0" }, { .compatible = "brcm,brcmnand-v6.0" }, { .compatible = "brcm,brcmnand-v6.1" }, + { .compatible = "brcm,brcmnand-v6.2" }, { .compatible = "brcm,brcmnand-v7.0" }, { .compatible = "brcm,brcmnand-v7.1" }, {}, From 2cd395d13a104cd96ddae5c5612871dc18553cca Mon Sep 17 00:00:00 2001 From: Han Xu Date: Mon, 4 Apr 2016 15:41:29 -0500 Subject: [PATCH 029/155] mtd: gpmi: fix raw_buffer pointer double free issue fix the raw_buffer pointer double free issue found by coverify. CID 18344 (#2 of 2): Double free (USE_AFTER_FREE) 3. double_free: Calling gpmi_alloc_dma_buffer frees pointer this->raw_buffer which has already been freed Signed-off-by: Han Xu Reviewed-by: Richard Weinberger Signed-off-by: Boris Brezillon --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 8122c699ccf2..dcb60b0fe5eb 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -797,6 +797,7 @@ static void gpmi_free_dma_buffer(struct gpmi_nand_data *this) this->cmd_buffer = NULL; this->data_buffer_dma = NULL; + this->raw_buffer = NULL; this->page_buffer_virt = NULL; this->page_buffer_size = 0; } From 11eaf6df1cce3c5e9f51b422aaedc42dc4aa1d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ezequiel=20Garc=C3=ADa?= Date: Fri, 1 Apr 2016 18:29:24 -0300 Subject: [PATCH 030/155] mtd: nand: Remove BUG() abuse in nand_scan_tail There's no reason to BUG() when parameters are being validated. Drivers can get things wrong, and it's much nicer to just throw a noisy warn and fail gracefully, than calling BUG() and throwing the whole system down the drain. Signed-off-by: Ezequiel Garcia Reviewed-by: Richard Weinberger Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 52 +++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 728543b97dd8..0f0c5b190316 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4107,10 +4107,12 @@ int nand_scan_tail(struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_buffers *nbuf; + int ret; /* New bad blocks should be marked in OOB, flash-based BBT, or both */ - BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && - !(chip->bbt_options & NAND_BBT_USE_FLASH)); + if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH))) + return -EINVAL; if (!(chip->options & NAND_OWN_BUFFERS)) { nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize @@ -4148,9 +4150,10 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->layout = &nand_oob_128; break; default: - pr_warn("No oob scheme defined for oobsize %d\n", - mtd->oobsize); - BUG(); + WARN(1, "No oob scheme defined for oobsize %d\n", + mtd->oobsize); + ret = -EINVAL; + goto err_free; } } @@ -4166,8 +4169,9 @@ int nand_scan_tail(struct mtd_info *mtd) case NAND_ECC_HW_OOB_FIRST: /* Similar to NAND_ECC_HW, but a separate read_page handle */ if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { - pr_warn("No ECC functions supplied; hardware ECC not possible\n"); - BUG(); + WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); + ret = -EINVAL; + goto err_free; } if (!ecc->read_page) ecc->read_page = nand_read_page_hwecc_oob_first; @@ -4197,8 +4201,9 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->read_page == nand_read_page_hwecc || !ecc->write_page || ecc->write_page == nand_write_page_hwecc)) { - pr_warn("No ECC functions supplied; hardware ECC not possible\n"); - BUG(); + WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); + ret = -EINVAL; + goto err_free; } /* Use standard syndrome read/write page function? */ if (!ecc->read_page) @@ -4216,8 +4221,9 @@ int nand_scan_tail(struct mtd_info *mtd) if (mtd->writesize >= ecc->size) { if (!ecc->strength) { - pr_warn("Driver must set ecc.strength when using hardware ECC\n"); - BUG(); + WARN(1, "Driver must set ecc.strength when using hardware ECC\n"); + ret = -EINVAL; + goto err_free; } break; } @@ -4243,8 +4249,9 @@ int nand_scan_tail(struct mtd_info *mtd) case NAND_ECC_SOFT_BCH: if (!mtd_nand_has_bch()) { - pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n"); - BUG(); + WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + ret = -EINVAL; + goto err_free; } ecc->calculate = nand_bch_calculate_ecc; ecc->correct = nand_bch_correct_data; @@ -4269,8 +4276,9 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->bytes = 0; ecc->priv = nand_bch_init(mtd); if (!ecc->priv) { - pr_warn("BCH ECC initialization failed!\n"); - BUG(); + WARN(1, "BCH ECC initialization failed!\n"); + ret = -EINVAL; + goto err_free; } break; @@ -4288,8 +4296,9 @@ int nand_scan_tail(struct mtd_info *mtd) break; default: - pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode); - BUG(); + WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode); + ret = -EINVAL; + goto err_free; } /* For many systems, the standard OOB write also works for raw */ @@ -4319,8 +4328,9 @@ int nand_scan_tail(struct mtd_info *mtd) */ ecc->steps = mtd->writesize / ecc->size; if (ecc->steps * ecc->size != mtd->writesize) { - pr_warn("Invalid ECC parameters\n"); - BUG(); + WARN(1, "Invalid ECC parameters\n"); + ret = -EINVAL; + goto err_free; } ecc->total = ecc->steps * ecc->bytes; @@ -4398,6 +4408,10 @@ int nand_scan_tail(struct mtd_info *mtd) /* Build bad block table */ return chip->scan_bbt(mtd); +err_free: + if (!(chip->options & NAND_OWN_BUFFERS)) + kfree(chip->buffers); + return ret; } EXPORT_SYMBOL(nand_scan_tail); From 7a654172161c8c9c7d59cbd0054d9e63c7411219 Mon Sep 17 00:00:00 2001 From: Raghav Dogra Date: Wed, 17 Feb 2016 16:54:18 +0530 Subject: [PATCH 031/155] mtd/ifc: Add support for IFC controller version 2.0 The new IFC controller version 2.0 has a different memory map page. Upto IFC 1.4 PAGE size is 4 KB and from IFC2.0 PAGE size is 64KB. This patch segregates the IFC global and runtime registers to appropriate PAGE sizes. Signed-off-by: Jaiprakash Singh Signed-off-by: Raghav Dogra Acked-by: Li Yang Signed-off-by: Raghav Dogra Acked-by: Scott Wood Acked-by: Brian Norris Signed-off-by: Boris Brezillon --- drivers/memory/fsl_ifc.c | 36 ++++++++--------- drivers/mtd/nand/fsl_ifc_nand.c | 72 ++++++++++++++++++--------------- include/linux/fsl_ifc.h | 45 ++++++++++++++------- 3 files changed, 87 insertions(+), 66 deletions(-) diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c index 2a691da8c1c7..904b4af5f142 100644 --- a/drivers/memory/fsl_ifc.c +++ b/drivers/memory/fsl_ifc.c @@ -59,11 +59,11 @@ int fsl_ifc_find(phys_addr_t addr_base) { int i = 0; - if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->gregs) return -ENODEV; for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) { - u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); + u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->gregs->cspr_cs[i].cspr); if (cspr & CSPR_V && (cspr & CSPR_BA) == convert_ifc_address(addr_base)) return i; @@ -75,7 +75,7 @@ EXPORT_SYMBOL(fsl_ifc_find); static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) { - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_global __iomem *ifc = ctrl->gregs; /* * Clear all the common status and event registers @@ -104,7 +104,7 @@ static int fsl_ifc_ctrl_remove(struct platform_device *dev) irq_dispose_mapping(ctrl->nand_irq); irq_dispose_mapping(ctrl->irq); - iounmap(ctrl->regs); + iounmap(ctrl->gregs); dev_set_drvdata(&dev->dev, NULL); kfree(ctrl); @@ -122,7 +122,7 @@ static DEFINE_SPINLOCK(nand_irq_lock); static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) { - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; unsigned long flags; u32 stat; @@ -157,7 +157,7 @@ static irqreturn_t fsl_ifc_nand_irq(int irqno, void *data) static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) { struct fsl_ifc_ctrl *ctrl = data; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_global __iomem *ifc = ctrl->gregs; u32 err_axiid, err_srcid, status, cs_err, err_addr; irqreturn_t ret = IRQ_NONE; @@ -215,6 +215,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) { int ret = 0; int version, banks; + void __iomem *addr; dev_info(&dev->dev, "Freescale Integrated Flash Controller\n"); @@ -225,22 +226,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) dev_set_drvdata(&dev->dev, fsl_ifc_ctrl_dev); /* IOMAP the entire IFC region */ - fsl_ifc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0); - if (!fsl_ifc_ctrl_dev->regs) { + fsl_ifc_ctrl_dev->gregs = of_iomap(dev->dev.of_node, 0); + if (!fsl_ifc_ctrl_dev->gregs) { dev_err(&dev->dev, "failed to get memory region\n"); ret = -ENODEV; goto err; } - version = ifc_in32(&fsl_ifc_ctrl_dev->regs->ifc_rev) & - FSL_IFC_VERSION_MASK; - banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; - dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", - version >> 24, (version >> 16) & 0xf, banks); - - fsl_ifc_ctrl_dev->version = version; - fsl_ifc_ctrl_dev->banks = banks; - if (of_property_read_bool(dev->dev.of_node, "little-endian")) { fsl_ifc_ctrl_dev->little_endian = true; dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n"); @@ -249,8 +241,9 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n"); } - version = ioread32be(&fsl_ifc_ctrl_dev->regs->ifc_rev) & + version = ifc_in32(&fsl_ifc_ctrl_dev->gregs->ifc_rev) & FSL_IFC_VERSION_MASK; + banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", version >> 24, (version >> 16) & 0xf, banks); @@ -258,6 +251,13 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) fsl_ifc_ctrl_dev->version = version; fsl_ifc_ctrl_dev->banks = banks; + addr = fsl_ifc_ctrl_dev->gregs; + if (version >= FSL_IFC_VERSION_2_0_0) + addr += PGOFFSET_64K; + else + addr += PGOFFSET_4K; + fsl_ifc_ctrl_dev->rregs = addr; + /* get the Controller level irq */ fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0); if (fsl_ifc_ctrl_dev->irq == 0) { diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 43f5a3a4873f..f8a016f038cd 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -232,7 +232,7 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) struct nand_chip *chip = mtd_to_nand(mtd); struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; int buf_num; ifc_nand_ctrl->page = page_addr; @@ -295,7 +295,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; u32 eccstat[4]; int i; @@ -371,7 +371,7 @@ static void fsl_ifc_do_read(struct nand_chip *chip, { struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ if (mtd->writesize > 512) { @@ -411,7 +411,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, struct nand_chip *chip = mtd_to_nand(mtd); struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; /* clear the read buffer */ ifc_nand_ctrl->read_bytes = 0; @@ -723,7 +723,7 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; u32 nand_fsr; /* Use READ_STATUS command, but wait for the device to be ready */ @@ -825,39 +825,42 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) { struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; + struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; uint32_t csor = 0, csor_8k = 0, csor_ext = 0; uint32_t cs = priv->bank; /* Save CSOR and CSOR_ext */ - csor = ifc_in32(&ifc->csor_cs[cs].csor); - csor_ext = ifc_in32(&ifc->csor_cs[cs].csor_ext); + csor = ifc_in32(&ifc_global->csor_cs[cs].csor); + csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext); /* chage PageSize 8K and SpareSize 1K*/ csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; - ifc_out32(csor_8k, &ifc->csor_cs[cs].csor); - ifc_out32(0x0000400, &ifc->csor_cs[cs].csor_ext); + ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor); + ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext); /* READID */ ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), - &ifc->ifc_nand.nand_fir0); + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), + &ifc_runtime->ifc_nand.nand_fir0); ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - ifc_out32(0x0, &ifc->ifc_nand.row3); + &ifc_runtime->ifc_nand.nand_fcr0); + ifc_out32(0x0, &ifc_runtime->ifc_nand.row3); - ifc_out32(0x0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr); /* Program ROW0/COL0 */ - ifc_out32(0x0, &ifc->ifc_nand.row0); - ifc_out32(0x0, &ifc->ifc_nand.col0); + ifc_out32(0x0, &ifc_runtime->ifc_nand.row0); + ifc_out32(0x0, &ifc_runtime->ifc_nand.col0); /* set the chip select for NAND Transaction */ - ifc_out32(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); + ifc_out32(cs << IFC_NAND_CSEL_SHIFT, + &ifc_runtime->ifc_nand.nand_csel); /* start read seq */ - ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); + ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, + &ifc_runtime->ifc_nand.nandseq_strt); /* wait for command complete flag or timeout */ wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, @@ -867,14 +870,15 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); /* Restore CSOR and CSOR_ext */ - ifc_out32(csor, &ifc->csor_cs[cs].csor); - ifc_out32(csor_ext, &ifc->csor_cs[cs].csor_ext); + ifc_out32(csor, &ifc_global->csor_cs[cs].csor); + ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext); } static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) { struct fsl_ifc_ctrl *ctrl = priv->ctrl; - struct fsl_ifc_regs __iomem *ifc = ctrl->regs; + struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; + struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; struct nand_chip *chip = &priv->chip; struct mtd_info *mtd = nand_to_mtd(&priv->chip); struct nand_ecclayout *layout; @@ -886,7 +890,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* fill in nand_chip structure */ /* set up function call table */ - if ((ifc_in32(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) + if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)) + & CSPR_PORT_SIZE_16) chip->read_byte = fsl_ifc_read_byte16; else chip->read_byte = fsl_ifc_read_byte; @@ -900,13 +905,14 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; - ifc_out32(0x0, &ifc->ifc_nand.ncfgr); + ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr); /* set up nand options */ chip->bbt_options = NAND_BBT_USE_FLASH; chip->options = NAND_NO_SUBPAGE_WRITE; - if (ifc_in32(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { + if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr) + & CSPR_PORT_SIZE_16) { chip->read_byte = fsl_ifc_read_byte16; chip->options |= NAND_BUSWIDTH_16; } else { @@ -919,7 +925,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->ecc.read_page = fsl_ifc_read_page; chip->ecc.write_page = fsl_ifc_write_page; - csor = ifc_in32(&ifc->csor_cs[priv->bank].csor); + csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); /* Hardware generates ECC per 512 Bytes */ chip->ecc.size = 512; @@ -1007,10 +1013,10 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) return 0; } -static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, +static int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank, phys_addr_t addr) { - u32 cspr = ifc_in32(&ifc->cspr_cs[bank].cspr); + u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr); if (!(cspr & CSPR_V)) return 0; @@ -1024,7 +1030,7 @@ static DEFINE_MUTEX(fsl_ifc_nand_mutex); static int fsl_ifc_nand_probe(struct platform_device *dev) { - struct fsl_ifc_regs __iomem *ifc; + struct fsl_ifc_runtime __iomem *ifc; struct fsl_ifc_mtd *priv; struct resource res; static const char *part_probe_types[] @@ -1034,9 +1040,9 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) struct device_node *node = dev->dev.of_node; struct mtd_info *mtd; - if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs) + if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) return -ENODEV; - ifc = fsl_ifc_ctrl_dev->regs; + ifc = fsl_ifc_ctrl_dev->rregs; /* get, allocate and map the memory resource */ ret = of_address_to_resource(node, 0, &res); @@ -1047,7 +1053,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) /* find which chip select it is connected to */ for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { - if (match_bank(ifc, bank, res.start)) + if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) break; } diff --git a/include/linux/fsl_ifc.h b/include/linux/fsl_ifc.h index 0023088b253b..3f9778cbc79d 100644 --- a/include/linux/fsl_ifc.h +++ b/include/linux/fsl_ifc.h @@ -39,6 +39,10 @@ #define FSL_IFC_VERSION_MASK 0x0F0F0000 #define FSL_IFC_VERSION_1_0_0 0x01000000 #define FSL_IFC_VERSION_1_1_0 0x01010000 +#define FSL_IFC_VERSION_2_0_0 0x02000000 + +#define PGOFFSET_64K (64*1024) +#define PGOFFSET_4K (4*1024) /* * CSPR - Chip Select Property Register @@ -723,20 +727,26 @@ struct fsl_ifc_nand { __be32 nand_evter_en; u32 res17[0x2]; __be32 nand_evter_intr_en; - u32 res18[0x2]; + __be32 nand_vol_addr_stat; + u32 res18; __be32 nand_erattr0; __be32 nand_erattr1; u32 res19[0x10]; __be32 nand_fsr; - u32 res20; - __be32 nand_eccstat[4]; - u32 res21[0x20]; + u32 res20[0x3]; + __be32 nand_eccstat[6]; + u32 res21[0x1c]; __be32 nanndcr; u32 res22[0x2]; __be32 nand_autoboot_trgr; u32 res23; __be32 nand_mdr; - u32 res24[0x5C]; + u32 res24[0x1C]; + __be32 nand_dll_lowcfg0; + __be32 nand_dll_lowcfg1; + u32 res25; + __be32 nand_dll_lowstat; + u32 res26[0x3c]; }; /* @@ -771,13 +781,12 @@ struct fsl_ifc_gpcm { __be32 gpcm_erattr1; __be32 gpcm_erattr2; __be32 gpcm_stat; - u32 res4[0x1F3]; }; /* * IFC Controller Registers */ -struct fsl_ifc_regs { +struct fsl_ifc_global { __be32 ifc_rev; u32 res1[0x2]; struct { @@ -803,21 +812,26 @@ struct fsl_ifc_regs { } ftim_cs[FSL_IFC_BANK_COUNT]; u32 res9[0x30]; __be32 rb_stat; - u32 res10[0x2]; + __be32 rb_map; + __be32 wb_map; __be32 ifc_gcr; - u32 res11[0x2]; + u32 res10[0x2]; __be32 cm_evter_stat; - u32 res12[0x2]; + u32 res11[0x2]; __be32 cm_evter_en; - u32 res13[0x2]; + u32 res12[0x2]; __be32 cm_evter_intr_en; - u32 res14[0x2]; + u32 res13[0x2]; __be32 cm_erattr0; __be32 cm_erattr1; - u32 res15[0x2]; + u32 res14[0x2]; __be32 ifc_ccr; __be32 ifc_csr; - u32 res16[0x2EB]; + __be32 ddr_ccr_low; +}; + + +struct fsl_ifc_runtime { struct fsl_ifc_nand ifc_nand; struct fsl_ifc_nor ifc_nor; struct fsl_ifc_gpcm ifc_gpcm; @@ -831,7 +845,8 @@ extern int fsl_ifc_find(phys_addr_t addr_base); struct fsl_ifc_ctrl { /* device info */ struct device *dev; - struct fsl_ifc_regs __iomem *regs; + struct fsl_ifc_global __iomem *gregs; + struct fsl_ifc_runtime __iomem *rregs; int irq; int nand_irq; spinlock_t lock; From 03a97550941d17c7d5b621afde5945bbc0da6546 Mon Sep 17 00:00:00 2001 From: Zhaoxiu Zeng Date: Tue, 12 Apr 2016 15:30:35 +0800 Subject: [PATCH 032/155] mtd: nand: s3c2410: fix bug in s3c2410_nand_correct_data() If there is only one bit difference in the ECC, the function should return 1. The result of "diff0 & ~(1< Signed-off-by: Boris Brezillon --- drivers/mtd/nand/s3c2410.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 9c9397b54b2c..86ffb73e1085 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -542,7 +542,8 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, diff0 |= (diff1 << 8); diff0 |= (diff2 << 16); - if ((diff0 & ~(1< Date: Fri, 1 Apr 2016 14:54:21 +0200 Subject: [PATCH 033/155] mtd: nand: remove unneeded of_mtd.h inclusions Some drivers are including linux/of_mtd.h even if they don't use any of the of_get_nand_xxx() helpers. Signed-off-by: Boris Brezillon Acked-by: Harvey Hunt --- drivers/mtd/nand/jz4780_nand.c | 1 - drivers/mtd/nand/lpc32xx_mlc.c | 1 - drivers/mtd/nand/qcom_nandc.c | 1 - drivers/mtd/nand/sunxi_nand.c | 1 - drivers/mtd/nand/vf610_nfc.c | 1 - 5 files changed, 5 deletions(-) diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/jz4780_nand.c index e1c016c9d32d..23a1999ae839 100644 --- a/drivers/mtd/nand/jz4780_nand.c +++ b/drivers/mtd/nand/jz4780_nand.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index d8c3e7afcc0b..8e439787b04e 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c index f550a57e6eea..f3de983475db 100644 --- a/drivers/mtd/nand/qcom_nandc.c +++ b/drivers/mtd/nand/qcom_nandc.c @@ -21,7 +21,6 @@ #include #include #include -#include #include /* NANDc reg offsets */ diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 1c03eee44f3d..3a97093662ea 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 293feb19b0b1..a7f2756c49ee 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include From f679888f29fe5c4a4c44459f2c1fc62b72188773 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 19 Apr 2016 20:29:58 +0200 Subject: [PATCH 034/155] mtd: nand: omap2: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon Acked-by: Roger Quadros Tested-by: Franklin S Cooper Jr. --- drivers/memory/omap-gpmc.c | 4 ++-- drivers/mtd/nand/omap2.c | 18 +++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 33d69b1e4c31..af4884ba6b7c 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -2066,7 +2065,8 @@ static int gpmc_probe_generic_child(struct platform_device *pdev, if (of_device_is_compatible(child, "ti,omap2-nand")) { /* NAND specific setup */ - val = of_get_nand_bus_width(child); + val = 8; + of_property_read_u32(child, "nand-bus-width", &val); switch (val) { case 8: gpmc_s.device_width = GPMC_DEVWIDTH_8BIT; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index e0b2b2f0fbde..c59bc85009d7 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -1701,7 +1700,7 @@ static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { if (!strcasecmp(s, nand_xfer_types[i])) { info->xfer_type = i; - goto next; + return 0; } } @@ -1709,12 +1708,6 @@ static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) return -EINVAL; } -next: - of_get_nand_on_flash_bbt(child); - - if (of_get_nand_bus_width(child) == 16) - info->devsize = NAND_BUSWIDTH_16; - return 0; } @@ -1810,9 +1803,7 @@ static int omap_nand_probe(struct platform_device *pdev) } if (info->flash_bbt) - nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; - else - nand_chip->options |= NAND_SKIP_BBTSCAN; + nand_chip->bbt_options |= NAND_BBT_USE_FLASH; /* scan NAND device connected to chip controller */ nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; @@ -1823,6 +1814,11 @@ static int omap_nand_probe(struct platform_device *pdev) goto return_error; } + if (nand_chip->bbt_options & NAND_BBT_USE_FLASH) + nand_chip->bbt_options |= NAND_BBT_NO_OOB; + else + nand_chip->options |= NAND_SKIP_BBTSCAN; + /* re-populate low-level callbacks based on xfer modes */ switch (info->xfer_type) { case NAND_OMAP_PREFETCH_POLLED: From 541e3c8912fe4257ba0b71908ea8fbf79143f510 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:24 +0200 Subject: [PATCH 035/155] mtd: nand: brcm: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon Acked-by: Brian Norris --- drivers/mtd/nand/brcmnand/brcmnand.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index 0b7a698cc233..e5f281f148dc 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -2001,8 +2000,8 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) */ chip->options |= NAND_USE_BOUNCE_BUFFER; - if (of_get_nand_on_flash_bbt(dn)) - chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; if (brcmnand_setup_dev(host)) return -ENXIO; From e58dd3c33d3046c3ad3c849cb95365b45b9ecb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 8 Apr 2016 12:23:44 +0200 Subject: [PATCH 036/155] mtd: nand: ams-delta: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/ams-delta.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 68b58c85789c..78e12cc8bac2 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -224,6 +224,7 @@ static int ams_delta_init(struct platform_device *pdev) /* 25 us command delay time */ this->chip_delay = 30; this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; platform_set_drvdata(pdev, io_base); From 050658c8f9bd8d7b2addf40f6a5b340d5d4ec21a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 8 Apr 2016 12:23:45 +0200 Subject: [PATCH 037/155] mtd: nand: gpio: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/gpio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index ded658fc7d73..6317f6836022 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -273,6 +273,7 @@ static int gpio_nand_probe(struct platform_device *pdev) nand_set_flash_node(chip, pdev->dev.of_node); chip->IO_ADDR_W = chip->IO_ADDR_R; chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; chip->options = gpiomtd->plat.options; chip->chip_delay = gpiomtd->plat.chip_delay; chip->cmd_ctrl = gpio_nand_cmd_ctrl; From c1c7040e072caac6445a1e298bd1fc5e8da0a5f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 8 Apr 2016 12:23:46 +0200 Subject: [PATCH 038/155] mtd: nand: mxc: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- 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 854c832597aa..57b1b74c8fa0 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1585,6 +1585,7 @@ static int mxcnd_probe(struct platform_device *pdev) this->ecc.mode = NAND_ECC_HW; } else { this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; } /* NAND bus width determines access functions used by upper layer */ From 37afb2034f17c5f17423bfe5a4fc100d6803b554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 8 Apr 2016 12:23:47 +0200 Subject: [PATCH 039/155] mtd: nand: nuc900: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nuc900_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index dbc5b571c2bb..8f64011d32ef 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -261,6 +261,7 @@ static int nuc900_nand_probe(struct platform_device *pdev) chip->chip_delay = 50; chip->options = 0; chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res); From ac7efcbe0e3d64565f5b086d231589e3637e9b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 8 Apr 2016 12:23:48 +0200 Subject: [PATCH 040/155] mtd: nand: orion: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/orion_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index d4614bfbfed6..40a7c4a2cf0d 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -130,6 +130,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) nc->cmd_ctrl = orion_nand_cmd_ctrl; nc->read_buf = orion_nand_read_buf; nc->ecc.mode = NAND_ECC_SOFT; + nc->ecc.algo = NAND_ECC_HAMMING; if (board->chip_delay) nc->chip_delay = board->chip_delay; From a9670a9c7ff9488d66d6fd7593415bb978017509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 8 Apr 2016 12:23:49 +0200 Subject: [PATCH 041/155] mtd: nand: pasemi: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/pasemi_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 3ab53ca53cca..63fcb8c63877 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -151,6 +151,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev) chip->write_buf = pasemi_write_buf; chip->chip_delay = 0; chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; /* Enable the following for a flash based bad block table */ chip->bbt_options = NAND_BBT_USE_FLASH; From 41ccb49e9123e268e8f84d019893cacd6a312c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 8 Apr 2016 12:23:50 +0200 Subject: [PATCH 042/155] mtd: nand: plat: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/plat_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index e4e50da30444..415a53a0deeb 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -74,6 +74,7 @@ static int plat_nand_probe(struct platform_device *pdev) data->chip.ecc.hwctl = pdata->ctrl.hwcontrol; data->chip.ecc.mode = NAND_ECC_SOFT; + data->chip.ecc.algo = NAND_ECC_HAMMING; platform_set_drvdata(pdev, data); From ce111afd01bce7c137c92e9ed0dbe294a0f2373a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 8 Apr 2016 12:23:51 +0200 Subject: [PATCH 043/155] mtd: nand: socrates: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/socrates_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index e3305f9dd6fb..888fd314c62a 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -180,6 +180,7 @@ static int socrates_nand_probe(struct platform_device *ofdev) nand_chip->dev_ready = socrates_nand_device_ready; nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ + nand_chip->ecc.algo = NAND_ECC_HAMMING; /* TODO: I have no idea what real delay is. */ nand_chip->chip_delay = 20; /* 20us command delay time */ From ff05fdb187cc25bff3e7ee9f79d94077c8d16a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 11:48:05 +0200 Subject: [PATCH 044/155] mtd: nand: pasemi: switch to dev_* printing functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It also contains some minor related changes: 1) Don't warn if kzalloc fails as it dumps stack on its own 2) Use %pR format for displaying whole resource to avoid invalid format warning Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/pasemi_nand.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 63fcb8c63877..5de7591b0510 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -92,8 +92,9 @@ int pasemi_device_ready(struct mtd_info *mtd) static int pasemi_nand_probe(struct platform_device *ofdev) { + struct device *dev = &ofdev->dev; struct pci_dev *pdev; - struct device_node *np = ofdev->dev.of_node; + struct device_node *np = dev->of_node; struct resource res; struct nand_chip *chip; int err = 0; @@ -107,13 +108,11 @@ static int pasemi_nand_probe(struct platform_device *ofdev) if (pasemi_nand_mtd) return -ENODEV; - pr_debug("pasemi_nand at %pR\n", &res); + dev_dbg(dev, "pasemi_nand at %pR\n", &res); /* Allocate memory for MTD device structure and private data */ chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); if (!chip) { - printk(KERN_WARNING - "Unable to allocate PASEMI NAND MTD device structure\n"); err = -ENOMEM; goto out; } @@ -121,7 +120,7 @@ static int pasemi_nand_probe(struct platform_device *ofdev) pasemi_nand_mtd = nand_to_mtd(chip); /* Link the private data with the MTD structure */ - pasemi_nand_mtd->dev.parent = &ofdev->dev; + pasemi_nand_mtd->dev.parent = dev; chip->IO_ADDR_R = of_iomap(np, 0); chip->IO_ADDR_W = chip->IO_ADDR_R; @@ -163,13 +162,13 @@ static int pasemi_nand_probe(struct platform_device *ofdev) } if (mtd_device_register(pasemi_nand_mtd, NULL, 0)) { - printk(KERN_ERR "pasemi_nand: Unable to register MTD device\n"); + dev_err(dev, "Unable to register MTD device\n"); err = -ENODEV; goto out_lpc; } - printk(KERN_INFO "PA Semi NAND flash at %08llx, control at I/O %x\n", - res.start, lpcctl); + dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res, + lpcctl); return 0; From 4f3cab9b4457108a8241ec7a66e03fe0b2cb3646 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:22 +0200 Subject: [PATCH 045/155] mtd: nand: atmel: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon Tested-by: Boris Brezillon Reviewed-by: Nicolas Ferre --- drivers/mtd/nand/atmel_nand.c | 133 +++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 60 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 0b5da7271440..5e716f2a8739 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -434,14 +433,13 @@ err_buf: static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand_host *host = nand_get_controller_data(chip); if (use_dma && len > mtd->oobsize) /* only use DMA for bigger than oob size: better performances */ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0) return; - if (host->board.bus_width_16) + if (chip->options & NAND_BUSWIDTH_16) atmel_read_buf16(mtd, buf, len); else atmel_read_buf8(mtd, buf, len); @@ -450,14 +448,13 @@ static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) { struct nand_chip *chip = mtd_to_nand(mtd); - struct atmel_nand_host *host = nand_get_controller_data(chip); if (use_dma && len > mtd->oobsize) /* only use DMA for bigger than oob size: better performances */ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0) return; - if (host->board.bus_width_16) + if (chip->options & NAND_BUSWIDTH_16) atmel_write_buf16(mtd, buf, len); else atmel_write_buf8(mtd, buf, len); @@ -1507,58 +1504,17 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); } -static int atmel_of_init_port(struct atmel_nand_host *host, - struct device_node *np) +static int atmel_of_init_ecc(struct atmel_nand_host *host, + struct device_node *np) { - u32 val; u32 offset[2]; - int ecc_mode; - struct atmel_nand_data *board = &host->board; - enum of_gpio_flags flags = 0; - - host->caps = (struct atmel_nand_caps *) - of_device_get_match_data(host->dev); - - if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { - if (val >= 32) { - dev_err(host->dev, "invalid addr-offset %u\n", val); - return -EINVAL; - } - board->ale = val; - } - - if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) { - if (val >= 32) { - dev_err(host->dev, "invalid cmd-offset %u\n", val); - return -EINVAL; - } - board->cle = val; - } - - ecc_mode = of_get_nand_ecc_mode(np); - - board->ecc_mode = ecc_mode < 0 ? NAND_ECC_SOFT : ecc_mode; - - board->on_flash_bbt = of_get_nand_on_flash_bbt(np); - - board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma"); - - if (of_get_nand_bus_width(np) == 16) - board->bus_width_16 = 1; - - board->rdy_pin = of_get_gpio_flags(np, 0, &flags); - board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW); - - board->enable_pin = of_get_gpio(np, 1); - board->det_pin = of_get_gpio(np, 2); + u32 val; host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc"); - /* load the nfc driver if there is */ - of_platform_populate(np, NULL, NULL, host->dev); - - if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc) - return 0; /* Not using PMECC */ + /* Not using PMECC */ + if (!(host->nand_chip.ecc.mode == NAND_ECC_HW) || !host->has_pmecc) + return 0; /* use PMECC, get correction capability, sector size and lookup * table offset. @@ -1599,16 +1555,64 @@ static int atmel_of_init_port(struct atmel_nand_host *host, /* Will build a lookup table and initialize the offset later */ return 0; } + if (!offset[0] && !offset[1]) { dev_err(host->dev, "Invalid PMECC lookup table offset\n"); return -EINVAL; } + host->pmecc_lookup_table_offset_512 = offset[0]; host->pmecc_lookup_table_offset_1024 = offset[1]; return 0; } +static int atmel_of_init_port(struct atmel_nand_host *host, + struct device_node *np) +{ + u32 val; + struct atmel_nand_data *board = &host->board; + enum of_gpio_flags flags = 0; + + host->caps = (struct atmel_nand_caps *) + of_device_get_match_data(host->dev); + + if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { + if (val >= 32) { + dev_err(host->dev, "invalid addr-offset %u\n", val); + return -EINVAL; + } + board->ale = val; + } + + if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) { + if (val >= 32) { + dev_err(host->dev, "invalid cmd-offset %u\n", val); + return -EINVAL; + } + board->cle = val; + } + + board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma"); + + board->rdy_pin = of_get_gpio_flags(np, 0, &flags); + board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW); + + board->enable_pin = of_get_gpio(np, 1); + board->det_pin = of_get_gpio(np, 2); + + /* load the nfc driver if there is */ + of_platform_populate(np, NULL, NULL, host->dev); + + /* + * Initialize ECC mode to NAND_ECC_SOFT so that we have a correct value + * even if the nand-ecc-mode property is not defined. + */ + host->nand_chip.ecc.mode = NAND_ECC_SOFT; + + return 0; +} + static int atmel_hw_nand_init_params(struct platform_device *pdev, struct atmel_nand_host *host) { @@ -2150,6 +2154,11 @@ static int atmel_nand_probe(struct platform_device *pdev) } else { memcpy(&host->board, dev_get_platdata(&pdev->dev), sizeof(struct atmel_nand_data)); + nand_chip->ecc.mode = host->board.ecc_mode; + + /* 16-bit bus width */ + if (host->board.bus_width_16) + nand_chip->options |= NAND_BUSWIDTH_16; } /* link the private data structures */ @@ -2191,11 +2200,8 @@ static int atmel_nand_probe(struct platform_device *pdev) nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; } - nand_chip->ecc.mode = host->board.ecc_mode; nand_chip->chip_delay = 40; /* 40us command delay time */ - if (host->board.bus_width_16) /* 16-bit bus width */ - nand_chip->options |= NAND_BUSWIDTH_16; nand_chip->read_buf = atmel_read_buf; nand_chip->write_buf = atmel_write_buf; @@ -2228,11 +2234,6 @@ static int atmel_nand_probe(struct platform_device *pdev) } } - if (host->board.on_flash_bbt || on_flash_bbt) { - dev_info(&pdev->dev, "Use On Flash BBT\n"); - nand_chip->bbt_options |= NAND_BBT_USE_FLASH; - } - if (!host->board.has_dma) use_dma = 0; @@ -2259,6 +2260,18 @@ static int atmel_nand_probe(struct platform_device *pdev) goto err_scan_ident; } + if (host->board.on_flash_bbt || on_flash_bbt) + nand_chip->bbt_options |= NAND_BBT_USE_FLASH; + + if (nand_chip->bbt_options & NAND_BBT_USE_FLASH) + dev_info(&pdev->dev, "Use On Flash BBT\n"); + + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + res = atmel_of_init_ecc(host, pdev->dev.of_node); + if (res) + goto err_hw_ecc; + } + if (nand_chip->ecc.mode == NAND_ECC_HW) { if (host->has_pmecc) res = atmel_pmecc_nand_init_params(pdev, host); From 44ccb64fa56ad6d6d2d33a8625d9464f54c63eba Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:30 +0200 Subject: [PATCH 046/155] mtd: nand: pxa3xx: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon Acked-by: Ezequiel Garcia --- drivers/mtd/nand/pxa3xx_nand.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index d6508856da99..38d26ab1f0f7 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #define CHIP_DELAY_TIMEOUT msecs_to_jiffies(200) @@ -1651,6 +1650,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) nand_writel(info, NDECCCTRL, 0x0); + if (pdata->flash_bbt) + chip->bbt_options |= NAND_BBT_USE_FLASH; + + chip->ecc.strength = pdata->ecc_strength; + chip->ecc.size = pdata->ecc_step_size; + if (nand_scan_ident(mtd, 1, NULL)) return -ENODEV; @@ -1663,13 +1668,12 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) } } - if (pdata->flash_bbt) { + if (chip->bbt_options & NAND_BBT_USE_FLASH) { /* * We'll use a bad block table stored in-flash and don't * allow writing the bad block marker to the flash. */ - chip->bbt_options |= NAND_BBT_USE_FLASH | - NAND_BBT_NO_OOB_BBM; + chip->bbt_options |= NAND_BBT_NO_OOB_BBM; chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; } @@ -1689,10 +1693,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) } } - if (pdata->ecc_strength && pdata->ecc_step_size) { - ecc_strength = pdata->ecc_strength; - ecc_step = pdata->ecc_step_size; - } else { + ecc_strength = chip->ecc.strength; + ecc_step = chip->ecc.size; + if (!ecc_strength || !ecc_step) { ecc_strength = chip->ecc_strength_ds; ecc_step = chip->ecc_step_ds; } @@ -1903,15 +1906,6 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev) if (of_get_property(np, "marvell,nand-keep-config", NULL)) pdata->keep_config = 1; of_property_read_u32(np, "num-cs", &pdata->num_cs); - pdata->flash_bbt = of_get_nand_on_flash_bbt(np); - - pdata->ecc_strength = of_get_nand_ecc_strength(np); - if (pdata->ecc_strength < 0) - pdata->ecc_strength = 0; - - pdata->ecc_step_size = of_get_nand_ecc_step_size(np); - if (pdata->ecc_step_size < 0) - pdata->ecc_step_size = 0; pdev->dev.platform_data = pdata; From 9edb47004e2afb5884d0c900cbd1ece8b2d6f403 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 2 Dec 2015 16:00:57 +0100 Subject: [PATCH 047/155] mtd: nand: sunxi: fix call order in sunxi_nand_chip_init() sunxi_nand_chip_set_timings() is extracting a pointer to the nfc from the nand->controller field, but this field is initialized after sunxi_nand_chip_set_timings() call. Reorder the calls to avoid any problem. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 3a97093662ea..546a9cae9bd1 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1535,21 +1535,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, } } - timings = onfi_async_timing_mode_to_sdr_timings(0); - if (IS_ERR(timings)) { - ret = PTR_ERR(timings); - dev_err(dev, - "could not retrieve timings for ONFI mode 0: %d\n", - ret); - return ret; - } - - ret = sunxi_nand_chip_set_timings(chip, timings); - if (ret) { - dev_err(dev, "could not configure chip timings: %d\n", ret); - return ret; - } - nand = &chip->nand; /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ nand->chip_delay = 200; @@ -1569,6 +1554,21 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, mtd = nand_to_mtd(nand); mtd->dev.parent = dev; + timings = onfi_async_timing_mode_to_sdr_timings(0); + if (IS_ERR(timings)) { + ret = PTR_ERR(timings); + dev_err(dev, + "could not retrieve timings for ONFI mode 0: %d\n", + ret); + return ret; + } + + ret = sunxi_nand_chip_set_timings(chip, timings); + if (ret) { + dev_err(dev, "could not configure chip timings: %d\n", ret); + return ret; + } + ret = nand_scan_ident(mtd, nsels, NULL); if (ret) return ret; From 2f9992e080b8892a10d189cbc846c06e6594ad0b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 2 Dec 2015 15:10:40 +0100 Subject: [PATCH 048/155] mtd: nand: sunxi: fix clk rate calculation Unlike what is specified in the Allwinner datasheets, the NAND clock rate is not equal to 2/T but 1/T. Fix the clock rate selection accordingly. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 546a9cae9bd1..05b33035f651 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1208,12 +1208,12 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); /* - * Convert min_clk_period into a clk frequency, then get the - * appropriate rate for the NAND controller IP given this formula - * (specified in the datasheet): - * nand clk_rate = 2 * min_clk_rate + * Unlike what is stated in Allwinner datasheet, the clk_rate should + * be set to (1 / min_clk_period), and not (2 / min_clk_period). + * This new formula was verified with a scope and validated by + * Allwinner engineers. */ - chip->clk_rate = (2 * NSEC_PER_SEC) / min_clk_period; + chip->clk_rate = NSEC_PER_SEC / min_clk_period; return 0; } From 2d43457f79e48ee427666fdbfe9a53f35d3a1672 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 2 Dec 2015 15:57:20 +0100 Subject: [PATCH 049/155] mtd: nand: sunxi: fix EDO mode selection The ONFI spec says that EDO should be enabled if the host drives tRC less than 30ns, but the code just tests for the tRC_min value extracted from the timings exposed by the NAND chip not the timings actually configured in the NAND controller. Fix that by first rounding down the requested clk_rate with clk_round_rate() and then checking if tRC is actually smaller than 30ns. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 05b33035f651..b906fc5a22fe 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1100,6 +1100,7 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, struct sunxi_nfc *nfc = to_sunxi_nfc(chip->nand.controller); u32 min_clk_period = 0; s32 tWB, tADL, tWHR, tRHW, tCAD; + long real_clk_rate; /* T1 <=> tCLS */ if (timings->tCLS_min > min_clk_period) @@ -1197,13 +1198,6 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ chip->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); - /* - * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data - * output cycle timings shall be used if the host drives tRC less than - * 30 ns. - */ - chip->timing_ctl = (timings->tRC_min < 30000) ? NFC_TIMING_CTL_EDO : 0; - /* Convert min_clk_period from picoseconds to nanoseconds */ min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); @@ -1214,6 +1208,16 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, * Allwinner engineers. */ chip->clk_rate = NSEC_PER_SEC / min_clk_period; + real_clk_rate = clk_round_rate(nfc->mod_clk, chip->clk_rate); + + /* + * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data + * output cycle timings shall be used if the host drives tRC less than + * 30 ns. + */ + min_clk_period = NSEC_PER_SEC / real_clk_rate; + chip->timing_ctl = ((min_clk_period * 2) < 30) ? + NFC_TIMING_CTL_EDO : 0; return 0; } From 5abcd95d8c69008c72d54d7763e0ee2b5df84ac4 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 11 Nov 2015 22:30:30 +0100 Subject: [PATCH 050/155] mtd: nand: sunxi: adapt clk_rate to tWB, tADL, tWHR and tRHW timings Adapt the NAND controller clk rate to the tWB, tADL, tWHR and tRHW timings instead of returning an error when the maximum clk divisor is not big enough to provide an appropriate timing. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index b906fc5a22fe..e30276d9a9e3 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1163,6 +1163,18 @@ static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); /* T16 - T19 + tCAD */ + if (timings->tWB_max > (min_clk_period * 20)) + min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20); + + if (timings->tADL_min > (min_clk_period * 32)) + min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32); + + if (timings->tWHR_min > (min_clk_period * 32)) + min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32); + + if (timings->tRHW_min > (min_clk_period * 20)) + min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20); + tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, min_clk_period); if (tWB < 0) { From 9d02fc2a5129449581c3108c260e96377cf35f7e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 26 Aug 2015 16:08:12 +0200 Subject: [PATCH 051/155] mtd: nand: export default read/write oob functions Export the default read/write oob functions (for the standard and syndrome scheme), so that drivers can use them for their raw implementation and implement their own functions for the normal oob operation. This is required if your ECC engine is capable of fixing some of the OOB data. In this case you have to overload the ->read_oob() and ->write_oob(), but if you don't specify the ->read/write_oob_raw() functions they are assigned to the ->read/write_oob() implementation, which is not what you want. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 18 ++++++++++-------- include/linux/mtd/nand.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0f0c5b190316..13fcddc8a10e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1893,13 +1893,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, * @chip: nand chip info structure * @page: page number to read */ -static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page) +int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); return 0; } +EXPORT_SYMBOL(nand_read_oob_std); /** * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC @@ -1908,8 +1908,8 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @page: page number to read */ -static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, - int page) +int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page) { int length = mtd->oobsize; int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; @@ -1937,6 +1937,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +EXPORT_SYMBOL(nand_read_oob_syndrome); /** * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function @@ -1944,8 +1945,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @page: page number to write */ -static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, - int page) +int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page) { int status = 0; const uint8_t *buf = chip->oob_poi; @@ -1960,6 +1960,7 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, return status & NAND_STATUS_FAIL ? -EIO : 0; } +EXPORT_SYMBOL(nand_write_oob_std); /** * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC @@ -1968,8 +1969,8 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, * @chip: nand chip info structure * @page: page number to write */ -static int nand_write_oob_syndrome(struct mtd_info *mtd, - struct nand_chip *chip, int page) +int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page) { int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size, length = mtd->oobsize; @@ -2019,6 +2020,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, return status & NAND_STATUS_FAIL ? -EIO : 0; } +EXPORT_SYMBOL(nand_write_oob_syndrome); /** * nand_do_read_oob - [INTERN] NAND read out-of-band diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1b673e19667c..7e06afb8552c 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -1078,4 +1078,18 @@ int nand_check_erased_ecc_chunk(void *data, int datalen, void *ecc, int ecclen, void *extraoob, int extraooblen, int threshold); + +/* Default write_oob implementation */ +int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); + +/* Default write_oob syndrome implementation */ +int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page); + +/* Default read_oob implementation */ +int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page); + +/* Default read_oob syndrome implementation */ +int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + int page); #endif /* __LINUX_MTD_NAND_H */ From 1c1bdd6f866e08b2a34318ce089b678200e8de85 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 2 Sep 2015 15:05:52 +0200 Subject: [PATCH 052/155] mtd: nand: sunxi: implement ->read_oob()/->write_oob() Allwinner's ECC engine is capable of protecting a few bytes of the OOB area. Implement specific OOB functions to benefit from this capability. Also, when in raw mode, the randomizer is disabled, which means you'll only be able to retrieve randomized data, which is not really useful for most applications. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index e30276d9a9e3..d8ad50fbadb7 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1073,6 +1073,40 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, return 0; } +static int sunxi_nfc_hw_common_ecc_read_oob(struct mtd_info *mtd, + struct nand_chip *chip, + int page) +{ + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + + chip->pagebuf = -1; + + return chip->ecc.read_page(mtd, chip, chip->buffers->databuf, 1, page); +} + +static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, + struct nand_chip *chip, + int page) +{ + int ret, status; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page); + + chip->pagebuf = -1; + + memset(chip->buffers->databuf, 0xff, mtd->writesize); + ret = chip->ecc.write_page(mtd, chip, chip->buffers->databuf, 1, page); + if (ret) + return ret; + + /* Send command to program the OOB data */ + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + + return status & NAND_STATUS_FAIL ? -EIO : 0; +} + static const s32 tWB_lut[] = {6, 12, 16, 20}; static const s32 tRHW_lut[] = {4, 8, 12, 20}; @@ -1320,6 +1354,8 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, layout->eccbytes = (ecc->bytes * nsectors); + ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob; + ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob; ecc->layout = layout; ecc->priv = data; @@ -1351,6 +1387,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, ecc->read_page = sunxi_nfc_hw_ecc_read_page; ecc->write_page = sunxi_nfc_hw_ecc_write_page; + ecc->read_oob_raw = nand_read_oob_std; + ecc->write_oob_raw = nand_write_oob_std; layout = ecc->layout; nsectors = mtd->writesize / ecc->size; @@ -1405,6 +1443,8 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, ecc->prepad = 4; ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; + ecc->read_oob_raw = nand_read_oob_syndrome; + ecc->write_oob_raw = nand_write_oob_syndrome; layout = ecc->layout; nsectors = mtd->writesize / ecc->size; From fe82ccefc992b255c35e0e56d6aded2ce090e9af Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 16 Sep 2015 09:01:45 +0200 Subject: [PATCH 053/155] mtd: nand: sunxi: implement ->read_subpage() Being able to read subpages can greatly improve read performances if the MTD user is only interested in a small section of a NAND page. This is particularly true with large pages (>= 8k). Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index d8ad50fbadb7..e4d487cf2ebd 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -973,6 +973,39 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, return max_bitflips; } +static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, + struct nand_chip *chip, + u32 data_offs, u32 readlen, + u8 *bufpoi, int page) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret, i, cur_off = 0; + unsigned int max_bitflips = 0; + + sunxi_nfc_hw_ecc_enable(mtd); + + chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + for (i = data_offs / ecc->size; + i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { + int data_off = i * ecc->size; + int oob_off = i * (ecc->bytes + 4); + u8 *data = bufpoi + data_off; + u8 *oob = chip->oob_poi + oob_off; + + ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, + oob, + oob_off + mtd->writesize, + &cur_off, &max_bitflips, + !i, page); + if (ret < 0) + return ret; + } + + sunxi_nfc_hw_ecc_disable(mtd); + + return max_bitflips; +} + static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, @@ -1389,6 +1422,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, ecc->write_page = sunxi_nfc_hw_ecc_write_page; ecc->read_oob_raw = nand_read_oob_std; ecc->write_oob_raw = nand_write_oob_std; + ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; layout = ecc->layout; nsectors = mtd->writesize / ecc->size; @@ -1635,6 +1669,8 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, if (nand->options & NAND_NEED_SCRAMBLING) nand->options |= NAND_NO_SUBPAGE_WRITE; + nand->options |= NAND_SUBPAGE_READ; + ret = sunxi_nand_chip_init_timings(chip, np); if (ret) { dev_err(dev, "could not configure chip timings: %d\n", ret); From e9aa671f69acb87db4835e4f0b41f5fa16d16562 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 16 Sep 2015 09:05:31 +0200 Subject: [PATCH 054/155] mtd: nand: sunxi: improve ->cmd_ctrl() function Try to pack address and command cycles into a single NAND controller command to avoid polling the status register for each single change on the NAND bus. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 52 +++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index e4d487cf2ebd..ab572dd5ad3c 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -238,6 +238,10 @@ struct sunxi_nand_chip { u32 timing_cfg; u32 timing_ctl; int selected; + int addr_cycles; + u32 addr[2]; + int cmd_cycles; + u8 cmd[2]; int nsels; struct sunxi_nand_chip_sel sels[0]; }; @@ -525,17 +529,49 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, writel(tmp, nfc->regs + NFC_REG_CTL); } - if (dat == NAND_CMD_NONE) - return; + if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) && + !(ctrl & (NAND_CLE | NAND_ALE))) { + u32 cmd = 0; - if (ctrl & NAND_CLE) { - writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD); - } else { - writel(dat, nfc->regs + NFC_REG_ADDR_LOW); - writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD); + if (!sunxi_nand->addr_cycles && !sunxi_nand->cmd_cycles) + return; + + if (sunxi_nand->cmd_cycles--) + cmd |= NFC_SEND_CMD1 | sunxi_nand->cmd[0]; + + if (sunxi_nand->cmd_cycles--) { + cmd |= NFC_SEND_CMD2; + writel(sunxi_nand->cmd[1], + nfc->regs + NFC_REG_RCMD_SET); + } + + sunxi_nand->cmd_cycles = 0; + + if (sunxi_nand->addr_cycles) { + cmd |= NFC_SEND_ADR | + NFC_ADR_NUM(sunxi_nand->addr_cycles); + writel(sunxi_nand->addr[0], + nfc->regs + NFC_REG_ADDR_LOW); + } + + if (sunxi_nand->addr_cycles > 4) + writel(sunxi_nand->addr[1], + nfc->regs + NFC_REG_ADDR_HIGH); + + writel(cmd, nfc->regs + NFC_REG_CMD); + sunxi_nand->addr[0] = 0; + sunxi_nand->addr[1] = 0; + sunxi_nand->addr_cycles = 0; + sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); } - sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + if (ctrl & NAND_CLE) { + sunxi_nand->cmd[sunxi_nand->cmd_cycles++] = dat; + } else if (ctrl & NAND_ALE) { + sunxi_nand->addr[sunxi_nand->addr_cycles / 4] |= + dat << ((sunxi_nand->addr_cycles % 4) * 8); + sunxi_nand->addr_cycles++; + } } /* These seed values have been extracted from Allwinner's BSP */ From ece03cfd5260e0349442dea1d1065f44fbed1ea8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 4 Mar 2016 17:21:35 +0100 Subject: [PATCH 055/155] mtd: nand: sunxi: let the NAND controller control the CE line We don't need to manually toggle the CE line since the controller handles it for us. Moreover, keeping the CE line low when interacting with a DDR NAND can be problematic (data loss in some corner cases). Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index ab572dd5ad3c..4dcc0e42e0ef 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -514,21 +514,11 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); int ret; - u32 tmp; ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) return; - if (ctrl & NAND_CTRL_CHANGE) { - tmp = readl(nfc->regs + NFC_REG_CTL); - if (ctrl & NAND_NCE) - tmp |= NFC_CE_CTL; - else - tmp &= ~NFC_CE_CTL; - writel(tmp, nfc->regs + NFC_REG_CTL); - } - if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) && !(ctrl & (NAND_CLE | NAND_ALE))) { u32 cmd = 0; From f8b04746a4ba389d609b5ddcfbf95835c7dfcb31 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 4 Mar 2016 17:25:08 +0100 Subject: [PATCH 056/155] mtd: nand: sunxi: fix the NFC_ECC_ERR_CNT() macro NFC_ECC_ERR_CNT() is not taking into account the case when the NAND chip contains more than 4 ECC blocks (NANDs with 4kB+ pages). Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 4dcc0e42e0ef..4cb5c6f866ff 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -154,7 +154,7 @@ /* define bit use in NFC_ECC_ST */ #define NFC_ECC_ERR(x) BIT(x) #define NFC_ECC_PAT_FOUND(x) BIT(x + 16) -#define NFC_ECC_ERR_CNT(b, x) (((x) >> ((b) * 8)) & 0xff) +#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) #define NFC_DEFAULT_TIMEOUT_MS 1000 From 68ffbf7f9fb11323238cb31c956b4f381e6b05cc Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 4 Mar 2016 17:29:20 +0100 Subject: [PATCH 057/155] mtd: nand: sunxi: fix NFC_CTL setting NFC_PAGE_SHIFT() already takes the real page_shift value and subtract 10 to it. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 4cb5c6f866ff..f8b3a7281af0 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -410,7 +410,7 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) sel = &sunxi_nand->sels[chip]; ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | - NFC_PAGE_SHIFT(nand->page_shift - 10); + NFC_PAGE_SHIFT(nand->page_shift); if (sel->rb.type == RB_NONE) { nand->dev_ready = NULL; } else { From dd26a4584c37687b8bbe89233bcc23279ca361b1 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 4 Mar 2016 18:26:40 +0100 Subject: [PATCH 058/155] mtd: nand: sunxi: disable clks on device removal mod and ahb clocks are not disabled when the NAND controller device is removed. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index f8b3a7281af0..cad20fb058a1 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1845,6 +1845,8 @@ static int sunxi_nfc_remove(struct platform_device *pdev) struct sunxi_nfc *nfc = platform_get_drvdata(pdev); sunxi_nand_chips_cleanup(nfc); + clk_disable_unprepare(nfc->mod_clk); + clk_disable_unprepare(nfc->ahb_clk); return 0; } From 336de7b1e07e6c666dbed522120547b852b3fba7 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 4 Mar 2016 17:33:10 +0100 Subject: [PATCH 059/155] mtd: nand: enable ECC pipelining When the NAND controller operates in DMA mode it can pipeline ECC operations which improves the throughput. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index cad20fb058a1..fe3730a6ba54 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -742,7 +742,8 @@ static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); - ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION; + ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION | + NFC_ECC_PIPELINE; writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); } From a9a416f0c723e10ecccfe7ea30e93dfee0dbf10d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 7 Mar 2016 15:10:28 +0100 Subject: [PATCH 060/155] mtd: nand: sunxi: fix ->dev_ready() implementation ->dev_ready() is not supposed to wait for busy to ready solution (this is the role of ->waitfunc()). Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index fe3730a6ba54..fff28512cd71 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -357,7 +357,6 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd) struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct sunxi_nand_rb *rb; - unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20); int ret; if (sunxi_nand->selected < 0) @@ -367,12 +366,6 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd) switch (rb->type) { case RB_NATIVE: - ret = !!(readl(nfc->regs + NFC_REG_ST) & - NFC_RB_STATE(rb->info.nativeid)); - if (ret) - break; - - sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo); ret = !!(readl(nfc->regs + NFC_REG_ST) & NFC_RB_STATE(rb->info.nativeid)); break; From 166f08c7b6ab2f98eebbb6fc1fdb0be1efe0a566 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 7 Mar 2016 15:25:17 +0100 Subject: [PATCH 061/155] mtd: nand: sunxi: make use of readl_poll_timeout() Replace open coded polling loops by readl_poll_timeout() calls. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index fff28512cd71..d4d79b2f1d43 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #define NFC_REG_CTL 0x0000 #define NFC_REG_ST 0x0004 @@ -322,33 +322,33 @@ static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags, static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) { - unsigned long timeout = jiffies + - msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS); + u32 status; + int ret; - do { - if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS)) - return 0; - } while (time_before(jiffies, timeout)); + ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, + !(status & NFC_CMD_FIFO_STATUS), 1, + NFC_DEFAULT_TIMEOUT_MS * 1000); + if (ret) + dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); - dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); - return -ETIMEDOUT; + return ret; } static int sunxi_nfc_rst(struct sunxi_nfc *nfc) { - unsigned long timeout = jiffies + - msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS); + u32 ctl; + int ret; writel(0, nfc->regs + NFC_REG_ECC_CTL); writel(NFC_RESET, nfc->regs + NFC_REG_CTL); - do { - if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET)) - return 0; - } while (time_before(jiffies, timeout)); + ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl, + !(ctl & NFC_RESET), 1, + NFC_DEFAULT_TIMEOUT_MS * 1000); + if (ret) + dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); - dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); - return -ETIMEDOUT; + return ret; } static int sunxi_nfc_dev_ready(struct mtd_info *mtd) From c0c9dfa8adf09dbbb7dc32c20d9ccb33e19250c0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 7 Mar 2016 15:34:39 +0100 Subject: [PATCH 062/155] mtd: nand: sunxi: poll for events instead of using interrupts Some NAND operations are so fast that it doesn't make any sense to use interrupt based waits (the scheduling overhead is not worth it). Rename sunxi_nfc_wait_int() into sunxi_nfc_wait_events() and add a parameter to specify whether polling should be used or not. Note that all sunxi_nfc_wait_int() are moved to the polling approach now, but this should change as soon as we have more information about the approximate time we are about to wait (can be extracted from the NAND timings, and the type of operation). Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 45 ++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index d4d79b2f1d43..8be3dba07ff7 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -301,23 +301,40 @@ static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags, - unsigned int timeout_ms) +static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events, + bool use_polling, unsigned int timeout_ms) { - init_completion(&nfc->complete); + int ret; - writel(flags, nfc->regs + NFC_REG_INT); + if (events & ~NFC_INT_MASK) + return -EINVAL; if (!timeout_ms) timeout_ms = NFC_DEFAULT_TIMEOUT_MS; - if (!wait_for_completion_timeout(&nfc->complete, - msecs_to_jiffies(timeout_ms))) { - dev_err(nfc->dev, "wait interrupt timedout\n"); - return -ETIMEDOUT; + if (!use_polling) { + init_completion(&nfc->complete); + + writel(events, nfc->regs + NFC_REG_INT); + + ret = wait_for_completion_timeout(&nfc->complete, + msecs_to_jiffies(timeout_ms)); + + writel(0, nfc->regs + NFC_REG_INT); + } else { + u32 status; + + ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, + (status & events) == events, 1, + timeout_ms * 1000); } - return 0; + writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST); + + if (ret) + dev_err(nfc->dev, "wait interrupt timedout\n"); + + return ret; } static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) @@ -448,7 +465,7 @@ static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; writel(tmp, nfc->regs + NFC_REG_CMD); - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); if (ret) break; @@ -483,7 +500,7 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, NFC_ACCESS_DIR; writel(tmp, nfc->regs + NFC_REG_CMD); - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); if (ret) break; @@ -545,7 +562,7 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, sunxi_nand->addr[0] = 0; sunxi_nand->addr[1] = 0; sunxi_nand->addr_cycles = 0; - sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); } if (ctrl & NAND_CLE) { @@ -788,7 +805,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, nfc->regs + NFC_REG_CMD); - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); sunxi_nfc_randomizer_disable(mtd); if (ret) return ret; @@ -926,7 +943,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, NFC_ACCESS_DIR | NFC_ECC_OP, nfc->regs + NFC_REG_CMD); - ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); sunxi_nfc_randomizer_disable(mtd); if (ret) return ret; From cc6822fb756b5080ca127fef1a89f35558bd2610 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 4 Mar 2016 17:56:47 +0100 Subject: [PATCH 063/155] mtd: nand: sunxi: move some ECC related operations to their own functions In order to support DMA operations in a clean way we need to extract some of the logic coded in sunxi_nfc_hw_ecc_read/write_page() into their own function. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 163 ++++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 55 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 8be3dba07ff7..c20bf59141b0 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -775,6 +775,94 @@ static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) buf[3] = user_data >> 24; } +static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) +{ + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + +static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob, + int step, bool bbm, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), + oob); + + /* De-randomize the Bad Block Marker. */ + if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) + sunxi_nfc_randomize_bbm(mtd, page, oob); +} + +static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd, + const u8 *oob, int step, + bool bbm, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + u8 user_data[4]; + + /* Randomize the Bad Block Marker. */ + if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { + memcpy(user_data, oob, sizeof(user_data)); + sunxi_nfc_randomize_bbm(mtd, page, user_data); + oob = user_data; + } + + writel(sunxi_nfc_buf_to_user_data(oob), + nfc->regs + NFC_REG_USER_DATA(step)); +} + +static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd, + unsigned int *max_bitflips, int ret) +{ + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + *max_bitflips = max_t(unsigned int, *max_bitflips, ret); + } +} + +static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob, + int step, bool *erased) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + u32 status, tmp; + + *erased = false; + + status = readl(nfc->regs + NFC_REG_ECC_ST); + + if (status & NFC_ECC_ERR(step)) + return -EBADMSG; + + if (status & NFC_ECC_PAT_FOUND(step)) { + u8 pattern; + + if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { + pattern = 0x0; + } else { + pattern = 0xff; + *erased = true; + } + + if (data) + memset(data, pattern, ecc->size); + + if (oob) + memset(oob, pattern, ecc->bytes + 4); + + return 0; + } + + tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); + + return NFC_ECC_ERR_CNT(step, tmp); +} + static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, u8 *data, int data_off, u8 *oob, int oob_off, @@ -786,7 +874,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct nand_ecc_ctrl *ecc = &nand->ecc; int raw_mode = 0; - u32 status; + bool erased; int ret; if (*cur_off != data_off) @@ -812,27 +900,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, *cur_off = oob_off + ecc->bytes + 4; - status = readl(nfc->regs + NFC_REG_ECC_ST); - if (status & NFC_ECC_PAT_FOUND(0)) { - u8 pattern = 0xff; - - if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) - pattern = 0x0; - - memset(data, pattern, ecc->size); - memset(oob, pattern, ecc->bytes + 4); - + ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob, 0, &erased); + if (erased) return 1; - } - ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0))); - - memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page); - - if (status & NFC_ECC_ERR(0)) { + if (ret < 0) { /* * Re-read the data with the randomizer disabled to identify * bitflips in erased pages. @@ -840,34 +912,31 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, if (nand->options & NAND_NEED_SCRAMBLING) { nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); nand->read_buf(mtd, data, ecc->size); - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - nand->read_buf(mtd, oob, ecc->bytes + 4); + } else { + memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, + ecc->size); } + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + nand->read_buf(mtd, oob, ecc->bytes + 4); + ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, ecc->bytes + 4, NULL, 0, ecc->strength); if (ret >= 0) raw_mode = 1; } else { - /* - * The engine protects 4 bytes of OOB data per chunk. - * Retrieve the corrected OOB bytes. - */ - sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)), - oob); + memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - /* De-randomize the Bad Block Marker. */ - if (bbm && nand->options & NAND_NEED_SCRAMBLING) - sunxi_nfc_randomize_bbm(mtd, page, oob); + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, + true, page); + + sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0, + bbm, page); } - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - *max_bitflips = max_t(unsigned int, *max_bitflips, ret); - } + sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret); return raw_mode; } @@ -897,11 +966,6 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, *cur_off = mtd->oobsize + mtd->writesize; } -static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) -{ - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); -} - static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, const u8 *data, int data_off, const u8 *oob, int oob_off, @@ -918,19 +982,6 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page); - /* Fill OOB data in */ - if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) { - u8 user_data[4]; - - memcpy(user_data, oob, 4); - sunxi_nfc_randomize_bbm(mtd, page, user_data); - writel(sunxi_nfc_buf_to_user_data(user_data), - nfc->regs + NFC_REG_USER_DATA(0)); - } else { - writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(0)); - } - if (data_off + ecc->size != oob_off) nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); @@ -939,6 +990,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, return ret; sunxi_nfc_randomizer_enable(mtd); + sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page); + writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | NFC_ECC_OP, nfc->regs + NFC_REG_CMD); From 828dec153044805506cc2b8e39cb3f62d5f76a12 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 4 Mar 2016 18:09:21 +0100 Subject: [PATCH 064/155] mtd: nand: sunxi: make OOB retrieval optional sunxi_nfc_hw_ecc_read_chunk() always retrieves the ECC and protected free bytes, no matter if the user really asked for it or not. This can take a non negligible amount of time, especially on NAND chips exposing large OOB areas (> 1KB). Make it optional. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index c20bf59141b0..b71053252a02 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -868,7 +868,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, u8 *oob, int oob_off, int *cur_off, unsigned int *max_bitflips, - bool bbm, int page) + bool bbm, bool oob_required, int page) { struct nand_chip *nand = mtd_to_nand(mtd); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); @@ -900,7 +900,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, *cur_off = oob_off + ecc->bytes + 4; - ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob, 0, &erased); + ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob_required ? oob : NULL, 0, + &erased); if (erased) return 1; @@ -928,12 +929,14 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, } else { memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, - true, page); + if (oob_required) { + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, + true, page); - sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0, - bbm, page); + sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0, + bbm, page); + } } sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret); @@ -1047,7 +1050,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, oob_off + mtd->writesize, &cur_off, &max_bitflips, - !i, page); + !i, oob_required, page); if (ret < 0) return ret; else if (ret) @@ -1085,8 +1088,8 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, oob_off + mtd->writesize, - &cur_off, &max_bitflips, - !i, page); + &cur_off, &max_bitflips, !i, + false, page); if (ret < 0) return ret; } @@ -1148,7 +1151,9 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, oob_off, &cur_off, - &max_bitflips, !i, page); + &max_bitflips, !i, + oob_required, + page); if (ret < 0) return ret; else if (ret) From c4f3ef2c6c219c3e58e0f5a9589a67685a75d787 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 4 Mar 2016 18:13:10 +0100 Subject: [PATCH 065/155] mtd: nand: sunxi: make cur_off parameter optional in extra oob helpers Allow for NULL cur_offs values when the caller does not know where the NAND page register pointer points to. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index b71053252a02..b12ed83ebd56 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -956,7 +956,7 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, if (len <= 0) return; - if (*cur_off != offset) + if (!cur_off || *cur_off != offset) nand->cmdfunc(mtd, NAND_CMD_RNDOUT, offset + mtd->writesize, -1); @@ -966,7 +966,8 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len, false, page); - *cur_off = mtd->oobsize + mtd->writesize; + if (cur_off) + *cur_off = mtd->oobsize + mtd->writesize; } static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, @@ -1021,13 +1022,14 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, if (len <= 0) return; - if (*cur_off != offset) + if (!cur_off || *cur_off != offset) nand->cmdfunc(mtd, NAND_CMD_RNDIN, offset + mtd->writesize, -1); sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page); - *cur_off = mtd->oobsize + mtd->writesize; + if (cur_off) + *cur_off = mtd->oobsize + mtd->writesize; } static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, From 75eb2cec251fda33c9bb716ecc372819abb9278a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 4 Feb 2016 09:52:30 +0100 Subject: [PATCH 066/155] mtd: add mtd_ooblayout_xxx() helper functions In order to make the ecclayout definition completely dynamic we need to rework the way the OOB layout are defined and iterated. Create a few mtd_ooblayout_xxx() helpers to ease OOB bytes manipulation and hide ecclayout internals to their users. Signed-off-by: Boris Brezillon --- drivers/mtd/mtdcore.c | 400 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/mtd.h | 33 ++++ 2 files changed, 433 insertions(+) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index bee180bd11e7..0290c41e44fc 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1016,6 +1016,406 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to, } EXPORT_SYMBOL_GPL(mtd_write_oob); +/** + * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section + * @mtd: MTD device structure + * @section: ECC section. Depending on the layout you may have all the ECC + * bytes stored in a single contiguous section, or one section + * per ECC chunk (and sometime several sections for a single ECC + * ECC chunk) + * @oobecc: OOB region struct filled with the appropriate ECC position + * information + * + * This functions return ECC section information in the OOB area. I you want + * to get all the ECC bytes information, then you should call + * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc) +{ + int eccbyte = 0, cursection = 0, length = 0, eccpos = 0; + + memset(oobecc, 0, sizeof(*oobecc)); + + if (!mtd || section < 0) + return -EINVAL; + + if (!mtd->ecclayout) + return -ENOTSUPP; + + /* + * This logic allows us to reuse the ->ecclayout information and + * expose them as ECC regions (as done for the OOB free regions). + * + * TODO: this should be dropped as soon as we get rid of the + * ->ecclayout field. + */ + for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) { + eccpos = mtd->ecclayout->eccpos[eccbyte]; + + if (eccbyte < mtd->ecclayout->eccbytes - 1) { + int neccpos = mtd->ecclayout->eccpos[eccbyte + 1]; + + if (eccpos + 1 == neccpos) { + length++; + continue; + } + } + + if (section == cursection) + break; + + length = 0; + cursection++; + } + + if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes) + return -ERANGE; + + oobecc->length = length + 1; + oobecc->offset = eccpos - length; + + return 0; +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); + +/** + * mtd_ooblayout_free - Get the OOB region definition of a specific free + * section + * @mtd: MTD device structure + * @section: Free section you are interested in. Depending on the layout + * you may have all the free bytes stored in a single contiguous + * section, or one section per ECC chunk plus an extra section + * for the remaining bytes (or other funky layout). + * @oobfree: OOB region struct filled with the appropriate free position + * information + * + * This functions return free bytes position in the OOB area. I you want + * to get all the free bytes information, then you should call + * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree) +{ + memset(oobfree, 0, sizeof(*oobfree)); + + if (!mtd || section < 0) + return -EINVAL; + + if (!mtd->ecclayout) + return -ENOTSUPP; + + if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE) + return -ERANGE; + + oobfree->offset = mtd->ecclayout->oobfree[section].offset; + oobfree->length = mtd->ecclayout->oobfree[section].length; + + return 0; +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_free); + +/** + * mtd_ooblayout_find_region - Find the region attached to a specific byte + * @mtd: mtd info structure + * @byte: the byte we are searching for + * @sectionp: pointer where the section id will be stored + * @oobregion: used to retrieve the ECC position + * @iter: iterator function. Should be either mtd_ooblayout_free or + * mtd_ooblayout_ecc depending on the region type you're searching for + * + * This functions returns the section id and oobregion information of a + * specific byte. For example, say you want to know where the 4th ECC byte is + * stored, you'll use: + * + * mtd_ooblayout_find_region(mtd, 3, §ion, &oobregion, mtd_ooblayout_ecc); + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte, + int *sectionp, struct mtd_oob_region *oobregion, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + int pos = 0, ret, section = 0; + + memset(oobregion, 0, sizeof(*oobregion)); + + while (1) { + ret = iter(mtd, section, oobregion); + if (ret) + return ret; + + if (pos + oobregion->length > byte) + break; + + pos += oobregion->length; + section++; + } + + /* + * Adjust region info to make it start at the beginning at the + * 'start' ECC byte. + */ + oobregion->offset += byte - pos; + oobregion->length -= byte - pos; + *sectionp = section; + + return 0; +} + +/** + * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific + * ECC byte + * @mtd: mtd info structure + * @eccbyte: the byte we are searching for + * @sectionp: pointer where the section id will be stored + * @oobregion: OOB region information + * + * Works like mtd_ooblayout_find_region() except it searches for a specific ECC + * byte. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, + int *section, + struct mtd_oob_region *oobregion) +{ + return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion, + mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion); + +/** + * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer + * @mtd: mtd info structure + * @buf: destination buffer to store OOB bytes + * @oobbuf: OOB buffer + * @start: first byte to retrieve + * @nbytes: number of bytes to retrieve + * @iter: section iterator + * + * Extract bytes attached to a specific category (ECC or free) + * from the OOB buffer and copy them into buf. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf, + const u8 *oobbuf, int start, int nbytes, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + struct mtd_oob_region oobregion = { }; + int section = 0, ret; + + ret = mtd_ooblayout_find_region(mtd, start, §ion, + &oobregion, iter); + + while (!ret) { + int cnt; + + cnt = oobregion.length > nbytes ? nbytes : oobregion.length; + memcpy(buf, oobbuf + oobregion.offset, cnt); + buf += cnt; + nbytes -= cnt; + + if (!nbytes) + break; + + ret = iter(mtd, ++section, &oobregion); + } + + return ret; +} + +/** + * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer + * @mtd: mtd info structure + * @buf: source buffer to get OOB bytes from + * @oobbuf: OOB buffer + * @start: first OOB byte to set + * @nbytes: number of OOB bytes to set + * @iter: section iterator + * + * Fill the OOB buffer with data provided in buf. The category (ECC or free) + * is selected by passing the appropriate iterator. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf, + u8 *oobbuf, int start, int nbytes, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + struct mtd_oob_region oobregion = { }; + int section = 0, ret; + + ret = mtd_ooblayout_find_region(mtd, start, §ion, + &oobregion, iter); + + while (!ret) { + int cnt; + + cnt = oobregion.length > nbytes ? nbytes : oobregion.length; + memcpy(oobbuf + oobregion.offset, buf, cnt); + buf += cnt; + nbytes -= cnt; + + if (!nbytes) + break; + + ret = iter(mtd, ++section, &oobregion); + } + + return ret; +} + +/** + * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category + * @mtd: mtd info structure + * @iter: category iterator + * + * Count the number of bytes in a given category. + * + * Returns a positive value on success, a negative error code otherwise. + */ +static int mtd_ooblayout_count_bytes(struct mtd_info *mtd, + int (*iter)(struct mtd_info *, + int section, + struct mtd_oob_region *oobregion)) +{ + struct mtd_oob_region oobregion = { }; + int section = 0, ret, nbytes = 0; + + while (1) { + ret = iter(mtd, section++, &oobregion); + if (ret) { + if (ret == -ERANGE) + ret = nbytes; + break; + } + + nbytes += oobregion.length; + } + + return ret; +} + +/** + * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer + * @mtd: mtd info structure + * @eccbuf: destination buffer to store ECC bytes + * @oobbuf: OOB buffer + * @start: first ECC byte to retrieve + * @nbytes: number of ECC bytes to retrieve + * + * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf, + const u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes, + mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes); + +/** + * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer + * @mtd: mtd info structure + * @eccbuf: source buffer to get ECC bytes from + * @oobbuf: OOB buffer + * @start: first ECC byte to set + * @nbytes: number of ECC bytes to set + * + * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf, + u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes, + mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes); + +/** + * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer + * @mtd: mtd info structure + * @databuf: destination buffer to store ECC bytes + * @oobbuf: OOB buffer + * @start: first ECC byte to retrieve + * @nbytes: number of ECC bytes to retrieve + * + * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf, + const u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes, + mtd_ooblayout_free); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes); + +/** + * mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer + * @mtd: mtd info structure + * @eccbuf: source buffer to get data bytes from + * @oobbuf: OOB buffer + * @start: first ECC byte to set + * @nbytes: number of ECC bytes to set + * + * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, + u8 *oobbuf, int start, int nbytes) +{ + return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes, + mtd_ooblayout_free); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes); + +/** + * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB + * @mtd: mtd info structure + * + * Works like mtd_ooblayout_count_bytes(), except it count free bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_count_freebytes(struct mtd_info *mtd) +{ + return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes); + +/** + * mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB + * @mtd: mtd info structure + * + * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes. + * + * Returns zero on success, a negative error code otherwise. + */ +int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd) +{ + return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc); +} +EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes); + /* * Method to access the protection register area, present in some flash * devices. The user data is one time programmable but the factory data is read diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index ef9fea4fc400..117ca1ff581d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -108,6 +108,21 @@ struct nand_ecclayout { struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE]; }; +/** + * struct mtd_oob_region - oob region definition + * @offset: region offset + * @length: region length + * + * This structure describes a region of the OOB area, and is used + * to retrieve ECC or free bytes sections. + * Each section is defined by an offset within the OOB area and a + * length. + */ +struct mtd_oob_region { + u32 offset; + u32 length; +}; + struct module; /* only needed for owner field in mtd_info */ struct mtd_info { @@ -253,6 +268,24 @@ struct mtd_info { int usecount; }; +int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc); +int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, + int *section, + struct mtd_oob_region *oobregion); +int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf, + const u8 *oobbuf, int start, int nbytes); +int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf, + u8 *oobbuf, int start, int nbytes); +int mtd_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree); +int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf, + const u8 *oobbuf, int start, int nbytes); +int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, + u8 *oobbuf, int start, int nbytes); +int mtd_ooblayout_count_freebytes(struct mtd_info *mtd); +int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd); + static inline void mtd_set_of_node(struct mtd_info *mtd, struct device_node *np) { From c2b78452a9db438d592bf72af7bb2ae3062cb922 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:10:30 +0100 Subject: [PATCH 067/155] mtd: use mtd_ooblayout_xxx() helpers where appropriate The mtd_ooblayout_xxx() helper functions have been added to avoid direct accesses to the ecclayout field, and thus ease for future reworks. Use these helpers in all places where the oobfree[] and eccpos[] arrays where directly accessed. Signed-off-by: Boris Brezillon --- drivers/mtd/mtdchar.c | 107 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 6d19835b80a9..cd64ab76dd7b 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -472,31 +472,104 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd, * nand_ecclayout flexibly (i.e. the struct may change size in new * releases without requiring major rewrites). */ -static int shrink_ecclayout(const struct nand_ecclayout *from, - struct nand_ecclayout_user *to) +static int shrink_ecclayout(struct mtd_info *mtd, + struct nand_ecclayout_user *to) { - int i; + struct mtd_oob_region oobregion; + int i, section = 0, ret; - if (!from || !to) + if (!mtd || !to) return -EINVAL; memset(to, 0, sizeof(*to)); - to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES); - for (i = 0; i < to->eccbytes; i++) - to->eccpos[i] = from->eccpos[i]; + to->eccbytes = 0; + for (i = 0; i < MTD_MAX_ECCPOS_ENTRIES;) { + u32 eccpos; + + ret = mtd_ooblayout_ecc(mtd, section, &oobregion); + if (ret < 0) { + if (ret != -ERANGE) + return ret; + + break; + } + + eccpos = oobregion.offset; + for (; i < MTD_MAX_ECCPOS_ENTRIES && + eccpos < oobregion.offset + oobregion.length; i++) { + to->eccpos[i] = eccpos++; + to->eccbytes++; + } + } for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) { - if (from->oobfree[i].length == 0 && - from->oobfree[i].offset == 0) + ret = mtd_ooblayout_free(mtd, i, &oobregion); + if (ret < 0) { + if (ret != -ERANGE) + return ret; + break; - to->oobavail += from->oobfree[i].length; - to->oobfree[i] = from->oobfree[i]; + } + + to->oobfree[i].offset = oobregion.offset; + to->oobfree[i].length = oobregion.length; + to->oobavail += to->oobfree[i].length; } return 0; } +static int get_oobinfo(struct mtd_info *mtd, struct nand_oobinfo *to) +{ + struct mtd_oob_region oobregion; + int i, section = 0, ret; + + if (!mtd || !to) + return -EINVAL; + + memset(to, 0, sizeof(*to)); + + to->eccbytes = 0; + for (i = 0; i < ARRAY_SIZE(to->eccpos);) { + u32 eccpos; + + ret = mtd_ooblayout_ecc(mtd, section, &oobregion); + if (ret < 0) { + if (ret != -ERANGE) + return ret; + + break; + } + + if (oobregion.length + i > ARRAY_SIZE(to->eccpos)) + return -EINVAL; + + eccpos = oobregion.offset; + for (; eccpos < oobregion.offset + oobregion.length; i++) { + to->eccpos[i] = eccpos++; + to->eccbytes++; + } + } + + for (i = 0; i < 8; i++) { + ret = mtd_ooblayout_free(mtd, i, &oobregion); + if (ret < 0) { + if (ret != -ERANGE) + return ret; + + break; + } + + to->oobfree[i][0] = oobregion.offset; + to->oobfree[i][1] = oobregion.length; + } + + to->useecc = MTD_NANDECC_AUTOPLACE; + + return 0; +} + static int mtdchar_blkpg_ioctl(struct mtd_info *mtd, struct blkpg_ioctl_arg *arg) { @@ -817,14 +890,10 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) if (!mtd->ecclayout) return -EOPNOTSUPP; - if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos)) - return -EINVAL; - oi.useecc = MTD_NANDECC_AUTOPLACE; - memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); - memcpy(&oi.oobfree, mtd->ecclayout->oobfree, - sizeof(oi.oobfree)); - oi.eccbytes = mtd->ecclayout->eccbytes; + ret = get_oobinfo(mtd, &oi); + if (ret) + return ret; if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) return -EFAULT; @@ -920,7 +989,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) if (!usrlay) return -ENOMEM; - shrink_ecclayout(mtd->ecclayout, usrlay); + shrink_ecclayout(mtd, usrlay); if (copy_to_user(argp, usrlay, sizeof(*usrlay))) ret = -EFAULT; From 846031d3e1837b71e350bd8098f00995069859a8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:11:00 +0100 Subject: [PATCH 068/155] mtd: nand: core: use mtd_ooblayout_xxx() helpers where appropriate The mtd_ooblayout_xxx() helper functions have been added to avoid direct accesses to the ecclayout field, and thus ease for future reworks. Use these helpers in all places where the oobfree[] and eccpos[] arrays where directly accessed. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 196 +++++++++++++++-------------------- drivers/mtd/nand/nand_bch.c | 13 ++- 2 files changed, 98 insertions(+), 111 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 13fcddc8a10e..24ed4450df57 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1279,13 +1279,12 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; + int i, eccsize = chip->ecc.size, ret; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; 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, 1, page); @@ -1293,8 +1292,10 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; eccsteps = chip->ecc.steps; p = buf; @@ -1326,14 +1327,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, int page) { - int start_step, end_step, num_steps; - uint32_t *eccpos = chip->ecc.layout->eccpos; + int start_step, end_step, num_steps, ret; uint8_t *p; int data_col_addr, i, gaps = 0; int datafrag_len, eccfrag_len, aligned_len, aligned_pos; int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; - int index; + int index, section = 0; unsigned int max_bitflips = 0; + struct mtd_oob_region oobregion = { }; /* Column address within the page aligned to ECC size (256bytes) */ start_step = data_offs / chip->ecc.size; @@ -1361,12 +1362,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, * The performance is faster if we position offsets according to * ecc.pos. Let's make sure that there are no gaps in ECC positions. */ - for (i = 0; i < eccfrag_len - 1; i++) { - if (eccpos[i + index] + 1 != eccpos[i + index + 1]) { - gaps = 1; - break; - } - } + ret = mtd_ooblayout_find_eccregion(mtd, index, §ion, &oobregion); + if (ret) + return ret; + + if (oobregion.length < eccfrag_len) + gaps = 1; + if (gaps) { chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -1375,20 +1377,23 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, * Send the command to read the particular ECC bytes take care * about buswidth alignment in read_buf. */ - aligned_pos = eccpos[index] & ~(busw - 1); + aligned_pos = oobregion.offset & ~(busw - 1); aligned_len = eccfrag_len; - if (eccpos[index] & (busw - 1)) + if (oobregion.offset & (busw - 1)) aligned_len++; - if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1)) + if ((oobregion.offset + (num_steps * chip->ecc.bytes)) & + (busw - 1)) aligned_len++; chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - mtd->writesize + aligned_pos, -1); + mtd->writesize + aligned_pos, -1); chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); } - for (i = 0; i < eccfrag_len; i++) - chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]]; + ret = mtd_ooblayout_get_eccbytes(mtd, chip->buffers->ecccode, + chip->oob_poi, index, eccfrag_len); + if (ret) + return ret; p = bufpoi + data_col_addr; for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { @@ -1429,13 +1434,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; + int i, eccsize = chip->ecc.size, ret; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; 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) { @@ -1445,8 +1449,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, } chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; eccsteps = chip->ecc.steps; p = buf; @@ -1491,12 +1497,11 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; + int i, eccsize = chip->ecc.size, ret; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; 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; @@ -1505,8 +1510,10 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); - for (i = 0; i < chip->ecc.total; i++) - ecc_code[i] = chip->oob_poi[eccpos[i]]; + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat; @@ -1607,14 +1614,17 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, /** * nand_transfer_oob - [INTERN] Transfer oob to client buffer - * @chip: nand chip structure + * @mtd: mtd info structure * @oob: oob destination address * @ops: oob ops structure * @len: size of oob to transfer */ -static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, +static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob, struct mtd_oob_ops *ops, size_t len) { + struct nand_chip *chip = mtd_to_nand(mtd); + int ret; + switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -1622,31 +1632,12 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, memcpy(oob, chip->oob_poi + ops->ooboffs, len); return oob + len; - case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; - uint32_t boffs = 0, roffs = ops->ooboffs; - size_t bytes = 0; + case MTD_OPS_AUTO_OOB: + ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi, + ops->ooboffs, len); + BUG_ON(ret); + return oob + len; - for (; free->length && len; free++, len -= bytes) { - /* Read request not from offset 0? */ - if (unlikely(roffs)) { - if (roffs >= free->length) { - roffs -= free->length; - continue; - } - boffs = free->offset + roffs; - bytes = min_t(size_t, len, - (free->length - roffs)); - roffs = 0; - } else { - bytes = min_t(size_t, len, free->length); - boffs = free->offset; - } - memcpy(oob, chip->oob_poi + boffs, bytes); - oob += bytes; - } - return oob; - } default: BUG(); } @@ -1780,7 +1771,7 @@ read_retry: int toread = min(oobreadlen, max_oobsize); if (toread) { - oob = nand_transfer_oob(chip, + oob = nand_transfer_oob(mtd, oob, ops, toread); oobreadlen -= toread; } @@ -2080,7 +2071,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, break; len = min(len, readlen); - buf = nand_transfer_oob(chip, buf, ops, len); + buf = nand_transfer_oob(mtd, buf, ops, len); if (chip->options & NAND_NEED_READRDY) { /* Apply delay or wait for ready/busy pin */ @@ -2239,19 +2230,20 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; + int i, eccsize = chip->ecc.size, ret; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; /* Software ECC calculation */ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; return chip->ecc.write_page_raw(mtd, chip, buf, 1, page); } @@ -2268,12 +2260,11 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - int i, eccsize = chip->ecc.size; + int i, eccsize = chip->ecc.size, ret; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; - uint32_t *eccpos = chip->ecc.layout->eccpos; for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_WRITE); @@ -2281,8 +2272,10 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, chip->ecc.calculate(mtd, p, &ecc_calc[i]); } - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -2310,11 +2303,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, int ecc_size = chip->ecc.size; int ecc_bytes = chip->ecc.bytes; int ecc_steps = chip->ecc.steps; - uint32_t *eccpos = chip->ecc.layout->eccpos; uint32_t start_step = offset / ecc_size; uint32_t end_step = (offset + data_len - 1) / ecc_size; int oob_bytes = mtd->oobsize / ecc_steps; - int step, i; + int step, ret; for (step = 0; step < ecc_steps; step++) { /* configure controller for WRITE access */ @@ -2342,8 +2334,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, /* copy calculated ECC for whole page to chip->buffer->oob */ /* this include masked-value(0xFF) for unwritten subpages */ ecc_calc = chip->buffers->ecccalc; - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; /* write OOB buffer to NAND device */ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -2480,6 +2474,7 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd_to_nand(mtd); + int ret; /* * Initialise to all 0xFF, to avoid the possibility of left over OOB @@ -2494,31 +2489,12 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len, memcpy(chip->oob_poi + ops->ooboffs, oob, len); return oob + len; - case MTD_OPS_AUTO_OOB: { - struct nand_oobfree *free = chip->ecc.layout->oobfree; - uint32_t boffs = 0, woffs = ops->ooboffs; - size_t bytes = 0; + case MTD_OPS_AUTO_OOB: + ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi, + ops->ooboffs, len); + BUG_ON(ret); + return oob + len; - for (; free->length && len; free++, len -= bytes) { - /* Write request not from offset 0? */ - if (unlikely(woffs)) { - if (woffs >= free->length) { - woffs -= free->length; - continue; - } - boffs = free->offset + woffs; - bytes = min_t(size_t, len, - (free->length - woffs)); - woffs = 0; - } else { - bytes = min_t(size_t, len, free->length); - boffs = free->offset; - } - memcpy(chip->oob_poi + boffs, oob, bytes); - oob += bytes; - } - return oob; - } default: BUG(); } @@ -4105,7 +4081,6 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd) */ int nand_scan_tail(struct mtd_info *mtd) { - int i; struct nand_chip *chip = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; struct nand_buffers *nbuf; @@ -4309,20 +4284,10 @@ int nand_scan_tail(struct mtd_info *mtd) if (!ecc->write_oob_raw) ecc->write_oob_raw = ecc->write_oob; - /* - * The number of bytes available for a client to place data into - * the out of band area. - */ - mtd->oobavail = 0; - if (ecc->layout) { - for (i = 0; ecc->layout->oobfree[i].length; i++) - mtd->oobavail += ecc->layout->oobfree[i].length; - } - - /* ECC sanity check: warn if it's too weak */ - if (!nand_ecc_strength_good(mtd)) - pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", - mtd->name); + /* propagate ecc info to mtd_info */ + mtd->ecclayout = ecc->layout; + mtd->ecc_strength = ecc->strength; + mtd->ecc_step_size = ecc->size; /* * Set the number of read / write steps for one page depending on ECC @@ -4336,6 +4301,21 @@ int nand_scan_tail(struct mtd_info *mtd) } ecc->total = ecc->steps * ecc->bytes; + /* + * The number of bytes available for a client to place data into + * the out of band area. + */ + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + ret = 0; + + mtd->oobavail = ret; + + /* ECC sanity check: warn if it's too weak */ + if (!nand_ecc_strength_good(mtd)) + pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", + mtd->name); + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { switch (ecc->steps) { @@ -4392,10 +4372,6 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->_block_markbad = nand_block_markbad; mtd->writebufsize = mtd->writesize; - /* propagate ecc info to mtd_info */ - mtd->ecclayout = ecc->layout; - mtd->ecc_strength = ecc->strength; - mtd->ecc_step_size = ecc->size; /* * Initialize bitflip_threshold to its default prior scan_bbt() call. * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c index b585bae37929..d8c03ebb62dc 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -196,7 +196,18 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) printk(KERN_WARNING "eccsize %u is too large\n", eccsize); goto fail; } - if (layout->eccbytes != (eccsteps*eccbytes)) { + + /* + * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), + * which is called by mtd_ooblayout_count_eccbytes(). + * Make sure they are properly initialized before calling + * mtd_ooblayout_count_eccbytes(). + * FIXME: we should probaly rework the sequencing in nand_scan_tail() + * to avoid setting those fields twice. + */ + nand->ecc.steps = eccsteps; + nand->ecc.total = eccsteps * eccbytes; + if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { printk(KERN_WARNING "invalid ecc layout\n"); goto fail; } From 78d28e8ec4fbd4d1e0c375d7a28f4f23e9e7b15e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:11:14 +0100 Subject: [PATCH 069/155] mtd: nand: atmel: use mtd_ooblayout_xxx() helpers where appropriate The mtd_ooblayout_xxx() helper functions have been added to avoid direct accesses to the ecclayout field, and thus ease for future reworks. Use these helpers in all places where the oobfree[] and eccpos[] arrays where directly accessed. Signed-off-by: Boris Brezillon Reviewed-by: Nicolas Ferre --- drivers/mtd/nand/atmel_nand.c | 50 ++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 5e716f2a8739..b132c8fd3701 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -833,13 +833,16 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc, dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", pos, bit_pos, err_byte, *(buf + byte_pos)); } else { + struct mtd_oob_region oobregion; + /* Bit flip in OOB area */ tmp = sector_num * nand_chip->ecc.bytes + (byte_pos - sector_size); err_byte = ecc[tmp]; ecc[tmp] ^= (1 << bit_pos); - pos = tmp + nand_chip->ecc.layout->eccpos[0]; + mtd_ooblayout_ecc(mtd, 0, &oobregion); + pos = tmp + oobregion.offset; dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", pos, bit_pos, err_byte, ecc[tmp]); } @@ -931,7 +934,6 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, struct atmel_nand_host *host = nand_get_controller_data(chip); int eccsize = chip->ecc.size * chip->ecc.steps; uint8_t *oob = chip->oob_poi; - uint32_t *eccpos = chip->ecc.layout->eccpos; uint32_t stat; unsigned long end_time; int bitflips = 0; @@ -953,7 +955,11 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, stat = pmecc_readl_relaxed(host->ecc, ISR); if (stat != 0) { - bitflips = pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]); + struct mtd_oob_region oobregion; + + mtd_ooblayout_ecc(mtd, 0, &oobregion); + bitflips = pmecc_correction(mtd, stat, buf, + &oob[oobregion.offset]); if (bitflips < 0) /* uncorrectable errors */ return 0; @@ -967,8 +973,8 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, int page) { struct atmel_nand_host *host = nand_get_controller_data(chip); - uint32_t *eccpos = chip->ecc.layout->eccpos; - int i, j; + struct mtd_oob_region oobregion = { }; + int i, j, section = 0; unsigned long end_time; if (!host->nfc || !host->nfc->write_by_sram) { @@ -987,11 +993,14 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, for (i = 0; i < chip->ecc.steps; i++) { for (j = 0; j < chip->ecc.bytes; j++) { - int pos; + if (!oobregion.length) + mtd_ooblayout_ecc(mtd, section, &oobregion); - pos = i * chip->ecc.bytes + j; - chip->oob_poi[eccpos[pos]] = + chip->oob_poi[oobregion.offset] = pmecc_readb_ecc_relaxed(host->ecc, i, j); + oobregion.length--; + oobregion.offset++; + section++; } } chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -1005,6 +1014,7 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) struct atmel_nand_host *host = nand_get_controller_data(nand_chip); uint32_t val = 0; struct nand_ecclayout *ecc_layout; + struct mtd_oob_region oobregion; pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); @@ -1056,9 +1066,10 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) ecc_layout = nand_chip->ecc.layout; pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1); - pmecc_writel(host->ecc, SADDR, ecc_layout->eccpos[0]); + mtd_ooblayout_ecc(mtd, 0, &oobregion); + pmecc_writel(host->ecc, SADDR, oobregion.offset); pmecc_writel(host->ecc, EADDR, - ecc_layout->eccpos[ecc_layout->eccbytes - 1]); + oobregion.offset + ecc_layout->eccbytes - 1); /* See datasheet about PMECC Clock Control Register */ pmecc_writel(host->ecc, CLK, 2); pmecc_writel(host->ecc, IDR, 0xff); @@ -1359,12 +1370,12 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, { int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; - uint32_t *eccpos = chip->ecc.layout->eccpos; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; uint8_t *ecc_pos; int stat; unsigned int max_bitflips = 0; + struct mtd_oob_region oobregion = {}; /* * Errata: ALE is incorrectly wired up to the ECC controller @@ -1382,19 +1393,20 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, p, eccsize); /* move to ECC position if needed */ - if (eccpos[0] != 0) { - /* This only works on large pages - * because the ECC controller waits for - * NAND_CMD_RNDOUTSTART after the - * NAND_CMD_RNDOUT. - * anyway, for small pages, the eccpos[0] == 0 + mtd_ooblayout_ecc(mtd, 0, &oobregion); + if (oobregion.offset != 0) { + /* + * This only works on large pages because the ECC controller + * waits for NAND_CMD_RNDOUTSTART after the NAND_CMD_RNDOUT. + * Anyway, for small pages, the first ECC byte is at offset + * 0 in the OOB area. */ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, - mtd->writesize + eccpos[0], -1); + mtd->writesize + oobregion.offset, -1); } /* the ECC controller needs to read the ECC just after the data */ - ecc_pos = oob + eccpos[0]; + ecc_pos = oob + oobregion.offset; chip->read_buf(mtd, ecc_pos, eccbytes); /* check if there's an error */ From 9ed92dd29015de67041cf45f364a46f1fa84bf79 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:11:32 +0100 Subject: [PATCH 070/155] mtd: nand: fsl_ifc: use mtd_ooblayout_xxx() helpers where appropriate The mtd_ooblayout_xxx() helper functions have been added to avoid direct accesses to the ecclayout field, and thus ease for future reworks. Use these helpers in all places where the oobfree[] and eccpos[] arrays where directly accessed. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsl_ifc_nand.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index f8a016f038cd..1d922a0f7436 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -257,18 +257,22 @@ static int is_blank(struct mtd_info *mtd, unsigned int bufnum) u8 __iomem *addr = priv->vbase + bufnum * (mtd->writesize * 2); u32 __iomem *mainarea = (u32 __iomem *)addr; u8 __iomem *oob = addr + mtd->writesize; - int i; + struct mtd_oob_region oobregion = { }; + int i, section = 0; for (i = 0; i < mtd->writesize / 4; i++) { if (__raw_readl(&mainarea[i]) != 0xffffffff) return 0; } - for (i = 0; i < chip->ecc.layout->eccbytes; i++) { - int pos = chip->ecc.layout->eccpos[i]; + mtd_ooblayout_ecc(mtd, section++, &oobregion); + while (oobregion.length) { + for (i = 0; i < oobregion.length; i++) { + if (__raw_readb(&oob[oobregion.offset + i]) != 0xff) + return 0; + } - if (__raw_readb(&oob[pos]) != 0xff) - return 0; + mtd_ooblayout_ecc(mtd, section++, &oobregion); } return 1; From 191a82946a99fe110814860873cdfe64170c660b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:11:44 +0100 Subject: [PATCH 071/155] mtd: nand: gpmi: use mtd_ooblayout_xxx() helpers where appropriate The mtd_ooblayout_xxx() helper functions have been added to avoid direct accesses to the ecclayout field, and thus ease for future reworks. Use these helpers in all places where the oobfree[] and eccpos[] arrays where directly accessed. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index dcb60b0fe5eb..4721008e0289 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1328,18 +1328,19 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, static int gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - struct nand_oobfree *of = mtd->ecclayout->oobfree; + struct mtd_oob_region of = { }; int status = 0; /* Do we have available oob area? */ - if (!of->length) + mtd_ooblayout_free(mtd, 0, &of); + if (!of.length) return -EPERM; if (!nand_is_slc(chip)) return -EPERM; - chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of->offset, page); - chip->write_buf(mtd, chip->oob_poi + of->offset, of->length); + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of.offset, page); + chip->write_buf(mtd, chip->oob_poi + of.offset, of.length); chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); From b9c0f65feab8a6dd4f37fdd8150cf5fe716304f5 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:12:04 +0100 Subject: [PATCH 072/155] mtd: nand: lpc32xx: use mtd_ooblayout_xxx() helpers where appropriate The mtd_ooblayout_xxx() helper functions have been added to avoid direct accesses to the ecclayout field, and thus ease for future reworks. Use these helpers in all places where the oobfree[] and eccpos[] arrays where directly accessed. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/lpc32xx_slc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index 3b8f3735f3e8..10cf8e6223bc 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -604,7 +604,8 @@ static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd, int oob_required, int page) { struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - int stat, i, status; + struct mtd_oob_region oobregion = { }; + int stat, i, status, error; uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE]; /* Issue read command */ @@ -620,7 +621,11 @@ static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd, lpc32xx_slc_ecc_copy(tmpecc, (uint32_t *) host->ecc_buf, chip->ecc.steps); /* Pointer to ECC data retrieved from NAND spare area */ - oobecc = chip->oob_poi + chip->ecc.layout->eccpos[0]; + error = mtd_ooblayout_ecc(mtd, 0, &oobregion); + if (error) + return error; + + oobecc = chip->oob_poi + oobregion.offset; for (i = 0; i < chip->ecc.steps; i++) { stat = chip->ecc.correct(mtd, buf, oobecc, @@ -666,7 +671,8 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, int oob_required, int page) { struct lpc32xx_nand_host *host = nand_get_controller_data(chip); - uint8_t *pb = chip->oob_poi + chip->ecc.layout->eccpos[0]; + struct mtd_oob_region oobregion = { }; + uint8_t *pb; int error; /* Write data, calculate ECC on outbound data */ @@ -678,6 +684,11 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd, * The calculated ECC needs some manual work done to it before * committing it to NAND. Process the calculated ECC and place * the resultant values directly into the OOB buffer. */ + error = mtd_ooblayout_ecc(mtd, 0, &oobregion); + if (error) + return error; + + pb = chip->oob_poi + oobregion.offset; lpc32xx_slc_ecc_copy(pb, (uint32_t *)host->ecc_buf, chip->ecc.steps); /* Write ECC data to device */ From 8cfc1e8b68f3e0b144f17a94709c757c6db05b82 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:12:19 +0100 Subject: [PATCH 073/155] mtd: nand: omap2: use mtd_ooblayout_xxx() helpers where appropriate The mtd_ooblayout_xxx() helper functions have been added to avoid direct accesses to the ecclayout field, and thus ease for future reworks. Use these helpers in all places where the oobfree[] and eccpos[] arrays where directly accessed. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/omap2.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index c59bc85009d7..38fe6b8a0b14 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1498,9 +1498,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - int i; + int ret; uint8_t *ecc_calc = chip->buffers->ecccalc; - uint32_t *eccpos = chip->ecc.layout->eccpos; /* Enable GPMC ecc engine */ chip->ecc.hwctl(mtd, NAND_ECC_WRITE); @@ -1511,8 +1510,10 @@ static int omap_write_page_bch(struct mtd_info *mtd, struct nand_chip *chip, /* Update ecc vector from GPMC result registers */ chip->ecc.calculate(mtd, buf, &ecc_calc[0]); - for (i = 0; i < chip->ecc.total; i++) - chip->oob_poi[eccpos[i]] = ecc_calc[i]; + ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; /* Write ecc vector to OOB area */ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -1539,10 +1540,7 @@ static int omap_read_page_bch(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; - uint8_t *oob = &chip->oob_poi[eccpos[0]]; - uint32_t oob_pos = mtd->writesize + chip->ecc.layout->eccpos[0]; - int stat; + int stat, ret; unsigned int max_bitflips = 0; /* Enable GPMC ecc engine */ @@ -1552,13 +1550,18 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, buf, mtd->writesize); /* Read oob bytes */ - chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, -1); - chip->read_buf(mtd, oob, chip->ecc.total); + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + mtd->writesize + BADBLOCK_MARKER_LENGTH, -1); + chip->read_buf(mtd, chip->oob_poi + BADBLOCK_MARKER_LENGTH, + chip->ecc.total); /* Calculate ecc bytes */ chip->ecc.calculate(mtd, buf, ecc_calc); - memcpy(ecc_code, &chip->oob_poi[eccpos[0]], chip->ecc.total); + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; stat = chip->ecc.correct(mtd, buf, ecc_code, ecc_calc); From aa02fcf555c8d42836584783288b33a2a0d40481 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 18 Mar 2016 17:53:31 +0100 Subject: [PATCH 074/155] mtd: nand: qcom: use mtd_ooblayout_xxx() helpers where appropriate The mtd_ooblayout_xxx() helper functions have been added to avoid direct accesses to ecclayout fields, and thus ease for future reworks. Use these helpers in all places where the oobfree[] and eccpos[] arrays where directly accessed. Signed-off-by: Boris Brezillon Tested-by: Archit Taneja --- drivers/mtd/nand/qcom_nandc.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c index f3de983475db..4ea2cb771191 100644 --- a/drivers/mtd/nand/qcom_nandc.c +++ b/drivers/mtd/nand/qcom_nandc.c @@ -1436,7 +1436,6 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; u8 *oob = chip->oob_poi; - int free_boff; int data_size, oob_size; int ret, status = 0; @@ -1450,12 +1449,11 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, /* calculate the data and oob size for the last codeword/step */ data_size = ecc->size - ((ecc->steps - 1) << 2); - oob_size = ecc->steps << 2; - - free_boff = ecc->layout->oobfree[0].offset; + oob_size = mtd->oobavail; /* override new oob content to last codeword */ - memcpy(nandc->data_buffer + data_size, oob + free_boff, oob_size); + mtd_ooblayout_get_databytes(mtd, nandc->data_buffer + data_size, oob, + 0, mtd->oobavail); set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false); From d30aae6d5630c27fa461f9da9ba2f40ae59e3287 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:12:31 +0100 Subject: [PATCH 075/155] mtd: onenand: use mtd_ooblayout_xxx() helpers where appropriate The mtd_ooblayout_xxx() helper functions have been added to avoid direct accesses to the ecclayout field, and thus ease for future reworks. Use these helpers in all places where the oobfree[] and eccpos[] arrays where directly accessed. Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/onenand_base.c | 73 ++++++------------------------ 1 file changed, 14 insertions(+), 59 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index af28bb3ae7cf..20fdf8cceae9 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1024,34 +1024,15 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col int thislen) { struct onenand_chip *this = mtd->priv; - struct nand_oobfree *free; - int readcol = column; - int readend = column + thislen; - int lastgap = 0; - unsigned int i; - uint8_t *oob_buf = this->oob_buf; + int ret; + + this->read_bufferram(mtd, ONENAND_SPARERAM, this->oob_buf, 0, + mtd->oobsize); + ret = mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf, + column, thislen); + if (ret) + return ret; - free = this->ecclayout->oobfree; - for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { - if (readcol >= lastgap) - readcol += free->offset - lastgap; - if (readend >= lastgap) - readend += free->offset - lastgap; - lastgap = free->offset + free->length; - } - this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); - free = this->ecclayout->oobfree; - for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { - int free_end = free->offset + free->length; - if (free->offset < readend && free_end > readcol) { - int st = max_t(int,free->offset,readcol); - int ed = min_t(int,free_end,readend); - int n = ed - st; - memcpy(buf, oob_buf + st, n); - buf += n; - } else if (column == 0) - break; - } return 0; } @@ -1808,34 +1789,7 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, const u_char *buf, int column, int thislen) { - struct onenand_chip *this = mtd->priv; - struct nand_oobfree *free; - int writecol = column; - int writeend = column + thislen; - int lastgap = 0; - unsigned int i; - - free = this->ecclayout->oobfree; - for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { - if (writecol >= lastgap) - writecol += free->offset - lastgap; - if (writeend >= lastgap) - writeend += free->offset - lastgap; - lastgap = free->offset + free->length; - } - free = this->ecclayout->oobfree; - for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { - int free_end = free->offset + free->length; - if (free->offset < writeend && free_end > writecol) { - int st = max_t(int,free->offset,writecol); - int ed = min_t(int,free_end,writeend); - int n = ed - st; - memcpy(oob_buf + st, buf, n); - buf += n; - } else if (column == 0) - break; - } - return 0; + return mtd_ooblayout_set_databytes(mtd, buf, oob_buf, column, thislen); } /** @@ -4037,10 +3991,11 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) * The number of bytes available for a client to place data into * the out of band area */ - mtd->oobavail = 0; - for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && - this->ecclayout->oobfree[i].length; i++) - mtd->oobavail += this->ecclayout->oobfree[i].length; + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + ret = 0; + + mtd->oobavail = ret; mtd->ecclayout = this->ecclayout; mtd->ecc_strength = 1; From 036d6543f85319ffe96afad6de73d3a220917a63 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 18:53:44 +0100 Subject: [PATCH 076/155] mtd: add mtd_set_ecclayout() helper function Add an mtd_set_ecclayout() helper function to avoid direct accesses to the mtd->ecclayout field. This will ease future reworks of ECC layout definition. Signed-off-by: Boris Brezillon --- include/linux/mtd/mtd.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 117ca1ff581d..e62da8462493 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -286,6 +286,12 @@ int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, int mtd_ooblayout_count_freebytes(struct mtd_info *mtd); int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd); +static inline void mtd_set_ecclayout(struct mtd_info *mtd, + struct nand_ecclayout *ecclayout) +{ + mtd->ecclayout = ecclayout; +} + static inline void mtd_set_of_node(struct mtd_info *mtd, struct device_node *np) { From f6a6da179308356cac0f1dde979b67094437ef16 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:13:26 +0100 Subject: [PATCH 077/155] mtd: use mtd_set_ecclayout() where appropriate Use the mtd_set_ecclayout() helper instead of directly assigning the mtd->ecclayout field. Signed-off-by: Boris Brezillon --- drivers/mtd/mtdconcat.c | 2 +- drivers/mtd/mtdpart.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 239a8c806b67..481565e5fbaa 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c } - concat->mtd.ecclayout = subdev[0]->ecclayout; + mtd_set_ecclayout(&concat->mtd, subdev[0]->ecclayout); concat->num_subdev = num_devs; concat->mtd.name = name; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 08de4b2cf0f5..f53d9d72b23a 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -533,7 +533,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, part->name); } - slave->mtd.ecclayout = master->ecclayout; + mtd_set_ecclayout(&slave->mtd, master->ecclayout); slave->mtd.ecc_step_size = master->ecc_step_size; slave->mtd.ecc_strength = master->ecc_strength; slave->mtd.bitflip_threshold = master->bitflip_threshold; From 70d105e4eec233d8f8758d604bfe6c411b6ded5f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:13:50 +0100 Subject: [PATCH 078/155] mtd: nand: use mtd_set_ecclayout() where appropriate Use the mtd_set_ecclayout() helper instead of directly assigning the mtd->ecclayout field. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 24ed4450df57..feb1448e9dd6 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4285,7 +4285,7 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->write_oob_raw = ecc->write_oob; /* propagate ecc info to mtd_info */ - mtd->ecclayout = ecc->layout; + mtd_set_ecclayout(mtd, ecc->layout); mtd->ecc_strength = ecc->strength; mtd->ecc_step_size = ecc->size; From eb8c2be581e0f6c98830bc9eee09905f9fefed32 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:14:03 +0100 Subject: [PATCH 079/155] mtd: onenand: use mtd_set_ecclayout() where appropriate Use the mtd_set_ecclayout() helper instead of directly assigning the mtd->ecclayout field. Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/onenand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 20fdf8cceae9..d0fa505d40bd 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -3997,7 +3997,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->oobavail = ret; - mtd->ecclayout = this->ecclayout; + mtd_set_ecclayout(mtd, this->ecclayout); mtd->ecc_strength = 1; /* Fill in remaining MTD driver data */ From 06af3b023c016bf97e39fbe22087b1d974f9e474 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:14:14 +0100 Subject: [PATCH 080/155] mtd: docg3: use mtd_set_ecclayout() where appropriate Use the mtd_set_ecclayout() helper instead of directly assigning the mtd->ecclayout field. Signed-off-by: Boris Brezillon Acked-by: Robert Jarzmik --- drivers/mtd/devices/docg3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index e7b2e439696c..6b516e14264b 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1857,7 +1857,7 @@ static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) mtd->_read_oob = doc_read_oob; mtd->_write_oob = doc_write_oob; mtd->_block_isbad = doc_block_isbad; - mtd->ecclayout = &docg3_oobinfo; + mtd_set_ecclayout(mtd, &docg3_oobinfo); mtd->oobavail = 8; mtd->ecc_strength = DOC_ECC_BCH_T; From adbbc3bc827eb1f43a932d783f09ba55c8ec8379 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:01:31 +0100 Subject: [PATCH 081/155] mtd: create an mtd_ooblayout_ops struct to ease ECC layout definition ECC layout definitions are currently exposed using the nand_ecclayout struct which embeds oobfree and eccpos arrays with predefined size. This approach was acceptable when NAND chips were providing relatively small OOB regions, but MLC and TLC now provide OOB regions of several hundreds of bytes, which implies a non negligible overhead for everybody even those who only need to support legacy NANDs. Create an mtd_ooblayout_ops interface providing the same functionality (expose the ECC and oobfree layout) without the need for this huge structure. The mtd->ecclayout is now deprecated and should be replaced by the equivalent mtd_ooblayout_ops. In the meantime we provide a wrapper around the ->ecclayout field to ease migration to this new model. Signed-off-by: Boris Brezillon --- drivers/mtd/mtdchar.c | 4 +- drivers/mtd/mtdconcat.c | 2 +- drivers/mtd/mtdcore.c | 165 +++++++++++++++++++++++++++++----------- drivers/mtd/mtdpart.c | 23 +++++- include/linux/mtd/mtd.h | 32 +++++++- 5 files changed, 174 insertions(+), 52 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index cd64ab76dd7b..3fad2c7425b0 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -888,7 +888,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct nand_oobinfo oi; - if (!mtd->ecclayout) + if (!mtd->ooblayout) return -EOPNOTSUPP; ret = get_oobinfo(mtd, &oi); @@ -982,7 +982,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct nand_ecclayout_user *usrlay; - if (!mtd->ecclayout) + if (!mtd->ooblayout) return -EOPNOTSUPP; usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 481565e5fbaa..d573606b91c2 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c } - mtd_set_ecclayout(&concat->mtd, subdev[0]->ecclayout); + mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout); concat->num_subdev = num_devs; concat->mtd.name = name; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 0290c41e44fc..134ed2f7b919 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1035,49 +1035,15 @@ EXPORT_SYMBOL_GPL(mtd_write_oob); int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc) { - int eccbyte = 0, cursection = 0, length = 0, eccpos = 0; - memset(oobecc, 0, sizeof(*oobecc)); if (!mtd || section < 0) return -EINVAL; - if (!mtd->ecclayout) + if (!mtd->ooblayout || !mtd->ooblayout->ecc) return -ENOTSUPP; - /* - * This logic allows us to reuse the ->ecclayout information and - * expose them as ECC regions (as done for the OOB free regions). - * - * TODO: this should be dropped as soon as we get rid of the - * ->ecclayout field. - */ - for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) { - eccpos = mtd->ecclayout->eccpos[eccbyte]; - - if (eccbyte < mtd->ecclayout->eccbytes - 1) { - int neccpos = mtd->ecclayout->eccpos[eccbyte + 1]; - - if (eccpos + 1 == neccpos) { - length++; - continue; - } - } - - if (section == cursection) - break; - - length = 0; - cursection++; - } - - if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes) - return -ERANGE; - - oobecc->length = length + 1; - oobecc->offset = eccpos - length; - - return 0; + return mtd->ooblayout->ecc(mtd, section, oobecc); } EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); @@ -1106,16 +1072,10 @@ int mtd_ooblayout_free(struct mtd_info *mtd, int section, if (!mtd || section < 0) return -EINVAL; - if (!mtd->ecclayout) + if (!mtd->ooblayout || !mtd->ooblayout->free) return -ENOTSUPP; - if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE) - return -ERANGE; - - oobfree->offset = mtd->ecclayout->oobfree[section].offset; - oobfree->length = mtd->ecclayout->oobfree[section].length; - - return 0; + return mtd->ooblayout->free(mtd, section, oobfree); } EXPORT_SYMBOL_GPL(mtd_ooblayout_free); @@ -1416,6 +1376,123 @@ int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd) } EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes); +/** + * mtd_ecclayout_ecc - Default ooblayout_ecc iterator implementation + * @mtd: MTD device structure + * @section: ECC section. Depending on the layout you may have all the ECC + * bytes stored in a single contiguous section, or one section + * per ECC chunk (and sometime several sections for a single ECC + * ECC chunk) + * @oobecc: OOB region struct filled with the appropriate ECC position + * information + * + * This function is just a wrapper around the mtd->ecclayout field and is + * here to ease the transition to the mtd_ooblayout_ops approach. + * All it does is convert the layout->eccpos information into proper oob + * region definitions. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ecclayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc) +{ + int eccbyte = 0, cursection = 0, length = 0, eccpos = 0; + + if (!mtd->ecclayout) + return -ENOTSUPP; + + /* + * This logic allows us to reuse the ->ecclayout information and + * expose them as ECC regions (as done for the OOB free regions). + * + * TODO: this should be dropped as soon as we get rid of the + * ->ecclayout field. + */ + for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) { + eccpos = mtd->ecclayout->eccpos[eccbyte]; + + if (eccbyte < mtd->ecclayout->eccbytes - 1) { + int neccpos = mtd->ecclayout->eccpos[eccbyte + 1]; + + if (eccpos + 1 == neccpos) { + length++; + continue; + } + } + + if (section == cursection) + break; + + length = 0; + cursection++; + } + + if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes) + return -ERANGE; + + oobecc->length = length + 1; + oobecc->offset = eccpos - length; + + return 0; +} + +/** + * mtd_ecclayout_ecc - Default ooblayout_free iterator implementation + * @mtd: MTD device structure + * @section: Free section. Depending on the layout you may have all the free + * bytes stored in a single contiguous section, or one section + * per ECC chunk (and sometime several sections for a single ECC + * ECC chunk) + * @oobfree: OOB region struct filled with the appropriate free position + * information + * + * This function is just a wrapper around the mtd->ecclayout field and is + * here to ease the transition to the mtd_ooblayout_ops approach. + * All it does is convert the layout->oobfree information into proper oob + * region definitions. + * + * Returns zero on success, a negative error code otherwise. + */ +static int mtd_ecclayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree) +{ + struct nand_ecclayout *layout = mtd->ecclayout; + + if (!layout) + return -ENOTSUPP; + + if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE || + !layout->oobfree[section].length) + return -ERANGE; + + oobfree->offset = layout->oobfree[section].offset; + oobfree->length = layout->oobfree[section].length; + + return 0; +} + +static const struct mtd_ooblayout_ops mtd_ecclayout_wrapper_ops = { + .ecc = mtd_ecclayout_ecc, + .free = mtd_ecclayout_free, +}; + +/** + * mtd_set_ecclayout - Attach an ecclayout to an MTD device + * @mtd: MTD device structure + * @ecclayout: The ecclayout to attach to the device + * + * Returns zero on success, a negative error code otherwise. + */ +void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout) +{ + if (!mtd || !ecclayout) + return; + + mtd->ecclayout = ecclayout; + mtd_set_ooblayout(mtd, &mtd_ecclayout_wrapper_ops); +} +EXPORT_SYMBOL_GPL(mtd_set_ecclayout); + /* * Method to access the protection register area, present in some flash * devices. The user data is one time programmable but the factory data is read diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index f53d9d72b23a..1f13e32556f8 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) return res; } +static int part_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct mtd_part *part = mtd_to_part(mtd); + + return mtd_ooblayout_ecc(part->master, section, oobregion); +} + +static int part_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct mtd_part *part = mtd_to_part(mtd); + + return mtd_ooblayout_free(part->master, section, oobregion); +} + +static const struct mtd_ooblayout_ops part_ooblayout_ops = { + .ecc = part_ooblayout_ecc, + .free = part_ooblayout_free, +}; + static inline void free_partition(struct mtd_part *p) { kfree(p->mtd.name); @@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, part->name); } - mtd_set_ecclayout(&slave->mtd, master->ecclayout); + mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops); slave->mtd.ecc_step_size = master->ecc_step_size; slave->mtd.ecc_strength = master->ecc_strength; slave->mtd.bitflip_threshold = master->bitflip_threshold; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index e62da8462493..177bf314ad70 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -101,6 +101,9 @@ struct mtd_oob_ops { * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained * for export to user-space via the ECCGETLAYOUT ioctl. * nand_ecclayout should be expandable in the future simply by the above macros. + * + * This structure is now deprecated, you should use struct nand_ecclayout_ops + * to describe your OOB layout. */ struct nand_ecclayout { __u32 eccbytes; @@ -123,6 +126,22 @@ struct mtd_oob_region { u32 length; }; +/* + * struct mtd_ooblayout_ops - NAND OOB layout operations + * @ecc: function returning an ECC region in the OOB area. + * Should return -ERANGE if %section exceeds the total number of + * ECC sections. + * @free: function returning a free region in the OOB area. + * Should return -ERANGE if %section exceeds the total number of + * free sections. + */ +struct mtd_ooblayout_ops { + int (*ecc)(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobecc); + int (*free)(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobfree); +}; + struct module; /* only needed for owner field in mtd_info */ struct mtd_info { @@ -181,9 +200,12 @@ struct mtd_info { const char *name; int index; - /* ECC layout structure pointer - read only! */ + /* [Deprecated] ECC layout structure pointer - read only! */ struct nand_ecclayout *ecclayout; + /* OOB layout description */ + const struct mtd_ooblayout_ops *ooblayout; + /* the ecc step size. */ unsigned int ecc_step_size; @@ -286,10 +308,12 @@ int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, int mtd_ooblayout_count_freebytes(struct mtd_info *mtd); int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd); -static inline void mtd_set_ecclayout(struct mtd_info *mtd, - struct nand_ecclayout *ecclayout) +void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout); + +static inline void mtd_set_ooblayout(struct mtd_info *mtd, + const struct mtd_ooblayout_ops *ooblayout) { - mtd->ecclayout = ecclayout; + mtd->ooblayout = ooblayout; } static inline void mtd_set_of_node(struct mtd_info *mtd, From 1bd0b24737710697e0a850fb1983dcdce1d01196 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:05:04 +0100 Subject: [PATCH 082/155] mtd: docg3: switch to mtd_ooblayout_ops Replace the nand_ecclayout definition by the equivalent mtd_ooblayout_ops definition. Signed-off-by: Boris Brezillon Acked-by: Robert Jarzmik --- drivers/mtd/devices/docg3.c | 46 ++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 6b516e14264b..b833e6cc684c 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -67,16 +67,40 @@ module_param(reliable_mode, uint, 0); MODULE_PARM_DESC(reliable_mode, "Set the docg3 mode (0=normal MLC, 1=fast, " "2=reliable) : MLC normal operations are in normal mode"); -/** - * struct docg3_oobinfo - DiskOnChip G3 OOB layout - * @eccbytes: 8 bytes are used (1 for Hamming ECC, 7 for BCH ECC) - * @eccpos: ecc positions (byte 7 is Hamming ECC, byte 8-14 are BCH ECC) - * @oobfree: free pageinfo bytes (byte 0 until byte 6, byte 15 - */ -static struct nand_ecclayout docg3_oobinfo = { - .eccbytes = 8, - .eccpos = {7, 8, 9, 10, 11, 12, 13, 14}, - .oobfree = {{0, 7}, {15, 1} }, +static int docg3_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + /* byte 7 is Hamming ECC, byte 8-14 are BCH ECC */ + oobregion->offset = 7; + oobregion->length = 8; + + return 0; +} + +static int docg3_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + /* free bytes: byte 0 until byte 6, byte 15 */ + if (!section) { + oobregion->offset = 0; + oobregion->length = 7; + } else { + oobregion->offset = 15; + oobregion->length = 1; + } + + return 0; +} + +static const struct mtd_ooblayout_ops nand_ooblayout_docg3_ops = { + .ecc = docg3_ooblayout_ecc, + .free = docg3_ooblayout_free, }; static inline u8 doc_readb(struct docg3 *docg3, u16 reg) @@ -1857,7 +1881,7 @@ static int __init doc_set_driver_info(int chip_id, struct mtd_info *mtd) mtd->_read_oob = doc_read_oob; mtd->_write_oob = doc_write_oob; mtd->_block_isbad = doc_block_isbad; - mtd_set_ecclayout(mtd, &docg3_oobinfo); + mtd_set_ooblayout(mtd, &nand_ooblayout_docg3_ops); mtd->oobavail = 8; mtd->ecc_strength = DOC_ECC_BCH_T; From 41b207a70d3a86b9e2eede155e87838234c7cbd5 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:06:15 +0100 Subject: [PATCH 083/155] mtd: nand: implement the default mtd_ooblayout_ops Replace the default nand_ecclayout definitions for large and small page devices with the equivalent mtd_ooblayout_ops. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 148 +++++++++++++++++++++++------------ include/linux/mtd/nand.h | 3 + 2 files changed, 99 insertions(+), 52 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index feb1448e9dd6..e8332ea45739 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -47,55 +47,97 @@ #include #include -/* Define default oob placement schemes for large and small page devices */ -static struct nand_ecclayout nand_oob_8 = { - .eccbytes = 3, - .eccpos = {0, 1, 2}, - .oobfree = { - {.offset = 3, - .length = 2}, - {.offset = 6, - .length = 2} } -}; - -static struct nand_ecclayout nand_oob_16 = { - .eccbytes = 6, - .eccpos = {0, 1, 2, 3, 6, 7}, - .oobfree = { - {.offset = 8, - . length = 8} } -}; - -static struct nand_ecclayout nand_oob_64 = { - .eccbytes = 24, - .eccpos = { - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { - {.offset = 2, - .length = 38} } -}; - -static struct nand_ecclayout nand_oob_128 = { - .eccbytes = 48, - .eccpos = { - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}, - .oobfree = { - {.offset = 2, - .length = 78} } -}; - static int nand_get_device(struct mtd_info *mtd, int new_state); static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); +/* Define default oob placement schemes for large and small page devices */ +static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section > 1) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + oobregion->length = 4; + } else { + oobregion->offset = 6; + oobregion->length = ecc->total - 4; + } + + return 0; +} + +static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + if (mtd->oobsize == 16) { + if (section) + return -ERANGE; + + oobregion->length = 8; + oobregion->offset = 8; + } else { + oobregion->length = 2; + if (!section) + oobregion->offset = 3; + else + oobregion->offset = 6; + } + + return 0; +} + +const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = { + .ecc = nand_ooblayout_ecc_sp, + .free = nand_ooblayout_free_sp, +}; +EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops); + +static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + oobregion->length = ecc->total; + oobregion->offset = mtd->oobsize - oobregion->length; + + return 0; +} + +static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + oobregion->length = mtd->oobsize - ecc->total - 2; + oobregion->offset = 2; + + return 0; +} + +const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = { + .ecc = nand_ooblayout_ecc_lp, + .free = nand_ooblayout_free_lp, +}; +EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops); + static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len) { @@ -4109,22 +4151,25 @@ int nand_scan_tail(struct mtd_info *mtd) /* Set the internal oob buffer location, just after the page data */ chip->oob_poi = chip->buffers->databuf + mtd->writesize; + /* + * Set the provided ECC layout. If ecc->layout is NULL, the MTD core + * will just leave mtd->ooblayout to NULL, if it's not NULL, it will + * set ->ooblayout to the default ecclayout wrapper. + */ + mtd_set_ecclayout(mtd, ecc->layout); + /* * If no default placement scheme is given, select an appropriate one. */ - if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) { + if (!mtd->ooblayout && (ecc->mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: - ecc->layout = &nand_oob_8; - break; case 16: - ecc->layout = &nand_oob_16; + mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops); break; case 64: - ecc->layout = &nand_oob_64; - break; case 128: - ecc->layout = &nand_oob_128; + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); break; default: WARN(1, "No oob scheme defined for oobsize %d\n", @@ -4285,7 +4330,6 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->write_oob_raw = ecc->write_oob; /* propagate ecc info to mtd_info */ - mtd_set_ecclayout(mtd, ecc->layout); mtd->ecc_strength = ecc->strength; mtd->ecc_step_size = ecc->size; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 7e06afb8552c..f2ded7b1b3b8 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -748,6 +748,9 @@ struct nand_chip { void *priv; }; +extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; +extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops; + static inline void nand_set_flash_node(struct nand_chip *chip, struct device_node *np) { From 025ce02d92b67d6c702676c11f7f1f5e8f0a81fa Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sun, 27 Mar 2016 12:13:54 -0400 Subject: [PATCH 084/155] mtd: maps: make uclinux.c driver more explicitly non-modular The Kconfig for this support is currently declared with: config MTD_UCLINUX bool "Generic uClinux RAM/ROM filesystem support" ...meaning that it currently is not being built as a module by anyone. Lets remove as much of the modular evidence that we can, so that when reading the driver there is less doubt it is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. We also replace module.h with moduleparam.h since the file does use a module_param, and leaving it as such is currently the easiest way to remain compatible with existing boot arg use cases. We also delete the MODULE_LICENSE tag etc. since all that information was (or is now) contained at the top of the file in the comments. Cc: David Woodhouse Cc: Greg Ungerer Signed-off-by: Paul Gortmaker Signed-off-by: Brian Norris --- drivers/mtd/maps/uclinux.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index c1af83db5202..00a8190797ec 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -4,11 +4,13 @@ * uclinux.c -- generic memory mapped MTD driver for uclinux * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) + * + * License: GPL */ /****************************************************************************/ -#include +#include #include #include #include @@ -117,27 +119,6 @@ static int __init uclinux_mtd_init(void) return(0); } - -/****************************************************************************/ - -static void __exit uclinux_mtd_cleanup(void) -{ - if (uclinux_ram_mtdinfo) { - mtd_device_unregister(uclinux_ram_mtdinfo); - map_destroy(uclinux_ram_mtdinfo); - uclinux_ram_mtdinfo = NULL; - } - if (uclinux_ram_map.virt) - uclinux_ram_map.virt = 0; -} - -/****************************************************************************/ - -module_init(uclinux_mtd_init); -module_exit(uclinux_mtd_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Greg Ungerer "); -MODULE_DESCRIPTION("Generic MTD for uClinux"); +device_initcall(uclinux_mtd_init); /****************************************************************************/ From e4106a7c8236eb7b91686d36f3bf33ee43db94b4 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 19 Apr 2016 14:33:33 +0200 Subject: [PATCH 085/155] mtd: maps: add __init attribute Add __init attribute on functions that are only called from other __init functions and that are not inlined, at least with gcc version 4.8.4 on an x86 machine with allyesconfig. Currently, the functions are put in the .text.unlikely segment. Declaring them as __init will cause them to be put in the .init.text and to disappear after initialization. The result of objdump -x on the functions before the change is as follows: 00000000000001bc l F .text.unlikely 00000000000006a2 ck804xrom_init_one.isra.1 00000000000001aa l F .text.unlikely 0000000000000764 esb2rom_init_one.isra.1 00000000000001db l F .text.unlikely 0000000000000716 ichxrom_init_one.isra.1 And after the change it is as follows: 0000000000000000 l F .init.text 000000000000069d ck804xrom_init_one.isra.1 0000000000000000 l F .init.text 000000000000075f esb2rom_init_one.isra.1 0000000000000000 l F .init.text 0000000000000711 ichxrom_init_one.isra.1 Done with the help of Coccinelle. The semantic patch checks for local static non-init functions that are called from an __init function and are not called from any other function. Note that in each case, the function is stored in the probe field of a pci_driver structure, but this code is under an #if 0. The #if 0s have been unchanged since 2009 at the latest. Signed-off-by: Julia Lawall Signed-off-by: Brian Norris --- drivers/mtd/maps/ck804xrom.c | 4 ++-- drivers/mtd/maps/esb2rom.c | 4 ++-- drivers/mtd/maps/ichxrom.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 0455166f05fa..4f206a99164c 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -112,8 +112,8 @@ static void ck804xrom_cleanup(struct ck804xrom_window *window) } -static int ck804xrom_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int __init ck804xrom_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; u8 byte; diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index 76ed651b515b..9646b0766ce0 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -144,8 +144,8 @@ static void esb2rom_cleanup(struct esb2rom_window *window) pci_dev_put(window->pdev); } -static int esb2rom_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int __init esb2rom_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; struct esb2rom_window *window = &esb2rom_window; diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 8636bba42200..e17d02ae03f0 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -84,8 +84,8 @@ static void ichxrom_cleanup(struct ichxrom_window *window) } -static int ichxrom_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int __init ichxrom_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; struct ichxrom_window *window = &ichxrom_window; From abbbc60a0c69e05c965ce51cb75c0bee56dc0025 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 19 Apr 2016 14:33:34 +0200 Subject: [PATCH 086/155] mtd: pmc551: add __init attribute Add __init attribute on a function that is only called from other __init functions and that is not inlined, at least with gcc version 4.8.4 on an x86 machine with allyesconfig. Currently, the function is put in the .text.unlikely segment. Declaring it as __init will cause it to be put in the .init.text and to disappear after initialization. The result of objdump -x on the function before the change is as follows: 00000000000000c6 l F .text.unlikely 000000000000091c fixup_pmc551 And after the change it is as follows: 0000000000000000 l F .init.text 0000000000000917 fixup_pmc551 Done with the help of Coccinelle. The semantic patch checks for local static non-init functions that are called from an __init function and are not called from any other function. Signed-off-by: Julia Lawall Signed-off-by: Brian Norris --- drivers/mtd/devices/pmc551.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index 708b7e8c8b18..220f9200fa52 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -353,7 +353,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len, * mechanism * returns the size of the memory region found. */ -static int fixup_pmc551(struct pci_dev *dev) +static int __init fixup_pmc551(struct pci_dev *dev) { #ifdef CONFIG_MTD_PMC551_BUGFIX u32 dram_data; From 7cf9c19a836a79c60deef63714cb2880246d9a85 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:53:40 +0100 Subject: [PATCH 087/155] mtd: nand: bch: switch to mtd_ooblayout_ops Replace the nand_ecclayout definition by the equivalent mtd_ooblayout_ops definition. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 15 +++++++++++++ drivers/mtd/nand/nand_bch.c | 41 ++++++++++-------------------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e8332ea45739..6c84a40dbddb 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4294,6 +4294,21 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->strength = 4; } + /* + * if no ecc placement scheme was provided pickup the default + * large page one. + */ + if (!mtd->ooblayout) { + /* handle large page devices only */ + if (mtd->oobsize < 64) { + WARN(1, "OOB layout is required when using software BCH on small pages\n"); + ret = -EINVAL; + goto err_free; + } + + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + } + /* See nand_bch_init() for details. */ ecc->bytes = 0; ecc->priv = nand_bch_init(mtd); diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c index d8c03ebb62dc..1dbc418baac4 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -32,13 +32,11 @@ /** * struct nand_bch_control - private NAND BCH control structure * @bch: BCH control structure - * @ecclayout: private ecc layout for this BCH configuration * @errloc: error location array * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid */ struct nand_bch_control { struct bch_control *bch; - struct nand_ecclayout ecclayout; unsigned int *errloc; unsigned char *eccmask; }; @@ -124,7 +122,6 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) { struct nand_chip *nand = mtd_to_nand(mtd); unsigned int m, t, eccsteps, i; - struct nand_ecclayout *layout = nand->ecc.layout; struct nand_bch_control *nbc = NULL; unsigned char *erased_page; unsigned int eccsize = nand->ecc.size; @@ -161,34 +158,18 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) eccsteps = mtd->writesize/eccsize; - /* if no ecc placement scheme was provided, build one */ - if (!layout) { + /* + * Rely on the default ecclayout to ooblayout wrapper provided by MTD + * core if ecc.layout is not NULL. + * FIXME: this should be removed when all callers have moved to the + * mtd_ooblayout_ops approach. + */ + mtd_set_ecclayout(mtd, nand->ecc.layout); - /* handle large page devices only */ - if (mtd->oobsize < 64) { - printk(KERN_WARNING "must provide an oob scheme for " - "oobsize %d\n", mtd->oobsize); - goto fail; - } - - layout = &nbc->ecclayout; - layout->eccbytes = eccsteps*eccbytes; - - /* reserve 2 bytes for bad block marker */ - if (layout->eccbytes+2 > mtd->oobsize) { - printk(KERN_WARNING "no suitable oob scheme available " - "for oobsize %d eccbytes %u\n", mtd->oobsize, - 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; - - nand->ecc.layout = layout; + /* Check that we have an oob layout description. */ + if (!mtd->ooblayout) { + pr_warn("missing oob scheme"); + goto fail; } /* sanity checks */ From e5b2d30e42e66122c9b1b17529df743bc938c041 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:58:11 +0100 Subject: [PATCH 088/155] mtd: nand: sharpsl: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- arch/arm/mach-pxa/spitz.c | 55 ++++++++++++++++++++++++++++++------- drivers/mtd/nand/sharpsl.c | 2 +- include/linux/mtd/sharpsl.h | 2 +- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index d9578bc49fdc..bd7cd8b6a286 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -763,14 +763,49 @@ static struct nand_bbt_descr spitz_nand_bbt = { .pattern = scan_ff_pattern }; -static struct nand_ecclayout akita_oobinfo = { - .oobfree = { {0x08, 0x09} }, - .eccbytes = 24, - .eccpos = { - 0x05, 0x01, 0x02, 0x03, 0x06, 0x07, 0x15, 0x11, - 0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23, - 0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37, - }, +static int akita_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 12) + return -ERANGE; + + switch (section % 3) { + case 0: + oobregion->offset = 5; + oobregion->length = 1; + break; + + case 1: + oobregion->offset = 1; + oobregion->length = 3; + break; + + case 2: + oobregion->offset = 6; + oobregion->length = 2; + break; + } + + oobregion->offset += (section / 3) * 0x10; + + return 0; +} + +static int akita_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 8; + oobregion->length = 9; + + return 0; +} + +static const struct mtd_ooblayout_ops akita_ooblayout_ops = { + .ecc = akita_ooblayout_ecc, + .free = akita_ooblayout_free, }; static struct sharpsl_nand_platform_data spitz_nand_pdata = { @@ -804,11 +839,11 @@ static void __init spitz_nand_init(void) } else if (machine_is_akita()) { spitz_nand_partitions[1].size = 58 * 1024 * 1024; spitz_nand_bbt.len = 1; - spitz_nand_pdata.ecc_layout = &akita_oobinfo; + spitz_nand_pdata.ecc_layout = &akita_ooblayout_ops; } else if (machine_is_borzoi()) { spitz_nand_partitions[1].size = 32 * 1024 * 1024; spitz_nand_bbt.len = 1; - spitz_nand_pdata.ecc_layout = &akita_oobinfo; + spitz_nand_pdata.ecc_layout = &akita_ooblayout_ops; } platform_device_register(&spitz_nand_device); diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index b7d1b55a160b..064ca1757589 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -148,6 +148,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev) /* Link the private data with the MTD structure */ mtd = nand_to_mtd(this); mtd->dev.parent = &pdev->dev; + mtd_set_ooblayout(mtd, data->ecc_layout); platform_set_drvdata(pdev, sharpsl); @@ -170,7 +171,6 @@ static int sharpsl_nand_probe(struct platform_device *pdev) this->ecc.bytes = 3; this->ecc.strength = 1; this->badblock_pattern = data->badblock_pattern; - this->ecc.layout = data->ecc_layout; this->ecc.hwctl = sharpsl_nand_enable_hwecc; this->ecc.calculate = sharpsl_nand_calculate_ecc; this->ecc.correct = nand_correct_data; diff --git a/include/linux/mtd/sharpsl.h b/include/linux/mtd/sharpsl.h index 25f4d2a845c1..65e91d0fa981 100644 --- a/include/linux/mtd/sharpsl.h +++ b/include/linux/mtd/sharpsl.h @@ -14,7 +14,7 @@ struct sharpsl_nand_platform_data { struct nand_bbt_descr *badblock_pattern; - struct nand_ecclayout *ecc_layout; + const struct mtd_ooblayout_ops *ecc_layout; struct mtd_partition *partitions; unsigned int nr_partitions; }; From 50533187eed5ace8d06e0e04aeea8fea1b70b8d7 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:58:55 +0100 Subject: [PATCH 089/155] mtd: nand: jz4740: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon Acked-by: Lars-Peter Clausen --- .../include/asm/mach-jz4740/jz4740_nand.h | 2 +- arch/mips/jz4740/board-qi_lb60.c | 87 +++++++++++-------- drivers/mtd/nand/jz4740_nand.c | 2 +- 3 files changed, 53 insertions(+), 38 deletions(-) diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h index 398733e3e2cf..7f7b0fc554da 100644 --- a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h +++ b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h @@ -27,7 +27,7 @@ struct jz_nand_platform_data { unsigned char banks[JZ_NAND_NUM_BANKS]; - void (*ident_callback)(struct platform_device *, struct nand_chip *, + void (*ident_callback)(struct platform_device *, struct mtd_info *, struct mtd_partition **, int *num_partitions); }; diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c index 934b15b5b575..a1c1afbbfc5d 100644 --- a/arch/mips/jz4740/board-qi_lb60.c +++ b/arch/mips/jz4740/board-qi_lb60.c @@ -50,20 +50,6 @@ static bool is_avt2; #define QI_LB60_GPIO_KEYIN8 JZ_GPIO_PORTD(26) /* NAND */ -static struct nand_ecclayout qi_lb60_ecclayout_1gb = { - .eccbytes = 36, - .eccpos = { - 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, - 38, 39, 40, 41 - }, - .oobfree = { - { .offset = 2, .length = 4 }, - { .offset = 42, .length = 22 } - }, -}; /* Early prototypes of the QI LB60 had only 1GB of NAND. * In order to support these devices as well the partition and ecc layout is @@ -86,25 +72,6 @@ static struct mtd_partition qi_lb60_partitions_1gb[] = { }, }; -static struct nand_ecclayout qi_lb60_ecclayout_2gb = { - .eccbytes = 72, - .eccpos = { - 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, - 76, 77, 78, 79, 80, 81, 82, 83 - }, - .oobfree = { - { .offset = 2, .length = 10 }, - { .offset = 84, .length = 44 }, - }, -}; - static struct mtd_partition qi_lb60_partitions_2gb[] = { { .name = "NAND BOOT partition", @@ -123,19 +90,67 @@ static struct mtd_partition qi_lb60_partitions_2gb[] = { }, }; +static int qi_lb60_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->length = 36; + oobregion->offset = 6; + + if (mtd->oobsize == 128) { + oobregion->length *= 2; + oobregion->offset *= 2; + } + + return 0; +} + +static int qi_lb60_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + int eccbytes = 36, eccoff = 6; + + if (section > 1) + return -ERANGE; + + if (mtd->oobsize == 128) { + eccbytes *= 2; + eccoff *= 2; + } + + if (!section) { + oobregion->offset = 2; + oobregion->length = eccoff - 2; + } else { + oobregion->offset = eccoff + eccbytes; + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops qi_lb60_ooblayout_ops = { + .ecc = qi_lb60_ooblayout_ecc, + .free = qi_lb60_ooblayout_free, +}; + static void qi_lb60_nand_ident(struct platform_device *pdev, - struct nand_chip *chip, struct mtd_partition **partitions, + struct mtd_info *mtd, struct mtd_partition **partitions, int *num_partitions) { + struct nand_chip *chip = mtd_to_nand(mtd); + if (chip->page_shift == 12) { - chip->ecc.layout = &qi_lb60_ecclayout_2gb; *partitions = qi_lb60_partitions_2gb; *num_partitions = ARRAY_SIZE(qi_lb60_partitions_2gb); } else { - chip->ecc.layout = &qi_lb60_ecclayout_1gb; *partitions = qi_lb60_partitions_1gb; *num_partitions = ARRAY_SIZE(qi_lb60_partitions_1gb); } + + mtd_set_ooblayout(mtd, &qi_lb60_ooblayout_ops); } static struct jz_nand_platform_data qi_lb60_nand_pdata = { diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index 673ceb2a0b44..df7440872df0 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -476,7 +476,7 @@ static int jz_nand_probe(struct platform_device *pdev) } if (pdata && pdata->ident_callback) { - pdata->ident_callback(pdev, chip, &pdata->partitions, + pdata->ident_callback(pdev, mtd, &pdata->partitions, &pdata->num_partitions); } From cc00383722db7a4b75e005f5bb8b08b4d874ed00 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:59:11 +0100 Subject: [PATCH 090/155] mtd: nand: atmel: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon Reviewed-by: Nicolas Ferre --- drivers/mtd/nand/atmel_nand.c | 84 ++++++++++++++++------------------- 1 file changed, 38 insertions(+), 46 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index b132c8fd3701..eec8ca739cae 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -71,30 +71,44 @@ struct atmel_nand_nfc_caps { uint32_t rb_mask; }; -/* oob layout for large page size +/* + * oob layout for large page size * bad block info is on bytes 0 and 1 * the bytes have to be consecutives to avoid * several NAND_CMD_RNDOUT during read - */ -static struct nand_ecclayout atmel_oobinfo_large = { - .eccbytes = 4, - .eccpos = {60, 61, 62, 63}, - .oobfree = { - {2, 58} - }, -}; - -/* oob layout for small page size + * + * oob layout for small page size * bad block info is on bytes 4 and 5 * the bytes have to be consecutives to avoid * several NAND_CMD_RNDOUT during read */ -static struct nand_ecclayout atmel_oobinfo_small = { - .eccbytes = 4, - .eccpos = {0, 1, 2, 3}, - .oobfree = { - {6, 10} - }, +static int atmel_ooblayout_ecc_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->length = 4; + oobregion->offset = 0; + + return 0; +} + +static int atmel_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 6; + oobregion->length = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops atmel_ooblayout_sp_ops = { + .ecc = atmel_ooblayout_ecc_sp, + .free = atmel_ooblayout_free_sp, }; struct atmel_nfc { @@ -162,8 +176,6 @@ struct atmel_nand_host { int *pmecc_delta; }; -static struct nand_ecclayout atmel_pmecc_oobinfo; - /* * Enable NAND. */ @@ -480,22 +492,6 @@ static int pmecc_get_ecc_bytes(int cap, int sector_size) return (m * cap + 7) / 8; } -static void pmecc_config_ecc_layout(struct nand_ecclayout *layout, - int oobsize, int ecc_len) -{ - int i; - - layout->eccbytes = ecc_len; - - /* ECC will occupy the last ecc_len bytes continuously */ - for (i = 0; i < ecc_len; i++) - layout->eccpos[i] = oobsize - ecc_len + i; - - layout->oobfree[0].offset = PMECC_OOB_RESERVED_BYTES; - layout->oobfree[0].length = - oobsize - ecc_len - layout->oobfree[0].offset; -} - static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) { int table_size; @@ -1012,8 +1008,8 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd_to_nand(mtd); struct atmel_nand_host *host = nand_get_controller_data(nand_chip); + int eccbytes = mtd_ooblayout_count_eccbytes(mtd); uint32_t val = 0; - struct nand_ecclayout *ecc_layout; struct mtd_oob_region oobregion; pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); @@ -1064,12 +1060,11 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) | PMECC_CFG_AUTO_DISABLE); pmecc_writel(host->ecc, CFG, val); - ecc_layout = nand_chip->ecc.layout; pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1); mtd_ooblayout_ecc(mtd, 0, &oobregion); pmecc_writel(host->ecc, SADDR, oobregion.offset); pmecc_writel(host->ecc, EADDR, - oobregion.offset + ecc_layout->eccbytes - 1); + oobregion.offset + eccbytes - 1); /* See datasheet about PMECC Clock Control Register */ pmecc_writel(host->ecc, CLK, 2); pmecc_writel(host->ecc, IDR, 0xff); @@ -1291,11 +1286,8 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev, err_no = -EINVAL; goto err; } - pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, - mtd->oobsize, - nand_chip->ecc.total); - nand_chip->ecc.layout = &atmel_pmecc_oobinfo; + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); break; default: dev_warn(host->dev, @@ -1650,19 +1642,19 @@ static int atmel_hw_nand_init_params(struct platform_device *pdev, /* set ECC page size and oob layout */ switch (mtd->writesize) { case 512: - nand_chip->ecc.layout = &atmel_oobinfo_small; + mtd_set_ooblayout(mtd, &atmel_ooblayout_sp_ops); ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528); break; case 1024: - nand_chip->ecc.layout = &atmel_oobinfo_large; + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056); break; case 2048: - nand_chip->ecc.layout = &atmel_oobinfo_large; + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112); break; case 4096: - nand_chip->ecc.layout = &atmel_oobinfo_large; + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224); break; default: From c8766e81ca3a224b092d087f6e1d77bf2c3d665d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:59:25 +0100 Subject: [PATCH 091/155] mtd: nand: bf5xx: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/bf5xx_nand.c | 51 +++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 7f6b30e615b7..b38f41470414 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -109,28 +109,33 @@ static const unsigned short bfin_nfc_pin_req[] = 0}; #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC -static struct nand_ecclayout bootrom_ecclayout = { - .eccbytes = 24, - .eccpos = { - 0x8 * 0, 0x8 * 0 + 1, 0x8 * 0 + 2, - 0x8 * 1, 0x8 * 1 + 1, 0x8 * 1 + 2, - 0x8 * 2, 0x8 * 2 + 1, 0x8 * 2 + 2, - 0x8 * 3, 0x8 * 3 + 1, 0x8 * 3 + 2, - 0x8 * 4, 0x8 * 4 + 1, 0x8 * 4 + 2, - 0x8 * 5, 0x8 * 5 + 1, 0x8 * 5 + 2, - 0x8 * 6, 0x8 * 6 + 1, 0x8 * 6 + 2, - 0x8 * 7, 0x8 * 7 + 1, 0x8 * 7 + 2 - }, - .oobfree = { - { 0x8 * 0 + 3, 5 }, - { 0x8 * 1 + 3, 5 }, - { 0x8 * 2 + 3, 5 }, - { 0x8 * 3 + 3, 5 }, - { 0x8 * 4 + 3, 5 }, - { 0x8 * 5 + 3, 5 }, - { 0x8 * 6 + 3, 5 }, - { 0x8 * 7 + 3, 5 }, - } +static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = section * 8; + oobregion->length = 3; + + return 0; +} + +static int bootrom_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = (section * 8) + 3; + oobregion->length = 5; + + return 0; +} + +static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = { + .ecc = bootrom_ooblayout_ecc, + .free = bootrom_ooblayout_free, }; #endif @@ -800,7 +805,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev) /* setup hardware ECC data struct */ if (hardware_ecc) { #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC - chip->ecc.layout = &bootrom_ecclayout; + mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops); #endif chip->read_buf = bf5xx_nand_dma_read_buf; chip->write_buf = bf5xx_nand_dma_write_buf; From ef5eeea6e911540bbf51a283fe0ffb7389cbe66a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:59:36 +0100 Subject: [PATCH 092/155] mtd: nand: brcm: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/brcmnand/brcmnand.c | 264 ++++++++++++++++----------- 1 file changed, 160 insertions(+), 104 deletions(-) diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index e5f281f148dc..c3331ffcaffd 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -780,55 +780,161 @@ static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg) } /* - * Returns a nand_ecclayout strucutre for the given layout/configuration. - * Returns NULL on failure. + * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given + * the layout/configuration. + * Returns -ERRCODE on failure. */ -static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, - struct brcmnand_host *host) +static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) { + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); struct brcmnand_cfg *cfg = &host->hwcfg; - int i, j; - struct nand_ecclayout *layout; - int req; - int sectors; - int sas; - int idx1, idx2; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); - layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL); - if (!layout) - return NULL; + if (section >= sectors) + return -ERANGE; - sectors = cfg->page_size / (512 << cfg->sector_size_1k); - sas = cfg->spare_area_size << cfg->sector_size_1k; + oobregion->offset = (section * sas) + 6; + oobregion->length = 3; - /* Hamming */ - if (is_hamming_ecc(cfg)) { - for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { - /* First sector of each page may have BBI */ - if (i == 0) { - layout->oobfree[idx2].offset = i * sas + 1; - /* Small-page NAND use byte 6 for BBI */ - if (cfg->page_size == 512) - layout->oobfree[idx2].offset--; - layout->oobfree[idx2].length = 5; - } else { - layout->oobfree[idx2].offset = i * sas; - layout->oobfree[idx2].length = 6; - } - idx2++; - layout->eccpos[idx1++] = i * sas + 6; - layout->eccpos[idx1++] = i * sas + 7; - layout->eccpos[idx1++] = i * sas + 8; - layout->oobfree[idx2].offset = i * sas + 9; - layout->oobfree[idx2].length = 7; - idx2++; - /* Leave zero-terminated entry for OOBFREE */ - if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || - idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) - break; + return 0; +} + +static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors * 2) + return -ERANGE; + + oobregion->offset = (section / 2) * sas; + + if (section & 1) { + oobregion->offset += 9; + oobregion->length = 7; + } else { + oobregion->length = 6; + + /* First sector of each page may have BBI */ + if (!section) { + /* + * Small-page NAND use byte 6 for BBI while large-page + * NAND use byte 0. + */ + if (cfg->page_size > 512) + oobregion->offset++; + oobregion->length--; } + } - return layout; + return 0; +} + +static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = { + .ecc = brcmnand_hamming_ooblayout_ecc, + .free = brcmnand_hamming_ooblayout_free, +}; + +static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + int sectors = cfg->page_size / (512 << cfg->sector_size_1k); + + if (section >= sectors) + return -ERANGE; + + if (sas <= chip->ecc.bytes) + return 0; + + oobregion->offset = section * sas; + oobregion->length = sas - chip->ecc.bytes; + + if (!section) { + oobregion->offset++; + oobregion->length--; + } + + return 0; +} + +static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct brcmnand_host *host = nand_get_controller_data(chip); + struct brcmnand_cfg *cfg = &host->hwcfg; + int sas = cfg->spare_area_size << cfg->sector_size_1k; + + if (section > 1 || sas - chip->ecc.bytes < 6 || + (section && sas - chip->ecc.bytes == 6)) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 6; + oobregion->length = sas - chip->ecc.bytes - 6; + } + + return 0; +} + +static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .free = brcmnand_bch_ooblayout_free_lp, +}; + +static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = { + .ecc = brcmnand_bch_ooblayout_ecc, + .free = brcmnand_bch_ooblayout_free_sp, +}; + +static int brcmstb_choose_ecc_layout(struct brcmnand_host *host) +{ + struct brcmnand_cfg *p = &host->hwcfg; + struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct nand_ecc_ctrl *ecc = &host->chip.ecc; + unsigned int ecc_level = p->ecc_level; + int sas = p->spare_area_size << p->sector_size_1k; + int sectors = p->page_size / (512 << p->sector_size_1k); + + if (p->sector_size_1k) + ecc_level <<= 1; + + if (is_hamming_ecc(p)) { + ecc->bytes = 3 * sectors; + mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops); + return 0; } /* @@ -837,70 +943,20 @@ static struct nand_ecclayout *brcmnand_create_layout(int ecc_level, * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8) * But we will just be conservative. */ - req = DIV_ROUND_UP(ecc_level * 14, 8); - if (req >= sas) { + ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8); + if (p->page_size == 512) + mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops); + else + mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops); + + if (ecc->bytes >= sas) { dev_err(&host->pdev->dev, "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n", - req, sas); - return NULL; + ecc->bytes, sas); + return -EINVAL; } - layout->eccbytes = req * sectors; - for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) { - for (j = sas - req; j < sas && idx1 < - MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++) - layout->eccpos[idx1] = i * sas + j; - - /* First sector of each page may have BBI */ - if (i == 0) { - if (cfg->page_size == 512 && (sas - req >= 6)) { - /* Small-page NAND use byte 6 for BBI */ - layout->oobfree[idx2].offset = 0; - layout->oobfree[idx2].length = 5; - idx2++; - if (sas - req > 6) { - layout->oobfree[idx2].offset = 6; - layout->oobfree[idx2].length = - sas - req - 6; - idx2++; - } - } else if (sas > req + 1) { - layout->oobfree[idx2].offset = i * sas + 1; - layout->oobfree[idx2].length = sas - req - 1; - idx2++; - } - } else if (sas > req) { - layout->oobfree[idx2].offset = i * sas; - layout->oobfree[idx2].length = sas - req; - idx2++; - } - /* Leave zero-terminated entry for OOBFREE */ - if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE || - idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1) - break; - } - - return layout; -} - -static struct nand_ecclayout *brcmstb_choose_ecc_layout( - struct brcmnand_host *host) -{ - struct nand_ecclayout *layout; - struct brcmnand_cfg *p = &host->hwcfg; - unsigned int ecc_level = p->ecc_level; - - if (p->sector_size_1k) - ecc_level <<= 1; - - layout = brcmnand_create_layout(ecc_level, host); - if (!layout) { - dev_err(&host->pdev->dev, - "no proper ecc_layout for this NAND cfg\n"); - return NULL; - } - - return layout; + return 0; } static void brcmnand_wp(struct mtd_info *mtd, int wp) @@ -2010,9 +2066,9 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) /* only use our internal HW threshold */ mtd->bitflip_threshold = 1; - chip->ecc.layout = brcmstb_choose_ecc_layout(host); - if (!chip->ecc.layout) - return -ENXIO; + ret = brcmstb_choose_ecc_layout(host); + if (ret) + return ret; if (nand_scan_tail(mtd)) return -ENXIO; From a8ed6e66f3b04d804fae425023aa4e7615dadab7 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:59:47 +0100 Subject: [PATCH 093/155] mtd: nand: cafe: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/cafe_nand.c | 44 ++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index e553aff68987..0b0c93702abb 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -459,10 +459,37 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, return max_bitflips; } -static struct nand_ecclayout cafe_oobinfo_2048 = { - .eccbytes = 14, - .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, - .oobfree = {{14, 50}} +static int cafe_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int cafe_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = chip->ecc.total; + oobregion->length = mtd->oobsize - chip->ecc.total; + + return 0; +} + +static const struct mtd_ooblayout_ops cafe_ooblayout_ops = { + .ecc = cafe_ooblayout_ecc, + .free = cafe_ooblayout_free, }; /* Ick. The BBT code really ought to be able to work this bit out @@ -494,12 +521,6 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_2048 = { .pattern = cafe_mirror_pattern_2048 }; -static struct nand_ecclayout cafe_oobinfo_512 = { - .eccbytes = 14, - .eccpos = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, - .oobfree = {{14, 2}} -}; - static struct nand_bbt_descr cafe_bbt_main_descr_512 = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION, @@ -743,12 +764,11 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe->ctl2 |= 1<<29; /* 2KiB page size */ /* Set up ECC according to the type of chip we found */ + mtd_set_ooblayout(mtd, &cafe_ooblayout_ops); if (mtd->writesize == 2048) { - cafe->nand.ecc.layout = &cafe_oobinfo_2048; cafe->nand.bbt_td = &cafe_bbt_main_descr_2048; cafe->nand.bbt_md = &cafe_bbt_mirror_descr_2048; } else if (mtd->writesize == 512) { - cafe->nand.ecc.layout = &cafe_oobinfo_512; cafe->nand.bbt_td = &cafe_bbt_main_descr_512; cafe->nand.bbt_md = &cafe_bbt_mirror_descr_512; } else { From e4aacaa1b175cd53db0c6c7f033bb0fe02df6ea5 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:59:58 +0100 Subject: [PATCH 094/155] mtd: nand: davinci: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/davinci_nand.c | 118 ++++++++++++-------------------- 1 file changed, 44 insertions(+), 74 deletions(-) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 8cb821b6686e..fe3fd29af435 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -54,7 +54,6 @@ */ struct davinci_nand_info { struct nand_chip chip; - struct nand_ecclayout ecclayout; struct device *dev; struct clk *clk; @@ -480,63 +479,46 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd) * ten ECC bytes plus the manufacturer's bad block marker byte, and * and not overlapping the default BBT markers. */ -static struct nand_ecclayout hwecc4_small = { - .eccbytes = 10, - .eccpos = { 0, 1, 2, 3, 4, - /* offset 5 holds the badblock marker */ - 6, 7, - 13, 14, 15, }, - .oobfree = { - {.offset = 8, .length = 5, }, - {.offset = 16, }, - }, -}; +static int hwecc4_ooblayout_small_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 2) + return -ERANGE; -/* An ECC layout for using 4-bit ECC with large-page (2048bytes) flash, - * storing ten ECC bytes plus the manufacturer's bad block marker byte, - * and not overlapping the default BBT markers. - */ -static struct nand_ecclayout hwecc4_2048 = { - .eccbytes = 40, - .eccpos = { - /* at the end of spare sector */ - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - }, - .oobfree = { - /* 2 bytes at offset 0 hold manufacturer badblock markers */ - {.offset = 2, .length = 22, }, - /* 5 bytes at offset 8 hold BBT markers */ - /* 8 bytes at offset 16 hold JFFS2 clean markers */ - }, -}; + if (!section) { + oobregion->offset = 0; + oobregion->length = 5; + } else if (section == 1) { + oobregion->offset = 6; + oobregion->length = 2; + } else { + oobregion->offset = 13; + oobregion->length = 3; + } -/* - * An ECC layout for using 4-bit ECC with large-page (4096bytes) flash, - * storing ten ECC bytes plus the manufacturer's bad block marker byte, - * and not overlapping the default BBT markers. - */ -static struct nand_ecclayout hwecc4_4096 = { - .eccbytes = 80, - .eccpos = { - /* at the end of spare sector */ - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, - 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, - 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - }, - .oobfree = { - /* 2 bytes at offset 0 hold manufacturer badblock markers */ - {.offset = 2, .length = 46, }, - /* 5 bytes at offset 8 hold BBT markers */ - /* 8 bytes at offset 16 hold JFFS2 clean markers */ - }, + return 0; +} + +static int hwecc4_ooblayout_small_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + if (!section) { + oobregion->offset = 8; + oobregion->length = 5; + } else { + oobregion->offset = 16; + oobregion->length = mtd->oobsize - 16; + } + + return 0; +} + +static const struct mtd_ooblayout_ops hwecc4_small_ooblayout_ops = { + .ecc = hwecc4_ooblayout_small_ecc, + .free = hwecc4_ooblayout_small_free, }; #if defined(CONFIG_OF) @@ -805,26 +787,14 @@ static int nand_davinci_probe(struct platform_device *pdev) * table marker fits in the free bytes. */ if (chunks == 1) { - info->ecclayout = hwecc4_small; - info->ecclayout.oobfree[1].length = mtd->oobsize - 16; - goto syndrome_done; - } - if (chunks == 4) { - info->ecclayout = hwecc4_2048; + mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops); + } else if (chunks == 4 || chunks == 8) { + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; - goto syndrome_done; + } else { + ret = -EIO; + goto err; } - if (chunks == 8) { - info->ecclayout = hwecc4_4096; - info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST; - goto syndrome_done; - } - - ret = -EIO; - goto err; - -syndrome_done: - info->chip.ecc.layout = &info->ecclayout; } ret = nand_scan_tail(mtd); From 14fad62b688fefd2dc032e98968d4f4a69adefe9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:00:11 +0100 Subject: [PATCH 095/155] mtd: nand: denali: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 50 +++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 30bf5f690f78..0476ae8776d9 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1374,13 +1374,41 @@ static void denali_hw_init(struct denali_nand_info *denali) * correction */ #define ECC_8BITS 14 -static struct nand_ecclayout nand_8bit_oob = { - .eccbytes = 14, -}; - #define ECC_15BITS 26 -static struct nand_ecclayout nand_15bit_oob = { - .eccbytes = 26, + +static int denali_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = denali->bbtskipbytes; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int denali_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = chip->ecc.total + denali->bbtskipbytes; + oobregion->length = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops denali_ooblayout_ops = { + .ecc = denali_ooblayout_ecc, + .free = denali_ooblayout_free, }; static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; @@ -1561,7 +1589,6 @@ int denali_init(struct denali_nand_info *denali) ECC_SECTOR_SIZE)))) { /* if MLC OOB size is large enough, use 15bit ECC*/ denali->nand.ecc.strength = 15; - denali->nand.ecc.layout = &nand_15bit_oob; denali->nand.ecc.bytes = ECC_15BITS; iowrite32(15, denali->flash_reg + ECC_CORRECTION); } else if (mtd->oobsize < (denali->bbtskipbytes + @@ -1571,20 +1598,13 @@ int denali_init(struct denali_nand_info *denali) goto failed_req_irq; } else { denali->nand.ecc.strength = 8; - denali->nand.ecc.layout = &nand_8bit_oob; denali->nand.ecc.bytes = ECC_8BITS; iowrite32(8, denali->flash_reg + ECC_CORRECTION); } + mtd_set_ooblayout(mtd, &denali_ooblayout_ops); denali->nand.ecc.bytes *= denali->devnum; denali->nand.ecc.strength *= denali->devnum; - denali->nand.ecc.layout->eccbytes *= - mtd->writesize / ECC_SECTOR_SIZE; - denali->nand.ecc.layout->oobfree[0].offset = - denali->bbtskipbytes + denali->nand.ecc.layout->eccbytes; - denali->nand.ecc.layout->oobfree[0].length = - mtd->oobsize - denali->nand.ecc.layout->eccbytes - - denali->bbtskipbytes; /* * Let driver know the total blocks number and how many blocks From 68c1b754822b01153359d77bcee94b9a423d5bb8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:00:23 +0100 Subject: [PATCH 096/155] mtd: nand: diskonchip: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/diskonchip.c | 60 ++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 547c1002941d..a023ab9e9cbf 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -950,20 +950,50 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, //u_char mydatabuf[528]; -/* The strange out-of-order .oobfree list below is a (possibly unneeded) - * attempt to retain compatibility. It used to read: - * .oobfree = { {8, 8} } - * Since that leaves two bytes unusable, it was changed. But the following - * scheme might affect existing jffs2 installs by moving the cleanmarker: - * .oobfree = { {6, 10} } - * jffs2 seems to handle the above gracefully, but the current scheme seems - * safer. The only problem with it is that any code that parses oobfree must - * be able to handle out-of-order segments. - */ -static struct nand_ecclayout doc200x_oobinfo = { - .eccbytes = 6, - .eccpos = {0, 1, 2, 3, 4, 5}, - .oobfree = {{8, 8}, {6, 2}} +static int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = 6; + + return 0; +} + +static int doc200x_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + /* + * The strange out-of-order free bytes definition is a (possibly + * unneeded) attempt to retain compatibility. It used to read: + * .oobfree = { {8, 8} } + * Since that leaves two bytes unusable, it was changed. But the + * following scheme might affect existing jffs2 installs by moving the + * cleanmarker: + * .oobfree = { {6, 10} } + * jffs2 seems to handle the above gracefully, but the current scheme + * seems safer. The only problem with it is that any code retrieving + * free bytes position must be able to handle out-of-order segments. + */ + if (!section) { + oobregion->offset = 8; + oobregion->length = 8; + } else { + oobregion->offset = 6; + oobregion->length = 2; + } + + return 0; +} + +static const struct mtd_ooblayout_ops doc200x_ooblayout_ops = { + .ecc = doc200x_ooblayout_ecc, + .free = doc200x_ooblayout_free, }; /* Find the (I)NFTL Media Header, and optionally also the mirror media header. @@ -1537,6 +1567,7 @@ static int __init doc_probe(unsigned long physadr) nand->bbt_md = nand->bbt_td + 1; mtd->owner = THIS_MODULE; + mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops); nand_set_controller_data(nand, doc); nand->select_chip = doc200x_select_chip; @@ -1548,7 +1579,6 @@ static int __init doc_probe(unsigned long physadr) nand->ecc.calculate = doc200x_calculate_ecc; nand->ecc.correct = doc200x_correct_data; - nand->ecc.layout = &doc200x_oobinfo; nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.size = 512; nand->ecc.bytes = 6; From 6b75065e0d8ca46f98984b5dc30e9a07e7c9a475 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:00:35 +0100 Subject: [PATCH 097/155] mtd: nand: docg4: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/docg4.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index d86a60e1bbcb..47316998017f 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -222,10 +222,33 @@ struct docg4_priv { * Bytes 8 - 14 are hw-generated ecc covering entire page + oob bytes 0 - 14. * Byte 15 (the last) is used by the driver as a "page written" flag. */ -static struct nand_ecclayout docg4_oobinfo = { - .eccbytes = 9, - .eccpos = {7, 8, 9, 10, 11, 12, 13, 14, 15}, - .oobfree = { {.offset = 2, .length = 5} } +static int docg4_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 7; + oobregion->length = 9; + + return 0; +} + +static int docg4_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 2; + oobregion->length = 5; + + return 0; +} + +static const struct mtd_ooblayout_ops docg4_ooblayout_ops = { + .ecc = docg4_ooblayout_ecc, + .free = docg4_ooblayout_free, }; /* @@ -1209,6 +1232,7 @@ static void __init init_mtd_structs(struct mtd_info *mtd) mtd->writesize = DOCG4_PAGE_SIZE; mtd->erasesize = DOCG4_BLOCK_SIZE; mtd->oobsize = DOCG4_OOB_SIZE; + mtd_set_ooblayout(mtd, &docg4_ooblayout_ops); nand->chipsize = DOCG4_CHIP_SIZE; nand->chip_shift = DOCG4_CHIP_SHIFT; nand->bbt_erase_shift = nand->phys_erase_shift = DOCG4_ERASE_SHIFT; @@ -1217,7 +1241,6 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->pagemask = 0x3ffff; nand->badblockpos = NAND_LARGE_BADBLOCK_POS; nand->badblockbits = 8; - nand->ecc.layout = &docg4_oobinfo; nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.size = DOCG4_PAGE_SIZE; nand->ecc.prepad = 8; From c2e197b0198c753e9a17557d16be79bf910525a2 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:01:04 +0100 Subject: [PATCH 098/155] mtd: nand: fsl_elbc: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsl_elbc_nand.c | 83 ++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 059d5f7ec124..487eae07fefc 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -79,32 +79,53 @@ struct fsl_elbc_fcm_ctrl { /* These map to the positions used by the FCM hardware ECC generator */ -/* Small Page FLASH with FMR[ECCM] = 0 */ -static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { - .eccbytes = 3, - .eccpos = {6, 7, 8}, - .oobfree = { {0, 5}, {9, 7} }, -}; +static int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); -/* Small Page FLASH with FMR[ECCM] = 1 */ -static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { - .eccbytes = 3, - .eccpos = {8, 9, 10}, - .oobfree = { {0, 5}, {6, 2}, {11, 5} }, -}; + if (section >= chip->ecc.steps) + return -ERANGE; -/* Large Page FLASH with FMR[ECCM] = 0 */ -static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { - .eccbytes = 12, - .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, - .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }, -}; + oobregion->offset = (16 * section) + 6; + if (priv->fmr & FMR_ECCM) + oobregion->offset += 2; -/* Large Page FLASH with FMR[ECCM] = 1 */ -static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { - .eccbytes = 12, - .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, - .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }, + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); + + if (section > chip->ecc.steps) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + if (mtd->writesize > 512) + oobregion->offset++; + oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5; + } else { + oobregion->offset = (16 * section) - + ((priv->fmr & FMR_ECCM) ? 5 : 7); + if (section < chip->ecc.steps) + oobregion->length = 13; + else + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = { + .ecc = fsl_elbc_ooblayout_ecc, + .free = fsl_elbc_ooblayout_free, }; /* @@ -657,8 +678,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) chip->ecc.bytes); dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n", chip->ecc.total); - dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n", - chip->ecc.layout); + dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n", + mtd->ooblayout); dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size); dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n", @@ -675,14 +696,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) } else if (mtd->writesize == 2048) { priv->page_size = 1; setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); - /* adjust ecc setup if needed */ - if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == - BR_DECC_CHK_GEN) { - chip->ecc.size = 512; - chip->ecc.layout = (priv->fmr & FMR_ECCM) ? - &fsl_elbc_oob_lp_eccm1 : - &fsl_elbc_oob_lp_eccm0; - } } else { dev_err(priv->dev, "fsl_elbc_init: page size %d is not supported\n", @@ -780,9 +793,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == BR_DECC_CHK_GEN) { chip->ecc.mode = NAND_ECC_HW; - /* put in small page settings and adjust later if needed */ - chip->ecc.layout = (priv->fmr & FMR_ECCM) ? - &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0; + mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops); chip->ecc.size = 512; chip->ecc.bytes = 3; chip->ecc.strength = 1; From caf5129e93c510d8f3306f894988e52a8369c766 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 9 Feb 2016 17:01:57 +0100 Subject: [PATCH 099/155] mtd: nand: fsl_ifc: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsl_ifc_nand.c | 230 +++++++++----------------------- 1 file changed, 66 insertions(+), 164 deletions(-) diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 1d922a0f7436..864aa12a1a13 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -67,136 +67,6 @@ struct fsl_ifc_nand_ctrl { static struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; -/* 512-byte page with 4-bit ECC, 8-bit */ -static struct nand_ecclayout oob_512_8bit_ecc4 = { - .eccbytes = 8, - .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, - .oobfree = { {0, 5}, {6, 2} }, -}; - -/* 512-byte page with 4-bit ECC, 16-bit */ -static struct nand_ecclayout oob_512_16bit_ecc4 = { - .eccbytes = 8, - .eccpos = {8, 9, 10, 11, 12, 13, 14, 15}, - .oobfree = { {2, 6}, }, -}; - -/* 2048-byte page size with 4-bit ECC */ -static struct nand_ecclayout oob_2048_ecc4 = { - .eccbytes = 32, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - }, - .oobfree = { {2, 6}, {40, 24} }, -}; - -/* 4096-byte page size with 4-bit ECC */ -static struct nand_ecclayout oob_4096_ecc4 = { - .eccbytes = 64, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - }, - .oobfree = { {2, 6}, {72, 56} }, -}; - -/* 4096-byte page size with 8-bit ECC -- requires 218-byte OOB */ -static struct nand_ecclayout oob_4096_ecc8 = { - .eccbytes = 128, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, - }, - .oobfree = { {2, 6}, {136, 82} }, -}; - -/* 8192-byte page size with 4-bit ECC */ -static struct nand_ecclayout oob_8192_ecc4 = { - .eccbytes = 128, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, - }, - .oobfree = { {2, 6}, {136, 208} }, -}; - -/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */ -static struct nand_ecclayout oob_8192_ecc8 = { - .eccbytes = 256, - .eccpos = { - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, - 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, - 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, - 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, - 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 198, 199, - 200, 201, 202, 203, 204, 205, 206, 207, - 208, 209, 210, 211, 212, 213, 214, 215, - 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 227, 228, 229, 230, 231, - 232, 233, 234, 235, 236, 237, 238, 239, - 240, 241, 242, 243, 244, 245, 246, 247, - 248, 249, 250, 251, 252, 253, 254, 255, - 256, 257, 258, 259, 260, 261, 262, 263, - }, - .oobfree = { {2, 6}, {264, 80} }, -}; - /* * Generic flash bbt descriptors */ @@ -223,6 +93,57 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern, }; +static int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = 8; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section > 1) + return -ERANGE; + + if (mtd->writesize == 512 && + !(chip->options & NAND_BUSWIDTH_16)) { + if (!section) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 6; + oobregion->length = 2; + } + + return 0; + } + + if (!section) { + oobregion->offset = 2; + oobregion->length = 6; + } else { + oobregion->offset = chip->ecc.total + 8; + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = { + .ecc = fsl_ifc_ooblayout_ecc, + .free = fsl_ifc_ooblayout_free, +}; + /* * Set up the IFC hardware block and page address fields, and the ifc nand * structure addr field to point to the correct IFC buffer in memory @@ -812,8 +733,8 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) chip->ecc.bytes); dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__, chip->ecc.total); - dev_dbg(priv->dev, "%s: nand->ecc.layout = %p\n", __func__, - chip->ecc.layout); + dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__, + mtd->ooblayout); dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags); dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size); dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__, @@ -885,7 +806,6 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; struct nand_chip *chip = &priv->chip; struct mtd_info *mtd = nand_to_mtd(&priv->chip); - struct nand_ecclayout *layout; u32 csor; /* Fill in fsl_ifc_mtd structure */ @@ -931,18 +851,9 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); - /* 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: - if (chip->options & NAND_BUSWIDTH_16) { - layout = &oob_512_16bit_ecc4; - } else { - layout = &oob_512_8bit_ecc4; - + if (!(chip->options & NAND_BUSWIDTH_16)) { /* Avoid conflict with bad block marker */ bbt_main_descr.offs = 0; bbt_mirror_descr.offs = 0; @@ -952,35 +863,16 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) break; case CSOR_NAND_PGS_2K: - layout = &oob_2048_ecc4; priv->bufnum_mask = 3; break; case CSOR_NAND_PGS_4K: - if ((csor & CSOR_NAND_ECC_MODE_MASK) == - CSOR_NAND_ECC_MODE_4) { - layout = &oob_4096_ecc4; - } else { - layout = &oob_4096_ecc8; - chip->ecc.bytes = 16; - chip->ecc.strength = 8; - } - priv->bufnum_mask = 1; break; case CSOR_NAND_PGS_8K: - if ((csor & CSOR_NAND_ECC_MODE_MASK) == - CSOR_NAND_ECC_MODE_4) { - layout = &oob_8192_ecc4; - } else { - layout = &oob_8192_ecc8; - chip->ecc.bytes = 16; - chip->ecc.strength = 8; - } - priv->bufnum_mask = 0; - break; + break; default: dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); @@ -990,7 +882,17 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ if (csor & CSOR_NAND_ECC_DEC_EN) { chip->ecc.mode = NAND_ECC_HW; - chip->ecc.layout = layout; + mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); + + /* Hardware generates ECC per 512 Bytes */ + chip->ecc.size = 512; + if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) { + chip->ecc.bytes = 8; + chip->ecc.strength = 4; + } else { + chip->ecc.bytes = 16; + chip->ecc.strength = 8; + } } else { chip->ecc.mode = NAND_ECC_SOFT; } From 22b469577625056e76b7ddb670eea3dc69210eb6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:01:42 +0100 Subject: [PATCH 100/155] mtd: nand: fsmc: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsmc_nand.c | 298 ++++++++++------------------------- 1 file changed, 82 insertions(+), 216 deletions(-) diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 1bdcd4fa26d4..275a98ca4f6a 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -39,212 +39,6 @@ #include #include -static struct nand_ecclayout fsmc_ecc1_128_layout = { - .eccbytes = 24, - .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, - 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, - .oobfree = { - {.offset = 8, .length = 8}, - {.offset = 24, .length = 8}, - {.offset = 40, .length = 8}, - {.offset = 56, .length = 8}, - {.offset = 72, .length = 8}, - {.offset = 88, .length = 8}, - {.offset = 104, .length = 8}, - {.offset = 120, .length = 8} - } -}; - -static struct nand_ecclayout fsmc_ecc1_64_layout = { - .eccbytes = 12, - .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52}, - .oobfree = { - {.offset = 8, .length = 8}, - {.offset = 24, .length = 8}, - {.offset = 40, .length = 8}, - {.offset = 56, .length = 8}, - } -}; - -static struct nand_ecclayout fsmc_ecc1_16_layout = { - .eccbytes = 3, - .eccpos = {2, 3, 4}, - .oobfree = { - {.offset = 8, .length = 8}, - } -}; - -/* - * ECC4 layout for NAND of pagesize 8192 bytes & OOBsize 256 bytes. 13*16 bytes - * of OB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 46 - * bytes are free for use. - */ -static struct nand_ecclayout fsmc_ecc4_256_layout = { - .eccbytes = 208, - .eccpos = { 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, - 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, - 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, - 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, - 66, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, - 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, - 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, - 114, 115, 116, 117, 118, 119, 120, - 121, 122, 123, 124, 125, 126, - 130, 131, 132, 133, 134, 135, 136, - 137, 138, 139, 140, 141, 142, - 146, 147, 148, 149, 150, 151, 152, - 153, 154, 155, 156, 157, 158, - 162, 163, 164, 165, 166, 167, 168, - 169, 170, 171, 172, 173, 174, - 178, 179, 180, 181, 182, 183, 184, - 185, 186, 187, 188, 189, 190, - 194, 195, 196, 197, 198, 199, 200, - 201, 202, 203, 204, 205, 206, - 210, 211, 212, 213, 214, 215, 216, - 217, 218, 219, 220, 221, 222, - 226, 227, 228, 229, 230, 231, 232, - 233, 234, 235, 236, 237, 238, - 242, 243, 244, 245, 246, 247, 248, - 249, 250, 251, 252, 253, 254 - }, - .oobfree = { - {.offset = 15, .length = 3}, - {.offset = 31, .length = 3}, - {.offset = 47, .length = 3}, - {.offset = 63, .length = 3}, - {.offset = 79, .length = 3}, - {.offset = 95, .length = 3}, - {.offset = 111, .length = 3}, - {.offset = 127, .length = 3}, - {.offset = 143, .length = 3}, - {.offset = 159, .length = 3}, - {.offset = 175, .length = 3}, - {.offset = 191, .length = 3}, - {.offset = 207, .length = 3}, - {.offset = 223, .length = 3}, - {.offset = 239, .length = 3}, - {.offset = 255, .length = 1} - } -}; - -/* - * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes - * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 - * bytes are free for use. - */ -static struct nand_ecclayout fsmc_ecc4_224_layout = { - .eccbytes = 104, - .eccpos = { 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, - 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, - 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, - 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, - 66, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, - 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, - 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, - 114, 115, 116, 117, 118, 119, 120, - 121, 122, 123, 124, 125, 126 - }, - .oobfree = { - {.offset = 15, .length = 3}, - {.offset = 31, .length = 3}, - {.offset = 47, .length = 3}, - {.offset = 63, .length = 3}, - {.offset = 79, .length = 3}, - {.offset = 95, .length = 3}, - {.offset = 111, .length = 3}, - {.offset = 127, .length = 97} - } -}; - -/* - * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 128 bytes. 13*8 bytes - * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 22 - * bytes are free for use. - */ -static struct nand_ecclayout fsmc_ecc4_128_layout = { - .eccbytes = 104, - .eccpos = { 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, - 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, - 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, - 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, - 66, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, - 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, - 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, - 114, 115, 116, 117, 118, 119, 120, - 121, 122, 123, 124, 125, 126 - }, - .oobfree = { - {.offset = 15, .length = 3}, - {.offset = 31, .length = 3}, - {.offset = 47, .length = 3}, - {.offset = 63, .length = 3}, - {.offset = 79, .length = 3}, - {.offset = 95, .length = 3}, - {.offset = 111, .length = 3}, - {.offset = 127, .length = 1} - } -}; - -/* - * ECC4 layout for NAND of pagesize 2048 bytes & OOBsize 64 bytes. 13*4 bytes of - * OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block and 10 - * bytes are free for use. - */ -static struct nand_ecclayout fsmc_ecc4_64_layout = { - .eccbytes = 52, - .eccpos = { 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, - 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, - 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, - 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, - }, - .oobfree = { - {.offset = 15, .length = 3}, - {.offset = 31, .length = 3}, - {.offset = 47, .length = 3}, - {.offset = 63, .length = 1}, - } -}; - -/* - * ECC4 layout for NAND of pagesize 512 bytes & OOBsize 16 bytes. 13 bytes of - * OOB size is reserved for ECC, Byte no. 4 & 5 reserved for bad block and One - * byte is free for use. - */ -static struct nand_ecclayout fsmc_ecc4_16_layout = { - .eccbytes = 13, - .eccpos = { 0, 1, 2, 3, 6, 7, 8, - 9, 10, 11, 12, 13, 14 - }, - .oobfree = { - {.offset = 15, .length = 1}, - } -}; - /* * ECC placement definitions in oobfree type format. * There are 13 bytes of ecc for every 512 byte block and it has to be read @@ -274,6 +68,84 @@ static struct fsmc_eccplace fsmc_ecc4_sp_place = { } }; +static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 2; + oobregion->length = 3; + + return 0; +} + +static int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 8; + + if (section < chip->ecc.steps - 1) + oobregion->length = 8; + else + oobregion->length = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = { + .ecc = fsmc_ecc1_ooblayout_ecc, + .free = fsmc_ecc1_ooblayout_free, +}; + +static int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->length = chip->ecc.bytes; + + if (!section && mtd->writesize <= 512) + oobregion->offset = 0; + else + oobregion->offset = (section * 16) + 2; + + return 0; +} + +static int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 15; + + if (section < chip->ecc.steps - 1) + oobregion->length = 3; + else + oobregion->length = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = { + .ecc = fsmc_ecc4_ooblayout_ecc, + .free = fsmc_ecc4_ooblayout_free, +}; + /** * struct fsmc_nand_data - structure for FSMC NAND device state * @@ -1084,23 +956,18 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (AMBA_REV_BITS(host->pid) >= 8) { switch (mtd->oobsize) { case 16: - nand->ecc.layout = &fsmc_ecc4_16_layout; host->ecc_place = &fsmc_ecc4_sp_place; break; case 64: - nand->ecc.layout = &fsmc_ecc4_64_layout; host->ecc_place = &fsmc_ecc4_lp_place; break; case 128: - nand->ecc.layout = &fsmc_ecc4_128_layout; host->ecc_place = &fsmc_ecc4_lp_place; break; case 224: - nand->ecc.layout = &fsmc_ecc4_224_layout; host->ecc_place = &fsmc_ecc4_lp_place; break; case 256: - nand->ecc.layout = &fsmc_ecc4_256_layout; host->ecc_place = &fsmc_ecc4_lp_place; break; default: @@ -1109,6 +976,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) ret = -EINVAL; goto err_probe; } + + mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops); } else { switch (nand->ecc.mode) { case NAND_ECC_HW: @@ -1135,13 +1004,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (nand->ecc.mode != NAND_ECC_SOFT_BCH) { switch (mtd->oobsize) { case 16: - nand->ecc.layout = &fsmc_ecc1_16_layout; - break; case 64: - nand->ecc.layout = &fsmc_ecc1_64_layout; - break; case 128: - nand->ecc.layout = &fsmc_ecc1_128_layout; + mtd_set_ooblayout(mtd, + &fsmc_ecc1_ooblayout_ops); break; default: dev_warn(&pdev->dev, From 04a123a99f089773fc6ee6e21af5f831d87fe362 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 9 Feb 2016 15:01:21 +0100 Subject: [PATCH 101/155] mtd: nand: fsmc: get rid of the fsmc_nand_eccplace struct Now that mtd_ooblayout_ecc() returns the ECC byte position using the OOB free method, we can get rid of the fsmc_nand_eccplace struct. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsmc_nand.c | 60 +++++++++--------------------------- include/linux/mtd/fsmc.h | 18 ----------- 2 files changed, 15 insertions(+), 63 deletions(-) diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 275a98ca4f6a..13720405ec81 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -39,35 +39,6 @@ #include #include -/* - * ECC placement definitions in oobfree type format. - * There are 13 bytes of ecc for every 512 byte block and it has to be read - * consecutively and immediately after the 512 byte data block for hardware to - * generate the error bit offsets in 512 byte data. - * Managing the ecc bytes in the following way makes it easier for software to - * read ecc bytes consecutive to data bytes. This way is similar to - * oobfree structure maintained already in generic nand driver - */ -static struct fsmc_eccplace fsmc_ecc4_lp_place = { - .eccplace = { - {.offset = 2, .length = 13}, - {.offset = 18, .length = 13}, - {.offset = 34, .length = 13}, - {.offset = 50, .length = 13}, - {.offset = 66, .length = 13}, - {.offset = 82, .length = 13}, - {.offset = 98, .length = 13}, - {.offset = 114, .length = 13} - } -}; - -static struct fsmc_eccplace fsmc_ecc4_sp_place = { - .eccplace = { - {.offset = 0, .length = 4}, - {.offset = 6, .length = 9} - } -}; - static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { @@ -105,6 +76,12 @@ static const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = { .free = fsmc_ecc1_ooblayout_free, }; +/* + * ECC placement definitions in oobfree type format. + * There are 13 bytes of ecc for every 512 byte block and it has to be read + * consecutively and immediately after the 512 byte data block for hardware to + * generate the error bit offsets in 512 byte data. + */ static int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { @@ -155,7 +132,6 @@ static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = { * @partitions: Partition info for a NAND Flash. * @nr_partitions: Total number of partition of a NAND flash. * - * @ecc_place: ECC placing locations in oobfree type format. * @bank: Bank number for probed device. * @clk: Clock structure for FSMC. * @@ -175,7 +151,6 @@ struct fsmc_nand_data { struct mtd_partition *partitions; unsigned int nr_partitions; - struct fsmc_eccplace *ecc_place; unsigned int bank; struct device *dev; enum access_mode mode; @@ -582,8 +557,6 @@ static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf, static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - struct fsmc_nand_data *host = mtd_to_fsmc(mtd); - struct fsmc_eccplace *ecc_place = host->ecc_place; int i, j, s, stat, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; @@ -606,9 +579,15 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, p, eccsize); for (j = 0; j < eccbytes;) { - off = ecc_place->eccplace[group].offset; - len = ecc_place->eccplace[group].length; - group++; + struct mtd_oob_region oobregion; + int ret; + + ret = mtd_ooblayout_ecc(mtd, group++, &oobregion); + if (ret) + return ret; + + off = oobregion.offset; + len = oobregion.length; /* * length is intentionally kept a higher multiple of 2 @@ -956,19 +935,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (AMBA_REV_BITS(host->pid) >= 8) { switch (mtd->oobsize) { case 16: - host->ecc_place = &fsmc_ecc4_sp_place; - break; case 64: - host->ecc_place = &fsmc_ecc4_lp_place; - break; case 128: - host->ecc_place = &fsmc_ecc4_lp_place; - break; case 224: - host->ecc_place = &fsmc_ecc4_lp_place; - break; case 256: - host->ecc_place = &fsmc_ecc4_lp_place; break; default: dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", diff --git a/include/linux/mtd/fsmc.h b/include/linux/mtd/fsmc.h index c8be32e9fc49..ad3c3488073c 100644 --- a/include/linux/mtd/fsmc.h +++ b/include/linux/mtd/fsmc.h @@ -103,24 +103,6 @@ #define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ) -/* - * There are 13 bytes of ecc for every 512 byte block in FSMC version 8 - * and it has to be read consecutively and immediately after the 512 - * byte data block for hardware to generate the error bit offsets - * Managing the ecc bytes in the following way is easier. This way is - * similar to oobfree structure maintained already in u-boot nand driver - */ -#define MAX_ECCPLACE_ENTRIES 32 - -struct fsmc_nand_eccplace { - uint8_t offset; - uint8_t length; -}; - -struct fsmc_eccplace { - struct fsmc_nand_eccplace eccplace[MAX_ECCPLACE_ENTRIES]; -}; - struct fsmc_nand_timings { uint8_t tclr; uint8_t tar; From 3f158e47dfe92ee53f1e09c5fa76a07116100f02 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:01:54 +0100 Subject: [PATCH 102/155] mtd: nand: gpmi: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon Acked-by: Han Xu Tested-by: Han Xu --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 52 ++++++++++++++++++++------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 4721008e0289..c50523b085ef 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -47,10 +47,44 @@ static struct nand_bbt_descr gpmi_bbt_descr = { * We may change the layout if we can get the ECC info from the datasheet, * else we will use all the (page + OOB). */ -static struct nand_ecclayout gpmi_hw_ecclayout = { - .eccbytes = 0, - .eccpos = { 0, }, - .oobfree = { {.offset = 0, .length = 0} } +static int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *geo = &this->bch_geometry; + + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = geo->page_size - mtd->writesize; + + return 0; +} + +static int gpmi_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct gpmi_nand_data *this = nand_get_controller_data(chip); + struct bch_geometry *geo = &this->bch_geometry; + + if (section) + return -ERANGE; + + /* The available oob size we have. */ + if (geo->page_size < mtd->writesize + mtd->oobsize) { + oobregion->offset = geo->page_size - mtd->writesize; + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { + .ecc = gpmi_ooblayout_ecc, + .free = gpmi_ooblayout_free, }; static const struct gpmi_devdata gpmi_devdata_imx23 = { @@ -141,7 +175,6 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) struct bch_geometry *geo = &this->bch_geometry; struct nand_chip *chip = &this->nand; struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree; unsigned int block_mark_bit_offset; if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) @@ -229,12 +262,6 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) geo->page_size = mtd->writesize + geo->metadata_size + (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; - /* The available oob size we have. */ - if (geo->page_size < mtd->writesize + mtd->oobsize) { - of->offset = geo->page_size - mtd->writesize; - of->length = mtd->oobsize - of->offset; - } - geo->payload_size = mtd->writesize; geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); @@ -1842,6 +1869,7 @@ static void gpmi_nand_exit(struct gpmi_nand_data *this) static int gpmi_init_last(struct gpmi_nand_data *this) { struct nand_chip *chip = &this->nand; + struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; struct bch_geometry *bch_geo = &this->bch_geometry; int ret; @@ -1863,7 +1891,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this) ecc->mode = NAND_ECC_HW; ecc->size = bch_geo->ecc_chunk_size; ecc->strength = bch_geo->ecc_strength; - ecc->layout = &gpmi_hw_ecclayout; + mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); /* * We only enable the subpage read when: From 2ca9ec9acd3bea9f3160e0e0975c06914da3ae4e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:02:09 +0100 Subject: [PATCH 103/155] mtd: nand: hisi504: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/hisi504_nand.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 96502b624cfb..7bf844c55085 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -631,8 +631,28 @@ static void hisi_nfc_host_init(struct hinfc_host *host) hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN); } -static struct nand_ecclayout nand_ecc_2K_16bits = { - .oobfree = { {2, 6} }, +static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + /* FIXME: add ECC bytes position */ + return -ENOTSUPP; +} + +static int hisi_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 2; + oobregion->length = 6; + + return 0; +} + +static const struct mtd_ooblayout_ops hisi_ooblayout_ops = { + .ecc = hisi_ooblayout_ecc, + .free = hisi_ooblayout_free, }; static int hisi_nfc_ecc_probe(struct hinfc_host *host) @@ -668,7 +688,7 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host) case 16: ecc_bits = 6; if (mtd->writesize == 2048) - chip->ecc.layout = &nand_ecc_2K_16bits; + mtd_set_ooblayout(mtd, &hisi_ooblayout_ops); /* TODO: add more page size support */ break; From 36886be7dd3b48f593d9da21c631d06d5bfae55d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:02:22 +0100 Subject: [PATCH 104/155] mtd: nand: jz4780: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon Tested-by: Harvey Hunt --- drivers/mtd/nand/jz4780_nand.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/jz4780_nand.c index 23a1999ae839..10f249ac148c 100644 --- a/drivers/mtd/nand/jz4780_nand.c +++ b/drivers/mtd/nand/jz4780_nand.c @@ -55,8 +55,6 @@ struct jz4780_nand_chip { struct nand_chip chip; struct list_head chip_list; - struct nand_ecclayout ecclayout; - struct gpio_desc *busy_gpio; struct gpio_desc *wp_gpio; unsigned int reading: 1; @@ -164,8 +162,7 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de struct nand_chip *chip = &nand->chip; struct mtd_info *mtd = nand_to_mtd(chip); struct jz4780_nand_controller *nfc = to_jz4780_nand_controller(chip->controller); - struct nand_ecclayout *layout = &nand->ecclayout; - u32 start, i; + int eccbytes; chip->ecc.bytes = fls((1 + 8) * chip->ecc.size) * (chip->ecc.strength / 8); @@ -200,23 +197,17 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de return 0; /* Generate ECC layout. ECC codes are right aligned in the OOB area. */ - layout->eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes; + eccbytes = mtd->writesize / chip->ecc.size * chip->ecc.bytes; - if (layout->eccbytes > mtd->oobsize - 2) { + if (eccbytes > mtd->oobsize - 2) { dev_err(dev, "invalid ECC config: required %d ECC bytes, but only %d are available", - layout->eccbytes, mtd->oobsize - 2); + eccbytes, mtd->oobsize - 2); return -EINVAL; } - start = mtd->oobsize - layout->eccbytes; - for (i = 0; i < layout->eccbytes; i++) - layout->eccpos[i] = start + i; + mtd->ooblayout = &nand_ooblayout_lp_ops; - layout->oobfree[0].offset = 2; - layout->oobfree[0].length = mtd->oobsize - layout->eccbytes - 2; - - chip->ecc.layout = layout; return 0; } From d50b5239867f02627fc24c4450a87bd9c3267c0a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:02:41 +0100 Subject: [PATCH 105/155] mtd: nand: lpc32xx: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/lpc32xx_mlc.c | 50 ++++++++++++++++++++++------------ drivers/mtd/nand/lpc32xx_slc.c | 41 ++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 25 deletions(-) diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index 8e439787b04e..852388171f20 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -138,22 +138,37 @@ struct lpc32xx_nand_cfg_mlc { unsigned num_parts; }; -static struct nand_ecclayout lpc32xx_nand_oob = { - .eccbytes = 40, - .eccpos = { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, - .oobfree = { - { .offset = 0, - .length = 6, }, - { .offset = 16, - .length = 6, }, - { .offset = 32, - .length = 6, }, - { .offset = 48, - .length = 6, }, - }, +static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = ((section + 1) * 16) - nand_chip->ecc.bytes; + oobregion->length = nand_chip->ecc.bytes; + + return 0; +} + +static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = 16 * section; + oobregion->length = 16 - nand_chip->ecc.bytes; + + return 0; +} + +static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = { + .ecc = lpc32xx_ooblayout_ecc, + .free = lpc32xx_ooblayout_free, }; static struct nand_bbt_descr lpc32xx_nand_bbt = { @@ -712,6 +727,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) nand_chip->ecc.write_oob = lpc32xx_write_oob; nand_chip->ecc.read_oob = lpc32xx_read_oob; nand_chip->ecc.strength = 4; + nand_chip->ecc.bytes = 10; nand_chip->waitfunc = lpc32xx_waitfunc; nand_chip->options = NAND_NO_SUBPAGE_WRITE; @@ -750,7 +766,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.size = 512; - nand_chip->ecc.layout = &lpc32xx_nand_oob; + mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); host->mlcsubpages = mtd->writesize / 512; /* initially clear interrupt status */ diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index 10cf8e6223bc..219dd674dc92 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -146,13 +146,38 @@ * NAND ECC Layout for small page NAND devices * Note: For large and huge page devices, the default layouts are used */ -static struct nand_ecclayout lpc32xx_nand_oob_16 = { - .eccbytes = 6, - .eccpos = {10, 11, 12, 13, 14, 15}, - .oobfree = { - { .offset = 0, .length = 4 }, - { .offset = 6, .length = 4 }, - }, +static int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->length = 6; + oobregion->offset = 10; + + return 0; +} + +static int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + oobregion->length = 4; + } else { + oobregion->offset = 6; + oobregion->length = 4; + } + + return 0; +} + +static const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = { + .ecc = lpc32xx_ooblayout_ecc, + .free = lpc32xx_ooblayout_free, }; static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; @@ -886,7 +911,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) * custom BBT marker layout. */ if (mtd->writesize <= 512) - chip->ecc.layout = &lpc32xx_nand_oob_16; + mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); /* These sizes remain the same regardless of page size */ chip->ecc.size = 256; From a894cf6c5a82c7ddcb1bad82d1e0af3d0ead9a41 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:02:54 +0100 Subject: [PATCH 106/155] mtd: nand: mxc: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 212 ++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 107 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 57b1b74c8fa0..c4e84e180325 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -149,7 +149,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; + const struct mtd_ooblayout_ops *ooblayout; 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); @@ -200,73 +200,6 @@ struct mxc_nand_host { struct mxc_nand_platform_data pdata; }; -/* OOB placement block for use with hardware ecc generation */ -static struct nand_ecclayout nandv1_hw_eccoob_smallpage = { - .eccbytes = 5, - .eccpos = {6, 7, 8, 9, 10}, - .oobfree = {{0, 5}, {12, 4}, } -}; - -static struct nand_ecclayout nandv1_hw_eccoob_largepage = { - .eccbytes = 20, - .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, - 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, - .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, } -}; - -/* OOB description for 512 byte pages with 16 byte OOB */ -static struct nand_ecclayout nandv2_hw_eccoob_smallpage = { - .eccbytes = 1 * 9, - .eccpos = { - 7, 8, 9, 10, 11, 12, 13, 14, 15 - }, - .oobfree = { - {.offset = 0, .length = 5} - } -}; - -/* OOB description for 2048 byte pages with 64 byte OOB */ -static struct nand_ecclayout nandv2_hw_eccoob_largepage = { - .eccbytes = 4 * 9, - .eccpos = { - 7, 8, 9, 10, 11, 12, 13, 14, 15, - 23, 24, 25, 26, 27, 28, 29, 30, 31, - 39, 40, 41, 42, 43, 44, 45, 46, 47, - 55, 56, 57, 58, 59, 60, 61, 62, 63 - }, - .oobfree = { - {.offset = 2, .length = 4}, - {.offset = 16, .length = 7}, - {.offset = 32, .length = 7}, - {.offset = 48, .length = 7} - } -}; - -/* OOB description for 4096 byte pages with 128 byte OOB */ -static struct nand_ecclayout nandv2_hw_eccoob_4k = { - .eccbytes = 8 * 9, - .eccpos = { - 7, 8, 9, 10, 11, 12, 13, 14, 15, - 23, 24, 25, 26, 27, 28, 29, 30, 31, - 39, 40, 41, 42, 43, 44, 45, 46, 47, - 55, 56, 57, 58, 59, 60, 61, 62, 63, - 71, 72, 73, 74, 75, 76, 77, 78, 79, - 87, 88, 89, 90, 91, 92, 93, 94, 95, - 103, 104, 105, 106, 107, 108, 109, 110, 111, - 119, 120, 121, 122, 123, 124, 125, 126, 127, - }, - .oobfree = { - {.offset = 2, .length = 4}, - {.offset = 16, .length = 7}, - {.offset = 32, .length = 7}, - {.offset = 48, .length = 7}, - {.offset = 64, .length = 7}, - {.offset = 80, .length = 7}, - {.offset = 96, .length = 7}, - {.offset = 112, .length = 7}, - } -}; - static const char * const part_probes[] = { "cmdlinepart", "RedBoot", "ofpart", NULL }; @@ -942,6 +875,99 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) } } +static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 6; + oobregion->length = nand_chip->ecc.bytes; + + return 0; +} + +static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section > nand_chip->ecc.steps) + return -ERANGE; + + if (!section) { + if (mtd->writesize <= 512) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 2; + oobregion->length = 4; + } + } else { + oobregion->offset = ((section - 1) * 16) + + nand_chip->ecc.bytes + 6; + if (section < nand_chip->ecc.steps) + oobregion->length = (section * 16) + 6 - + oobregion->offset; + else + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = { + .ecc = mxc_v1_ooblayout_ecc, + .free = mxc_v1_ooblayout_free, +}; + +static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * stepsize) + 7; + oobregion->length = nand_chip->ecc.bytes; + + return 0; +} + +static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; + + if (section > nand_chip->ecc.steps) + return -ERANGE; + + if (!section) { + if (mtd->writesize <= 512) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 2; + oobregion->length = 4; + } + } else { + oobregion->offset = section * stepsize; + oobregion->length = 7; + } + + return 0; +} + +static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = { + .ecc = mxc_v2_ooblayout_ecc, + .free = mxc_v2_ooblayout_free, +}; + /* * v2 and v3 type controllers can do 4bit or 8bit ecc depending * on how much oob the nand chip has. For 8bit ecc we need at least @@ -959,23 +985,6 @@ static int get_eccsize(struct mtd_info *mtd) return 8; } -static void ecc_8bit_layout_4k(struct nand_ecclayout *layout) -{ - int i, j; - - layout->eccbytes = 8*18; - for (i = 0; i < 8; i++) - for (j = 0; j < 18; j++) - layout->eccpos[i*18 + j] = i*26 + j + 7; - - layout->oobfree[0].offset = 2; - layout->oobfree[0].length = 4; - for (i = 1; i < 8; i++) { - layout->oobfree[i].offset = i*26; - layout->oobfree[i].length = 7; - } -} - static void preset_v1(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd_to_nand(mtd); @@ -1269,9 +1278,7 @@ 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 */ + .ooblayout = &mxc_v1_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v1, .irqpending_quirk = 1, @@ -1294,9 +1301,7 @@ static const struct mxc_nand_devtype_data imx27_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 */ + .ooblayout = &mxc_v1_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v1, .irqpending_quirk = 0, @@ -1320,9 +1325,7 @@ 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, + .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v2, .correct_data = mxc_nand_correct_data_v2_v3, .irqpending_quirk = 0, @@ -1346,9 +1349,7 @@ 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 */ + .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v2_v3, .irqpending_quirk = 0, @@ -1373,9 +1374,7 @@ static const struct mxc_nand_devtype_data imx53_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 */ + .ooblayout = &mxc_v2_ooblayout_ops, .select_chip = mxc_nand_select_chip_v1_v3, .correct_data = mxc_nand_correct_data_v2_v3, .irqpending_quirk = 0, @@ -1576,7 +1575,7 @@ static int mxcnd_probe(struct platform_device *pdev) this->select_chip = host->devtype_data->select_chip; this->ecc.size = 512; - this->ecc.layout = host->devtype_data->ecclayout_512; + mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); if (host->pdata.hw_ecc) { this->ecc.calculate = mxc_nand_calculate_ecc; @@ -1650,12 +1649,11 @@ static int mxcnd_probe(struct platform_device *pdev) /* Call preset again, with correct writesize this time */ host->devtype_data->preset(mtd); - if (mtd->writesize == 2048) - this->ecc.layout = host->devtype_data->ecclayout_2k; - else if (mtd->writesize == 4096) { - this->ecc.layout = host->devtype_data->ecclayout_4k; - if (get_eccsize(mtd) == 8) - ecc_8bit_layout_4k(this->ecc.layout); + if (!this->ecc.bytes) { + if (host->eccsize == 8) + this->ecc.bytes = 18; + else if (host->eccsize == 4) + this->ecc.bytes = 9; } /* From e04dbf352820245b54f74200ac97327959f02c56 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:03:04 +0100 Subject: [PATCH 107/155] mtd: nand: omap2: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/omap2.c | 202 +++++++++++++++++++++++---------------- 1 file changed, 121 insertions(+), 81 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 38fe6b8a0b14..55916786d04e 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -178,8 +178,6 @@ struct omap_nand_info { struct gpmc_nand_regs reg; struct gpmc_nand_ops *ops; bool flash_bbt; - /* generated at runtime depending on ECC algorithm and layout selected */ - struct nand_ecclayout oobinfo; /* fields specific for BCHx_HW ECC scheme */ struct device *elm_dev; /* NAND ready gpio */ @@ -1714,20 +1712,115 @@ static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) return 0; } +static int omap_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + struct nand_chip *chip = &info->nand; + int off = BADBLOCK_MARKER_LENGTH; + + if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && + !(chip->options & NAND_BUSWIDTH_16)) + off = 1; + + if (section) + return -ERANGE; + + oobregion->offset = off; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int omap_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct omap_nand_info *info = mtd_to_omap(mtd); + struct nand_chip *chip = &info->nand; + int off = BADBLOCK_MARKER_LENGTH; + + if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && + !(chip->options & NAND_BUSWIDTH_16)) + off = 1; + + if (section) + return -ERANGE; + + off += chip->ecc.total; + if (off >= mtd->oobsize) + return -ERANGE; + + oobregion->offset = off; + oobregion->length = mtd->oobsize - off; + + return 0; +} + +static const struct mtd_ooblayout_ops omap_ooblayout_ops = { + .ecc = omap_ooblayout_ecc, + .free = omap_ooblayout_free, +}; + +static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int off = BADBLOCK_MARKER_LENGTH; + + if (section >= chip->ecc.steps) + return -ERANGE; + + /* + * When SW correction is employed, one OMAP specific marker byte is + * reserved after each ECC step. + */ + oobregion->offset = off + (section * (chip->ecc.bytes + 1)); + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int off = BADBLOCK_MARKER_LENGTH; + + if (section) + return -ERANGE; + + /* + * When SW correction is employed, one OMAP specific marker byte is + * reserved after each ECC step. + */ + off += ((chip->ecc.bytes + 1) * chip->ecc.steps); + if (off >= mtd->oobsize) + return -ERANGE; + + oobregion->offset = off; + oobregion->length = mtd->oobsize - off; + + return 0; +} + +static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = { + .ecc = omap_sw_ooblayout_ecc, + .free = omap_sw_ooblayout_free, +}; + static int omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; struct omap_nand_platform_data *pdata = NULL; struct mtd_info *mtd; struct nand_chip *nand_chip; - struct nand_ecclayout *ecclayout; int err; - int i; dma_cap_mask_t mask; unsigned sig; - unsigned oob_index; struct resource *res; struct device *dev = &pdev->dev; + int min_oobbytes = BADBLOCK_MARKER_LENGTH; + int oobbytes_per_step; info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), GFP_KERNEL); @@ -1915,7 +2008,7 @@ static int omap_nand_probe(struct platform_device *pdev) /* * Bail out earlier to let NAND_ECC_SOFT code create its own - * ecclayout instead of using ours. + * ooblayout instead of using ours. */ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { nand_chip->ecc.mode = NAND_ECC_SOFT; @@ -1923,8 +2016,6 @@ static int omap_nand_probe(struct platform_device *pdev) } /* populate MTD interface based on ECC scheme */ - ecclayout = &info->oobinfo; - nand_chip->ecc.layout = ecclayout; switch (info->ecc_opt) { case OMAP_ECC_HAM1_CODE_HW: pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); @@ -1935,19 +2026,12 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.calculate = omap_calculate_ecc; nand_chip->ecc.hwctl = omap_enable_hwecc; nand_chip->ecc.correct = omap_correct_data; - /* define ECC layout */ - ecclayout->eccbytes = nand_chip->ecc.bytes * - (mtd->writesize / - nand_chip->ecc.size); - if (nand_chip->options & NAND_BUSWIDTH_16) - oob_index = BADBLOCK_MARKER_LENGTH; - else - oob_index = 1; - for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) - ecclayout->eccpos[i] = oob_index; - /* no reserved-marker in ecclayout for this ecc-scheme */ - ecclayout->oobfree->offset = - ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + mtd_set_ooblayout(mtd, &omap_ooblayout_ops); + oobbytes_per_step = nand_chip->ecc.bytes; + + if (!(nand_chip->options & NAND_BUSWIDTH_16)) + min_oobbytes = 1; + break; case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: @@ -1959,19 +2043,9 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = nand_bch_correct_data; nand_chip->ecc.calculate = omap_calculate_ecc_bch; - /* define ECC layout */ - ecclayout->eccbytes = nand_chip->ecc.bytes * - (mtd->writesize / - nand_chip->ecc.size); - oob_index = BADBLOCK_MARKER_LENGTH; - for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) { - ecclayout->eccpos[i] = oob_index; - if (((i + 1) % nand_chip->ecc.bytes) == 0) - oob_index++; - } - /* include reserved-marker in ecclayout->oobfree calculation */ - ecclayout->oobfree->offset = 1 + - ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); + /* Reserve one byte for the OMAP marker */ + oobbytes_per_step = nand_chip->ecc.bytes + 1; /* software bch library is used for locating errors */ nand_chip->ecc.priv = nand_bch_init(mtd); if (!nand_chip->ecc.priv) { @@ -1993,16 +2067,8 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.write_page = omap_write_page_bch; - /* define ECC layout */ - ecclayout->eccbytes = nand_chip->ecc.bytes * - (mtd->writesize / - nand_chip->ecc.size); - oob_index = BADBLOCK_MARKER_LENGTH; - for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) - ecclayout->eccpos[i] = oob_index; - /* reserved marker already included in ecclayout->eccbytes */ - ecclayout->oobfree->offset = - ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + mtd_set_ooblayout(mtd, &omap_ooblayout_ops); + oobbytes_per_step = nand_chip->ecc.bytes; err = elm_config(info->elm_dev, BCH4_ECC, mtd->writesize / nand_chip->ecc.size, @@ -2020,19 +2086,9 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = nand_bch_correct_data; nand_chip->ecc.calculate = omap_calculate_ecc_bch; - /* define ECC layout */ - ecclayout->eccbytes = nand_chip->ecc.bytes * - (mtd->writesize / - nand_chip->ecc.size); - oob_index = BADBLOCK_MARKER_LENGTH; - for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) { - ecclayout->eccpos[i] = oob_index; - if (((i + 1) % nand_chip->ecc.bytes) == 0) - oob_index++; - } - /* include reserved-marker in ecclayout->oobfree calculation */ - ecclayout->oobfree->offset = 1 + - ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; + mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); + /* Reserve one byte for the OMAP marker */ + oobbytes_per_step = nand_chip->ecc.bytes + 1; /* software bch library is used for locating errors */ nand_chip->ecc.priv = nand_bch_init(mtd); if (!nand_chip->ecc.priv) { @@ -2054,6 +2110,8 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.write_page = omap_write_page_bch; + mtd_set_ooblayout(mtd, &omap_ooblayout_ops); + oobbytes_per_step = nand_chip->ecc.bytes; err = elm_config(info->elm_dev, BCH8_ECC, mtd->writesize / nand_chip->ecc.size, @@ -2061,16 +2119,6 @@ static int omap_nand_probe(struct platform_device *pdev) if (err < 0) goto return_error; - /* define ECC layout */ - ecclayout->eccbytes = nand_chip->ecc.bytes * - (mtd->writesize / - nand_chip->ecc.size); - oob_index = BADBLOCK_MARKER_LENGTH; - for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) - ecclayout->eccpos[i] = oob_index; - /* reserved marker already included in ecclayout->eccbytes */ - ecclayout->oobfree->offset = - ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; break; case OMAP_ECC_BCH16_CODE_HW: @@ -2084,6 +2132,8 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.write_page = omap_write_page_bch; + mtd_set_ooblayout(mtd, &omap_ooblayout_ops); + oobbytes_per_step = nand_chip->ecc.bytes; err = elm_config(info->elm_dev, BCH16_ECC, mtd->writesize / nand_chip->ecc.size, @@ -2091,16 +2141,6 @@ static int omap_nand_probe(struct platform_device *pdev) if (err < 0) goto return_error; - /* define ECC layout */ - ecclayout->eccbytes = nand_chip->ecc.bytes * - (mtd->writesize / - nand_chip->ecc.size); - oob_index = BADBLOCK_MARKER_LENGTH; - for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) - ecclayout->eccpos[i] = oob_index; - /* reserved marker already included in ecclayout->eccbytes */ - ecclayout->oobfree->offset = - ecclayout->eccpos[ecclayout->eccbytes - 1] + 1; break; default: dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n"); @@ -2108,13 +2148,13 @@ static int omap_nand_probe(struct platform_device *pdev) goto return_error; } - /* all OOB bytes from oobfree->offset till end off OOB are free */ - ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset; /* check if NAND device's OOB is enough to store ECC signatures */ - if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) { + min_oobbytes += (oobbytes_per_step * + (mtd->writesize / nand_chip->ecc.size)); + if (mtd->oobsize < min_oobbytes) { dev_err(&info->pdev->dev, "not enough OOB bytes required = %d, available=%d\n", - ecclayout->eccbytes, mtd->oobsize); + min_oobbytes, mtd->oobsize); err = -EINVAL; goto return_error; } From 39980c5616d6a4723143e258550f2844e0138d36 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:03:18 +0100 Subject: [PATCH 108/155] mtd: nand: pxa3xx: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/pxa3xx_nand.c | 104 ++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 38d26ab1f0f7..436dd6dc11f4 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -323,6 +323,62 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = { { 0xba20, 16, 16, &timing[3] }, }; +static int pxa3xx_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int nchunks = mtd->writesize / info->chunk_size; + + if (section >= nchunks) + return -ERANGE; + + oobregion->offset = ((info->ecc_size + info->spare_size) * section) + + info->spare_size; + oobregion->length = info->ecc_size; + + return 0; +} + +static int pxa3xx_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct pxa3xx_nand_host *host = nand_get_controller_data(chip); + struct pxa3xx_nand_info *info = host->info_data; + int nchunks = mtd->writesize / info->chunk_size; + + if (section >= nchunks) + return -ERANGE; + + if (!info->spare_size) + return 0; + + oobregion->offset = section * (info->ecc_size + info->spare_size); + oobregion->length = info->spare_size; + if (!section) { + /* + * Bootrom looks in bytes 0 & 5 for bad blocks for the + * 4KB page / 4bit BCH combination. + */ + if (mtd->writesize == 4096 && info->chunk_size == 2048) { + oobregion->offset += 6; + oobregion->length -= 6; + } else { + oobregion->offset += 2; + oobregion->length -= 2; + } + } + + return 0; +} + +static const struct mtd_ooblayout_ops pxa3xx_ooblayout_ops = { + .ecc = pxa3xx_ooblayout_ecc, + .free = pxa3xx_ooblayout_free, +}; + static u8 bbt_pattern[] = {'M', 'V', 'B', 'b', 't', '0' }; static u8 bbt_mirror_pattern[] = {'1', 't', 'b', 'B', 'V', 'M' }; @@ -346,41 +402,6 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = bbt_mirror_pattern }; -static struct nand_ecclayout ecc_layout_2KB_bch4bit = { - .eccbytes = 32, - .eccpos = { - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { {2, 30} } -}; - -static struct nand_ecclayout ecc_layout_4KB_bch4bit = { - .eccbytes = 64, - .eccpos = { - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}, - /* Bootrom looks in bytes 0 & 5 for bad blocks */ - .oobfree = { {6, 26}, { 64, 32} } -}; - -static struct nand_ecclayout ecc_layout_4KB_bch8bit = { - .eccbytes = 128, - .eccpos = { - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { } -}; - #define NDTR0_tCH(c) (min((c), 7) << 19) #define NDTR0_tCS(c) (min((c), 7) << 16) #define NDTR0_tWH(c) (min((c), 7) << 11) @@ -1545,9 +1566,12 @@ static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) } static int pxa_ecc_init(struct pxa3xx_nand_info *info, - struct nand_ecc_ctrl *ecc, + struct mtd_info *mtd, int strength, int ecc_stepsize, int page_size) { + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { info->nfullchunks = 1; info->ntotalchunks = 1; @@ -1581,7 +1605,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info, info->ecc_size = 32; ecc->mode = NAND_ECC_HW; ecc->size = info->chunk_size; - ecc->layout = &ecc_layout_2KB_bch4bit; + mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); ecc->strength = 16; } else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { @@ -1593,7 +1617,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info, info->ecc_size = 32; ecc->mode = NAND_ECC_HW; ecc->size = info->chunk_size; - ecc->layout = &ecc_layout_4KB_bch4bit; + mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); ecc->strength = 16; /* @@ -1611,7 +1635,7 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info, info->ecc_size = 32; ecc->mode = NAND_ECC_HW; ecc->size = info->chunk_size; - ecc->layout = &ecc_layout_4KB_bch8bit; + mtd_set_ooblayout(mtd, &pxa3xx_ooblayout_ops); ecc->strength = 16; } else { dev_err(&info->pdev->dev, @@ -1706,7 +1730,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) ecc_step = 512; } - ret = pxa_ecc_init(info, &chip->ecc, ecc_strength, + ret = pxa_ecc_init(info, mtd, ecc_strength, ecc_step, mtd->writesize); if (ret) return ret; From bf01e06b32c6cb4b1a08d03434b4559974656669 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:03:30 +0100 Subject: [PATCH 109/155] mtd: nand: s3c2410: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/s3c2410.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 86ffb73e1085..4c043db55131 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -84,11 +84,33 @@ /* new oob placement block for use with hardware ecc generation */ +static int s3c2410_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; -static struct nand_ecclayout nand_hw_eccoob = { - .eccbytes = 3, - .eccpos = {0, 1, 2}, - .oobfree = {{8, 8}} + oobregion->offset = 0; + oobregion->length = 3; + + return 0; +} + +static int s3c2410_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 8; + oobregion->length = 8; + + return 0; +} + +static const struct mtd_ooblayout_ops s3c2410_ooblayout_ops = { + .ecc = s3c2410_ooblayout_ecc, + .free = s3c2410_ooblayout_free, }; /* controller and mtd information */ @@ -920,7 +942,7 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, } else { chip->ecc.size = 512; chip->ecc.bytes = 3; - chip->ecc.layout = &nand_hw_eccoob; + mtd_set_ooblayout(nand_to_mtd(chip), &s3c2410_ooblayout_ops); } } From e7049f298ecc39f6917d72542effabab64c2bce8 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:03:47 +0100 Subject: [PATCH 110/155] mtd: nand: sh_flctl: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sh_flctl.c | 87 ++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 4814402902f9..fa4661060a2d 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -43,26 +43,73 @@ #include #include -static struct nand_ecclayout flctl_4secc_oob_16 = { - .eccbytes = 10, - .eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, - .oobfree = { - {.offset = 12, - . length = 4} }, +static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 12; + oobregion->length = 4; + + return 0; +} + +static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = { + .ecc = flctl_4secc_ooblayout_sp_ecc, + .free = flctl_4secc_ooblayout_sp_free, }; -static struct nand_ecclayout flctl_4secc_oob_64 = { - .eccbytes = 4 * 10, - .eccpos = { - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, - .oobfree = { - {.offset = 2, .length = 4}, - {.offset = 16, .length = 6}, - {.offset = 32, .length = 6}, - {.offset = 48, .length = 6} }, +static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 6; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = section * 16; + oobregion->length = 6; + + if (!section) { + oobregion->offset += 2; + oobregion->length -= 2; + } + + return 0; +} + +static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = { + .ecc = flctl_4secc_ooblayout_lp_ecc, + .free = flctl_4secc_ooblayout_lp_free, }; static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; @@ -987,10 +1034,10 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) if (flctl->hwecc) { if (mtd->writesize == 512) { - chip->ecc.layout = &flctl_4secc_oob_16; + mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops); chip->badblock_pattern = &flctl_4secc_smallpage; } else { - chip->ecc.layout = &flctl_4secc_oob_64; + mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops); chip->badblock_pattern = &flctl_4secc_largepage; } From 987b913c2b7e440e9784724d138cdaf8440f34c6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:04:59 +0100 Subject: [PATCH 111/155] mtd: nand: sm_common: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sm_common.c | 95 +++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index c514740f9a83..5939dff253c2 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -12,14 +12,47 @@ #include #include "sm_common.h" -static struct nand_ecclayout nand_oob_sm = { - .eccbytes = 6, - .eccpos = {8, 9, 10, 13, 14, 15}, - .oobfree = { - {.offset = 0 , .length = 4}, /* reserved */ - {.offset = 6 , .length = 2}, /* LBA1 */ - {.offset = 11, .length = 2} /* LBA2 */ +static int oob_sm_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + oobregion->length = 3; + oobregion->offset = ((section + 1) * 8) - 3; + + return 0; +} + +static int oob_sm_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + switch (section) { + case 0: + /* reserved */ + oobregion->offset = 0; + oobregion->length = 4; + break; + case 1: + /* LBA1 */ + oobregion->offset = 6; + oobregion->length = 2; + break; + case 2: + /* LBA2 */ + oobregion->offset = 11; + oobregion->length = 2; + break; + default: + return -ERANGE; } + + return 0; +} + +static const struct mtd_ooblayout_ops oob_sm_ops = { + .ecc = oob_sm_ooblayout_ecc, + .free = oob_sm_ooblayout_free, }; /* NOTE: This layout is is not compatabable with SmartMedia, */ @@ -28,15 +61,43 @@ static struct nand_ecclayout nand_oob_sm = { /* If you use smftl, it will bypass this and work correctly */ /* If you not, then you break SmartMedia compliance anyway */ -static struct nand_ecclayout nand_oob_sm_small = { - .eccbytes = 3, - .eccpos = {0, 1, 2}, - .oobfree = { - {.offset = 3 , .length = 2}, /* reserved */ - {.offset = 6 , .length = 2}, /* LBA1 */ - } -}; +static int oob_sm_small_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + oobregion->length = 3; + oobregion->offset = 0; + + return 0; +} + +static int oob_sm_small_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + switch (section) { + case 0: + /* reserved */ + oobregion->offset = 3; + oobregion->length = 2; + break; + case 1: + /* LBA1 */ + oobregion->offset = 6; + oobregion->length = 2; + break; + default: + return -ERANGE; + } + + return 0; +} + +static const struct mtd_ooblayout_ops oob_sm_small_ops = { + .ecc = oob_sm_small_ooblayout_ecc, + .free = oob_sm_small_ooblayout_free, +}; static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) { @@ -121,9 +182,9 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia) /* ECC layout */ if (mtd->writesize == SM_SECTOR_SIZE) - chip->ecc.layout = &nand_oob_sm; + mtd_set_ooblayout(mtd, &oob_sm_ops); else if (mtd->writesize == SM_SMALL_PAGE) - chip->ecc.layout = &nand_oob_sm_small; + mtd_set_ooblayout(mtd, &oob_sm_small_ops); else return -ENODEV; From c66811e6d350dda53624d1bb4a15698a375b9e79 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:05:13 +0100 Subject: [PATCH 112/155] mtd: nand: sunxi: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 114 ++++++++++++++++------------------ 1 file changed, 52 insertions(+), 62 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index b12ed83ebd56..1baf89836210 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -211,12 +211,9 @@ struct sunxi_nand_chip_sel { * sunxi HW ECC infos: stores information related to HW ECC support * * @mode: the sunxi ECC mode field deduced from ECC requirements - * @layout: the OOB layout depending on the ECC requirements and the - * selected ECC mode */ struct sunxi_nand_hw_ecc { int mode; - struct nand_ecclayout layout; }; /* @@ -1436,6 +1433,57 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip, return sunxi_nand_chip_set_timings(chip, timings); } +static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &nand->ecc; + + if (section >= ecc->steps) + return -ERANGE; + + oobregion->offset = section * (ecc->bytes + 4) + 4; + oobregion->length = ecc->bytes; + + return 0; +} + +static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &nand->ecc; + + if (section > ecc->steps) + return -ERANGE; + + /* + * The first 2 bytes are used for BB markers, hence we + * only have 2 bytes available in the first user data + * section. + */ + if (!section && ecc->mode == NAND_ECC_HW) { + oobregion->offset = 2; + oobregion->length = 2; + + return 0; + } + + oobregion->offset = section * (ecc->bytes + 4); + + if (section < ecc->steps) + oobregion->length = 4; + else + oobregion->offset = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { + .ecc = sunxi_nand_ooblayout_ecc, + .free = sunxi_nand_ooblayout_free, +}; + static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, struct device_node *np) @@ -1445,7 +1493,6 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct sunxi_nand_hw_ecc *data; - struct nand_ecclayout *layout; int nsectors; int ret; int i; @@ -1474,7 +1521,6 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, /* HW ECC always work with even numbers of ECC bytes */ ecc->bytes = ALIGN(ecc->bytes, 2); - layout = &data->layout; nsectors = mtd->writesize / ecc->size; if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { @@ -1482,11 +1528,9 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, goto err; } - layout->eccbytes = (ecc->bytes * nsectors); - ecc->read_oob = sunxi_nfc_hw_common_ecc_read_oob; ecc->write_oob = sunxi_nfc_hw_common_ecc_write_oob; - ecc->layout = layout; + mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); ecc->priv = data; return 0; @@ -1506,9 +1550,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, struct device_node *np) { - struct nand_ecclayout *layout; - int nsectors; - int i, j; int ret; ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); @@ -1520,40 +1561,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, ecc->read_oob_raw = nand_read_oob_std; ecc->write_oob_raw = nand_write_oob_std; ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; - layout = ecc->layout; - nsectors = mtd->writesize / ecc->size; - - for (i = 0; i < nsectors; i++) { - if (i) { - layout->oobfree[i].offset = - layout->oobfree[i - 1].offset + - layout->oobfree[i - 1].length + - ecc->bytes; - layout->oobfree[i].length = 4; - } else { - /* - * The first 2 bytes are used for BB markers, hence we - * only have 2 bytes available in the first user data - * section. - */ - layout->oobfree[i].length = 2; - layout->oobfree[i].offset = 2; - } - - for (j = 0; j < ecc->bytes; j++) - layout->eccpos[(ecc->bytes * i) + j] = - layout->oobfree[i].offset + - layout->oobfree[i].length + j; - } - - if (mtd->oobsize > (ecc->bytes + 4) * nsectors) { - layout->oobfree[nsectors].offset = - layout->oobfree[nsectors - 1].offset + - layout->oobfree[nsectors - 1].length + - ecc->bytes; - layout->oobfree[nsectors].length = mtd->oobsize - - ((ecc->bytes + 4) * nsectors); - } return 0; } @@ -1562,9 +1569,6 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, struct device_node *np) { - struct nand_ecclayout *layout; - int nsectors; - int i; int ret; ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np); @@ -1577,15 +1581,6 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, ecc->read_oob_raw = nand_read_oob_syndrome; ecc->write_oob_raw = nand_write_oob_syndrome; - layout = ecc->layout; - nsectors = mtd->writesize / ecc->size; - - for (i = 0; i < (ecc->bytes * nsectors); i++) - layout->eccpos[i] = i; - - layout->oobfree[0].length = mtd->oobsize - i; - layout->oobfree[0].offset = i; - return 0; } @@ -1597,7 +1592,6 @@ static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc); break; case NAND_ECC_NONE: - kfree(ecc->layout); default: break; } @@ -1631,10 +1625,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, return ret; break; case NAND_ECC_NONE: - ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL); - if (!ecc->layout) - return -ENOMEM; - ecc->layout->oobfree[0].length = mtd->oobsize; case NAND_ECC_SOFT: break; default: From 3cf32d180227c1a1f5e017c997085d52a10a106f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 20:05:45 +0100 Subject: [PATCH 113/155] mtd: nand: vf610: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon Tested-by: Stefan Agner Acked-by: Stefan Agner --- drivers/mtd/nand/vf610_nfc.c | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index a7f2756c49ee..3ad514c44dcb 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -174,34 +174,6 @@ static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd) return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip); } -static struct nand_ecclayout vf610_nfc_ecc45 = { - .eccbytes = 45, - .eccpos = {19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { - {.offset = 2, - .length = 17} } -}; - -static struct nand_ecclayout vf610_nfc_ecc60 = { - .eccbytes = 60, - .eccpos = { 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63 }, - .oobfree = { - {.offset = 2, - .length = 2} } -}; - static inline u32 vf610_nfc_read(struct vf610_nfc *nfc, uint reg) { return readl(nfc->regs + reg); @@ -780,14 +752,16 @@ static int vf610_nfc_probe(struct platform_device *pdev) if (mtd->oobsize > 64) mtd->oobsize = 64; + /* + * mtd->ecclayout is not specified here because we're using the + * default large page ECC layout defined in NAND core. + */ if (chip->ecc.strength == 32) { nfc->ecc_mode = ECC_60_BYTE; chip->ecc.bytes = 60; - chip->ecc.layout = &vf610_nfc_ecc60; } else if (chip->ecc.strength == 24) { nfc->ecc_mode = ECC_45_BYTE; chip->ecc.bytes = 45; - chip->ecc.layout = &vf610_nfc_ecc45; } else { dev_err(nfc->dev, "Unsupported ECC strength\n"); err = -ENXIO; From 421e81c4c6c03a5f6cbc40ea208fb07ad5797e09 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 18 Mar 2016 17:54:27 +0100 Subject: [PATCH 114/155] mtd: nand: qcom: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Signed-off-by: Boris Brezillon Tested-by: Archit Taneja --- drivers/mtd/nand/qcom_nandc.c | 87 +++++++++++++++-------------------- 1 file changed, 38 insertions(+), 49 deletions(-) diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c index 4ea2cb771191..de7d28e62d4e 100644 --- a/drivers/mtd/nand/qcom_nandc.c +++ b/drivers/mtd/nand/qcom_nandc.c @@ -1707,61 +1707,52 @@ static void qcom_nandc_select_chip(struct mtd_info *mtd, int chipnr) * This layout is read as is when ECC is disabled. When ECC is enabled, the * inaccessible Bad Block byte(s) are ignored when we write to a page/oob, * and assumed as 0xffs when we read a page/oob. The ECC, unused and - * dummy/real bad block bytes are grouped as ecc bytes in nand_ecclayout (i.e, - * ecc->bytes is the sum of the three). + * dummy/real bad block bytes are grouped as ecc bytes (i.e, ecc->bytes is + * the sum of the three). */ - -static struct nand_ecclayout * -qcom_nand_create_layout(struct qcom_nand_host *host) +static int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) { - struct nand_chip *chip = &host->chip; - struct mtd_info *mtd = nand_to_mtd(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_host *host = to_qcom_nand_host(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; - struct nand_ecclayout *layout; - int i, j, steps, pos = 0, shift = 0; - layout = devm_kzalloc(nandc->dev, sizeof(*layout), GFP_KERNEL); - if (!layout) - return NULL; + if (section > 1) + return -ERANGE; - steps = mtd->writesize / ecc->size; - layout->eccbytes = steps * ecc->bytes; - - layout->oobfree[0].offset = (steps - 1) * ecc->bytes + host->bbm_size; - layout->oobfree[0].length = steps << 2; - - /* - * the oob bytes in the first n - 1 codewords are all grouped together - * in the format: - * DUMMY_BBM + UNUSED + ECC - */ - for (i = 0; i < steps - 1; i++) { - for (j = 0; j < ecc->bytes; j++) - layout->eccpos[pos++] = i * ecc->bytes + j; + if (!section) { + oobregion->length = (ecc->bytes * (ecc->steps - 1)) + + host->bbm_size; + oobregion->offset = 0; + } else { + oobregion->length = host->ecc_bytes_hw + host->spare_bytes; + oobregion->offset = mtd->oobsize - oobregion->length; } - /* - * the oob bytes in the last codeword are grouped in the format: - * BBM + FREE OOB + UNUSED + ECC - */ - - /* fill up the bbm positions */ - for (j = 0; j < host->bbm_size; j++) - layout->eccpos[pos++] = i * ecc->bytes + j; - - /* - * fill up the ecc and reserved positions, their indices are offseted - * by the free oob region - */ - shift = layout->oobfree[0].length + host->bbm_size; - - for (j = 0; j < (host->ecc_bytes_hw + host->spare_bytes); j++) - layout->eccpos[pos++] = i * ecc->bytes + shift + j; - - return layout; + return 0; } +static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (section) + return -ERANGE; + + oobregion->length = ecc->steps * 4; + oobregion->offset = ((ecc->steps - 1) * ecc->bytes) + host->bbm_size; + + return 0; +} + +static const struct mtd_ooblayout_ops qcom_nand_ooblayout_ops = { + .ecc = qcom_nand_ooblayout_ecc, + .free = qcom_nand_ooblayout_free, +}; + static int qcom_nand_host_setup(struct qcom_nand_host *host) { struct nand_chip *chip = &host->chip; @@ -1848,9 +1839,7 @@ static int qcom_nand_host_setup(struct qcom_nand_host *host) ecc->mode = NAND_ECC_HW; - ecc->layout = qcom_nand_create_layout(host); - if (!ecc->layout) - return -ENOMEM; + mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); cwperpage = mtd->writesize / ecc->size; From a411679fb5fd7ee2df64a55c23c81538ceeb6d06 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 7 Dec 2015 22:46:45 +0100 Subject: [PATCH 115/155] mtd: onenand: switch to mtd_ooblayout_ops Implementing the mtd_ooblayout_ops interface is the new way of exposing ECC/OOB layout to MTD users. Modify the onenand drivers to switch to this approach. Signed-off-by: Boris Brezillon --- drivers/mtd/onenand/onenand_base.c | 162 +++++++++++++++++------------ include/linux/mtd/onenand.h | 2 - 2 files changed, 97 insertions(+), 67 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index d0fa505d40bd..a4b029a417f0 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -68,21 +68,33 @@ MODULE_PARM_DESC(otp, "Corresponding behaviour of OneNAND in OTP" * flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page * For now, we expose only 64 out of 80 ecc bytes */ -static struct nand_ecclayout flexonenand_oob_128 = { - .eccbytes = 64, - .eccpos = { - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, - 102, 103, 104, 105 - }, - .oobfree = { - {2, 4}, {18, 4}, {34, 4}, {50, 4}, - {66, 4}, {82, 4}, {98, 4}, {114, 4} - } +static int flexonenand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = (section * 16) + 6; + oobregion->length = 10; + + return 0; +} + +static int flexonenand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = (section * 16) + 2; + oobregion->length = 4; + + return 0; +} + +static const struct mtd_ooblayout_ops flexonenand_ooblayout_ops = { + .ecc = flexonenand_ooblayout_ecc, + .free = flexonenand_ooblayout_free, }; /* @@ -91,56 +103,77 @@ static struct nand_ecclayout flexonenand_oob_128 = { * Based on specification: * 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010 * - * For eccpos we expose only 64 bytes out of 72 (see struct nand_ecclayout) - * - * oobfree uses the spare area fields marked as - * "Managed by internal ECC logic for Logical Sector Number area" */ -static struct nand_ecclayout onenand_oob_128 = { - .eccbytes = 64, - .eccpos = { - 7, 8, 9, 10, 11, 12, 13, 14, 15, - 23, 24, 25, 26, 27, 28, 29, 30, 31, - 39, 40, 41, 42, 43, 44, 45, 46, 47, - 55, 56, 57, 58, 59, 60, 61, 62, 63, - 71, 72, 73, 74, 75, 76, 77, 78, 79, - 87, 88, 89, 90, 91, 92, 93, 94, 95, - 103, 104, 105, 106, 107, 108, 109, 110, 111, - 119 - }, - .oobfree = { - {2, 3}, {18, 3}, {34, 3}, {50, 3}, - {66, 3}, {82, 3}, {98, 3}, {114, 3} - } +static int onenand_ooblayout_128_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 7) + return -ERANGE; + + oobregion->offset = (section * 16) + 7; + oobregion->length = 9; + + return 0; +} + +static int onenand_ooblayout_128_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section >= 8) + return -ERANGE; + + /* + * free bytes are using the spare area fields marked as + * "Managed by internal ECC logic for Logical Sector Number area" + */ + oobregion->offset = (section * 16) + 2; + oobregion->length = 3; + + return 0; +} + +static const struct mtd_ooblayout_ops onenand_oob_128_ooblayout_ops = { + .ecc = onenand_ooblayout_128_ecc, + .free = onenand_ooblayout_128_free, }; /** - * onenand_oob_64 - oob info for large (2KB) page + * onenand_oob_32_64 - oob info for large (2KB) page */ -static struct nand_ecclayout onenand_oob_64 = { - .eccbytes = 20, - .eccpos = { - 8, 9, 10, 11, 12, - 24, 25, 26, 27, 28, - 40, 41, 42, 43, 44, - 56, 57, 58, 59, 60, - }, - .oobfree = { - {2, 3}, {14, 2}, {18, 3}, {30, 2}, - {34, 3}, {46, 2}, {50, 3}, {62, 2} - } -}; +static int onenand_ooblayout_32_64_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 3) + return -ERANGE; -/** - * onenand_oob_32 - oob info for middle (1KB) page - */ -static struct nand_ecclayout onenand_oob_32 = { - .eccbytes = 10, - .eccpos = { - 8, 9, 10, 11, 12, - 24, 25, 26, 27, 28, - }, - .oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} } + oobregion->offset = (section * 16) + 8; + oobregion->length = 5; + + return 0; +} + +static int onenand_ooblayout_32_64_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + int sections = (mtd->oobsize / 32) * 2; + + if (section >= sections) + return -ERANGE; + + if (section & 1) { + oobregion->offset = ((section - 1) * 16) + 14; + oobregion->length = 2; + } else { + oobregion->offset = (section * 16) + 2; + oobregion->length = 3; + } + + return 0; +} + +static const struct mtd_ooblayout_ops onenand_oob_32_64_ooblayout_ops = { + .ecc = onenand_ooblayout_32_64_ecc, + .free = onenand_ooblayout_32_64_free, }; static const unsigned char ffchars[] = { @@ -3957,22 +3990,22 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) switch (mtd->oobsize) { case 128: if (FLEXONENAND(this)) { - this->ecclayout = &flexonenand_oob_128; + mtd_set_ooblayout(mtd, &flexonenand_ooblayout_ops); mtd->subpage_sft = 0; } else { - this->ecclayout = &onenand_oob_128; + mtd_set_ooblayout(mtd, &onenand_oob_128_ooblayout_ops); mtd->subpage_sft = 2; } if (ONENAND_IS_NOP_1(this)) mtd->subpage_sft = 0; break; case 64: - this->ecclayout = &onenand_oob_64; + mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops); mtd->subpage_sft = 2; break; case 32: - this->ecclayout = &onenand_oob_32; + mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops); mtd->subpage_sft = 1; break; @@ -3981,7 +4014,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) __func__, mtd->oobsize); mtd->subpage_sft = 0; /* To prevent kernel oops */ - this->ecclayout = &onenand_oob_32; + mtd_set_ooblayout(mtd, &onenand_oob_32_64_ooblayout_ops); break; } @@ -3997,7 +4030,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->oobavail = ret; - mtd_set_ecclayout(mtd, this->ecclayout); mtd->ecc_strength = 1; /* Fill in remaining MTD driver data */ diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 4596503c9da9..0aaa98b219a4 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -80,7 +80,6 @@ struct onenand_bufferram { * @page_buf: [INTERN] page main data buffer * @oob_buf: [INTERN] page oob data buffer * @subpagesize: [INTERN] holds the subpagesize - * @ecclayout: [REPLACEABLE] the default ecc placement scheme * @bbm: [REPLACEABLE] pointer to Bad Block Management * @priv: [OPTIONAL] pointer to private chip date */ @@ -134,7 +133,6 @@ struct onenand_chip { #endif int subpagesize; - struct nand_ecclayout *ecclayout; void *bbm; From e1d132b4ae3b5dfc897cf117419404b22caf6892 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:13:39 +0100 Subject: [PATCH 116/155] staging: mt29f_spinand: switch to mtd_ooblayout_ops Replace the nand_ecclayout definition by the equivalent mtd_ooblayout_ops definition. Signed-off-by: Boris Brezillon --- drivers/staging/mt29f_spinand/mt29f_spinand.c | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c index 163f21a1298d..f50369941590 100644 --- a/drivers/staging/mt29f_spinand/mt29f_spinand.c +++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c @@ -42,23 +42,33 @@ static inline struct spinand_state *mtd_to_state(struct mtd_info *mtd) static int enable_hw_ecc; static int enable_read_hw_ecc; -static struct nand_ecclayout spinand_oob_64 = { - .eccbytes = 24, - .eccpos = { - 1, 2, 3, 4, 5, 6, - 17, 18, 19, 20, 21, 22, - 33, 34, 35, 36, 37, 38, - 49, 50, 51, 52, 53, 54, }, - .oobfree = { - {.offset = 8, - .length = 8}, - {.offset = 24, - .length = 8}, - {.offset = 40, - .length = 8}, - {.offset = 56, - .length = 8}, - } +static int spinand_ooblayout_64_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 3) + return -ERANGE; + + oobregion->offset = (section * 16) + 1; + oobregion->length = 6; + + return 0; +} + +static int spinand_ooblayout_64_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 3) + return -ERANGE; + + oobregion->offset = (section * 16) + 8; + oobregion->length = 8; + + return 0; +} + +static const struct mtd_ooblayout_ops spinand_oob_64_ops = { + .ecc = spinand_ooblayout_64_ecc, + .free = spinand_ooblayout_64_free, }; #endif @@ -886,7 +896,6 @@ static int spinand_probe(struct spi_device *spi_nand) chip->ecc.strength = 1; chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; - chip->ecc.layout = &spinand_oob_64; chip->ecc.read_page = spinand_read_page_hwecc; chip->ecc.write_page = spinand_write_page_hwecc; #else @@ -912,6 +921,9 @@ static int spinand_probe(struct spi_device *spi_nand) mtd->dev.parent = &spi_nand->dev; mtd->oobsize = 64; +#ifdef CONFIG_MTD_SPINAND_ONDIEECC + mtd_set_ooblayout(mtd, &spinand_oob_64_ops); +#endif if (nand_scan(mtd, 1)) return -ENXIO; From 7f2b092c9eeda055ae60af194a8edacaea5f7a10 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 3 Feb 2016 19:24:10 +0100 Subject: [PATCH 117/155] mtd: nand: kill the ecc->layout field Now that all NAND drivers have switched to mtd_ooblayout_ops, we can kill the ecc->layout field. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 7 ------- drivers/mtd/nand/nand_bch.c | 8 -------- include/linux/mtd/nand.h | 2 -- 3 files changed, 17 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6c84a40dbddb..0001be19b90c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4151,13 +4151,6 @@ int nand_scan_tail(struct mtd_info *mtd) /* Set the internal oob buffer location, just after the page data */ chip->oob_poi = chip->buffers->databuf + mtd->writesize; - /* - * Set the provided ECC layout. If ecc->layout is NULL, the MTD core - * will just leave mtd->ooblayout to NULL, if it's not NULL, it will - * set ->ooblayout to the default ecclayout wrapper. - */ - mtd_set_ecclayout(mtd, ecc->layout); - /* * If no default placement scheme is given, select an appropriate one. */ diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c index 1dbc418baac4..28ef7b178eac 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -158,14 +158,6 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) eccsteps = mtd->writesize/eccsize; - /* - * Rely on the default ecclayout to ooblayout wrapper provided by MTD - * core if ecc.layout is not NULL. - * FIXME: this should be removed when all callers have moved to the - * mtd_ooblayout_ops approach. - */ - mtd_set_ecclayout(mtd, nand->ecc.layout); - /* Check that we have an oob layout description. */ if (!mtd->ooblayout) { pr_warn("missing oob scheme"); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index f2ded7b1b3b8..e851839daf09 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -473,7 +473,6 @@ struct nand_hw_control { * @prepad: padding information for syndrome based ECC generators * @postpad: padding information for syndrome based ECC generators * @options: ECC specific options (see NAND_ECC_XXX flags defined above) - * @layout: ECC layout control struct pointer * @priv: pointer to private ECC control data * @hwctl: function to control hardware ECC generator. Must only * be provided if an hardware ECC is available @@ -524,7 +523,6 @@ struct nand_ecc_ctrl { int prepad; int postpad; unsigned int options; - struct nand_ecclayout *layout; void *priv; void (*hwctl)(struct mtd_info *mtd, int mode); int (*calculate)(struct mtd_info *mtd, const uint8_t *dat, From aab616e31d1c7ec3726f7f5cbdaaec98759ebe93 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 4 Feb 2016 10:16:18 +0100 Subject: [PATCH 118/155] mtd: kill the nand_ecclayout struct Now that all MTD drivers have moved to the mtd_ooblayout_ops model we can safely remove the struct nand_ecclayout definition, and all the remaining places where it was still used. Signed-off-by: Boris Brezillon --- drivers/mtd/mtdchar.c | 12 ++-- drivers/mtd/mtdcore.c | 117 ------------------------------------- include/linux/mtd/mtd.h | 20 ------- include/uapi/mtd/mtd-abi.h | 2 +- 4 files changed, 7 insertions(+), 144 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 3fad2c7425b0..2a47a3f0e730 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -465,12 +465,12 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd, } /* - * Copies (and truncates, if necessary) data from the larger struct, - * nand_ecclayout, to the smaller, deprecated layout struct, - * nand_ecclayout_user. This is necessary only to support the deprecated - * API ioctl ECCGETLAYOUT while allowing all new functionality to use - * nand_ecclayout flexibly (i.e. the struct may change size in new - * releases without requiring major rewrites). + * Copies (and truncates, if necessary) OOB layout information to the + * deprecated layout struct, nand_ecclayout_user. This is necessary only to + * support the deprecated API ioctl ECCGETLAYOUT while allowing all new + * functionality to use mtd_ooblayout_ops flexibly (i.e. mtd_ooblayout_ops + * can describe any kind of OOB layout with almost zero overhead from a + * memory usage point of view). */ static int shrink_ecclayout(struct mtd_info *mtd, struct nand_ecclayout_user *to) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 134ed2f7b919..e3936b847c6b 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1376,123 +1376,6 @@ int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd) } EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes); -/** - * mtd_ecclayout_ecc - Default ooblayout_ecc iterator implementation - * @mtd: MTD device structure - * @section: ECC section. Depending on the layout you may have all the ECC - * bytes stored in a single contiguous section, or one section - * per ECC chunk (and sometime several sections for a single ECC - * ECC chunk) - * @oobecc: OOB region struct filled with the appropriate ECC position - * information - * - * This function is just a wrapper around the mtd->ecclayout field and is - * here to ease the transition to the mtd_ooblayout_ops approach. - * All it does is convert the layout->eccpos information into proper oob - * region definitions. - * - * Returns zero on success, a negative error code otherwise. - */ -static int mtd_ecclayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobecc) -{ - int eccbyte = 0, cursection = 0, length = 0, eccpos = 0; - - if (!mtd->ecclayout) - return -ENOTSUPP; - - /* - * This logic allows us to reuse the ->ecclayout information and - * expose them as ECC regions (as done for the OOB free regions). - * - * TODO: this should be dropped as soon as we get rid of the - * ->ecclayout field. - */ - for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) { - eccpos = mtd->ecclayout->eccpos[eccbyte]; - - if (eccbyte < mtd->ecclayout->eccbytes - 1) { - int neccpos = mtd->ecclayout->eccpos[eccbyte + 1]; - - if (eccpos + 1 == neccpos) { - length++; - continue; - } - } - - if (section == cursection) - break; - - length = 0; - cursection++; - } - - if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes) - return -ERANGE; - - oobecc->length = length + 1; - oobecc->offset = eccpos - length; - - return 0; -} - -/** - * mtd_ecclayout_ecc - Default ooblayout_free iterator implementation - * @mtd: MTD device structure - * @section: Free section. Depending on the layout you may have all the free - * bytes stored in a single contiguous section, or one section - * per ECC chunk (and sometime several sections for a single ECC - * ECC chunk) - * @oobfree: OOB region struct filled with the appropriate free position - * information - * - * This function is just a wrapper around the mtd->ecclayout field and is - * here to ease the transition to the mtd_ooblayout_ops approach. - * All it does is convert the layout->oobfree information into proper oob - * region definitions. - * - * Returns zero on success, a negative error code otherwise. - */ -static int mtd_ecclayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobfree) -{ - struct nand_ecclayout *layout = mtd->ecclayout; - - if (!layout) - return -ENOTSUPP; - - if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE || - !layout->oobfree[section].length) - return -ERANGE; - - oobfree->offset = layout->oobfree[section].offset; - oobfree->length = layout->oobfree[section].length; - - return 0; -} - -static const struct mtd_ooblayout_ops mtd_ecclayout_wrapper_ops = { - .ecc = mtd_ecclayout_ecc, - .free = mtd_ecclayout_free, -}; - -/** - * mtd_set_ecclayout - Attach an ecclayout to an MTD device - * @mtd: MTD device structure - * @ecclayout: The ecclayout to attach to the device - * - * Returns zero on success, a negative error code otherwise. - */ -void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout) -{ - if (!mtd || !ecclayout) - return; - - mtd->ecclayout = ecclayout; - mtd_set_ooblayout(mtd, &mtd_ecclayout_wrapper_ops); -} -EXPORT_SYMBOL_GPL(mtd_set_ecclayout); - /* * Method to access the protection register area, present in some flash * devices. The user data is one time programmable but the factory data is read diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 177bf314ad70..29a170612203 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -96,21 +96,6 @@ struct mtd_oob_ops { #define MTD_MAX_OOBFREE_ENTRIES_LARGE 32 #define MTD_MAX_ECCPOS_ENTRIES_LARGE 640 -/* - * Internal ECC layout control structure. For historical reasons, there is a - * similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained - * for export to user-space via the ECCGETLAYOUT ioctl. - * nand_ecclayout should be expandable in the future simply by the above macros. - * - * This structure is now deprecated, you should use struct nand_ecclayout_ops - * to describe your OOB layout. - */ -struct nand_ecclayout { - __u32 eccbytes; - __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE]; - struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE]; -}; - /** * struct mtd_oob_region - oob region definition * @offset: region offset @@ -200,9 +185,6 @@ struct mtd_info { const char *name; int index; - /* [Deprecated] ECC layout structure pointer - read only! */ - struct nand_ecclayout *ecclayout; - /* OOB layout description */ const struct mtd_ooblayout_ops *ooblayout; @@ -308,8 +290,6 @@ int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf, int mtd_ooblayout_count_freebytes(struct mtd_info *mtd); int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd); -void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout); - static inline void mtd_set_ooblayout(struct mtd_info *mtd, const struct mtd_ooblayout_ops *ooblayout) { diff --git a/include/uapi/mtd/mtd-abi.h b/include/uapi/mtd/mtd-abi.h index 763bb6950402..0ec1da2ef652 100644 --- a/include/uapi/mtd/mtd-abi.h +++ b/include/uapi/mtd/mtd-abi.h @@ -228,7 +228,7 @@ struct nand_oobfree { * complete set of ECC information. The ioctl truncates the larger internal * structure to retain binary compatibility with the static declaration of the * ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of - * the user struct, not the MAX size of the internal struct nand_ecclayout. + * the user struct, not the MAX size of the internal OOB layout representation. */ struct nand_ecclayout_user { __u32 eccbytes; From d7b83b8a6dda3edada04f9395e2ef6463e099277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 14:06:54 +0200 Subject: [PATCH 119/155] mtd: nand: omap2: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/omap2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 55916786d04e..16da65baa4c7 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -2012,6 +2012,7 @@ static int omap_nand_probe(struct platform_device *pdev) */ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.algo = NAND_ECC_HAMMING; goto scan_tail; } From c4fe72a733c7749ab2c05e51bd37393525a86e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 14:06:55 +0200 Subject: [PATCH 120/155] mtd: nand: s3c2410: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/s3c2410.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 4c043db55131..d9309cf0ce2e 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -882,6 +882,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, } #else chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; #endif if (set->disable_ecc) From e020cc05915e11bad3a923b52458baa2daac53cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 14:06:56 +0200 Subject: [PATCH 121/155] mtd: nand: sh_flctl: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sh_flctl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index fa4661060a2d..a5abf6f70f72 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -1052,6 +1052,7 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) flctl->flcmncr_base |= _4ECCEN; } else { chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; } return 0; From c2ec6b30a521691b752ac61ee05267133a389834 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 14:06:57 +0200 Subject: [PATCH 122/155] mtd: nand: au1550nd: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/au1550nd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 341ea4904164..9bf6d9915694 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -459,6 +459,7 @@ static int au1550nd_probe(struct platform_device *pdev) /* 30 us command delay time */ this->chip_delay = 30; this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; if (pd->devwidth) this->options |= NAND_BUSWIDTH_16; From 7079e7ed4a4e8b74a7dc3feafc0e70cc027cab32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 14:06:58 +0200 Subject: [PATCH 123/155] mtd: nand: mpc5121_nfc: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mpc5121_nfc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 5d7843ffff6a..7eacb2f545f5 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -710,6 +710,7 @@ static int mpc5121_nfc_probe(struct platform_device *op) chip->select_chip = mpc5121_nfc_select_chip; chip->bbt_options = NAND_BBT_USE_FLASH; chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; /* Support external chip-select logic on ADS5121 board */ if (of_machine_is_compatible("fsl,mpc5121ads")) { From d9944e1fdcd6c363a24577ac64c6d1128152ff6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 14:06:59 +0200 Subject: [PATCH 124/155] mtd: nand: cmx270: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/cmx270_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 6f97ebba52c4..49133783ca53 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -187,6 +187,7 @@ static int __init cmx270_init(void) /* 15 us command delay time */ this->chip_delay = 20; this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; /* read/write functions */ this->read_byte = cmx270_read_byte; From ab2f5a809871dead7237a86c962d83e2f7b12e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 14:07:00 +0200 Subject: [PATCH 125/155] mtd: nand: fsl_upm: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsl_upm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index cafd12de7276..d85fa2555b68 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -170,6 +170,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun, fun->chip.read_buf = fun_read_buf; fun->chip.write_buf = fun_write_buf; fun->chip.ecc.mode = NAND_ECC_SOFT; + fun->chip.ecc.algo = NAND_ECC_HAMMING; if (fun->mchip_count > 1) fun->chip.select_chip = fun_select_chip; From ff1ef3501fae2fd92f4f1132fd27182507a749ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 14:07:01 +0200 Subject: [PATCH 126/155] mtd: nand: fsl_ifc: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsl_ifc_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 864aa12a1a13..4e9e5fd8faf3 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -895,6 +895,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) } } else { chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; } if (ctrl->version == FSL_IFC_VERSION_1_1_0) From e99b0d9865949e1bb41d1462427e200a6f9eb973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 13 Apr 2016 14:07:02 +0200 Subject: [PATCH 127/155] mtd: nand: fsl_elbc: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsl_elbc_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 487eae07fefc..60a88f24c6b3 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -800,6 +800,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) } else { /* otherwise fall back to default software ECC */ chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; } return 0; From 99fec60aad7a5520effbaadd3bbbe8001e9242eb Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Mon, 18 Apr 2016 18:04:56 +0200 Subject: [PATCH 128/155] mtd: nand: jz4740: Remove unused local variable Signed-off-by: Maarten ter Huurne Acked-by: Harvey Hunt Signed-off-by: Boris Brezillon --- drivers/mtd/nand/jz4740_nand.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index df7440872df0..5551c36adbdf 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -221,7 +221,6 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, struct jz_nand *nand = mtd_to_jz_nand(mtd); int i, error_count, index; uint32_t reg, status, error; - uint32_t t; unsigned int timeout = 1000; for (i = 0; i < 9; ++i) From 57a605b101bbdff6a388e8dcb4853db9ffe947f7 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 14 Apr 2016 08:54:30 +0200 Subject: [PATCH 129/155] mtd: nand: omap2: fix compare_const_fl.cocci warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move constants to the right of binary operators. Generated by: scripts/coccinelle/misc/compare_const_fl.cocci CC: Rafał Miłecki Signed-off-by: Fengguang Wu Signed-off-by: Julia Lawall Signed-off-by: Boris Brezillon --- drivers/mtd/nand/omap2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 16da65baa4c7..141852fc6455 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -216,7 +216,7 @@ static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode, */ val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) | PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH | - (dma_mode << DMA_MPU_MODE_SHIFT) | (0x1 & is_write)); + (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1)); writel(val, info->reg.gpmc_prefetch_config1); /* Start the prefetch engine */ From f05f6a10fb2ef4aa7ebec75696a162f3110861a0 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:26 +0200 Subject: [PATCH 130/155] mtd: nand: gpmi: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon Acked-by: Han xu --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index c50523b085ef..798ed5961e8f 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -25,7 +25,6 @@ #include #include #include -#include #include "gpmi-nand.h" #include "bch-regs.h" @@ -1944,16 +1943,6 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ this->swap_block_mark = !GPMI_IS_MX23(this); - if (of_get_nand_on_flash_bbt(this->dev->of_node)) { - chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; - - if (of_property_read_bool(this->dev->of_node, - "fsl,no-blockmark-swap")) - this->swap_block_mark = false; - } - dev_dbg(this->dev, "Blockmark swapping %sabled\n", - this->swap_block_mark ? "en" : "dis"); - /* * Allocate a temporary DMA buffer for reading ID in the * nand_scan_ident(). @@ -1968,6 +1957,16 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) if (ret) goto err_out; + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + chip->bbt_options |= NAND_BBT_NO_OOB; + + if (of_property_read_bool(this->dev->of_node, + "fsl,no-blockmark-swap")) + this->swap_block_mark = false; + } + dev_dbg(this->dev, "Blockmark swapping %sabled\n", + this->swap_block_mark ? "en" : "dis"); + ret = gpmi_init_last(this); if (ret) goto err_out; From 363b5db21284b3f44866eaee5d2d34c3c9963f7d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:25 +0200 Subject: [PATCH 131/155] mtd: nand: davinci: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/davinci_nand.c | 83 +++++++++++++++------------------ 1 file changed, 38 insertions(+), 45 deletions(-) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index fe3fd29af435..0198536a1944 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -559,8 +558,6 @@ static struct davinci_nand_pdata "ti,davinci-mask-chipsel", &prop)) pdata->mask_chipsel = prop; if (!of_property_read_string(pdev->dev.of_node, - "nand-ecc-mode", &mode) || - !of_property_read_string(pdev->dev.of_node, "ti,davinci-ecc-mode", &mode)) { if (!strncmp("none", mode, 4)) pdata->ecc_mode = NAND_ECC_NONE; @@ -573,14 +570,11 @@ static struct davinci_nand_pdata "ti,davinci-ecc-bits", &prop)) pdata->ecc_bits = prop; - prop = of_get_nand_bus_width(pdev->dev.of_node); - if (0 < prop || !of_property_read_u32(pdev->dev.of_node, - "ti,davinci-nand-buswidth", &prop)) - if (prop == 16) - pdata->options |= NAND_BUSWIDTH_16; + if (!of_property_read_u32(pdev->dev.of_node, + "ti,davinci-nand-buswidth", &prop) && prop == 16) + pdata->options |= NAND_BUSWIDTH_16; + if (of_property_read_bool(pdev->dev.of_node, - "nand-on-flash-bbt") || - of_property_read_bool(pdev->dev.of_node, "ti,davinci-nand-use-bbt")) pdata->bbt_options = NAND_BBT_USE_FLASH; @@ -610,7 +604,6 @@ static int nand_davinci_probe(struct platform_device *pdev) void __iomem *base; int ret; uint32_t val; - nand_ecc_modes_t ecc_mode; struct mtd_info *mtd; pdata = nand_davinci_get_pdata(pdev); @@ -694,10 +687,41 @@ static int nand_davinci_probe(struct platform_device *pdev) info->chip.write_buf = nand_davinci_write_buf; /* Use board-specific ECC config */ - ecc_mode = pdata->ecc_mode; + info->chip.ecc.mode = pdata->ecc_mode; ret = -EINVAL; - switch (ecc_mode) { + + info->clk = devm_clk_get(&pdev->dev, "aemif"); + if (IS_ERR(info->clk)) { + ret = PTR_ERR(info->clk); + dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(info->clk); + if (ret < 0) { + dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n", + ret); + goto err_clk_enable; + } + + spin_lock_irq(&davinci_nand_lock); + + /* put CSxNAND into NAND mode */ + val = davinci_nand_readl(info, NANDFCR_OFFSET); + val |= BIT(info->core_chipsel); + davinci_nand_writel(info, NANDFCR_OFFSET, val); + + spin_unlock_irq(&davinci_nand_lock); + + /* Scan to find existence of the device(s) */ + ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL); + if (ret < 0) { + dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); + goto err; + } + + switch (info->chip.ecc.mode) { case NAND_ECC_NONE: case NAND_ECC_SOFT: pdata->ecc_bits = 0; @@ -736,37 +760,6 @@ static int nand_davinci_probe(struct platform_device *pdev) default: return -EINVAL; } - info->chip.ecc.mode = ecc_mode; - - info->clk = devm_clk_get(&pdev->dev, "aemif"); - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(info->clk); - if (ret < 0) { - dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n", - ret); - goto err_clk_enable; - } - - spin_lock_irq(&davinci_nand_lock); - - /* put CSxNAND into NAND mode */ - val = davinci_nand_readl(info, NANDFCR_OFFSET); - val |= BIT(info->core_chipsel); - davinci_nand_writel(info, NANDFCR_OFFSET, val); - - spin_unlock_irq(&davinci_nand_lock); - - /* Scan to find existence of the device(s) */ - ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL); - if (ret < 0) { - dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); - goto err; - } /* Update ECC layout if needed ... for 1-bit HW ECC, the default * is OK, but it allocates 6 bytes when only 3 are needed (for @@ -820,7 +813,7 @@ err: err_clk_enable: spin_lock_irq(&davinci_nand_lock); - if (ecc_mode == NAND_ECC_HW_SYNDROME) + if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) ecc4_busy = false; spin_unlock_irq(&davinci_nand_lock); return ret; From 2d01922c11601bed642e5a3d1c7051685218d0fc Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:27 +0200 Subject: [PATCH 132/155] mtd: nand: hisi504: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/hisi504_nand.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c index 7bf844c55085..9432546f4cd4 100644 --- a/drivers/mtd/nand/hisi504_nand.c +++ b/drivers/mtd/nand/hisi504_nand.c @@ -19,7 +19,6 @@ * GNU General Public License for more details. */ #include -#include #include #include #include @@ -662,10 +661,9 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host) struct device *dev = host->dev; struct nand_chip *chip = &host->chip; struct mtd_info *mtd = nand_to_mtd(chip); - struct device_node *np = host->dev->of_node; - size = of_get_nand_ecc_step_size(np); - strength = of_get_nand_ecc_strength(np); + size = chip->ecc.size; + strength = chip->ecc.strength; if (size != 1024) { dev_err(dev, "error ecc size: %d\n", size); return -EINVAL; @@ -715,7 +713,7 @@ static int hisi_nfc_ecc_probe(struct hinfc_host *host) static int hisi_nfc_probe(struct platform_device *pdev) { - int ret = 0, irq, buswidth, flag, max_chips = HINFC504_MAX_CHIP; + int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP; struct device *dev = &pdev->dev; struct hinfc_host *host; struct nand_chip *chip; @@ -767,12 +765,6 @@ static int hisi_nfc_probe(struct platform_device *pdev) chip->read_buf = hisi_nfc_read_buf; chip->chip_delay = HINFC504_CHIP_DELAY; - chip->ecc.mode = of_get_nand_ecc_mode(np); - - buswidth = of_get_nand_bus_width(np); - if (buswidth == 16) - chip->options |= NAND_BUSWIDTH_16; - hisi_nfc_host_init(host); ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host); From f6c36aaa3b6828fe2720fbdae70b58e6c0cccae6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:28 +0200 Subject: [PATCH 133/155] mtd: nand: lpc32xx: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/lpc32xx_slc.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index 219dd674dc92..8d3edc34958e 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -219,7 +218,6 @@ struct lpc32xx_nand_cfg_slc { uint32_t rwidth; uint32_t rhold; uint32_t rsetup; - bool use_bbt; int wp_gpio; struct mtd_partition *parts; unsigned num_parts; @@ -783,7 +781,6 @@ static struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev) return NULL; } - ncfg->use_bbt = of_get_nand_on_flash_bbt(np); ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0); return ncfg; @@ -918,19 +915,15 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; chip->ecc.prepad = chip->ecc.postpad = 0; - /* Avoid extra scan if using BBT, setup BBT support */ - if (host->ncfg->use_bbt) { - chip->bbt_options |= NAND_BBT_USE_FLASH; - - /* - * Use a custom BBT marker setup for small page FLASH that - * won't interfere with the ECC layout. Large and huge page - * FLASH use the standard layout. - */ - if (mtd->writesize <= 512) { - chip->bbt_td = &bbt_smallpage_main_descr; - chip->bbt_md = &bbt_smallpage_mirror_descr; - } + /* + * Use a custom BBT marker setup for small page FLASH that + * won't interfere with the ECC layout. Large and huge page + * FLASH use the standard layout. + */ + if ((chip->bbt_options & NAND_BBT_USE_FLASH) && + mtd->writesize <= 512) { + chip->bbt_td = &bbt_smallpage_main_descr; + chip->bbt_md = &bbt_smallpage_mirror_descr; } /* From 609468f94883c7840b9b22ec0ee9674513363d9e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:29 +0200 Subject: [PATCH 134/155] mtd: nand: mxc: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/mxc_nand.c | 45 +++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index c4e84e180325..aa0ba4b99395 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -1460,25 +1459,12 @@ MODULE_DEVICE_TABLE(of, mxcnd_dt_ids); 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; @@ -1578,9 +1564,6 @@ static int mxcnd_probe(struct platform_device *pdev) mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); 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; this->ecc.mode = NAND_ECC_HW; } else { this->ecc.mode = NAND_ECC_SOFT; @@ -1591,12 +1574,9 @@ static int mxcnd_probe(struct platform_device *pdev) if (host->pdata.width == 2) this->options |= NAND_BUSWIDTH_16; - if (host->pdata.flash_bbt) { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; - /* update flash based bbt */ + /* update flash based bbt */ + if (host->pdata.flash_bbt) this->bbt_options |= NAND_BBT_USE_FLASH; - } init_completion(&host->op_completion); @@ -1637,6 +1617,27 @@ static int mxcnd_probe(struct platform_device *pdev) goto escan; } + switch (this->ecc.mode) { + case NAND_ECC_HW: + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.correct = host->devtype_data->correct_data; + break; + + case NAND_ECC_SOFT: + case NAND_ECC_SOFT_BCH: + break; + + default: + err = -EINVAL; + goto escan; + } + + if (this->bbt_options & NAND_BBT_USE_FLASH) { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } + /* allocate the right size buffer now */ devm_kfree(&pdev->dev, (void *)host->data_buf); host->data_buf = devm_kzalloc(&pdev->dev, mtd->writesize + mtd->oobsize, From 14667d8d586f93cd1514dfcfe85aeda41dc47142 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:31 +0200 Subject: [PATCH 135/155] mtd: nand: sh_flctl: rely on generic DT parsing done in nand_scan_ident() The core now takes care of parsing generic DT properties in nand_scan_ident() when nand_set_flash_node() has been called. Rely on this initialization instead of calling of_get_nand_xxx() manually. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sh_flctl.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index a5abf6f70f72..6fa3bcd59769 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -1092,8 +1091,6 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) const struct of_device_id *match; struct flctl_soc_config *config; struct sh_flctl_platform_data *pdata; - struct device_node *dn = dev->of_node; - int ret; match = of_match_device(of_flctl_match, dev); if (match) @@ -1113,15 +1110,6 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) pdata->has_hwecc = config->has_hwecc; pdata->use_holden = config->use_holden; - /* parse user defined options */ - ret = of_get_nand_bus_width(dn); - if (ret == 16) - pdata->flcmncr_val |= SEL_16BIT; - else if (ret != 8) { - dev_err(dev, "%s: invalid bus width\n", __func__); - return NULL; - } - return pdata; } @@ -1184,15 +1172,14 @@ static int flctl_probe(struct platform_device *pdev) nand->chip_delay = 20; nand->read_byte = flctl_read_byte; + nand->read_word = flctl_read_word; nand->write_buf = flctl_write_buf; nand->read_buf = flctl_read_buf; nand->select_chip = flctl_select_chip; nand->cmdfunc = flctl_cmdfunc; - if (pdata->flcmncr_val & SEL_16BIT) { + if (pdata->flcmncr_val & SEL_16BIT) nand->options |= NAND_BUSWIDTH_16; - nand->read_word = flctl_read_word; - } pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); @@ -1203,6 +1190,16 @@ static int flctl_probe(struct platform_device *pdev) if (ret) goto err_chip; + if (nand->options & NAND_BUSWIDTH_16) { + /* + * NAND_BUSWIDTH_16 may have been set by nand_scan_ident(). + * Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign + * flctl->flcmncr_base to pdata->flcmncr_val. + */ + pdata->flcmncr_val |= SEL_16BIT; + flctl->flcmncr_base = pdata->flcmncr_val; + } + ret = flctl_chip_init_tail(flctl_mtd); if (ret) goto err_chip; From d48f62b9a0a035d6c16de4a4dae315f7332a8939 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 1 Apr 2016 14:54:32 +0200 Subject: [PATCH 136/155] mtd: nand: move of_get_nand_xxx() helpers into nand_base.c Now that all drivers go through nand_set_flash_node() to parse the generic NAND properties, we can move all of_get_nand_xxx() helpers in to nand_base.c, make them static and remove of_mtd.c and of_mtd.h. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 94 ++++++++++++++++++++- drivers/of/Makefile | 1 - drivers/of/of_mtd.c | 155 ----------------------------------- include/linux/of_mtd.h | 56 ------------- 4 files changed, 93 insertions(+), 213 deletions(-) delete mode 100644 drivers/of/of_mtd.c delete mode 100644 include/linux/of_mtd.h diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0001be19b90c..9f157bb3faa2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -45,7 +45,7 @@ #include #include #include -#include +#include static int nand_get_device(struct mtd_info *mtd, int new_state); @@ -3971,6 +3971,98 @@ ident_done: return type; } +static const char * const nand_ecc_modes[] = { + [NAND_ECC_NONE] = "none", + [NAND_ECC_SOFT] = "soft", + [NAND_ECC_HW] = "hw", + [NAND_ECC_HW_SYNDROME] = "hw_syndrome", + [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first", + [NAND_ECC_SOFT_BCH] = "soft_bch", +}; + +static int of_get_nand_ecc_mode(struct device_node *np) +{ + const char *pm; + int err, i; + + err = of_property_read_string(np, "nand-ecc-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++) + if (!strcasecmp(pm, nand_ecc_modes[i])) + return i; + + return -ENODEV; +} + +static int of_get_nand_ecc_algo(struct device_node *np) +{ + const char *pm; + int err; + + /* + * TODO: Read ECC algo OF property and map it to enum nand_ecc_algo. + * It's not implemented yet as currently NAND subsystem ignores + * algorithm explicitly set this way. Once it's handled we should + * document & support new property. + */ + + /* + * For backward compatibility we also read "nand-ecc-mode" checking + * for some obsoleted values that were specifying ECC algorithm. + */ + err = of_property_read_string(np, "nand-ecc-mode", &pm); + if (err < 0) + return err; + + if (!strcasecmp(pm, "soft")) + return NAND_ECC_HAMMING; + else if (!strcasecmp(pm, "soft_bch")) + return NAND_ECC_BCH; + + return -ENODEV; +} + +static int of_get_nand_ecc_step_size(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-step-size", &val); + return ret ? ret : val; +} + +static int of_get_nand_ecc_strength(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-strength", &val); + return ret ? ret : val; +} + +static int of_get_nand_bus_width(struct device_node *np) +{ + u32 val; + + if (of_property_read_u32(np, "nand-bus-width", &val)) + return 8; + + switch (val) { + case 8: + case 16: + return val; + default: + return -EIO; + } +} + +static bool of_get_nand_on_flash_bbt(struct device_node *np) +{ + return of_property_read_bool(np, "nand-on-flash-bbt"); +} + static int nand_dt_init(struct nand_chip *chip) { struct device_node *dn = nand_get_flash_node(chip); diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 156c072b3117..e31bdc123c6e 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_OF_UNITTEST) += unittest.o obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o -obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o obj-$(CONFIG_OF_RESOLVE) += resolver.o obj-$(CONFIG_OF_OVERLAY) += overlay.o diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c deleted file mode 100644 index 15d056e181d2..000000000000 --- a/drivers/of/of_mtd.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD - * - * OF helpers for mtd. - * - * This file is released under the GPLv2 - * - */ -#include -#include -#include -#include - -/** - * It maps 'enum nand_ecc_modes_t' found in include/linux/mtd/nand.h - * into the device tree binding of 'nand-ecc', so that MTD - * device driver can get nand ecc from device tree. - */ -static const char *nand_ecc_modes[] = { - [NAND_ECC_NONE] = "none", - [NAND_ECC_SOFT] = "soft", - [NAND_ECC_HW] = "hw", - [NAND_ECC_HW_SYNDROME] = "hw_syndrome", - [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first", - [NAND_ECC_SOFT_BCH] = "soft_bch", -}; - -/** - * of_get_nand_ecc_mode - Get nand ecc mode for given device_node - * @np: Pointer to the given device_node - * - * The function gets ecc mode string from property 'nand-ecc-mode', - * and return its index in nand_ecc_modes table, or errno in error case. - */ -int of_get_nand_ecc_mode(struct device_node *np) -{ - const char *pm; - int err, i; - - err = of_property_read_string(np, "nand-ecc-mode", &pm); - if (err < 0) - return err; - - for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++) - if (!strcasecmp(pm, nand_ecc_modes[i])) - return i; - - return -ENODEV; -} -EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode); - -/** - * of_get_nand_ecc_algo - Get nand ecc algorithm for given device_node - * @np: Pointer to the given device_node - * - * The function gets ecc algorithm and returns its enum value, or errno in error - * case. - */ -int of_get_nand_ecc_algo(struct device_node *np) -{ - const char *pm; - int err; - - /* - * TODO: Read ECC algo OF property and map it to enum nand_ecc_algo. - * It's not implemented yet as currently NAND subsystem ignores - * algorithm explicitly set this way. Once it's handled we should - * document & support new property. - */ - - /* - * For backward compatibility we also read "nand-ecc-mode" checking - * for some obsoleted values that were specifying ECC algorithm. - */ - err = of_property_read_string(np, "nand-ecc-mode", &pm); - if (err < 0) - return err; - - if (!strcasecmp(pm, "soft")) - return NAND_ECC_HAMMING; - else if (!strcasecmp(pm, "soft_bch")) - return NAND_ECC_BCH; - - return -ENODEV; -} -EXPORT_SYMBOL_GPL(of_get_nand_ecc_algo); - -/** - * of_get_nand_ecc_step_size - Get ECC step size associated to - * the required ECC strength (see below). - * @np: Pointer to the given device_node - * - * return the ECC step size, or errno in error case. - */ -int of_get_nand_ecc_step_size(struct device_node *np) -{ - int ret; - u32 val; - - ret = of_property_read_u32(np, "nand-ecc-step-size", &val); - return ret ? ret : val; -} -EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size); - -/** - * of_get_nand_ecc_strength - Get required ECC strength over the - * correspnding step size as defined by 'nand-ecc-size' - * @np: Pointer to the given device_node - * - * return the ECC strength, or errno in error case. - */ -int of_get_nand_ecc_strength(struct device_node *np) -{ - int ret; - u32 val; - - ret = of_property_read_u32(np, "nand-ecc-strength", &val); - return ret ? ret : val; -} -EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength); - -/** - * of_get_nand_bus_width - Get nand bus witdh for given device_node - * @np: Pointer to the given device_node - * - * return bus width option, or errno in error case. - */ -int of_get_nand_bus_width(struct device_node *np) -{ - u32 val; - - if (of_property_read_u32(np, "nand-bus-width", &val)) - return 8; - - switch(val) { - case 8: - case 16: - return val; - default: - return -EIO; - } -} -EXPORT_SYMBOL_GPL(of_get_nand_bus_width); - -/** - * of_get_nand_on_flash_bbt - Get nand on flash bbt for given device_node - * @np: Pointer to the given device_node - * - * return true if present false other wise - */ -bool of_get_nand_on_flash_bbt(struct device_node *np) -{ - return of_property_read_bool(np, "nand-on-flash-bbt"); -} -EXPORT_SYMBOL_GPL(of_get_nand_on_flash_bbt); diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h deleted file mode 100644 index 0f6aca5c6f2f..000000000000 --- a/include/linux/of_mtd.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2012 Jean-Christophe PLAGNIOL-VILLARD - * - * OF helpers for mtd. - * - * This file is released under the GPLv2 - */ - -#ifndef __LINUX_OF_MTD_H -#define __LINUX_OF_MTD_H - -#ifdef CONFIG_OF_MTD - -#include -int of_get_nand_ecc_mode(struct device_node *np); -int of_get_nand_ecc_algo(struct device_node *np); -int of_get_nand_ecc_step_size(struct device_node *np); -int of_get_nand_ecc_strength(struct device_node *np); -int of_get_nand_bus_width(struct device_node *np); -bool of_get_nand_on_flash_bbt(struct device_node *np); - -#else /* CONFIG_OF_MTD */ - -static inline int of_get_nand_ecc_mode(struct device_node *np) -{ - return -ENOSYS; -} - -static inline int of_get_nand_ecc_algo(struct device_node *np) -{ - return -ENOSYS; -} - -static inline int of_get_nand_ecc_step_size(struct device_node *np) -{ - return -ENOSYS; -} - -static inline int of_get_nand_ecc_strength(struct device_node *np) -{ - return -ENOSYS; -} - -static inline int of_get_nand_bus_width(struct device_node *np) -{ - return -ENOSYS; -} - -static inline bool of_get_nand_on_flash_bbt(struct device_node *np) -{ - return false; -} - -#endif /* CONFIG_OF_MTD */ - -#endif /* __LINUX_OF_MTD_H */ From 7778478501938e071d40eeadb5d7d01770ce799f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 19 Apr 2016 14:33:35 +0200 Subject: [PATCH 137/155] mtd: nandsim: add __init attribute Add __init attribute on functions that are only called from other __init functions and that are not inlined, at least with gcc version 4.8.4 on an x86 machine with allyesconfig. Currently, the functions are put in the .text.unlikely segment. Declaring them as __init will cause them to be put in the .init.text and to disappear after initialization. The result of objdump -x on the functions before the change is as follows: 000000000000059a l F .text.unlikely 0000000000000239 alloc_device 000000000000034e l F .text.unlikely 000000000000002e get_partition_name 00000000000007d3 l F .text.unlikely 00000000000005da init_nandsim And after the change it is as follows: 0000000000000029 l F .init.text 0000000000000234 alloc_device 0000000000000000 l F .init.text 0000000000000029 get_partition_name 000000000000025d l F .init.text 00000000000005d5 init_nandsim Done with the help of Coccinelle. The semantic patch checks for local static non-init functions that are called from an __init function and are not called from any other function. Signed-off-by: Julia Lawall Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nandsim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 6ff1d8d31ac2..66eebb9e77f3 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -569,7 +569,7 @@ static void nandsim_debugfs_remove(struct nandsim *ns) * * RETURNS: 0 if success, -ENOMEM if memory alloc fails. */ -static int alloc_device(struct nandsim *ns) +static int __init alloc_device(struct nandsim *ns) { struct file *cfile; int i, err; @@ -654,7 +654,7 @@ static void free_device(struct nandsim *ns) } } -static char *get_partition_name(int i) +static char __init *get_partition_name(int i) { return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i); } @@ -664,7 +664,7 @@ static char *get_partition_name(int i) * * RETURNS: 0 if success, -ERRNO if failure. */ -static int init_nandsim(struct mtd_info *mtd) +static int __init init_nandsim(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); struct nandsim *ns = nand_get_controller_data(chip); From 03d3a1df6da660999d8f14552ac4d875e94090a3 Mon Sep 17 00:00:00 2001 From: "Cooper Jr., Franklin" Date: Fri, 15 Apr 2016 15:28:58 -0500 Subject: [PATCH 138/155] mtd: nand: omap2: Start dma request before enabling prefetch The prefetch engine sends a dma request once a FIFO threshold has been met. No other requests are received until the previous request is handled. Starting a dma transfer (dma_async_issue_pending) results in any previous event for the dma channel to be cleared. Therefore, starting the prefetch engine before initiating the dma transfer may result in the prefetch triggering a dma request but instead of it being handled it can end up being cleared. This will result in a hang since the code will continue to wait for the dma request to complete. By initiating the dma request before enabling the prefetch engine this race condition is avoided and no dma request are missed/cleared. Signed-off-by: Franklin S Cooper Jr Signed-off-by: Boris Brezillon --- drivers/mtd/nand/omap2.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 141852fc6455..d1496b1276df 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -503,6 +503,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, tx->callback_param = &info->comp; dmaengine_submit(tx); + init_completion(&info->comp); + + /* setup and start DMA using dma_addr */ + dma_async_issue_pending(info->dma); + /* configure and start prefetch transfer */ ret = omap_prefetch_enable(info->gpmc_cs, PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info); @@ -510,10 +515,6 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, /* PFPW engine is busy, use cpu copy method */ goto out_copy_unmap; - init_completion(&info->comp); - dma_async_issue_pending(info->dma); - - /* setup and start DMA using dma_addr */ wait_for_completion(&info->comp); tim = 0; limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); From 8c6f0fc4d2d505b17fc67675f0797fe3fc6e6c26 Mon Sep 17 00:00:00 2001 From: "Cooper Jr., Franklin" Date: Fri, 15 Apr 2016 15:28:59 -0500 Subject: [PATCH 139/155] mtd: nand: omap2: Fix high memory dma prefetch transfer Based on DMA documentation and testing using high memory buffer when doing dma transfers can lead to various issues including kernel panics. To workaround this simply use cpu copy. Signed-off-by: Franklin S Cooper Jr Signed-off-by: Boris Brezillon --- drivers/mtd/nand/omap2.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index d1496b1276df..08e158895635 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -473,17 +473,8 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, int ret; u32 val; - if (addr >= high_memory) { - struct page *p1; - - if (((size_t)addr & PAGE_MASK) != - ((size_t)(addr + len - 1) & PAGE_MASK)) - goto out_copy; - p1 = vmalloc_to_page(addr); - if (!p1) - goto out_copy; - addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK); - } + if (!virt_addr_valid(addr)) + goto out_copy; sg_init_one(&sg, addr, len); n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); From 04dae62c89bd6e754518f9dcffac480e39f28758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:52:57 +0200 Subject: [PATCH 140/155] mtd: nand: bf5xx: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/bf5xx_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index b38f41470414..37da4236ab90 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -817,6 +817,7 @@ static int bf5xx_nand_probe(struct platform_device *pdev) chip->ecc.write_page_raw = bf5xx_nand_write_page_raw; } else { chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; } /* scan hardware nand chip and setup mtd info data struct */ From 867f9873fef69414355e2e1ef547f9b080c3fd69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:52:58 +0200 Subject: [PATCH 141/155] mtd: nand: davinci: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/davinci_nand.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 0198536a1944..cc07ba0f044d 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -723,8 +723,17 @@ static int nand_davinci_probe(struct platform_device *pdev) switch (info->chip.ecc.mode) { case NAND_ECC_NONE: + pdata->ecc_bits = 0; + break; case NAND_ECC_SOFT: pdata->ecc_bits = 0; + /* + * This driver expects Hamming based ECC when ecc_mode is set + * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to + * avoid adding an extra ->ecc_algo field to + * davinci_nand_pdata. + */ + info->chip.ecc.algo = NAND_ECC_HAMMING; break; case NAND_ECC_HW: if (pdata->ecc_bits == 4) { From d5f304ac6aff1462ddf3c14f48e2b849ab68d5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:52:59 +0200 Subject: [PATCH 142/155] mtd: nand: atmel: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/atmel_nand.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index eec8ca739cae..efc8ea250c1d 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1212,6 +1212,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev, dev_warn(host->dev, "Can't get I/O resource regs for PMECC controller, rolling back on software ECC\n"); nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.algo = NAND_ECC_HAMMING; return 0; } @@ -1295,6 +1296,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev, /* page size not handled by HW ECC */ /* switching back to soft ECC */ nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.algo = NAND_ECC_HAMMING; return 0; } @@ -1613,6 +1615,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host, * even if the nand-ecc-mode property is not defined. */ host->nand_chip.ecc.mode = NAND_ECC_SOFT; + host->nand_chip.ecc.algo = NAND_ECC_HAMMING; return 0; } @@ -1629,6 +1632,7 @@ static int atmel_hw_nand_init_params(struct platform_device *pdev, dev_err(host->dev, "Can't get I/O resource regs, use software ECC\n"); nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.algo = NAND_ECC_HAMMING; return 0; } @@ -1661,6 +1665,7 @@ static int atmel_hw_nand_init_params(struct platform_device *pdev, /* page size not handled by HW ECC */ /* switching back to soft ECC */ nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.algo = NAND_ECC_HAMMING; return 0; } @@ -2160,6 +2165,14 @@ static int atmel_nand_probe(struct platform_device *pdev) sizeof(struct atmel_nand_data)); nand_chip->ecc.mode = host->board.ecc_mode; + /* + * When using software ECC every supported avr32 board means + * Hamming algorithm. If that ever changes we'll need to add + * ecc_algo field to the struct atmel_nand_data. + */ + if (nand_chip->ecc.mode == NAND_ECC_SOFT) + nand_chip->ecc.algo = NAND_ECC_HAMMING; + /* 16-bit bus width */ if (host->board.bus_width_16) nand_chip->options |= NAND_BUSWIDTH_16; From 40438a1ea7fb8326daffa692dbf41a5c60bbf3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:53:00 +0200 Subject: [PATCH 143/155] CRIS v32: nand: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Acked-by: Mikael Starvik Acked-by: Jesper Nilsson Signed-off-by: Boris Brezillon --- arch/cris/arch-v32/drivers/mach-a3/nandflash.c | 1 + arch/cris/arch-v32/drivers/mach-fs/nandflash.c | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/cris/arch-v32/drivers/mach-a3/nandflash.c b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c index 5aa3f5162310..3f646c787e58 100644 --- a/arch/cris/arch-v32/drivers/mach-a3/nandflash.c +++ b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c @@ -157,6 +157,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void) /* 20 us command delay time */ this->chip_delay = 20; this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; /* Enable the following for a flash based bad block table */ /* this->bbt_options = NAND_BBT_USE_FLASH; */ diff --git a/arch/cris/arch-v32/drivers/mach-fs/nandflash.c b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c index a7c17b0f172a..a74540514bdb 100644 --- a/arch/cris/arch-v32/drivers/mach-fs/nandflash.c +++ b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c @@ -148,6 +148,7 @@ struct mtd_info *__init crisv32_nand_flash_probe(void) /* 20 us command delay time */ this->chip_delay = 20; this->ecc.mode = NAND_ECC_SOFT; + this->ecc.algo = NAND_ECC_HAMMING; /* Enable the following for a flash based bad block table */ /* this->bbt_options = NAND_BBT_USE_FLASH; */ From cc71538fab2f5470775b060c59eddafe8ffa2674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:53:01 +0200 Subject: [PATCH 144/155] staging: mt29f_spinand: set ECC algorithm explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Acked-by: Greg Kroah-Hartman Signed-off-by: Boris Brezillon --- drivers/staging/mt29f_spinand/mt29f_spinand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c index f50369941590..e389009fca42 100644 --- a/drivers/staging/mt29f_spinand/mt29f_spinand.c +++ b/drivers/staging/mt29f_spinand/mt29f_spinand.c @@ -900,6 +900,7 @@ static int spinand_probe(struct spi_device *spi_nand) chip->ecc.write_page = spinand_write_page_hwecc; #else chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; if (spinand_disable_ecc(spi_nand) < 0) dev_info(&spi_nand->dev, "%s: disable ecc failed!\n", __func__); From e9d4faed71d5a63fa0ef8fcfef559f67f8b85a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:53:02 +0200 Subject: [PATCH 145/155] mtd: nand: set ECC algorithm to Hamming on fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is part of process deprecating NAND_ECC_SOFT_BCH (and switching to enum nand_ecc_algo). Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9f157bb3faa2..2a049576ab31 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4337,6 +4337,7 @@ int nand_scan_tail(struct mtd_info *mtd) pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", ecc->size, mtd->writesize); ecc->mode = NAND_ECC_SOFT; + ecc->algo = NAND_ECC_HAMMING; case NAND_ECC_SOFT: ecc->calculate = nand_calculate_ecc; From ef296dc947f6a9300a7fb5b696d1e1f543479e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:53:04 +0200 Subject: [PATCH 146/155] mtd: nand: fsmc: validate ECC setup by checking algorithm directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NAND core sets ECC algorithm in algo field now and it should be preferred over the mode field. This also prepares driver for dropping NAND_ECC_SOFT_BCH. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsmc_nand.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 13720405ec81..0f8c63f85f86 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -958,9 +958,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->ecc.strength = 1; break; + case NAND_ECC_SOFT: case NAND_ECC_SOFT_BCH: - dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n"); - break; + if (nand->ecc.algo == NAND_ECC_BCH) { + dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n"); + break; + } default: dev_err(&pdev->dev, "Unsupported ECC mode!\n"); From 06f384c9010ea7fa1146b9dfdd419d99c9b8a962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:53:05 +0200 Subject: [PATCH 147/155] mtd: nand: read ECC algorithm from the new field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have all drivers properly setting this new field we can start using it. For a very short period of time we should support both values: NAND_ECC_SOFT and NAND_ECC_SOFT_BCH treating them the same. It's because of_get_nand_ecc_mode may still be setting NAND_ECC_SOFT_BCH. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 146 ++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 61 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2a049576ab31..9f967531bc68 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4171,6 +4171,83 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, } EXPORT_SYMBOL(nand_scan_ident); +static int nand_set_ecc_soft_ops(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + if (WARN_ON(ecc->mode != NAND_ECC_SOFT && + ecc->mode != NAND_ECC_SOFT_BCH)) + return -EINVAL; + + switch (ecc->algo) { + case NAND_ECC_HAMMING: + ecc->calculate = nand_calculate_ecc; + ecc->correct = nand_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + if (!ecc->size) + ecc->size = 256; + ecc->bytes = 3; + ecc->strength = 1; + return 0; + case NAND_ECC_BCH: + if (!mtd_nand_has_bch()) { + WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + return -EINVAL; + } + ecc->calculate = nand_bch_calculate_ecc; + ecc->correct = nand_bch_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + /* + * Board driver should supply ecc.size and ecc.strength + * values to select how many bits are correctable. + * Otherwise, default to 4 bits for large page devices. + */ + if (!ecc->size && (mtd->oobsize >= 64)) { + ecc->size = 512; + ecc->strength = 4; + } + + /* + * if no ecc placement scheme was provided pickup the default + * large page one. + */ + if (!mtd->ooblayout) { + /* handle large page devices only */ + if (mtd->oobsize < 64) { + WARN(1, "OOB layout is required when using software BCH on small pages\n"); + return -EINVAL; + } + + mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + } + + /* See nand_bch_init() for details. */ + ecc->bytes = 0; + ecc->priv = nand_bch_init(mtd); + if (!ecc->priv) { + WARN(1, "BCH ECC initialization failed!\n"); + return -EINVAL; + } + return 0; + default: + WARN(1, "Unsupported ECC algorithm!\n"); + return -EINVAL; + } +} + /* * Check if the chip configuration meet the datasheet requirements. @@ -4246,7 +4323,9 @@ int nand_scan_tail(struct mtd_info *mtd) /* * If no default placement scheme is given, select an appropriate one. */ - if (!mtd->ooblayout && (ecc->mode != NAND_ECC_SOFT_BCH)) { + if (!mtd->ooblayout && + !((ecc->mode == NAND_ECC_SOFT || ecc->mode == NAND_ECC_SOFT_BCH) && + ecc->algo == NAND_ECC_BCH)) { switch (mtd->oobsize) { case 8: case 16: @@ -4340,66 +4419,9 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->algo = NAND_ECC_HAMMING; case NAND_ECC_SOFT: - ecc->calculate = nand_calculate_ecc; - ecc->correct = nand_correct_data; - ecc->read_page = nand_read_page_swecc; - ecc->read_subpage = nand_read_subpage; - ecc->write_page = nand_write_page_swecc; - ecc->read_page_raw = nand_read_page_raw; - ecc->write_page_raw = nand_write_page_raw; - ecc->read_oob = nand_read_oob_std; - ecc->write_oob = nand_write_oob_std; - if (!ecc->size) - ecc->size = 256; - ecc->bytes = 3; - ecc->strength = 1; - break; - case NAND_ECC_SOFT_BCH: - if (!mtd_nand_has_bch()) { - WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n"); - ret = -EINVAL; - goto err_free; - } - ecc->calculate = nand_bch_calculate_ecc; - ecc->correct = nand_bch_correct_data; - ecc->read_page = nand_read_page_swecc; - ecc->read_subpage = nand_read_subpage; - ecc->write_page = nand_write_page_swecc; - ecc->read_page_raw = nand_read_page_raw; - ecc->write_page_raw = nand_write_page_raw; - ecc->read_oob = nand_read_oob_std; - ecc->write_oob = nand_write_oob_std; - /* - * Board driver should supply ecc.size and ecc.strength values - * to select how many bits are correctable. Otherwise, default - * to 4 bits for large page devices. - */ - if (!ecc->size && (mtd->oobsize >= 64)) { - ecc->size = 512; - ecc->strength = 4; - } - - /* - * if no ecc placement scheme was provided pickup the default - * large page one. - */ - if (!mtd->ooblayout) { - /* handle large page devices only */ - if (mtd->oobsize < 64) { - WARN(1, "OOB layout is required when using software BCH on small pages\n"); - ret = -EINVAL; - goto err_free; - } - - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); - } - - /* See nand_bch_init() for details. */ - ecc->bytes = 0; - ecc->priv = nand_bch_init(mtd); - if (!ecc->priv) { - WARN(1, "BCH ECC initialization failed!\n"); + ret = nand_set_ecc_soft_ops(mtd); + if (ret) { ret = -EINVAL; goto err_free; } @@ -4585,7 +4607,9 @@ void nand_release(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); - if (chip->ecc.mode == NAND_ECC_SOFT_BCH) + if ((chip->ecc.mode == NAND_ECC_SOFT || + chip->ecc.mode == NAND_ECC_SOFT_BCH) && + chip->ecc.algo == NAND_ECC_BCH) nand_bch_free((struct nand_bch_control *)chip->ecc.priv); mtd_device_unregister(mtd); From ae211bcfa1d013bea16912e7caebb1367740d4b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:53:06 +0200 Subject: [PATCH 148/155] mtd: drop support for NAND_ECC_SOFT_BCH as "soft_bch" mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There isn't any difference between handling NAND_ECC_SOFT and NAND_ECC_SOFT_BCH now. What matters is the new field called "algo". Please note we're keeping backward DT compatibility. We are still treating "soft_bch" value as the one setting Hamming algorithm, it's just handled in of_get_nand_ecc_algo now. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/nand_base.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9f967531bc68..1d74b802aa0a 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3977,7 +3977,6 @@ static const char * const nand_ecc_modes[] = { [NAND_ECC_HW] = "hw", [NAND_ECC_HW_SYNDROME] = "hw_syndrome", [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first", - [NAND_ECC_SOFT_BCH] = "soft_bch", }; static int of_get_nand_ecc_mode(struct device_node *np) @@ -3993,6 +3992,14 @@ static int of_get_nand_ecc_mode(struct device_node *np) if (!strcasecmp(pm, nand_ecc_modes[i])) return i; + /* + * For backward compatibility we support few obsoleted values that don't + * have their mappings into nand_ecc_modes_t anymore (they were merged + * with other enums). + */ + if (!strcasecmp(pm, "soft_bch")) + return NAND_ECC_SOFT; + return -ENODEV; } From e4225ae8234cf5548c38dc887b233ad1d45b4d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 17 Apr 2016 22:53:07 +0200 Subject: [PATCH 149/155] mtd: mtd: drop NAND_ECC_SOFT_BCH enum value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This value should not be part of nand_ecc_modes_t as it specifies algorithm not a mode. We successfully managed to introduce new "algo" field which is respected now. Signed-off-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/fsmc_nand.c | 3 +-- drivers/mtd/nand/jz4780_nand.c | 1 - drivers/mtd/nand/mxc_nand.c | 1 - drivers/mtd/nand/nand_base.c | 11 +++-------- drivers/mtd/nand/nandsim.c | 2 +- drivers/mtd/nand/sunxi_nand.c | 2 -- include/linux/mtd/nand.h | 1 - 7 files changed, 5 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 0f8c63f85f86..d4f454a4b35e 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -959,7 +959,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) break; case NAND_ECC_SOFT: - case NAND_ECC_SOFT_BCH: if (nand->ecc.algo == NAND_ECC_BCH) { dev_info(&pdev->dev, "Using 4-bit SW BCH ECC scheme\n"); break; @@ -974,7 +973,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * Don't set layout for BCH4 SW ECC. This will be * generated later in nand_bch_init() later. */ - if (nand->ecc.mode != NAND_ECC_SOFT_BCH) { + if (nand->ecc.mode == NAND_ECC_HW) { switch (mtd->oobsize) { case 16: case 64: diff --git a/drivers/mtd/nand/jz4780_nand.c b/drivers/mtd/nand/jz4780_nand.c index 10f249ac148c..daf3c4217f4d 100644 --- a/drivers/mtd/nand/jz4780_nand.c +++ b/drivers/mtd/nand/jz4780_nand.c @@ -179,7 +179,6 @@ static int jz4780_nand_init_ecc(struct jz4780_nand_chip *nand, struct device *de chip->ecc.correct = jz4780_nand_ecc_correct; /* fall through */ case NAND_ECC_SOFT: - case NAND_ECC_SOFT_BCH: dev_info(dev, "using %s (strength %d, size %d, bytes %d)\n", (nfc->bch) ? "hardware BCH" : "software ECC", chip->ecc.strength, chip->ecc.size, chip->ecc.bytes); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index aa0ba4b99395..5173fadc9a4e 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1625,7 +1625,6 @@ static int mxcnd_probe(struct platform_device *pdev) break; case NAND_ECC_SOFT: - case NAND_ECC_SOFT_BCH: break; default: diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 1d74b802aa0a..c9d6230eab08 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4183,8 +4183,7 @@ static int nand_set_ecc_soft_ops(struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; - if (WARN_ON(ecc->mode != NAND_ECC_SOFT && - ecc->mode != NAND_ECC_SOFT_BCH)) + if (WARN_ON(ecc->mode != NAND_ECC_SOFT)) return -EINVAL; switch (ecc->algo) { @@ -4331,8 +4330,7 @@ int nand_scan_tail(struct mtd_info *mtd) * If no default placement scheme is given, select an appropriate one. */ if (!mtd->ooblayout && - !((ecc->mode == NAND_ECC_SOFT || ecc->mode == NAND_ECC_SOFT_BCH) && - ecc->algo == NAND_ECC_BCH)) { + !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) { switch (mtd->oobsize) { case 8: case 16: @@ -4426,7 +4424,6 @@ int nand_scan_tail(struct mtd_info *mtd) ecc->algo = NAND_ECC_HAMMING; case NAND_ECC_SOFT: - case NAND_ECC_SOFT_BCH: ret = nand_set_ecc_soft_ops(mtd); if (ret) { ret = -EINVAL; @@ -4514,7 +4511,6 @@ int nand_scan_tail(struct mtd_info *mtd) /* Large page NAND with SOFT_ECC should support subpage reads */ switch (ecc->mode) { case NAND_ECC_SOFT: - case NAND_ECC_SOFT_BCH: if (chip->page_shift > 9) chip->options |= NAND_SUBPAGE_READ; break; @@ -4614,8 +4610,7 @@ void nand_release(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); - if ((chip->ecc.mode == NAND_ECC_SOFT || - chip->ecc.mode == NAND_ECC_SOFT_BCH) && + if (chip->ecc.mode == NAND_ECC_SOFT && chip->ecc.algo == NAND_ECC_BCH) nand_bch_free((struct nand_bch_control *)chip->ecc.priv); diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 66eebb9e77f3..794745dff7f1 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -2339,7 +2339,7 @@ static int __init ns_init_module(void) retval = -EINVAL; goto error; } - chip->ecc.mode = NAND_ECC_SOFT_BCH; + chip->ecc.mode = NAND_ECC_SOFT; chip->ecc.algo = NAND_ECC_BCH; chip->ecc.size = 512; chip->ecc.strength = bch; diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 1baf89836210..a83a690688b4 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1612,8 +1612,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc, return -EINVAL; switch (ecc->mode) { - case NAND_ECC_SOFT_BCH: - break; case NAND_ECC_HW: ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np); if (ret) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index e851839daf09..fbe8e164a4ee 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -116,7 +116,6 @@ typedef enum { NAND_ECC_HW, NAND_ECC_HW_SYNDROME, NAND_ECC_HW_OOB_FIRST, - NAND_ECC_SOFT_BCH, } nand_ecc_modes_t; enum nand_ecc_algo { From ba4f46b28f87396d3270d05d8d0e08467471b7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 22 Apr 2016 13:23:13 +0200 Subject: [PATCH 150/155] mtd: nand: add support for "nand-ecc-algo" DT property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far it was only possible to specify ECC algorithm using "soft" and "soft_bch" values of nand-ecc-mode prop. There wasn't a way to specify it for a hardware ECC mode. Now that we have independent field in NAND subsystem for storing info about ECC algorithm we may also add support for this new DT property. Signed-off-by: Rafał Miłecki Acked-by: Rob Herring Signed-off-by: Boris Brezillon --- .../devicetree/bindings/mtd/nand.txt | 2 ++ drivers/mtd/nand/nand_base.c | 20 ++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt index a17662b1dfcb..5ac4ab75ea0c 100644 --- a/Documentation/devicetree/bindings/mtd/nand.txt +++ b/Documentation/devicetree/bindings/mtd/nand.txt @@ -22,6 +22,8 @@ Optional NAND chip properties: - nand-ecc-mode : String, operation mode of the NAND ecc mode. Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", "soft_bch". +- nand-ecc-algo: string, algorithm of NAND ECC. + Supported values are: "hamming", "bch". - nand-bus-width : 8 or 16 bus width if not present 8 - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c9d6230eab08..c26f1852787b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4003,17 +4003,23 @@ static int of_get_nand_ecc_mode(struct device_node *np) return -ENODEV; } +static const char * const nand_ecc_algos[] = { + [NAND_ECC_HAMMING] = "hamming", + [NAND_ECC_BCH] = "bch", +}; + static int of_get_nand_ecc_algo(struct device_node *np) { const char *pm; - int err; + int err, i; - /* - * TODO: Read ECC algo OF property and map it to enum nand_ecc_algo. - * It's not implemented yet as currently NAND subsystem ignores - * algorithm explicitly set this way. Once it's handled we should - * document & support new property. - */ + err = of_property_read_string(np, "nand-ecc-algo", &pm); + if (!err) { + for (i = NAND_ECC_HAMMING; i < ARRAY_SIZE(nand_ecc_algos); i++) + if (!strcasecmp(pm, nand_ecc_algos[i])) + return i; + return -ENODEV; + } /* * For backward compatibility we also read "nand-ecc-mode" checking From 32698aafc9f0c6373b240f15a91fefb65c55907c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 22 Apr 2016 13:23:14 +0200 Subject: [PATCH 151/155] Documentation: devicetree: deprecate "soft_bch" nand-ecc-mode value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we support nand-ecc-algo property it should be used together with "soft" to specify software BCH ECC. Signed-off-by: Rafał Miłecki Acked-by: Rob Herring Signed-off-by: Boris Brezillon --- Documentation/devicetree/bindings/mtd/nand.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt index 5ac4ab75ea0c..68342eac2383 100644 --- a/Documentation/devicetree/bindings/mtd/nand.txt +++ b/Documentation/devicetree/bindings/mtd/nand.txt @@ -20,8 +20,10 @@ Optional NAND controller properties Optional NAND chip properties: - nand-ecc-mode : String, operation mode of the NAND ecc mode. - Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", - "soft_bch". + Supported values are: "none", "soft", "hw", "hw_syndrome", + "hw_oob_first". + Deprecated values: + "soft_bch": use "soft" and nand-ecc-algo instead - nand-ecc-algo: string, algorithm of NAND ECC. Supported values are: "hamming", "bch". - nand-bus-width : 8 or 16 bus width if not present 8 From bd2e778c9ee361c23ccb2b10591712e129d97893 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 25 Apr 2016 14:35:12 +0200 Subject: [PATCH 152/155] gpmi-nand: Handle ECC Errors in erased pages ECC is only calculated for written pages. As erased pages are not actively written the ECC is always invalid. For this purpose the Hardware BCH unit is able to check for erased pages and does not raise an ECC error in this case. This behaviour can be influenced using the BCH_MODE register which sets the number of allowed bitflips in an erased page. Unfortunately the unit is not capable of fixing the bitflips in memory. To avoid complete software checks for erased pages, we can simply check buffers with uncorrectable ECC errors because we know that any erased page with errors is uncorrectable by the BCH unit. This patch adds the generic nand_check_erased_ecc_chunk() to gpmi-nand to correct erased pages. To have the valid data in the buffer before using them, this patch moves the read_page_swap_end() call before the ECC status checking for-loop. Signed-off-by: Markus Pargmann [Squashed patches by Stefan and Boris to check ECC area] Tested-by: Stefan Christ Acked-by: Han xu Signed-off-by: Boris Brezillon --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 78 ++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 798ed5961e8f..6e461560c6a8 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1064,14 +1064,87 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, /* Loop over status bytes, accumulating ECC status. */ status = auxiliary_virt + nfc_geo->auxiliary_status_offset; + read_page_swap_end(this, buf, nfc_geo->payload_size, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); + for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) continue; if (*status == STATUS_UNCORRECTABLE) { + int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; + u8 *eccbuf = this->raw_buffer; + int offset, bitoffset; + int eccbytes; + int flips; + + /* Read ECC bytes into our internal raw_buffer */ + offset = nfc_geo->metadata_size * 8; + offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1); + offset -= eccbits; + bitoffset = offset % 8; + eccbytes = DIV_ROUND_UP(offset + eccbits, 8); + offset /= 8; + eccbytes -= offset; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + chip->read_buf(mtd, eccbuf, eccbytes); + + /* + * ECC data are not byte aligned and we may have + * in-band data in the first and last byte of + * eccbuf. Set non-eccbits to one so that + * nand_check_erased_ecc_chunk() does not count them + * as bitflips. + */ + if (bitoffset) + eccbuf[0] |= GENMASK(bitoffset - 1, 0); + + bitoffset = (bitoffset + eccbits) % 8; + if (bitoffset) + eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset); + + /* + * The ECC hardware has an uncorrectable ECC status + * code in case we have bitflips in an erased page. As + * nothing was written into this subpage the ECC is + * obviously wrong and we can not trust it. We assume + * at this point that we are reading an erased page and + * try to correct the bitflips in buffer up to + * ecc_strength bitflips. If this is a page with random + * data, we exceed this number of bitflips and have a + * ECC failure. Otherwise we use the corrected buffer. + */ + if (i == 0) { + /* The first block includes metadata */ + flips = nand_check_erased_ecc_chunk( + buf + i * nfc_geo->ecc_chunk_size, + nfc_geo->ecc_chunk_size, + eccbuf, eccbytes, + auxiliary_virt, + nfc_geo->metadata_size, + nfc_geo->ecc_strength); + } else { + flips = nand_check_erased_ecc_chunk( + buf + i * nfc_geo->ecc_chunk_size, + nfc_geo->ecc_chunk_size, + eccbuf, eccbytes, + NULL, 0, + nfc_geo->ecc_strength); + } + + if (flips > 0) { + max_bitflips = max_t(unsigned int, max_bitflips, + flips); + mtd->ecc_stats.corrected += flips; + continue; + } + mtd->ecc_stats.failed++; continue; } + mtd->ecc_stats.corrected += *status; max_bitflips = max_t(unsigned int, max_bitflips, *status); } @@ -1091,11 +1164,6 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; } - read_page_swap_end(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); - return max_bitflips; } From 666b65683dad9aa90efaa4aad24ef3710101e3aa Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 25 Apr 2016 22:53:55 -0700 Subject: [PATCH 153/155] mtd: brcmnand: respect ECC algorithm set by NAND subsystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is more obvious than guessing based on ECC strength. It allows using NAND on devices with BCH-1 (e.g. D-Link DIR-885L). This maintains DT backward compatibility by defaulting to Hamming if a 1-bit ECC algorithm is specified without a corresponding algorithm selection. i.e., to use BCH-1, you must specify: nand-ecc-strength = <1>; nand-ecc-step-size = <512>; nand-ecc-algo = "bch"; Also adds a check to ensure we haven't allowed someone to get by with SW ECC. If we want to support SW ECC, we need to refactor some other pieces of this driver. Signed-off-by: Brian Norris Tested-by: Rafał Miłecki Signed-off-by: Boris Brezillon --- drivers/mtd/nand/brcmnand/brcmnand.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index c3331ffcaffd..b76ad7c0144f 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c @@ -1925,9 +1925,31 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) cfg->col_adr_bytes = 2; cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize); + if (chip->ecc.mode != NAND_ECC_HW) { + dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n", + chip->ecc.mode); + return -EINVAL; + } + + if (chip->ecc.algo == NAND_ECC_UNKNOWN) { + if (chip->ecc.strength == 1 && chip->ecc.size == 512) + /* Default to Hamming for 1-bit ECC, if unspecified */ + chip->ecc.algo = NAND_ECC_HAMMING; + else + /* Otherwise, BCH */ + chip->ecc.algo = NAND_ECC_BCH; + } + + if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 || + chip->ecc.size != 512)) { + dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n", + chip->ecc.strength, chip->ecc.size); + return -EINVAL; + } + switch (chip->ecc.size) { case 512: - if (chip->ecc.strength == 1) /* Hamming */ + if (chip->ecc.algo == NAND_ECC_HAMMING) cfg->ecc_level = 15; else cfg->ecc_level = chip->ecc.strength; From 3603ea0aeed7dc2b54144b5913eb303173361123 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 6 May 2016 09:31:18 -0700 Subject: [PATCH 154/155] mtd: nand_bch: fix spelling of "probably" Signed-off-by: Brian Norris Acked-by: Boris Brezillon --- drivers/mtd/nand/nand_bch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c index 28ef7b178eac..44763f87eae4 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -175,7 +175,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) * which is called by mtd_ooblayout_count_eccbytes(). * Make sure they are properly initialized before calling * mtd_ooblayout_count_eccbytes(). - * FIXME: we should probaly rework the sequencing in nand_scan_tail() + * FIXME: we should probably rework the sequencing in nand_scan_tail() * to avoid setting those fields twice. */ nand->ecc.steps = eccsteps; From e5366a266a8cd4cd6b0fe66876462cca2e1c6a89 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 6 May 2016 08:37:41 -0700 Subject: [PATCH 155/155] mtd: spi-nor: support GigaDevice gd25lq64c Also note the GigaDevice JEDEC ID. No write-protect support yet, since this flash uses a different status register layout. Cc: Ezequiel Garcia Signed-off-by: Brian Norris Acked-by: Marek Vasut --- drivers/mtd/spi-nor/spi-nor.c | 1 + include/linux/mtd/spi-nor.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 157841dc3e99..c52e45594bfd 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -832,6 +832,7 @@ static const struct flash_info spi_nor_ids[] = { /* GigaDevice */ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) }, + { "gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, SECT_4K) }, /* Intel/Numonyx -- xxxs33b */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 3c36113a88e1..7f041bd88b82 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -21,6 +21,7 @@ * Sometimes these are the same as CFI IDs, but sometimes they aren't. */ #define SNOR_MFR_ATMEL CFI_MFR_ATMEL +#define SNOR_MFR_GIGADEVICE 0xc8 #define SNOR_MFR_INTEL CFI_MFR_INTEL #define SNOR_MFR_MICRON CFI_MFR_ST /* ST Micro <--> Micron */ #define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX