From c4adf9db5d382be6d13a19585f28de6f3e4e3d61 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 6 Jun 2016 10:16:56 +0200 Subject: spl: nand: sunxi: remove support for so-called 'syndrome' mode The sunxi SPL NAND controller driver supports use 'BootROM'-like configs, that is, configs where the ECC bytes and real data are interleaved in the page instead of putting ECC bytes in the OOB area. Doing that has several drawbacks: - since you're interleaving data and ECC bytes you can't use the whole page otherwise you might override the bad block marker with non-FF bytes. - to solve the bad block marker problem, the ROM code supports partially using the page, but this introduces a huge penalty both in term of read speed and NAND memory usage. While this is fine for rather small binaries(like the SPL one which is at maximum 24KB large), it becomes non-negligible for the bootloader image (several hundred of KB). - auto-detection of the page size is not reliable (this is in my opinion the biggest problem). If you get the page size wrong, you'll end up reading data at a different offset than what was specified by the caller and the reading may succeed (if valid data were written at this address). For all those reasons I think it's wiser to completely remove support for 'syndrome' configs. If we ever need to support it again, then I'd recommend specifying all the config parameters through Kconfig options. Signed-off-by: Boris Brezillon Acked-by: Hans de Goede --- drivers/mtd/nand/sunxi_nand_spl.c | 56 ++++++++++----------------------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c index b0e07aa2d0..1739da26c7 100644 --- a/drivers/mtd/nand/sunxi_nand_spl.c +++ b/drivers/mtd/nand/sunxi_nand_spl.c @@ -119,9 +119,6 @@ const uint16_t random_seed[128] = { 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, }; -/* random seed used for syndrome calls */ -const uint16_t random_seed_syndrome = 0x4a80; - #define MAX_RETRIES 10 static int check_value_inner(int offset, int expected_bits, @@ -183,7 +180,7 @@ void nand_init(void) } static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, - int addr_cycles, uint32_t real_addr, dma_addr_t dst, int syndrome) + int addr_cycles, uint32_t real_addr, dma_addr_t dst) { uint32_t val; int i, ecc_off = 0; @@ -209,17 +206,11 @@ static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, page = real_addr / page_size; column = real_addr % page_size; - if (syndrome) - column += (column / ecc_page_size) * ecc_off; - /* clear ecc status */ writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); /* Choose correct seed */ - if (syndrome) - rand_seed = random_seed_syndrome; - else - rand_seed = random_seed[page % 128]; + rand_seed = random_seed[page % 128]; writel((rand_seed << 16) | NFC_ECC_RANDOM_EN | NFC_ECC_EN | NFC_ECC_PIPELINE | (ecc_mode << 12), @@ -228,9 +219,8 @@ static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, val = readl(SUNXI_NFC_BASE + NFC_CTL); writel(val | NFC_CTL_RAM_METHOD, SUNXI_NFC_BASE + NFC_CTL); - if (!syndrome) - writel(page_size + (column / ecc_page_size) * ecc_off, - SUNXI_NFC_BASE + NFC_SPARE_AREA); + writel(page_size + (column / ecc_page_size) * ecc_off, + SUNXI_NFC_BASE + NFC_SPARE_AREA); flush_dcache_range(dst, ALIGN(dst + ecc_page_size, ARCH_DMA_MINALIGN)); @@ -266,7 +256,7 @@ static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_DATA_TRANS | NFC_PAGE_CMD | NFC_WAIT_FLAG | ((addr_cycles - 1) << NFC_ADDR_NUM_OFFSET) | - NFC_SEND_ADR | NFC_DATA_SWAP_METHOD | (syndrome ? NFC_SEQ : 0), + NFC_SEND_ADR | NFC_DATA_SWAP_METHOD, SUNXI_NFC_BASE + NFC_CMD); if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG, @@ -292,7 +282,7 @@ static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, } static int nand_read_ecc(int page_size, int ecc_strength, int ecc_page_size, - int addr_cycles, uint32_t offs, uint32_t size, void *dest, int syndrome) + int addr_cycles, uint32_t offs, uint32_t size, void *dest) { void *end = dest + size; @@ -301,16 +291,14 @@ static int nand_read_ecc(int page_size, int ecc_strength, int ecc_page_size, for ( ;dest < end; dest += ecc_page_size, offs += ecc_page_size) { if (nand_read_page(page_size, ecc_strength, ecc_page_size, - addr_cycles, offs, (dma_addr_t)dest, - syndrome)) + addr_cycles, offs, (dma_addr_t)dest)) return -1; } return 0; } -static int nand_read_buffer(uint32_t offs, unsigned int size, void *dest, - int syndrome) +static int nand_read_buffer(uint32_t offs, unsigned int size, void *dest) { const struct { int page_size; @@ -337,7 +325,7 @@ static int nand_read_buffer(uint32_t offs, unsigned int size, void *dest, nand_configs[i].ecc_strength, nand_configs[i].ecc_page_size, nand_configs[i].addr_cycles, - offs, size, dest, syndrome) == 0) { + offs, size, dest) == 0) { debug("success\n"); nand_config = i; return 0; @@ -351,48 +339,32 @@ static int nand_read_buffer(uint32_t offs, unsigned int size, void *dest, nand_configs[nand_config].ecc_strength, nand_configs[nand_config].ecc_page_size, nand_configs[nand_config].addr_cycles, - offs, size, dest, syndrome); + offs, size, dest); } int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) { -#if CONFIG_SYS_NAND_U_BOOT_OFFS == CONFIG_SPL_PAD_TO - /* - * u-boot-dtb.bin appended to SPL, use syndrome (like the BROM does) - * and try different erase block sizes to find the backup. - */ - const uint32_t boot_offsets[] = { - 0 * 1024 * 1024 + CONFIG_SYS_NAND_U_BOOT_OFFS, - 1 * 1024 * 1024 + CONFIG_SYS_NAND_U_BOOT_OFFS, - 2 * 1024 * 1024 + CONFIG_SYS_NAND_U_BOOT_OFFS, - 4 * 1024 * 1024 + CONFIG_SYS_NAND_U_BOOT_OFFS, - }; - const int syndrome = 1; -#else /* - * u-boot-dtb.bin on its own partition, do not use syndrome, u-boot - * partition sits after 2 eraseblocks (spl, spl-backup), look for - * backup u-boot 1 erase block further. + * u-boot partition sits after 2 eraseblocks (spl, spl-backup), look + * for backup u-boot 1 erase block further. */ const uint32_t eraseblock_size = CONFIG_SYS_NAND_U_BOOT_OFFS / 2; const uint32_t boot_offsets[] = { CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_OFFS + eraseblock_size, }; - const int syndrome = 0; -#endif int i; if (offs == CONFIG_SYS_NAND_U_BOOT_OFFS) { for (i = 0; i < ARRAY_SIZE(boot_offsets); i++) { if (nand_read_buffer(boot_offsets[i], size, - dest, syndrome) == 0) + dest) == 0) return 0; } return -1; } - return nand_read_buffer(offs, size, dest, syndrome); + return nand_read_buffer(offs, size, dest); } void nand_deselect(void) -- cgit v1.2.1 From 494e108651a3c55b85d060202e941e4d8d68ce5e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 6 Jun 2016 10:16:57 +0200 Subject: spl: nand: rework SYS_NAND_U_BOOT_OFFS Kconfig option dependency The SYS_NAND_U_BOOT_OFFS is quite generic, but the Kconfig entry is forced to explicitly depend on platforms that are not already defining it in their include/configs/.h header. Add the SYS_NAND_U_BOOT_LOCATIONS option, make the SYS_NAND_U_BOOT_OFFS depends on it, remove the dependency on NAND_SUNXI and make it dependent on SPL selection. Signed-off-by: Boris Brezillon Acked-by: Hans de Goede --- drivers/mtd/nand/Kconfig | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 2fc73ef4e5..5fe169f6ce 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -99,17 +99,24 @@ config SYS_NAND_BUSWIDTH_16BIT not available while configuring controller. So a static CONFIG_NAND_xx is needed to know the device's bus-width in advance. -# Enhance depends when converting drivers to Kconfig which use this config +if SPL + +config SYS_NAND_U_BOOT_LOCATIONS + bool "Define U-boot binaries locations in NAND" + help + Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig. + This option should not be enabled when compiling U-boot for boards + defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/.h + file. + config SYS_NAND_U_BOOT_OFFS hex "Location in NAND to read U-Boot from" default 0x8000 if NAND_SUNXI - depends on NAND_SUNXI + depends on SYS_NAND_U_BOOT_LOCATIONS help Set the offset from the start of the nand where u-boot should be loaded from. -if SPL - config SPL_NAND_DENALI bool "Support Denali NAND controller for SPL" help -- cgit v1.2.1 From 80ef700f8df1f85dd3b0880584e2e2a998a9b3dd Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 6 Jun 2016 10:16:58 +0200 Subject: spl: nand: support redundant u-boot image On modern NAND it's more than recommended to have a backup copy of the u-boot binary to recover from corruption: bitflips are quite common on MLC NANDs, and the read-disturbance will corrupt your u-boot partitition more quickly than what you would see on an SLC NAND. Add an extra Kconfig option to specify the offset of the redundant u-boot image. Signed-off-by: Boris Brezillon Acked-by: Hans de Goede [scottwood: added ifdef to fix build break] Signed-off-by: Scott Wood --- common/spl/spl_nand.c | 7 +++++++ drivers/mtd/nand/Kconfig | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/common/spl/spl_nand.c b/common/spl/spl_nand.c index 7cf0d1b986..0e35e0fd6b 100644 --- a/common/spl/spl_nand.c +++ b/common/spl/spl_nand.c @@ -134,6 +134,13 @@ int spl_nand_load_image(void) #endif /* Load u-boot */ err = spl_nand_load_element(CONFIG_SYS_NAND_U_BOOT_OFFS, header); +#ifdef CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND +#if CONFIG_SYS_NAND_U_BOOT_OFFS != CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND + if (err) + err = spl_nand_load_element(CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND, + header); +#endif +#endif nand_deselect(); return err; } diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5fe169f6ce..8c46a2ff8e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -117,6 +117,14 @@ config SYS_NAND_U_BOOT_OFFS Set the offset from the start of the nand where u-boot should be loaded from. +config SYS_NAND_U_BOOT_OFFS_REDUND + hex "Location in NAND to read U-Boot from" + default SYS_NAND_U_BOOT_OFFS + depends on SYS_NAND_U_BOOT_LOCATIONS + help + Set the offset from the start of the nand where the redundant u-boot + should be loaded from. + config SPL_NAND_DENALI bool "Support Denali NAND controller for SPL" help -- cgit v1.2.1 From fa3011587887ba222bc9337d9ac17f3000db63b2 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 6 Jun 2016 10:16:59 +0200 Subject: spl: nand: sunxi: stop guessing the redundant u-boot offset Use CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND value instead of trying to guess where the redundant u-boot image is based on simple (and most of the time erroneous) heuristics. Signed-off-by: Boris Brezillon Acked-by: Hans de Goede # Conflicts: # drivers/mtd/nand/sunxi_nand_spl.c --- drivers/mtd/nand/sunxi_nand_spl.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c index 1739da26c7..13e6eab44b 100644 --- a/drivers/mtd/nand/sunxi_nand_spl.c +++ b/drivers/mtd/nand/sunxi_nand_spl.c @@ -344,26 +344,6 @@ static int nand_read_buffer(uint32_t offs, unsigned int size, void *dest) int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) { - /* - * u-boot partition sits after 2 eraseblocks (spl, spl-backup), look - * for backup u-boot 1 erase block further. - */ - const uint32_t eraseblock_size = CONFIG_SYS_NAND_U_BOOT_OFFS / 2; - const uint32_t boot_offsets[] = { - CONFIG_SYS_NAND_U_BOOT_OFFS, - CONFIG_SYS_NAND_U_BOOT_OFFS + eraseblock_size, - }; - int i; - - if (offs == CONFIG_SYS_NAND_U_BOOT_OFFS) { - for (i = 0; i < ARRAY_SIZE(boot_offsets); i++) { - if (nand_read_buffer(boot_offsets[i], size, - dest) == 0) - return 0; - } - return -1; - } - return nand_read_buffer(offs, size, dest); } -- cgit v1.2.1 From bb9783b66bf56a27b4db8dc444dda6c380816e99 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 6 Jun 2016 10:17:00 +0200 Subject: spl: nand: sunxi: rework status polling loop check_value_xxx() helpers are using a 1ms delay between each test, which can be quite long for some operations (like a page read on an SLC NAND). Since we don't have anything to do but to poll this register, reduce the delay between each test to 1us. While we're at it, rename the max_number_of_retries parameters and the MAX_RETRIES macro into timeout_us and DEFAULT_TIMEOUT_US to reflect that we're actually waiting a given amount of time and not only a number of retries. Signed-off-by: Boris Brezillon Acked-by: Hans de Goede --- drivers/mtd/nand/sunxi_nand_spl.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c index 13e6eab44b..55b3c8a3d1 100644 --- a/drivers/mtd/nand/sunxi_nand_spl.c +++ b/drivers/mtd/nand/sunxi_nand_spl.c @@ -119,35 +119,31 @@ const uint16_t random_seed[128] = { 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, }; -#define MAX_RETRIES 10 +#define DEFAULT_TIMEOUT_US 100000 static int check_value_inner(int offset, int expected_bits, - int max_number_of_retries, int negation) + int timeout_us, int negation) { - int retries = 0; do { int val = readl(offset) & expected_bits; if (negation ? !val : val) return 1; - mdelay(1); - retries++; - } while (retries < max_number_of_retries); + udelay(1); + } while (--timeout_us); return 0; } static inline int check_value(int offset, int expected_bits, - int max_number_of_retries) + int timeout_us) { - return check_value_inner(offset, expected_bits, - max_number_of_retries, 0); + return check_value_inner(offset, expected_bits, timeout_us, 0); } static inline int check_value_negated(int offset, int unexpected_bits, - int max_number_of_retries) + int timeout_us) { - return check_value_inner(offset, unexpected_bits, - max_number_of_retries, 1); + return check_value_inner(offset, unexpected_bits, timeout_us, 1); } void nand_init(void) @@ -162,7 +158,7 @@ void nand_init(void) SUNXI_NFC_BASE + NFC_CTL); if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL, - NFC_CTL_RESET, MAX_RETRIES)) { + NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) { printf("Couldn't initialize nand\n"); } @@ -172,7 +168,7 @@ void nand_init(void) SUNXI_NFC_BASE + NFC_CMD); if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, - MAX_RETRIES)) { + DEFAULT_TIMEOUT_US)) { printf("Error timeout waiting for nand reset\n"); return; } @@ -260,14 +256,15 @@ static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, SUNXI_NFC_BASE + NFC_CMD); if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG, - MAX_RETRIES)) { + DEFAULT_TIMEOUT_US)) { printf("Error while initializing dma interrupt\n"); return -1; } writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0, - SUNXI_DMA_DDMA_CFG_REG_LOADING, MAX_RETRIES)) { + SUNXI_DMA_DDMA_CFG_REG_LOADING, + DEFAULT_TIMEOUT_US)) { printf("Error while waiting for dma transfer to finish\n"); return -1; } -- cgit v1.2.1 From 4e7d1b3bebc5bf5176d35ac32378bcf0ff6e5914 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 6 Jun 2016 10:17:01 +0200 Subject: spl: nand: sunxi: split 'load page' and 'read page' logic Split the 'load page' and 'read page' logic in 2 different functions so we can later load the page and test different ECC configs without the penalty of reloading the same page in the NAND cache. We also move common setup to a dedicated function (nand_apply_config()) to avoid rewriting the same values in NFC registers each time we read a page. These new functions are passed a pointer to an nfc_config struct to limit the number of parameters. Signed-off-by: Boris Brezillon Acked-by: Hans de Goede --- drivers/mtd/nand/sunxi_nand_spl.c | 185 +++++++++++++++++++++++--------------- 1 file changed, 114 insertions(+), 71 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c index 55b3c8a3d1..b43f2afc22 100644 --- a/drivers/mtd/nand/sunxi_nand_spl.c +++ b/drivers/mtd/nand/sunxi_nand_spl.c @@ -66,6 +66,8 @@ #define NFC_ROW_AUTO_INC (1 << 27) #define NFC_SEND_CMD3 (1 << 28) #define NFC_SEND_CMD4 (1 << 29) +#define NFC_RAW_CMD (0 << 30) +#define NFC_PAGE_CMD (2 << 30) #define NFC_ST_CMD_INT_FLAG (1 << 1) #define NFC_ST_DMA_INT_FLAG (1 << 2) @@ -78,9 +80,6 @@ #define NFC_CMD_RNDOUT 0x05 #define NFC_CMD_READSTART 0x30 - -#define NFC_PAGE_CMD (2 << 30) - #define SUNXI_DMA_CFG_REG0 0x300 #define SUNXI_DMA_SRC_START_ADDR_REG0 0x304 #define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308 @@ -97,6 +96,15 @@ #define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0) #define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8) +struct nfc_config { + int page_size; + int ecc_strength; + int ecc_size; + int addr_cycles; + int nseeds; + bool randomize; +}; + /* minimal "boot0" style NAND support for Allwinner A20 */ /* random seed used by linux */ @@ -175,50 +183,70 @@ void nand_init(void) writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); } -static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, - int addr_cycles, uint32_t real_addr, dma_addr_t dst) +static void nand_apply_config(const struct nfc_config *conf) { - uint32_t val; - int i, ecc_off = 0; - uint16_t ecc_mode = 0; - uint16_t rand_seed; - uint32_t page; - uint16_t column; - static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; + u32 val; - for (i = 0; i < ARRAY_SIZE(strengths); i++) { - if (ecc_strength == strengths[i]) { - ecc_mode = i; - break; - } + val = readl(SUNXI_NFC_BASE + NFC_CTL); + val &= ~NFC_CTL_PAGE_SIZE_MASK; + writel(val | NFC_CTL_RAM_METHOD | NFC_CTL_PAGE_SIZE(conf->page_size), + SUNXI_NFC_BASE + NFC_CTL); + writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT); + writel(conf->page_size, SUNXI_NFC_BASE + NFC_SPARE_AREA); +} + +static int nand_load_page(const struct nfc_config *conf, u32 offs) +{ + int page = offs / conf->page_size; + + writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | + (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | + (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET), + SUNXI_NFC_BASE + NFC_RCMD_SET); + writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW); + writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); + writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); + writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | NFC_WAIT_FLAG | + ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADR, + SUNXI_NFC_BASE + NFC_CMD); + + if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, + DEFAULT_TIMEOUT_US)) { + printf("Error while initializing dma interrupt\n"); + return -EIO; } - /* HW ECC always request ECC bytes for 1024 bytes blocks */ - ecc_off = DIV_ROUND_UP(ecc_strength * fls(8 * 1024), 8); - /* HW ECC always work with even numbers of ECC bytes */ - ecc_off += (ecc_off & 1); - ecc_off += 4; /* prepad */ + return 0; +} + +static int nand_read_page(const struct nfc_config *conf, u32 offs, + void *dest, int len) +{ + dma_addr_t dst = (dma_addr_t)dest; + int nsectors = len / conf->ecc_size; + u16 rand_seed; + u32 val; + int page; + + page = offs / conf->page_size; - page = real_addr / page_size; - column = real_addr % page_size; + if (offs % conf->page_size || len % conf->ecc_size || + len > conf->page_size || len < 0) + return -EINVAL; /* clear ecc status */ writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); /* Choose correct seed */ - rand_seed = random_seed[page % 128]; + rand_seed = random_seed[page % conf->nseeds]; - writel((rand_seed << 16) | NFC_ECC_RANDOM_EN | NFC_ECC_EN - | NFC_ECC_PIPELINE | (ecc_mode << 12), + writel((rand_seed << 16) | (conf->ecc_strength << 12) | + (conf->randomize ? NFC_ECC_RANDOM_EN : 0) | + (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) | + NFC_ECC_EN | NFC_ECC_PIPELINE | NFC_ECC_EXCEPTION, SUNXI_NFC_BASE + NFC_ECC_CTL); - val = readl(SUNXI_NFC_BASE + NFC_CTL); - writel(val | NFC_CTL_RAM_METHOD, SUNXI_NFC_BASE + NFC_CTL); - - writel(page_size + (column / ecc_page_size) * ecc_off, - SUNXI_NFC_BASE + NFC_SPARE_AREA); - - flush_dcache_range(dst, ALIGN(dst + ecc_page_size, ARCH_DMA_MINALIGN)); + flush_dcache_range(dst, ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN)); /* SUNXI_DMA */ writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */ @@ -227,38 +255,27 @@ static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0); /* read to RAM */ writel(dst, SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0); - writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC - | SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE, - SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0); - writel(ecc_page_size, - SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0); /* 1kB */ - writel(SUNXI_DMA_DDMA_CFG_REG_LOADING - | SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 - | SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM - | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 - | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO - | SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC, - SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); - - writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) - | (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) - | (NFC_CMD_READSTART | NFC_READ_CMD_OFFSET), SUNXI_NFC_BASE - + NFC_RCMD_SET); - writel(1, SUNXI_NFC_BASE + NFC_SECTOR_NUM); - writel(((page & 0xFFFF) << 16) | column, - SUNXI_NFC_BASE + NFC_ADDR_LOW); - writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); + writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC | + SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE, + SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0); + writel(len, SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0); + writel(SUNXI_DMA_DDMA_CFG_REG_LOADING | + SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 | + SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM | + SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 | + SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO | + SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC, + SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); + + writel(nsectors, SUNXI_NFC_BASE + NFC_SECTOR_NUM); writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); - writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_DATA_TRANS | - NFC_PAGE_CMD | NFC_WAIT_FLAG | - ((addr_cycles - 1) << NFC_ADDR_NUM_OFFSET) | - NFC_SEND_ADR | NFC_DATA_SWAP_METHOD, - SUNXI_NFC_BASE + NFC_CMD); + writel(NFC_DATA_TRANS | NFC_PAGE_CMD | NFC_DATA_SWAP_METHOD, + SUNXI_NFC_BASE + NFC_CMD); if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG, DEFAULT_TIMEOUT_US)) { printf("Error while initializing dma interrupt\n"); - return -1; + return -EIO; } writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); @@ -266,29 +283,55 @@ static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, SUNXI_DMA_DDMA_CFG_REG_LOADING, DEFAULT_TIMEOUT_US)) { printf("Error while waiting for dma transfer to finish\n"); - return -1; + return -EIO; } invalidate_dcache_range(dst, - ALIGN(dst + ecc_page_size, ARCH_DMA_MINALIGN)); + ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN)); - if (readl(SUNXI_NFC_BASE + NFC_ECC_ST)) - return -1; + val = readl(SUNXI_NFC_BASE + NFC_ECC_ST); - return 0; + /* ECC error detected. */ + if (val & 0xffff) + return -EIO; + + /* + * Return 1 if the page is empty. + * We consider the page as empty if the first ECC block is marked + * empty. + */ + return (val & 0x10000) ? 1 : 0; } static int nand_read_ecc(int page_size, int ecc_strength, int ecc_page_size, int addr_cycles, uint32_t offs, uint32_t size, void *dest) { void *end = dest + size; + static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; + struct nfc_config conf = { + .page_size = page_size, + .ecc_size = ecc_page_size, + .addr_cycles = addr_cycles, + .nseeds = ARRAY_SIZE(random_seed), + .randomize = true, + }; + int i; - clrsetbits_le32(SUNXI_NFC_BASE + NFC_CTL, NFC_CTL_PAGE_SIZE_MASK, - NFC_CTL_PAGE_SIZE(page_size)); + for (i = 0; i < ARRAY_SIZE(strengths); i++) { + if (ecc_strength == strengths[i]) { + conf.ecc_strength = i; + break; + } + } + + + nand_apply_config(&conf); + + for ( ;dest < end; dest += ecc_page_size, offs += page_size) { + if (nand_load_page(&conf, offs)) + return -1; - for ( ;dest < end; dest += ecc_page_size, offs += ecc_page_size) { - if (nand_read_page(page_size, ecc_strength, ecc_page_size, - addr_cycles, offs, (dma_addr_t)dest)) + if (nand_read_page(&conf, offs, dest, page_size)) return -1; } -- cgit v1.2.1 From 7748b4148207d6e0dca8c0b5b95975e8e64179db Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Mon, 6 Jun 2016 10:17:02 +0200 Subject: spl: nand: sunxi: add support for NAND config auto-detection NAND chips are supposed to expose their capabilities through advanced mechanisms like READID, ONFI or JEDEC parameter tables. While those methods are appropriate for the bootloader itself, it's way to complicated and takes too much space to fit in the SPL. Replace those mechanisms by a dumb 'trial and error' mechanism. With this new approach we can get rid of the fixed config list that was used in the sunxi NAND SPL driver. Signed-off-by: Boris Brezillon Acked-by: Hans de Goede --- drivers/mtd/nand/sunxi_nand_spl.c | 262 +++++++++++++++++++++++++++++--------- 1 file changed, 204 insertions(+), 58 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c index b43f2afc22..1ef7366d4c 100644 --- a/drivers/mtd/nand/sunxi_nand_spl.c +++ b/drivers/mtd/nand/sunxi_nand_spl.c @@ -103,6 +103,7 @@ struct nfc_config { int addr_cycles; int nseeds; bool randomize; + bool valid; }; /* minimal "boot0" style NAND support for Allwinner A20 */ @@ -219,6 +220,26 @@ static int nand_load_page(const struct nfc_config *conf, u32 offs) return 0; } +static int nand_reset_column(void) +{ + writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | + (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | + (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), + SUNXI_NFC_BASE + NFC_RCMD_SET); + writel(0, SUNXI_NFC_BASE + NFC_ADDR_LOW); + writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | + (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADR | NFC_CMD_RNDOUT, + SUNXI_NFC_BASE + NFC_CMD); + + if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, + DEFAULT_TIMEOUT_US)) { + printf("Error while initializing dma interrupt\n"); + return -1; + } + + return 0; +} + static int nand_read_page(const struct nfc_config *conf, u32 offs, void *dest, int len) { @@ -303,88 +324,213 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, return (val & 0x10000) ? 1 : 0; } -static int nand_read_ecc(int page_size, int ecc_strength, int ecc_page_size, - int addr_cycles, uint32_t offs, uint32_t size, void *dest) +static int nand_max_ecc_strength(struct nfc_config *conf) { - void *end = dest + size; - static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; - struct nfc_config conf = { - .page_size = page_size, - .ecc_size = ecc_page_size, - .addr_cycles = addr_cycles, - .nseeds = ARRAY_SIZE(random_seed), - .randomize = true, - }; + static const int ecc_bytes[] = { 32, 46, 54, 60, 74, 88, 102, 110, 116 }; + int max_oobsize, max_ecc_bytes; + int nsectors = conf->page_size / conf->ecc_size; int i; - for (i = 0; i < ARRAY_SIZE(strengths); i++) { - if (ecc_strength == strengths[i]) { - conf.ecc_strength = i; + /* + * ECC strength is limited by the size of the OOB area which is + * correlated with the page size. + */ + switch (conf->page_size) { + case 2048: + max_oobsize = 64; + break; + case 4096: + max_oobsize = 256; + break; + case 8192: + max_oobsize = 640; + break; + case 16384: + max_oobsize = 1664; + break; + default: + return -EINVAL; + } + + max_ecc_bytes = max_oobsize / nsectors; + + for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) { + if (ecc_bytes[i] > max_ecc_bytes) break; - } } + if (!i) + return -EINVAL; - nand_apply_config(&conf); + return i - 1; +} - for ( ;dest < end; dest += ecc_page_size, offs += page_size) { - if (nand_load_page(&conf, offs)) - return -1; +static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs, + void *dest) +{ + /* NAND with pages > 4k will likely require 1k sector size. */ + int min_ecc_size = conf->page_size > 4096 ? 1024 : 512; + int page = offs / conf->page_size; + int ret; - if (nand_read_page(&conf, offs, dest, page_size)) - return -1; + /* + * In most cases, 1k sectors are preferred over 512b ones, start + * testing this config first. + */ + for (conf->ecc_size = 1024; conf->ecc_size >= min_ecc_size; + conf->ecc_size >>= 1) { + int max_ecc_strength = nand_max_ecc_strength(conf); + + nand_apply_config(conf); + + /* + * We are starting from the maximum ECC strength because + * most of the time NAND vendors provide an OOB area that + * barely meets the ECC requirements. + */ + for (conf->ecc_strength = max_ecc_strength; + conf->ecc_strength >= 0; + conf->ecc_strength--) { + conf->randomize = false; + if (nand_reset_column()) + return -EIO; + + /* + * Only read the first sector to speedup detection. + */ + ret = nand_read_page(conf, offs, dest, conf->ecc_size); + if (!ret) { + return 0; + } else if (ret > 0) { + /* + * If page is empty we can't deduce anything + * about the ECC config => stop the detection. + */ + return -EINVAL; + } + + conf->randomize = true; + conf->nseeds = ARRAY_SIZE(random_seed); + do { + if (nand_reset_column()) + return -EIO; + + if (!nand_read_page(conf, offs, dest, + conf->ecc_size)) + return 0; + + /* + * Find the next ->nseeds value that would + * change the randomizer seed for the page + * we're trying to read. + */ + while (conf->nseeds >= 16) { + int seed = page % conf->nseeds; + + conf->nseeds >>= 1; + if (seed != page % conf->nseeds) + break; + } + } while (conf->nseeds >= 16); + } } - return 0; + return -EINVAL; } -static int nand_read_buffer(uint32_t offs, unsigned int size, void *dest) +static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest) { - const struct { - int page_size; - int ecc_strength; - int ecc_page_size; - int addr_cycles; - } nand_configs[] = { - { 8192, 40, 1024, 5 }, - { 16384, 56, 1024, 5 }, - { 8192, 24, 1024, 5 }, - { 4096, 24, 1024, 5 }, - }; - static int nand_config = -1; - int i; + if (conf->valid) + return 0; - if (nand_config == -1) { - for (i = 0; i < ARRAY_SIZE(nand_configs); i++) { - debug("nand: trying page %d ecc %d / %d addr %d: ", - nand_configs[i].page_size, - nand_configs[i].ecc_strength, - nand_configs[i].ecc_page_size, - nand_configs[i].addr_cycles); - if (nand_read_ecc(nand_configs[i].page_size, - nand_configs[i].ecc_strength, - nand_configs[i].ecc_page_size, - nand_configs[i].addr_cycles, - offs, size, dest) == 0) { - debug("success\n"); - nand_config = i; + /* + * Modern NANDs are more likely than legacy ones, so we start testing + * with 5 address cycles. + */ + for (conf->addr_cycles = 5; + conf->addr_cycles >= 4; + conf->addr_cycles--) { + int max_page_size = conf->addr_cycles == 4 ? 2048 : 16384; + + /* + * Ignoring 1k pages cause I'm not even sure this case exist + * in the real world. + */ + for (conf->page_size = 2048; conf->page_size <= max_page_size; + conf->page_size <<= 1) { + if (nand_load_page(conf, offs)) + return -1; + + if (!nand_detect_ecc_config(conf, offs, dest)) { + conf->valid = true; return 0; } - debug("failed\n"); } - return -1; } - return nand_read_ecc(nand_configs[nand_config].page_size, - nand_configs[nand_config].ecc_strength, - nand_configs[nand_config].ecc_page_size, - nand_configs[nand_config].addr_cycles, - offs, size, dest); + return -EINVAL; +} + +static int nand_read_buffer(struct nfc_config *conf, uint32_t offs, + unsigned int size, void *dest) +{ + int first_seed, page, ret; + + size = ALIGN(size, conf->page_size); + page = offs / conf->page_size; + first_seed = page % conf->nseeds; + + for (; size; size -= conf->page_size) { + if (nand_load_page(conf, offs)) + return -1; + + ret = nand_read_page(conf, offs, dest, conf->page_size); + /* + * The ->nseeds value should be equal to the number of pages + * in an eraseblock. Since we don't know this information in + * advance we might have picked a wrong value. + */ + if (ret < 0 && conf->randomize) { + int cur_seed = page % conf->nseeds; + + /* + * We already tried all the seed values => we are + * facing a real corruption. + */ + if (cur_seed < first_seed) + return -EIO; + + /* Try to adjust ->nseeds and read the page again... */ + conf->nseeds = cur_seed; + + if (nand_reset_column()) + return -EIO; + + /* ... it still fails => it's a real corruption. */ + if (nand_read_page(conf, offs, dest, conf->page_size)) + return -EIO; + } else if (ret && conf->randomize) { + memset(dest, 0xff, conf->page_size); + } + + page++; + offs += conf->page_size; + dest += conf->page_size; + } + + return 0; } int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) { - return nand_read_buffer(offs, size, dest); + static struct nfc_config conf = { }; + int ret; + + ret = nand_detect_config(&conf, offs, dest); + if (ret) + return ret; + + return nand_read_buffer(&conf, offs, size, dest); } void nand_deselect(void) -- cgit v1.2.1 From 61717571f2b55747e394f217b2fd519213433f88 Mon Sep 17 00:00:00 2001 From: Sergey Kubushyn Date: Tue, 7 Jun 2016 14:22:59 -0700 Subject: common: fb_nand: won't compile Somehow this got overlooked when getting rid of nand_info. Small patch, won't affect anything else, no reason to wait for the next cycle. Signed-off-by: Sergey Kubushyn --- common/fb_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/fb_nand.c b/common/fb_nand.c index e55ea3872f..ae34f4891b 100644 --- a/common/fb_nand.c +++ b/common/fb_nand.c @@ -34,7 +34,7 @@ __weak int board_fastboot_write_partition_setup(char *name) } static int fb_nand_lookup(const char *partname, char *response, - struct mtd_info **nand, + struct mtd_info **mtd, struct part_info **part) { struct mtd_device *dev; -- cgit v1.2.1 From e1c29086d58f619423f0648748d0678af28f9871 Mon Sep 17 00:00:00 2001 From: Max Krummenacher Date: Mon, 13 Jun 2016 10:15:47 +0200 Subject: nand: nand torture: follow sync with linux v4.6 follow parameter name change (nand to mtd) to fix compiler error. Signed-off-by: Max Krummenacher --- drivers/mtd/nand/nand_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 5bba66a728..e8bcc34ab4 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -820,7 +820,7 @@ int nand_torture(struct mtd_info *mtd, loff_t offset) { u_char patterns[] = {0xa5, 0x5a, 0x00}; struct erase_info instr = { - .mtd = nand, + .mtd = mtd, .addr = offset, .len = mtd->erasesize, }; -- cgit v1.2.1 From 1866be7d28ce807397e4aedd93f70564ac8bebc0 Mon Sep 17 00:00:00 2001 From: Max Krummenacher Date: Mon, 13 Jun 2016 10:15:48 +0200 Subject: nand: extend nand torture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nand torture currently works on exactly one nand block which is specified by giving the byteoffset to the beginning of the block. Extend this by allowing for a second parameter specifying the byte size to be tested. e.g. ==> nand torture 1000000 NAND torture: device 0 offset 0x1000000 size 0x20000 (block size 0x20000) Passed: 1, failed: 0 ==> nand torture 1000000 40000 NAND torture: device 0 offset 0x1000000 size 0x40000 (block size 0x20000) Passed: 2, failed: 0 Signed-off-by: Max Krummenacher Reviewed-by: Benoît Thébaudeau [scottwood: fix usage to show size as optional, and add misssing braces] Signed-off-by: Scott Wood --- cmd/nand.c | 41 +++++++++++++++++++++++++++++++++++------ doc/README.nand | 6 +++++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/cmd/nand.c b/cmd/nand.c index 583a18f341..ffdeea41a5 100644 --- a/cmd/nand.c +++ b/cmd/nand.c @@ -647,6 +647,9 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) #ifdef CONFIG_CMD_NAND_TORTURE if (strcmp(cmd, "torture") == 0) { + loff_t endoff; + unsigned int failed = 0, passed = 0; + if (argc < 3) goto usage; @@ -655,12 +658,37 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return 1; } - printf("\nNAND torture: device %d offset 0x%llx size 0x%x\n", - dev, off, mtd->erasesize); - ret = nand_torture(mtd, off); - printf(" %s\n", ret ? "Failed" : "Passed"); + size = mtd->erasesize; + if (argc > 3) { + if (!str2off(argv[3], &size)) { + puts("Size is not a valid number\n"); + return 1; + } + } - return ret == 0 ? 0 : 1; + endoff = off + size; + if (endoff > mtd->size) { + puts("Arguments beyond end of NAND\n"); + return 1; + } + + off = round_down(off, mtd->erasesize); + endoff = round_up(endoff, mtd->erasesize); + size = endoff - off; + printf("\nNAND torture: device %d offset 0x%llx size 0x%llx (block size 0x%x)\n", + dev, off, size, mtd->erasesize); + while (off < endoff) { + ret = nand_torture(mtd, off); + if (ret) { + failed++; + printf(" block at 0x%llx failed\n", off); + } else { + passed++; + } + off += mtd->erasesize; + } + printf(" Passed: %u, failed: %u\n", passed, failed); + return failed != 0; } #endif @@ -775,7 +803,8 @@ static char nand_help_text[] = "nand bad - show bad blocks\n" "nand dump[.oob] off - dump page\n" #ifdef CONFIG_CMD_NAND_TORTURE - "nand torture off - torture block at offset\n" + "nand torture off - torture one block at offset\n" + "nand torture off [size] - torture blocks from off to off+size\n" #endif "nand scrub [-y] off size | scrub.part partition | scrub.chip\n" " really clean NAND erasing bad blocks (UNSAFE)\n" diff --git a/doc/README.nand b/doc/README.nand index 96ffc48940..4ecf9dee65 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -307,7 +307,7 @@ Miscellaneous and testing commands: DANGEROUS!!! Factory set bad blocks will be lost. Use only to remove artificial bad blocks created with the "markbad" command. - "torture offset" + "torture offset [size]" Torture block to determine if it is still reliable. Enabled by the CONFIG_CMD_NAND_TORTURE configuration option. This command returns 0 if the block is still reliable, else 1. @@ -324,6 +324,10 @@ Miscellaneous and testing commands: automate actions following a nand->write() error. This would e.g. be required in order to program or update safely firmware to NAND, especially for the UBI part of such firmware. + Optionally, a second parameter size can be given to test multiple blocks with + one call. If size is not a multiple of the NAND's erase size, then the block + that contains offset + size will be tested in full. If used with size, this + command returns 0 if all tested blocks have been found reliable, else 1. NAND locking command (for chips with active LOCKPRE pin) -- cgit v1.2.1 From caad0d00a8bc3d44ca6581030b95dce97386689c Mon Sep 17 00:00:00 2001 From: Fabian Mewes Date: Tue, 14 Jun 2016 02:46:14 +0200 Subject: nand: doc: fix example ecc scheme calculation Signed-off-by: Fabian Mewes --- doc/README.nand | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.nand b/doc/README.nand index 4ecf9dee65..edb45eb567 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -271,7 +271,7 @@ Platform specific options However, for 4K pagesize NAND NAND_PAGESIZE = 4096 - NAND_OOBSIZE = 64 + NAND_OOBSIZE = 224 ECC_BYTES = 26 2 + (4096 / 512) * 26 = 210 < NAND_OOBSIZE Thus BCH16 can be supported on 4K page NAND. -- cgit v1.2.1 From 30780f948346436f9974fd6eae89aa2eb841b436 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Wed, 15 Jun 2016 20:56:10 +0200 Subject: mtd: nand: Patch remaining places where nand_to_mtd() should be used Some drivers are still directly accessing the chip->mtd field. Patch them to use nand_to_mtd() instead. Signed-off-by: Boris Brezillon --- doc/README.nand | 2 +- drivers/mtd/nand/am335x_spl_bch.c | 2 +- drivers/mtd/nand/atmel_nand.c | 2 +- drivers/mtd/nand/lpc32xx_nand_mlc.c | 2 +- drivers/mtd/nand/mxs_nand_spl.c | 2 +- drivers/mtd/nand/nand_spl_simple.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/README.nand b/doc/README.nand index edb45eb567..f1c20ff645 100644 --- a/doc/README.nand +++ b/doc/README.nand @@ -137,7 +137,7 @@ Configuration Options: init: /* chip is struct nand_chip, and is now provided by the driver. */ - mtd = &chip.mtd; + mtd = nand_to_mtd(&chip); /* * Fill in appropriate values if this driver uses these fields, diff --git a/drivers/mtd/nand/am335x_spl_bch.c b/drivers/mtd/nand/am335x_spl_bch.c index f8770e0ad8..a8a7a66a18 100644 --- a/drivers/mtd/nand/am335x_spl_bch.c +++ b/drivers/mtd/nand/am335x_spl_bch.c @@ -223,7 +223,7 @@ void nand_init(void) /* * Init board specific nand support */ - mtd = &nand_chip.mtd; + mtd = nand_to_mtd(&nand_chip); nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; board_nand_init(&nand_chip); diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 75e830724c..a66cfc1887 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1448,7 +1448,7 @@ int board_nand_init(struct nand_chip *nand) void nand_init(void) { - mtd = &nand_chip.mtd; + mtd = nand_to_mtd(&nand_chip); mtd->writesize = CONFIG_SYS_NAND_PAGE_SIZE; mtd->oobsize = CONFIG_SYS_NAND_OOBSIZE; nand_chip.IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE; diff --git a/drivers/mtd/nand/lpc32xx_nand_mlc.c b/drivers/mtd/nand/lpc32xx_nand_mlc.c index 4262029819..a793115331 100644 --- a/drivers/mtd/nand/lpc32xx_nand_mlc.c +++ b/drivers/mtd/nand/lpc32xx_nand_mlc.c @@ -541,7 +541,7 @@ static struct nand_chip lpc32xx_chip; void board_nand_init(void) { - struct mtd_info *mtd = &lpc32xx_chip.mtd; + struct mtd_info *mtd = nand_to_mtd(&lpc32xx_chip); int ret; /* Set all BOARDSPECIFIC (actually core-specific) fields */ diff --git a/drivers/mtd/nand/mxs_nand_spl.c b/drivers/mtd/nand/mxs_nand_spl.c index a8a3084d03..ff28df4c19 100644 --- a/drivers/mtd/nand/mxs_nand_spl.c +++ b/drivers/mtd/nand/mxs_nand_spl.c @@ -147,7 +147,7 @@ static int mxs_nand_init(void) /* init mxs nand driver */ board_nand_init(&nand_chip); - mtd = &nand_chip.mtd; + mtd = nand_to_mtd(&nand_chip); /* set mtd functions */ nand_chip.cmdfunc = mxs_nand_command; nand_chip.numchips = 1; diff --git a/drivers/mtd/nand/nand_spl_simple.c b/drivers/mtd/nand/nand_spl_simple.c index b023e00313..60a7607073 100644 --- a/drivers/mtd/nand/nand_spl_simple.c +++ b/drivers/mtd/nand/nand_spl_simple.c @@ -249,7 +249,7 @@ void nand_init(void) /* * Init board specific nand support */ - mtd = &nand_chip.mtd; + mtd = nand_to_mtd(&nand_chip); nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; board_nand_init(&nand_chip); -- cgit v1.2.1