Commit 2313d48445e59f063ec9a3b4940fe8252737db76
Exists in
v2017.01-smarct4x
and in
25 other branches
Merge git://git.denx.de/u-boot-nand-flash
Showing 12 changed files Side-by-side Diff
- cmd/nand.c
- common/fb_nand.c
- common/spl/spl_nand.c
- doc/README.nand
- drivers/mtd/nand/Kconfig
- drivers/mtd/nand/am335x_spl_bch.c
- drivers/mtd/nand/atmel_nand.c
- drivers/mtd/nand/lpc32xx_nand_mlc.c
- drivers/mtd/nand/mxs_nand_spl.c
- drivers/mtd/nand/nand_spl_simple.c
- drivers/mtd/nand/nand_util.c
- drivers/mtd/nand/sunxi_nand_spl.c
cmd/nand.c
... | ... | @@ -647,6 +647,9 @@ |
647 | 647 | |
648 | 648 | #ifdef CONFIG_CMD_NAND_TORTURE |
649 | 649 | if (strcmp(cmd, "torture") == 0) { |
650 | + loff_t endoff; | |
651 | + unsigned int failed = 0, passed = 0; | |
652 | + | |
650 | 653 | if (argc < 3) |
651 | 654 | goto usage; |
652 | 655 | |
653 | 656 | |
... | ... | @@ -655,12 +658,37 @@ |
655 | 658 | return 1; |
656 | 659 | } |
657 | 660 | |
658 | - printf("\nNAND torture: device %d offset 0x%llx size 0x%x\n", | |
659 | - dev, off, mtd->erasesize); | |
660 | - ret = nand_torture(mtd, off); | |
661 | - printf(" %s\n", ret ? "Failed" : "Passed"); | |
661 | + size = mtd->erasesize; | |
662 | + if (argc > 3) { | |
663 | + if (!str2off(argv[3], &size)) { | |
664 | + puts("Size is not a valid number\n"); | |
665 | + return 1; | |
666 | + } | |
667 | + } | |
662 | 668 | |
663 | - return ret == 0 ? 0 : 1; | |
669 | + endoff = off + size; | |
670 | + if (endoff > mtd->size) { | |
671 | + puts("Arguments beyond end of NAND\n"); | |
672 | + return 1; | |
673 | + } | |
674 | + | |
675 | + off = round_down(off, mtd->erasesize); | |
676 | + endoff = round_up(endoff, mtd->erasesize); | |
677 | + size = endoff - off; | |
678 | + printf("\nNAND torture: device %d offset 0x%llx size 0x%llx (block size 0x%x)\n", | |
679 | + dev, off, size, mtd->erasesize); | |
680 | + while (off < endoff) { | |
681 | + ret = nand_torture(mtd, off); | |
682 | + if (ret) { | |
683 | + failed++; | |
684 | + printf(" block at 0x%llx failed\n", off); | |
685 | + } else { | |
686 | + passed++; | |
687 | + } | |
688 | + off += mtd->erasesize; | |
689 | + } | |
690 | + printf(" Passed: %u, failed: %u\n", passed, failed); | |
691 | + return failed != 0; | |
664 | 692 | } |
665 | 693 | #endif |
666 | 694 | |
... | ... | @@ -775,7 +803,8 @@ |
775 | 803 | "nand bad - show bad blocks\n" |
776 | 804 | "nand dump[.oob] off - dump page\n" |
777 | 805 | #ifdef CONFIG_CMD_NAND_TORTURE |
778 | - "nand torture off - torture block at offset\n" | |
806 | + "nand torture off - torture one block at offset\n" | |
807 | + "nand torture off [size] - torture blocks from off to off+size\n" | |
779 | 808 | #endif |
780 | 809 | "nand scrub [-y] off size | scrub.part partition | scrub.chip\n" |
781 | 810 | " really clean NAND erasing bad blocks (UNSAFE)\n" |
common/fb_nand.c
common/spl/spl_nand.c
... | ... | @@ -134,6 +134,13 @@ |
134 | 134 | #endif |
135 | 135 | /* Load u-boot */ |
136 | 136 | err = spl_nand_load_element(CONFIG_SYS_NAND_U_BOOT_OFFS, header); |
137 | +#ifdef CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND | |
138 | +#if CONFIG_SYS_NAND_U_BOOT_OFFS != CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND | |
139 | + if (err) | |
140 | + err = spl_nand_load_element(CONFIG_SYS_NAND_U_BOOT_OFFS_REDUND, | |
141 | + header); | |
142 | +#endif | |
143 | +#endif | |
137 | 144 | nand_deselect(); |
138 | 145 | return err; |
139 | 146 | } |
doc/README.nand
... | ... | @@ -137,7 +137,7 @@ |
137 | 137 | init: |
138 | 138 | |
139 | 139 | /* chip is struct nand_chip, and is now provided by the driver. */ |
140 | - mtd = &chip.mtd; | |
140 | + mtd = nand_to_mtd(&chip); | |
141 | 141 | |
142 | 142 | /* |
143 | 143 | * Fill in appropriate values if this driver uses these fields, |
... | ... | @@ -271,7 +271,7 @@ |
271 | 271 | |
272 | 272 | However, for 4K pagesize NAND |
273 | 273 | NAND_PAGESIZE = 4096 |
274 | - NAND_OOBSIZE = 64 | |
274 | + NAND_OOBSIZE = 224 | |
275 | 275 | ECC_BYTES = 26 |
276 | 276 | 2 + (4096 / 512) * 26 = 210 < NAND_OOBSIZE |
277 | 277 | Thus BCH16 can be supported on 4K page NAND. |
... | ... | @@ -307,7 +307,7 @@ |
307 | 307 | DANGEROUS!!! Factory set bad blocks will be lost. Use only |
308 | 308 | to remove artificial bad blocks created with the "markbad" command. |
309 | 309 | |
310 | - "torture offset" | |
310 | + "torture offset [size]" | |
311 | 311 | Torture block to determine if it is still reliable. |
312 | 312 | Enabled by the CONFIG_CMD_NAND_TORTURE configuration option. |
313 | 313 | This command returns 0 if the block is still reliable, else 1. |
... | ... | @@ -324,6 +324,10 @@ |
324 | 324 | automate actions following a nand->write() error. This would e.g. be required |
325 | 325 | in order to program or update safely firmware to NAND, especially for the UBI |
326 | 326 | part of such firmware. |
327 | + Optionally, a second parameter size can be given to test multiple blocks with | |
328 | + one call. If size is not a multiple of the NAND's erase size, then the block | |
329 | + that contains offset + size will be tested in full. If used with size, this | |
330 | + command returns 0 if all tested blocks have been found reliable, else 1. | |
327 | 331 | |
328 | 332 | |
329 | 333 | NAND locking command (for chips with active LOCKPRE pin) |
drivers/mtd/nand/Kconfig
... | ... | @@ -99,16 +99,31 @@ |
99 | 99 | not available while configuring controller. So a static CONFIG_NAND_xx |
100 | 100 | is needed to know the device's bus-width in advance. |
101 | 101 | |
102 | -# Enhance depends when converting drivers to Kconfig which use this config | |
102 | +if SPL | |
103 | + | |
104 | +config SYS_NAND_U_BOOT_LOCATIONS | |
105 | + bool "Define U-boot binaries locations in NAND" | |
106 | + help | |
107 | + Enable CONFIG_SYS_NAND_U_BOOT_OFFS though Kconfig. | |
108 | + This option should not be enabled when compiling U-boot for boards | |
109 | + defining CONFIG_SYS_NAND_U_BOOT_OFFS in their include/configs/<board>.h | |
110 | + file. | |
111 | + | |
103 | 112 | config SYS_NAND_U_BOOT_OFFS |
104 | 113 | hex "Location in NAND to read U-Boot from" |
105 | 114 | default 0x8000 if NAND_SUNXI |
106 | - depends on NAND_SUNXI | |
115 | + depends on SYS_NAND_U_BOOT_LOCATIONS | |
107 | 116 | help |
108 | 117 | Set the offset from the start of the nand where u-boot should be |
109 | 118 | loaded from. |
110 | 119 | |
111 | -if SPL | |
120 | +config SYS_NAND_U_BOOT_OFFS_REDUND | |
121 | + hex "Location in NAND to read U-Boot from" | |
122 | + default SYS_NAND_U_BOOT_OFFS | |
123 | + depends on SYS_NAND_U_BOOT_LOCATIONS | |
124 | + help | |
125 | + Set the offset from the start of the nand where the redundant u-boot | |
126 | + should be loaded from. | |
112 | 127 | |
113 | 128 | config SPL_NAND_DENALI |
114 | 129 | bool "Support Denali NAND controller for SPL" |
drivers/mtd/nand/am335x_spl_bch.c
drivers/mtd/nand/atmel_nand.c
... | ... | @@ -1449,7 +1449,7 @@ |
1449 | 1449 | |
1450 | 1450 | void nand_init(void) |
1451 | 1451 | { |
1452 | - mtd = &nand_chip.mtd; | |
1452 | + mtd = nand_to_mtd(&nand_chip); | |
1453 | 1453 | mtd->writesize = CONFIG_SYS_NAND_PAGE_SIZE; |
1454 | 1454 | mtd->oobsize = CONFIG_SYS_NAND_OOBSIZE; |
1455 | 1455 | nand_chip.IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE; |
drivers/mtd/nand/lpc32xx_nand_mlc.c
drivers/mtd/nand/mxs_nand_spl.c
drivers/mtd/nand/nand_spl_simple.c
drivers/mtd/nand/nand_util.c
drivers/mtd/nand/sunxi_nand_spl.c
... | ... | @@ -66,6 +66,8 @@ |
66 | 66 | #define NFC_ROW_AUTO_INC (1 << 27) |
67 | 67 | #define NFC_SEND_CMD3 (1 << 28) |
68 | 68 | #define NFC_SEND_CMD4 (1 << 29) |
69 | +#define NFC_RAW_CMD (0 << 30) | |
70 | +#define NFC_PAGE_CMD (2 << 30) | |
69 | 71 | |
70 | 72 | #define NFC_ST_CMD_INT_FLAG (1 << 1) |
71 | 73 | #define NFC_ST_DMA_INT_FLAG (1 << 2) |
... | ... | @@ -78,9 +80,6 @@ |
78 | 80 | #define NFC_CMD_RNDOUT 0x05 |
79 | 81 | #define NFC_CMD_READSTART 0x30 |
80 | 82 | |
81 | - | |
82 | -#define NFC_PAGE_CMD (2 << 30) | |
83 | - | |
84 | 83 | #define SUNXI_DMA_CFG_REG0 0x300 |
85 | 84 | #define SUNXI_DMA_SRC_START_ADDR_REG0 0x304 |
86 | 85 | #define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308 |
... | ... | @@ -97,6 +96,16 @@ |
97 | 96 | #define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0) |
98 | 97 | #define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8) |
99 | 98 | |
99 | +struct nfc_config { | |
100 | + int page_size; | |
101 | + int ecc_strength; | |
102 | + int ecc_size; | |
103 | + int addr_cycles; | |
104 | + int nseeds; | |
105 | + bool randomize; | |
106 | + bool valid; | |
107 | +}; | |
108 | + | |
100 | 109 | /* minimal "boot0" style NAND support for Allwinner A20 */ |
101 | 110 | |
102 | 111 | /* random seed used by linux */ |
103 | 112 | |
104 | 113 | |
105 | 114 | |
106 | 115 | |
107 | 116 | |
108 | 117 | |
109 | 118 | |
110 | 119 | |
... | ... | @@ -119,38 +128,31 @@ |
119 | 128 | 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, |
120 | 129 | }; |
121 | 130 | |
122 | -/* random seed used for syndrome calls */ | |
123 | -const uint16_t random_seed_syndrome = 0x4a80; | |
131 | +#define DEFAULT_TIMEOUT_US 100000 | |
124 | 132 | |
125 | -#define MAX_RETRIES 10 | |
126 | - | |
127 | 133 | static int check_value_inner(int offset, int expected_bits, |
128 | - int max_number_of_retries, int negation) | |
134 | + int timeout_us, int negation) | |
129 | 135 | { |
130 | - int retries = 0; | |
131 | 136 | do { |
132 | 137 | int val = readl(offset) & expected_bits; |
133 | 138 | if (negation ? !val : val) |
134 | 139 | return 1; |
135 | - mdelay(1); | |
136 | - retries++; | |
137 | - } while (retries < max_number_of_retries); | |
140 | + udelay(1); | |
141 | + } while (--timeout_us); | |
138 | 142 | |
139 | 143 | return 0; |
140 | 144 | } |
141 | 145 | |
142 | 146 | static inline int check_value(int offset, int expected_bits, |
143 | - int max_number_of_retries) | |
147 | + int timeout_us) | |
144 | 148 | { |
145 | - return check_value_inner(offset, expected_bits, | |
146 | - max_number_of_retries, 0); | |
149 | + return check_value_inner(offset, expected_bits, timeout_us, 0); | |
147 | 150 | } |
148 | 151 | |
149 | 152 | static inline int check_value_negated(int offset, int unexpected_bits, |
150 | - int max_number_of_retries) | |
153 | + int timeout_us) | |
151 | 154 | { |
152 | - return check_value_inner(offset, unexpected_bits, | |
153 | - max_number_of_retries, 1); | |
155 | + return check_value_inner(offset, unexpected_bits, timeout_us, 1); | |
154 | 156 | } |
155 | 157 | |
156 | 158 | void nand_init(void) |
... | ... | @@ -165,7 +167,7 @@ |
165 | 167 | SUNXI_NFC_BASE + NFC_CTL); |
166 | 168 | |
167 | 169 | if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL, |
168 | - NFC_CTL_RESET, MAX_RETRIES)) { | |
170 | + NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) { | |
169 | 171 | printf("Couldn't initialize nand\n"); |
170 | 172 | } |
171 | 173 | |
172 | 174 | |
173 | 175 | |
174 | 176 | |
175 | 177 | |
176 | 178 | |
177 | 179 | |
178 | 180 | |
179 | 181 | |
180 | 182 | |
181 | 183 | |
182 | 184 | |
... | ... | @@ -175,65 +177,98 @@ |
175 | 177 | SUNXI_NFC_BASE + NFC_CMD); |
176 | 178 | |
177 | 179 | if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, |
178 | - MAX_RETRIES)) { | |
180 | + DEFAULT_TIMEOUT_US)) { | |
179 | 181 | printf("Error timeout waiting for nand reset\n"); |
180 | 182 | return; |
181 | 183 | } |
182 | 184 | writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
183 | 185 | } |
184 | 186 | |
185 | -static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, | |
186 | - int addr_cycles, uint32_t real_addr, dma_addr_t dst, int syndrome) | |
187 | +static void nand_apply_config(const struct nfc_config *conf) | |
187 | 188 | { |
188 | - uint32_t val; | |
189 | - int i, ecc_off = 0; | |
190 | - uint16_t ecc_mode = 0; | |
191 | - uint16_t rand_seed; | |
192 | - uint32_t page; | |
193 | - uint16_t column; | |
194 | - static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; | |
189 | + u32 val; | |
195 | 190 | |
196 | - for (i = 0; i < ARRAY_SIZE(strengths); i++) { | |
197 | - if (ecc_strength == strengths[i]) { | |
198 | - ecc_mode = i; | |
199 | - break; | |
200 | - } | |
191 | + val = readl(SUNXI_NFC_BASE + NFC_CTL); | |
192 | + val &= ~NFC_CTL_PAGE_SIZE_MASK; | |
193 | + writel(val | NFC_CTL_RAM_METHOD | NFC_CTL_PAGE_SIZE(conf->page_size), | |
194 | + SUNXI_NFC_BASE + NFC_CTL); | |
195 | + writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT); | |
196 | + writel(conf->page_size, SUNXI_NFC_BASE + NFC_SPARE_AREA); | |
197 | +} | |
198 | + | |
199 | +static int nand_load_page(const struct nfc_config *conf, u32 offs) | |
200 | +{ | |
201 | + int page = offs / conf->page_size; | |
202 | + | |
203 | + writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | | |
204 | + (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | | |
205 | + (NFC_CMD_READSTART << NFC_READ_CMD_OFFSET), | |
206 | + SUNXI_NFC_BASE + NFC_RCMD_SET); | |
207 | + writel(((page & 0xFFFF) << 16), SUNXI_NFC_BASE + NFC_ADDR_LOW); | |
208 | + writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); | |
209 | + writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); | |
210 | + writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | NFC_WAIT_FLAG | | |
211 | + ((conf->addr_cycles - 1) << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADR, | |
212 | + SUNXI_NFC_BASE + NFC_CMD); | |
213 | + | |
214 | + if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, | |
215 | + DEFAULT_TIMEOUT_US)) { | |
216 | + printf("Error while initializing dma interrupt\n"); | |
217 | + return -EIO; | |
201 | 218 | } |
202 | 219 | |
203 | - /* HW ECC always request ECC bytes for 1024 bytes blocks */ | |
204 | - ecc_off = DIV_ROUND_UP(ecc_strength * fls(8 * 1024), 8); | |
205 | - /* HW ECC always work with even numbers of ECC bytes */ | |
206 | - ecc_off += (ecc_off & 1); | |
207 | - ecc_off += 4; /* prepad */ | |
220 | + return 0; | |
221 | +} | |
208 | 222 | |
209 | - page = real_addr / page_size; | |
210 | - column = real_addr % page_size; | |
223 | +static int nand_reset_column(void) | |
224 | +{ | |
225 | + writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | | |
226 | + (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | | |
227 | + (NFC_CMD_RNDOUTSTART << NFC_READ_CMD_OFFSET), | |
228 | + SUNXI_NFC_BASE + NFC_RCMD_SET); | |
229 | + writel(0, SUNXI_NFC_BASE + NFC_ADDR_LOW); | |
230 | + writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_RAW_CMD | | |
231 | + (1 << NFC_ADDR_NUM_OFFSET) | NFC_SEND_ADR | NFC_CMD_RNDOUT, | |
232 | + SUNXI_NFC_BASE + NFC_CMD); | |
211 | 233 | |
212 | - if (syndrome) | |
213 | - column += (column / ecc_page_size) * ecc_off; | |
234 | + if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, | |
235 | + DEFAULT_TIMEOUT_US)) { | |
236 | + printf("Error while initializing dma interrupt\n"); | |
237 | + return -1; | |
238 | + } | |
214 | 239 | |
240 | + return 0; | |
241 | +} | |
242 | + | |
243 | +static int nand_read_page(const struct nfc_config *conf, u32 offs, | |
244 | + void *dest, int len) | |
245 | +{ | |
246 | + dma_addr_t dst = (dma_addr_t)dest; | |
247 | + int nsectors = len / conf->ecc_size; | |
248 | + u16 rand_seed; | |
249 | + u32 val; | |
250 | + int page; | |
251 | + | |
252 | + page = offs / conf->page_size; | |
253 | + | |
254 | + if (offs % conf->page_size || len % conf->ecc_size || | |
255 | + len > conf->page_size || len < 0) | |
256 | + return -EINVAL; | |
257 | + | |
215 | 258 | /* clear ecc status */ |
216 | 259 | writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); |
217 | 260 | |
218 | 261 | /* Choose correct seed */ |
219 | - if (syndrome) | |
220 | - rand_seed = random_seed_syndrome; | |
221 | - else | |
222 | - rand_seed = random_seed[page % 128]; | |
262 | + rand_seed = random_seed[page % conf->nseeds]; | |
223 | 263 | |
224 | - writel((rand_seed << 16) | NFC_ECC_RANDOM_EN | NFC_ECC_EN | |
225 | - | NFC_ECC_PIPELINE | (ecc_mode << 12), | |
264 | + writel((rand_seed << 16) | (conf->ecc_strength << 12) | | |
265 | + (conf->randomize ? NFC_ECC_RANDOM_EN : 0) | | |
266 | + (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) | | |
267 | + NFC_ECC_EN | NFC_ECC_PIPELINE | NFC_ECC_EXCEPTION, | |
226 | 268 | SUNXI_NFC_BASE + NFC_ECC_CTL); |
227 | 269 | |
228 | - val = readl(SUNXI_NFC_BASE + NFC_CTL); | |
229 | - writel(val | NFC_CTL_RAM_METHOD, SUNXI_NFC_BASE + NFC_CTL); | |
270 | + flush_dcache_range(dst, ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN)); | |
230 | 271 | |
231 | - if (!syndrome) | |
232 | - writel(page_size + (column / ecc_page_size) * ecc_off, | |
233 | - SUNXI_NFC_BASE + NFC_SPARE_AREA); | |
234 | - | |
235 | - flush_dcache_range(dst, ALIGN(dst + ecc_page_size, ARCH_DMA_MINALIGN)); | |
236 | - | |
237 | 272 | /* SUNXI_DMA */ |
238 | 273 | writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */ |
239 | 274 | /* read from REG_IO_DATA */ |
240 | 275 | |
241 | 276 | |
242 | 277 | |
243 | 278 | |
244 | 279 | |
245 | 280 | |
246 | 281 | |
247 | 282 | |
248 | 283 | |
249 | 284 | |
250 | 285 | |
251 | 286 | |
252 | 287 | |
253 | 288 | |
254 | 289 | |
255 | 290 | |
256 | 291 | |
257 | 292 | |
258 | 293 | |
259 | 294 | |
260 | 295 | |
261 | 296 | |
262 | 297 | |
263 | 298 | |
264 | 299 | |
265 | 300 | |
266 | 301 | |
267 | 302 | |
268 | 303 | |
... | ... | @@ -241,158 +276,261 @@ |
241 | 276 | SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0); |
242 | 277 | /* read to RAM */ |
243 | 278 | writel(dst, SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0); |
244 | - writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC | |
245 | - | SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE, | |
246 | - SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0); | |
247 | - writel(ecc_page_size, | |
248 | - SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0); /* 1kB */ | |
249 | - writel(SUNXI_DMA_DDMA_CFG_REG_LOADING | |
250 | - | SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 | |
251 | - | SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM | |
252 | - | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 | |
253 | - | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO | |
254 | - | SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC, | |
255 | - SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); | |
279 | + writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC | | |
280 | + SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE, | |
281 | + SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0); | |
282 | + writel(len, SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0); | |
283 | + writel(SUNXI_DMA_DDMA_CFG_REG_LOADING | | |
284 | + SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 | | |
285 | + SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM | | |
286 | + SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 | | |
287 | + SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO | | |
288 | + SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC, | |
289 | + SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); | |
256 | 290 | |
257 | - writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | |
258 | - | (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | |
259 | - | (NFC_CMD_READSTART | NFC_READ_CMD_OFFSET), SUNXI_NFC_BASE | |
260 | - + NFC_RCMD_SET); | |
261 | - writel(1, SUNXI_NFC_BASE + NFC_SECTOR_NUM); | |
262 | - writel(((page & 0xFFFF) << 16) | column, | |
263 | - SUNXI_NFC_BASE + NFC_ADDR_LOW); | |
264 | - writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); | |
291 | + writel(nsectors, SUNXI_NFC_BASE + NFC_SECTOR_NUM); | |
265 | 292 | writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
266 | - writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_DATA_TRANS | | |
267 | - NFC_PAGE_CMD | NFC_WAIT_FLAG | | |
268 | - ((addr_cycles - 1) << NFC_ADDR_NUM_OFFSET) | | |
269 | - NFC_SEND_ADR | NFC_DATA_SWAP_METHOD | (syndrome ? NFC_SEQ : 0), | |
270 | - SUNXI_NFC_BASE + NFC_CMD); | |
293 | + writel(NFC_DATA_TRANS | NFC_PAGE_CMD | NFC_DATA_SWAP_METHOD, | |
294 | + SUNXI_NFC_BASE + NFC_CMD); | |
271 | 295 | |
272 | 296 | if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG, |
273 | - MAX_RETRIES)) { | |
297 | + DEFAULT_TIMEOUT_US)) { | |
274 | 298 | printf("Error while initializing dma interrupt\n"); |
275 | - return -1; | |
299 | + return -EIO; | |
276 | 300 | } |
277 | 301 | writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
278 | 302 | |
279 | 303 | if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0, |
280 | - SUNXI_DMA_DDMA_CFG_REG_LOADING, MAX_RETRIES)) { | |
304 | + SUNXI_DMA_DDMA_CFG_REG_LOADING, | |
305 | + DEFAULT_TIMEOUT_US)) { | |
281 | 306 | printf("Error while waiting for dma transfer to finish\n"); |
282 | - return -1; | |
307 | + return -EIO; | |
283 | 308 | } |
284 | 309 | |
285 | 310 | invalidate_dcache_range(dst, |
286 | - ALIGN(dst + ecc_page_size, ARCH_DMA_MINALIGN)); | |
311 | + ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN)); | |
287 | 312 | |
288 | - if (readl(SUNXI_NFC_BASE + NFC_ECC_ST)) | |
289 | - return -1; | |
313 | + val = readl(SUNXI_NFC_BASE + NFC_ECC_ST); | |
290 | 314 | |
291 | - return 0; | |
315 | + /* ECC error detected. */ | |
316 | + if (val & 0xffff) | |
317 | + return -EIO; | |
318 | + | |
319 | + /* | |
320 | + * Return 1 if the page is empty. | |
321 | + * We consider the page as empty if the first ECC block is marked | |
322 | + * empty. | |
323 | + */ | |
324 | + return (val & 0x10000) ? 1 : 0; | |
292 | 325 | } |
293 | 326 | |
294 | -static int nand_read_ecc(int page_size, int ecc_strength, int ecc_page_size, | |
295 | - int addr_cycles, uint32_t offs, uint32_t size, void *dest, int syndrome) | |
327 | +static int nand_max_ecc_strength(struct nfc_config *conf) | |
296 | 328 | { |
297 | - void *end = dest + size; | |
329 | + static const int ecc_bytes[] = { 32, 46, 54, 60, 74, 88, 102, 110, 116 }; | |
330 | + int max_oobsize, max_ecc_bytes; | |
331 | + int nsectors = conf->page_size / conf->ecc_size; | |
332 | + int i; | |
298 | 333 | |
299 | - clrsetbits_le32(SUNXI_NFC_BASE + NFC_CTL, NFC_CTL_PAGE_SIZE_MASK, | |
300 | - NFC_CTL_PAGE_SIZE(page_size)); | |
334 | + /* | |
335 | + * ECC strength is limited by the size of the OOB area which is | |
336 | + * correlated with the page size. | |
337 | + */ | |
338 | + switch (conf->page_size) { | |
339 | + case 2048: | |
340 | + max_oobsize = 64; | |
341 | + break; | |
342 | + case 4096: | |
343 | + max_oobsize = 256; | |
344 | + break; | |
345 | + case 8192: | |
346 | + max_oobsize = 640; | |
347 | + break; | |
348 | + case 16384: | |
349 | + max_oobsize = 1664; | |
350 | + break; | |
351 | + default: | |
352 | + return -EINVAL; | |
353 | + } | |
301 | 354 | |
302 | - for ( ;dest < end; dest += ecc_page_size, offs += ecc_page_size) { | |
303 | - if (nand_read_page(page_size, ecc_strength, ecc_page_size, | |
304 | - addr_cycles, offs, (dma_addr_t)dest, | |
305 | - syndrome)) | |
306 | - return -1; | |
355 | + max_ecc_bytes = max_oobsize / nsectors; | |
356 | + | |
357 | + for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) { | |
358 | + if (ecc_bytes[i] > max_ecc_bytes) | |
359 | + break; | |
307 | 360 | } |
308 | 361 | |
309 | - return 0; | |
362 | + if (!i) | |
363 | + return -EINVAL; | |
364 | + | |
365 | + return i - 1; | |
310 | 366 | } |
311 | 367 | |
312 | -static int nand_read_buffer(uint32_t offs, unsigned int size, void *dest, | |
313 | - int syndrome) | |
368 | +static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs, | |
369 | + void *dest) | |
314 | 370 | { |
315 | - const struct { | |
316 | - int page_size; | |
317 | - int ecc_strength; | |
318 | - int ecc_page_size; | |
319 | - int addr_cycles; | |
320 | - } nand_configs[] = { | |
321 | - { 8192, 40, 1024, 5 }, | |
322 | - { 16384, 56, 1024, 5 }, | |
323 | - { 8192, 24, 1024, 5 }, | |
324 | - { 4096, 24, 1024, 5 }, | |
325 | - }; | |
326 | - static int nand_config = -1; | |
327 | - int i; | |
371 | + /* NAND with pages > 4k will likely require 1k sector size. */ | |
372 | + int min_ecc_size = conf->page_size > 4096 ? 1024 : 512; | |
373 | + int page = offs / conf->page_size; | |
374 | + int ret; | |
328 | 375 | |
329 | - if (nand_config == -1) { | |
330 | - for (i = 0; i < ARRAY_SIZE(nand_configs); i++) { | |
331 | - debug("nand: trying page %d ecc %d / %d addr %d: ", | |
332 | - nand_configs[i].page_size, | |
333 | - nand_configs[i].ecc_strength, | |
334 | - nand_configs[i].ecc_page_size, | |
335 | - nand_configs[i].addr_cycles); | |
336 | - if (nand_read_ecc(nand_configs[i].page_size, | |
337 | - nand_configs[i].ecc_strength, | |
338 | - nand_configs[i].ecc_page_size, | |
339 | - nand_configs[i].addr_cycles, | |
340 | - offs, size, dest, syndrome) == 0) { | |
341 | - debug("success\n"); | |
342 | - nand_config = i; | |
376 | + /* | |
377 | + * In most cases, 1k sectors are preferred over 512b ones, start | |
378 | + * testing this config first. | |
379 | + */ | |
380 | + for (conf->ecc_size = 1024; conf->ecc_size >= min_ecc_size; | |
381 | + conf->ecc_size >>= 1) { | |
382 | + int max_ecc_strength = nand_max_ecc_strength(conf); | |
383 | + | |
384 | + nand_apply_config(conf); | |
385 | + | |
386 | + /* | |
387 | + * We are starting from the maximum ECC strength because | |
388 | + * most of the time NAND vendors provide an OOB area that | |
389 | + * barely meets the ECC requirements. | |
390 | + */ | |
391 | + for (conf->ecc_strength = max_ecc_strength; | |
392 | + conf->ecc_strength >= 0; | |
393 | + conf->ecc_strength--) { | |
394 | + conf->randomize = false; | |
395 | + if (nand_reset_column()) | |
396 | + return -EIO; | |
397 | + | |
398 | + /* | |
399 | + * Only read the first sector to speedup detection. | |
400 | + */ | |
401 | + ret = nand_read_page(conf, offs, dest, conf->ecc_size); | |
402 | + if (!ret) { | |
343 | 403 | return 0; |
404 | + } else if (ret > 0) { | |
405 | + /* | |
406 | + * If page is empty we can't deduce anything | |
407 | + * about the ECC config => stop the detection. | |
408 | + */ | |
409 | + return -EINVAL; | |
344 | 410 | } |
345 | - debug("failed\n"); | |
411 | + | |
412 | + conf->randomize = true; | |
413 | + conf->nseeds = ARRAY_SIZE(random_seed); | |
414 | + do { | |
415 | + if (nand_reset_column()) | |
416 | + return -EIO; | |
417 | + | |
418 | + if (!nand_read_page(conf, offs, dest, | |
419 | + conf->ecc_size)) | |
420 | + return 0; | |
421 | + | |
422 | + /* | |
423 | + * Find the next ->nseeds value that would | |
424 | + * change the randomizer seed for the page | |
425 | + * we're trying to read. | |
426 | + */ | |
427 | + while (conf->nseeds >= 16) { | |
428 | + int seed = page % conf->nseeds; | |
429 | + | |
430 | + conf->nseeds >>= 1; | |
431 | + if (seed != page % conf->nseeds) | |
432 | + break; | |
433 | + } | |
434 | + } while (conf->nseeds >= 16); | |
346 | 435 | } |
347 | - return -1; | |
348 | 436 | } |
349 | 437 | |
350 | - return nand_read_ecc(nand_configs[nand_config].page_size, | |
351 | - nand_configs[nand_config].ecc_strength, | |
352 | - nand_configs[nand_config].ecc_page_size, | |
353 | - nand_configs[nand_config].addr_cycles, | |
354 | - offs, size, dest, syndrome); | |
438 | + return -EINVAL; | |
355 | 439 | } |
356 | 440 | |
357 | -int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) | |
441 | +static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest) | |
358 | 442 | { |
359 | -#if CONFIG_SYS_NAND_U_BOOT_OFFS == CONFIG_SPL_PAD_TO | |
443 | + if (conf->valid) | |
444 | + return 0; | |
445 | + | |
360 | 446 | /* |
361 | - * u-boot-dtb.bin appended to SPL, use syndrome (like the BROM does) | |
362 | - * and try different erase block sizes to find the backup. | |
447 | + * Modern NANDs are more likely than legacy ones, so we start testing | |
448 | + * with 5 address cycles. | |
363 | 449 | */ |
364 | - const uint32_t boot_offsets[] = { | |
365 | - 0 * 1024 * 1024 + CONFIG_SYS_NAND_U_BOOT_OFFS, | |
366 | - 1 * 1024 * 1024 + CONFIG_SYS_NAND_U_BOOT_OFFS, | |
367 | - 2 * 1024 * 1024 + CONFIG_SYS_NAND_U_BOOT_OFFS, | |
368 | - 4 * 1024 * 1024 + CONFIG_SYS_NAND_U_BOOT_OFFS, | |
369 | - }; | |
370 | - const int syndrome = 1; | |
371 | -#else | |
372 | - /* | |
373 | - * u-boot-dtb.bin on its own partition, do not use syndrome, u-boot | |
374 | - * partition sits after 2 eraseblocks (spl, spl-backup), look for | |
375 | - * backup u-boot 1 erase block further. | |
376 | - */ | |
377 | - const uint32_t eraseblock_size = CONFIG_SYS_NAND_U_BOOT_OFFS / 2; | |
378 | - const uint32_t boot_offsets[] = { | |
379 | - CONFIG_SYS_NAND_U_BOOT_OFFS, | |
380 | - CONFIG_SYS_NAND_U_BOOT_OFFS + eraseblock_size, | |
381 | - }; | |
382 | - const int syndrome = 0; | |
383 | -#endif | |
384 | - int i; | |
450 | + for (conf->addr_cycles = 5; | |
451 | + conf->addr_cycles >= 4; | |
452 | + conf->addr_cycles--) { | |
453 | + int max_page_size = conf->addr_cycles == 4 ? 2048 : 16384; | |
385 | 454 | |
386 | - if (offs == CONFIG_SYS_NAND_U_BOOT_OFFS) { | |
387 | - for (i = 0; i < ARRAY_SIZE(boot_offsets); i++) { | |
388 | - if (nand_read_buffer(boot_offsets[i], size, | |
389 | - dest, syndrome) == 0) | |
455 | + /* | |
456 | + * Ignoring 1k pages cause I'm not even sure this case exist | |
457 | + * in the real world. | |
458 | + */ | |
459 | + for (conf->page_size = 2048; conf->page_size <= max_page_size; | |
460 | + conf->page_size <<= 1) { | |
461 | + if (nand_load_page(conf, offs)) | |
462 | + return -1; | |
463 | + | |
464 | + if (!nand_detect_ecc_config(conf, offs, dest)) { | |
465 | + conf->valid = true; | |
390 | 466 | return 0; |
467 | + } | |
391 | 468 | } |
392 | - return -1; | |
393 | 469 | } |
394 | 470 | |
395 | - return nand_read_buffer(offs, size, dest, syndrome); | |
471 | + return -EINVAL; | |
472 | +} | |
473 | + | |
474 | +static int nand_read_buffer(struct nfc_config *conf, uint32_t offs, | |
475 | + unsigned int size, void *dest) | |
476 | +{ | |
477 | + int first_seed, page, ret; | |
478 | + | |
479 | + size = ALIGN(size, conf->page_size); | |
480 | + page = offs / conf->page_size; | |
481 | + first_seed = page % conf->nseeds; | |
482 | + | |
483 | + for (; size; size -= conf->page_size) { | |
484 | + if (nand_load_page(conf, offs)) | |
485 | + return -1; | |
486 | + | |
487 | + ret = nand_read_page(conf, offs, dest, conf->page_size); | |
488 | + /* | |
489 | + * The ->nseeds value should be equal to the number of pages | |
490 | + * in an eraseblock. Since we don't know this information in | |
491 | + * advance we might have picked a wrong value. | |
492 | + */ | |
493 | + if (ret < 0 && conf->randomize) { | |
494 | + int cur_seed = page % conf->nseeds; | |
495 | + | |
496 | + /* | |
497 | + * We already tried all the seed values => we are | |
498 | + * facing a real corruption. | |
499 | + */ | |
500 | + if (cur_seed < first_seed) | |
501 | + return -EIO; | |
502 | + | |
503 | + /* Try to adjust ->nseeds and read the page again... */ | |
504 | + conf->nseeds = cur_seed; | |
505 | + | |
506 | + if (nand_reset_column()) | |
507 | + return -EIO; | |
508 | + | |
509 | + /* ... it still fails => it's a real corruption. */ | |
510 | + if (nand_read_page(conf, offs, dest, conf->page_size)) | |
511 | + return -EIO; | |
512 | + } else if (ret && conf->randomize) { | |
513 | + memset(dest, 0xff, conf->page_size); | |
514 | + } | |
515 | + | |
516 | + page++; | |
517 | + offs += conf->page_size; | |
518 | + dest += conf->page_size; | |
519 | + } | |
520 | + | |
521 | + return 0; | |
522 | +} | |
523 | + | |
524 | +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) | |
525 | +{ | |
526 | + static struct nfc_config conf = { }; | |
527 | + int ret; | |
528 | + | |
529 | + ret = nand_detect_config(&conf, offs, dest); | |
530 | + if (ret) | |
531 | + return ret; | |
532 | + | |
533 | + return nand_read_buffer(&conf, offs, size, dest); | |
396 | 534 | } |
397 | 535 | |
398 | 536 | void nand_deselect(void) |