]>
Commit | Line | Data |
---|---|---|
aeb4b0d3 PZ |
1 | /* |
2 | * Copyright (c) 2014-2015, Antmicro Ltd <www.antmicro.com> | |
3 | * Copyright (c) 2015, AW-SOM Technologies <www.aw-som.com> | |
4 | * | |
5 | * SPDX-License-Identifier: GPL-2.0+ | |
6 | */ | |
7 | ||
5d65c67b HG |
8 | #include <asm/arch/clock.h> |
9 | #include <asm/io.h> | |
aeb4b0d3 PZ |
10 | #include <common.h> |
11 | #include <config.h> | |
aeb4b0d3 PZ |
12 | #include <nand.h> |
13 | ||
14 | /* registers */ | |
15 | #define NFC_CTL 0x00000000 | |
16 | #define NFC_ST 0x00000004 | |
17 | #define NFC_INT 0x00000008 | |
18 | #define NFC_TIMING_CTL 0x0000000C | |
19 | #define NFC_TIMING_CFG 0x00000010 | |
20 | #define NFC_ADDR_LOW 0x00000014 | |
21 | #define NFC_ADDR_HIGH 0x00000018 | |
22 | #define NFC_SECTOR_NUM 0x0000001C | |
23 | #define NFC_CNT 0x00000020 | |
24 | #define NFC_CMD 0x00000024 | |
25 | #define NFC_RCMD_SET 0x00000028 | |
26 | #define NFC_WCMD_SET 0x0000002C | |
27 | #define NFC_IO_DATA 0x00000030 | |
28 | #define NFC_ECC_CTL 0x00000034 | |
29 | #define NFC_ECC_ST 0x00000038 | |
30 | #define NFC_DEBUG 0x0000003C | |
31 | #define NFC_ECC_CNT0 0x00000040 | |
32 | #define NFC_ECC_CNT1 0x00000044 | |
33 | #define NFC_ECC_CNT2 0x00000048 | |
34 | #define NFC_ECC_CNT3 0x0000004C | |
35 | #define NFC_USER_DATA_BASE 0x00000050 | |
36 | #define NFC_EFNAND_STATUS 0x00000090 | |
37 | #define NFC_SPARE_AREA 0x000000A0 | |
38 | #define NFC_PATTERN_ID 0x000000A4 | |
39 | #define NFC_RAM0_BASE 0x00000400 | |
40 | #define NFC_RAM1_BASE 0x00000800 | |
41 | ||
42 | #define NFC_CTL_EN (1 << 0) | |
43 | #define NFC_CTL_RESET (1 << 1) | |
44 | #define NFC_CTL_RAM_METHOD (1 << 14) | |
0a247554 HG |
45 | #define NFC_CTL_PAGE_SIZE_MASK (0xf << 8) |
46 | #define NFC_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8) | |
aeb4b0d3 PZ |
47 | |
48 | ||
49 | #define NFC_ECC_EN (1 << 0) | |
50 | #define NFC_ECC_PIPELINE (1 << 3) | |
51 | #define NFC_ECC_EXCEPTION (1 << 4) | |
52 | #define NFC_ECC_BLOCK_SIZE (1 << 5) | |
53 | #define NFC_ECC_RANDOM_EN (1 << 9) | |
54 | #define NFC_ECC_RANDOM_DIRECTION (1 << 10) | |
55 | ||
56 | ||
57 | #define NFC_ADDR_NUM_OFFSET 16 | |
58 | #define NFC_SEND_ADR (1 << 19) | |
59 | #define NFC_ACCESS_DIR (1 << 20) | |
60 | #define NFC_DATA_TRANS (1 << 21) | |
61 | #define NFC_SEND_CMD1 (1 << 22) | |
62 | #define NFC_WAIT_FLAG (1 << 23) | |
63 | #define NFC_SEND_CMD2 (1 << 24) | |
64 | #define NFC_SEQ (1 << 25) | |
65 | #define NFC_DATA_SWAP_METHOD (1 << 26) | |
66 | #define NFC_ROW_AUTO_INC (1 << 27) | |
67 | #define NFC_SEND_CMD3 (1 << 28) | |
68 | #define NFC_SEND_CMD4 (1 << 29) | |
4e7d1b3b BB |
69 | #define NFC_RAW_CMD (0 << 30) |
70 | #define NFC_PAGE_CMD (2 << 30) | |
aeb4b0d3 | 71 | |
ddd37fe8 BB |
72 | #define NFC_ST_CMD_INT_FLAG (1 << 1) |
73 | #define NFC_ST_DMA_INT_FLAG (1 << 2) | |
aeb4b0d3 PZ |
74 | |
75 | #define NFC_READ_CMD_OFFSET 0 | |
76 | #define NFC_RANDOM_READ_CMD0_OFFSET 8 | |
77 | #define NFC_RANDOM_READ_CMD1_OFFSET 16 | |
78 | ||
79 | #define NFC_CMD_RNDOUTSTART 0xE0 | |
80 | #define NFC_CMD_RNDOUT 0x05 | |
81 | #define NFC_CMD_READSTART 0x30 | |
82 | ||
aeb4b0d3 PZ |
83 | #define SUNXI_DMA_CFG_REG0 0x300 |
84 | #define SUNXI_DMA_SRC_START_ADDR_REG0 0x304 | |
85 | #define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308 | |
86 | #define SUNXI_DMA_DDMA_BC_REG0 0x30C | |
87 | #define SUNXI_DMA_DDMA_PARA_REG0 0x318 | |
88 | ||
89 | #define SUNXI_DMA_DDMA_CFG_REG_LOADING (1 << 31) | |
90 | #define SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 (2 << 25) | |
10d069b7 | 91 | #define SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM (1 << 16) |
aeb4b0d3 PZ |
92 | #define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 (2 << 9) |
93 | #define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO (1 << 5) | |
94 | #define SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC (3 << 0) | |
95 | ||
96 | #define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0) | |
97 | #define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8) | |
98 | ||
4e7d1b3b BB |
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; | |
7748b414 | 106 | bool valid; |
4e7d1b3b BB |
107 | }; |
108 | ||
aeb4b0d3 PZ |
109 | /* minimal "boot0" style NAND support for Allwinner A20 */ |
110 | ||
aeb4b0d3 PZ |
111 | /* random seed used by linux */ |
112 | const uint16_t random_seed[128] = { | |
113 | 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, | |
114 | 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, | |
115 | 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, | |
116 | 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, | |
117 | 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, | |
118 | 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, | |
119 | 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, | |
120 | 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, | |
121 | 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, | |
122 | 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, | |
123 | 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, | |
124 | 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, | |
125 | 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, | |
126 | 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, | |
127 | 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, | |
128 | 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, | |
129 | }; | |
130 | ||
bb9783b6 | 131 | #define DEFAULT_TIMEOUT_US 100000 |
aeb4b0d3 PZ |
132 | |
133 | static int check_value_inner(int offset, int expected_bits, | |
bb9783b6 | 134 | int timeout_us, int negation) |
aeb4b0d3 | 135 | { |
aeb4b0d3 PZ |
136 | do { |
137 | int val = readl(offset) & expected_bits; | |
138 | if (negation ? !val : val) | |
139 | return 1; | |
bb9783b6 BB |
140 | udelay(1); |
141 | } while (--timeout_us); | |
aeb4b0d3 PZ |
142 | |
143 | return 0; | |
144 | } | |
145 | ||
146 | static inline int check_value(int offset, int expected_bits, | |
bb9783b6 | 147 | int timeout_us) |
aeb4b0d3 | 148 | { |
bb9783b6 | 149 | return check_value_inner(offset, expected_bits, timeout_us, 0); |
aeb4b0d3 PZ |
150 | } |
151 | ||
152 | static inline int check_value_negated(int offset, int unexpected_bits, | |
bb9783b6 | 153 | int timeout_us) |
aeb4b0d3 | 154 | { |
bb9783b6 | 155 | return check_value_inner(offset, unexpected_bits, timeout_us, 1); |
aeb4b0d3 PZ |
156 | } |
157 | ||
158 | void nand_init(void) | |
159 | { | |
160 | uint32_t val; | |
161 | ||
f62bfa56 HG |
162 | board_nand_init(); |
163 | ||
aeb4b0d3 PZ |
164 | val = readl(SUNXI_NFC_BASE + NFC_CTL); |
165 | /* enable and reset CTL */ | |
166 | writel(val | NFC_CTL_EN | NFC_CTL_RESET, | |
167 | SUNXI_NFC_BASE + NFC_CTL); | |
168 | ||
169 | if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL, | |
bb9783b6 | 170 | NFC_CTL_RESET, DEFAULT_TIMEOUT_US)) { |
aeb4b0d3 PZ |
171 | printf("Couldn't initialize nand\n"); |
172 | } | |
630cf2e7 HG |
173 | |
174 | /* reset NAND */ | |
ddd37fe8 | 175 | writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
630cf2e7 HG |
176 | writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET, |
177 | SUNXI_NFC_BASE + NFC_CMD); | |
178 | ||
ddd37fe8 | 179 | if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, |
bb9783b6 | 180 | DEFAULT_TIMEOUT_US)) { |
630cf2e7 HG |
181 | printf("Error timeout waiting for nand reset\n"); |
182 | return; | |
183 | } | |
ddd37fe8 | 184 | writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
aeb4b0d3 PZ |
185 | } |
186 | ||
4e7d1b3b | 187 | static void nand_apply_config(const struct nfc_config *conf) |
aeb4b0d3 | 188 | { |
4e7d1b3b | 189 | u32 val; |
aeb4b0d3 | 190 | |
4e7d1b3b BB |
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; | |
aeb4b0d3 PZ |
218 | } |
219 | ||
4e7d1b3b BB |
220 | return 0; |
221 | } | |
222 | ||
7748b414 BB |
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); | |
233 | ||
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 | } | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
4e7d1b3b BB |
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; | |
ea3f750c | 248 | u16 rand_seed = 0; |
4e7d1b3b BB |
249 | u32 val; |
250 | int page; | |
251 | ||
252 | page = offs / conf->page_size; | |
aeb4b0d3 | 253 | |
4e7d1b3b BB |
254 | if (offs % conf->page_size || len % conf->ecc_size || |
255 | len > conf->page_size || len < 0) | |
256 | return -EINVAL; | |
aeb4b0d3 | 257 | |
aeb4b0d3 PZ |
258 | /* clear ecc status */ |
259 | writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); | |
260 | ||
ea3f750c MR |
261 | /* Choose correct seed if randomized */ |
262 | if (conf->randomize) | |
263 | rand_seed = random_seed[page % conf->nseeds]; | |
aeb4b0d3 | 264 | |
4e7d1b3b BB |
265 | writel((rand_seed << 16) | (conf->ecc_strength << 12) | |
266 | (conf->randomize ? NFC_ECC_RANDOM_EN : 0) | | |
267 | (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) | | |
268 | NFC_ECC_EN | NFC_ECC_PIPELINE | NFC_ECC_EXCEPTION, | |
aeb4b0d3 PZ |
269 | SUNXI_NFC_BASE + NFC_ECC_CTL); |
270 | ||
4e7d1b3b | 271 | flush_dcache_range(dst, ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN)); |
2a43973f | 272 | |
aeb4b0d3 PZ |
273 | /* SUNXI_DMA */ |
274 | writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */ | |
275 | /* read from REG_IO_DATA */ | |
276 | writel(SUNXI_NFC_BASE + NFC_IO_DATA, | |
277 | SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0); | |
278 | /* read to RAM */ | |
10d069b7 | 279 | writel(dst, SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0); |
4e7d1b3b BB |
280 | writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC | |
281 | SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE, | |
282 | SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0); | |
283 | writel(len, SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0); | |
284 | writel(SUNXI_DMA_DDMA_CFG_REG_LOADING | | |
285 | SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 | | |
286 | SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM | | |
287 | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 | | |
288 | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO | | |
289 | SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC, | |
290 | SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); | |
291 | ||
292 | writel(nsectors, SUNXI_NFC_BASE + NFC_SECTOR_NUM); | |
ddd37fe8 | 293 | writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
4e7d1b3b BB |
294 | writel(NFC_DATA_TRANS | NFC_PAGE_CMD | NFC_DATA_SWAP_METHOD, |
295 | SUNXI_NFC_BASE + NFC_CMD); | |
aeb4b0d3 | 296 | |
ddd37fe8 | 297 | if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG, |
bb9783b6 | 298 | DEFAULT_TIMEOUT_US)) { |
aeb4b0d3 | 299 | printf("Error while initializing dma interrupt\n"); |
4e7d1b3b | 300 | return -EIO; |
aeb4b0d3 | 301 | } |
ddd37fe8 | 302 | writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
aeb4b0d3 PZ |
303 | |
304 | if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0, | |
bb9783b6 BB |
305 | SUNXI_DMA_DDMA_CFG_REG_LOADING, |
306 | DEFAULT_TIMEOUT_US)) { | |
aeb4b0d3 | 307 | printf("Error while waiting for dma transfer to finish\n"); |
4e7d1b3b | 308 | return -EIO; |
aeb4b0d3 PZ |
309 | } |
310 | ||
2a43973f | 311 | invalidate_dcache_range(dst, |
4e7d1b3b | 312 | ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN)); |
2a43973f | 313 | |
4e7d1b3b | 314 | val = readl(SUNXI_NFC_BASE + NFC_ECC_ST); |
f5916d18 | 315 | |
4e7d1b3b BB |
316 | /* ECC error detected. */ |
317 | if (val & 0xffff) | |
318 | return -EIO; | |
319 | ||
320 | /* | |
321 | * Return 1 if the page is empty. | |
322 | * We consider the page as empty if the first ECC block is marked | |
323 | * empty. | |
324 | */ | |
325 | return (val & 0x10000) ? 1 : 0; | |
aeb4b0d3 PZ |
326 | } |
327 | ||
7748b414 | 328 | static int nand_max_ecc_strength(struct nfc_config *conf) |
aeb4b0d3 | 329 | { |
7748b414 BB |
330 | static const int ecc_bytes[] = { 32, 46, 54, 60, 74, 88, 102, 110, 116 }; |
331 | int max_oobsize, max_ecc_bytes; | |
332 | int nsectors = conf->page_size / conf->ecc_size; | |
4e7d1b3b BB |
333 | int i; |
334 | ||
7748b414 BB |
335 | /* |
336 | * ECC strength is limited by the size of the OOB area which is | |
337 | * correlated with the page size. | |
338 | */ | |
339 | switch (conf->page_size) { | |
340 | case 2048: | |
341 | max_oobsize = 64; | |
342 | break; | |
343 | case 4096: | |
344 | max_oobsize = 256; | |
345 | break; | |
346 | case 8192: | |
347 | max_oobsize = 640; | |
348 | break; | |
349 | case 16384: | |
350 | max_oobsize = 1664; | |
351 | break; | |
352 | default: | |
353 | return -EINVAL; | |
354 | } | |
355 | ||
356 | max_ecc_bytes = max_oobsize / nsectors; | |
357 | ||
358 | for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) { | |
359 | if (ecc_bytes[i] > max_ecc_bytes) | |
4e7d1b3b | 360 | break; |
4e7d1b3b | 361 | } |
aeb4b0d3 | 362 | |
7748b414 BB |
363 | if (!i) |
364 | return -EINVAL; | |
f5916d18 | 365 | |
7748b414 BB |
366 | return i - 1; |
367 | } | |
4e7d1b3b | 368 | |
7748b414 BB |
369 | static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs, |
370 | void *dest) | |
371 | { | |
372 | /* NAND with pages > 4k will likely require 1k sector size. */ | |
373 | int min_ecc_size = conf->page_size > 4096 ? 1024 : 512; | |
374 | int page = offs / conf->page_size; | |
375 | int ret; | |
4e7d1b3b | 376 | |
7748b414 BB |
377 | /* |
378 | * In most cases, 1k sectors are preferred over 512b ones, start | |
379 | * testing this config first. | |
380 | */ | |
381 | for (conf->ecc_size = 1024; conf->ecc_size >= min_ecc_size; | |
382 | conf->ecc_size >>= 1) { | |
383 | int max_ecc_strength = nand_max_ecc_strength(conf); | |
384 | ||
385 | nand_apply_config(conf); | |
386 | ||
387 | /* | |
388 | * We are starting from the maximum ECC strength because | |
389 | * most of the time NAND vendors provide an OOB area that | |
390 | * barely meets the ECC requirements. | |
391 | */ | |
392 | for (conf->ecc_strength = max_ecc_strength; | |
393 | conf->ecc_strength >= 0; | |
394 | conf->ecc_strength--) { | |
395 | conf->randomize = false; | |
396 | if (nand_reset_column()) | |
397 | return -EIO; | |
398 | ||
399 | /* | |
400 | * Only read the first sector to speedup detection. | |
401 | */ | |
402 | ret = nand_read_page(conf, offs, dest, conf->ecc_size); | |
403 | if (!ret) { | |
404 | return 0; | |
405 | } else if (ret > 0) { | |
406 | /* | |
407 | * If page is empty we can't deduce anything | |
408 | * about the ECC config => stop the detection. | |
409 | */ | |
410 | return -EINVAL; | |
411 | } | |
412 | ||
413 | conf->randomize = true; | |
414 | conf->nseeds = ARRAY_SIZE(random_seed); | |
415 | do { | |
416 | if (nand_reset_column()) | |
417 | return -EIO; | |
418 | ||
419 | if (!nand_read_page(conf, offs, dest, | |
420 | conf->ecc_size)) | |
421 | return 0; | |
422 | ||
423 | /* | |
424 | * Find the next ->nseeds value that would | |
425 | * change the randomizer seed for the page | |
426 | * we're trying to read. | |
427 | */ | |
428 | while (conf->nseeds >= 16) { | |
429 | int seed = page % conf->nseeds; | |
430 | ||
431 | conf->nseeds >>= 1; | |
432 | if (seed != page % conf->nseeds) | |
433 | break; | |
434 | } | |
435 | } while (conf->nseeds >= 16); | |
436 | } | |
aeb4b0d3 | 437 | } |
f5916d18 | 438 | |
7748b414 | 439 | return -EINVAL; |
f5916d18 HG |
440 | } |
441 | ||
7748b414 | 442 | static int nand_detect_config(struct nfc_config *conf, u32 offs, void *dest) |
f5916d18 | 443 | { |
7748b414 BB |
444 | if (conf->valid) |
445 | return 0; | |
2b8a01a9 | 446 | |
7748b414 BB |
447 | /* |
448 | * Modern NANDs are more likely than legacy ones, so we start testing | |
449 | * with 5 address cycles. | |
450 | */ | |
451 | for (conf->addr_cycles = 5; | |
452 | conf->addr_cycles >= 4; | |
453 | conf->addr_cycles--) { | |
454 | int max_page_size = conf->addr_cycles == 4 ? 2048 : 16384; | |
455 | ||
456 | /* | |
457 | * Ignoring 1k pages cause I'm not even sure this case exist | |
458 | * in the real world. | |
459 | */ | |
460 | for (conf->page_size = 2048; conf->page_size <= max_page_size; | |
461 | conf->page_size <<= 1) { | |
462 | if (nand_load_page(conf, offs)) | |
463 | return -1; | |
464 | ||
465 | if (!nand_detect_ecc_config(conf, offs, dest)) { | |
466 | conf->valid = true; | |
2b8a01a9 HG |
467 | return 0; |
468 | } | |
2b8a01a9 | 469 | } |
2b8a01a9 HG |
470 | } |
471 | ||
7748b414 BB |
472 | return -EINVAL; |
473 | } | |
474 | ||
475 | static int nand_read_buffer(struct nfc_config *conf, uint32_t offs, | |
476 | unsigned int size, void *dest) | |
477 | { | |
478 | int first_seed, page, ret; | |
479 | ||
480 | size = ALIGN(size, conf->page_size); | |
481 | page = offs / conf->page_size; | |
482 | first_seed = page % conf->nseeds; | |
483 | ||
484 | for (; size; size -= conf->page_size) { | |
485 | if (nand_load_page(conf, offs)) | |
486 | return -1; | |
487 | ||
488 | ret = nand_read_page(conf, offs, dest, conf->page_size); | |
489 | /* | |
490 | * The ->nseeds value should be equal to the number of pages | |
491 | * in an eraseblock. Since we don't know this information in | |
492 | * advance we might have picked a wrong value. | |
493 | */ | |
494 | if (ret < 0 && conf->randomize) { | |
495 | int cur_seed = page % conf->nseeds; | |
496 | ||
497 | /* | |
498 | * We already tried all the seed values => we are | |
499 | * facing a real corruption. | |
500 | */ | |
501 | if (cur_seed < first_seed) | |
502 | return -EIO; | |
503 | ||
504 | /* Try to adjust ->nseeds and read the page again... */ | |
505 | conf->nseeds = cur_seed; | |
506 | ||
507 | if (nand_reset_column()) | |
508 | return -EIO; | |
509 | ||
510 | /* ... it still fails => it's a real corruption. */ | |
511 | if (nand_read_page(conf, offs, dest, conf->page_size)) | |
512 | return -EIO; | |
513 | } else if (ret && conf->randomize) { | |
514 | memset(dest, 0xff, conf->page_size); | |
515 | } | |
516 | ||
517 | page++; | |
518 | offs += conf->page_size; | |
519 | dest += conf->page_size; | |
520 | } | |
521 | ||
522 | return 0; | |
f5916d18 HG |
523 | } |
524 | ||
525 | int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) | |
526 | { | |
7748b414 BB |
527 | static struct nfc_config conf = { }; |
528 | int ret; | |
529 | ||
530 | ret = nand_detect_config(&conf, offs, dest); | |
531 | if (ret) | |
532 | return ret; | |
533 | ||
534 | return nand_read_buffer(&conf, offs, size, dest); | |
aeb4b0d3 PZ |
535 | } |
536 | ||
5d65c67b HG |
537 | void nand_deselect(void) |
538 | { | |
539 | struct sunxi_ccm_reg *const ccm = | |
540 | (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; | |
541 | ||
542 | clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); | |
543 | #ifdef CONFIG_MACH_SUN9I | |
544 | clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); | |
545 | #else | |
546 | clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); | |
547 | #endif | |
548 | clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1); | |
549 | } |