]>
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) | |
69 | ||
ddd37fe8 BB |
70 | #define NFC_ST_CMD_INT_FLAG (1 << 1) |
71 | #define NFC_ST_DMA_INT_FLAG (1 << 2) | |
aeb4b0d3 PZ |
72 | |
73 | #define NFC_READ_CMD_OFFSET 0 | |
74 | #define NFC_RANDOM_READ_CMD0_OFFSET 8 | |
75 | #define NFC_RANDOM_READ_CMD1_OFFSET 16 | |
76 | ||
77 | #define NFC_CMD_RNDOUTSTART 0xE0 | |
78 | #define NFC_CMD_RNDOUT 0x05 | |
79 | #define NFC_CMD_READSTART 0x30 | |
80 | ||
81 | ||
82 | #define NFC_PAGE_CMD (2 << 30) | |
83 | ||
84 | #define SUNXI_DMA_CFG_REG0 0x300 | |
85 | #define SUNXI_DMA_SRC_START_ADDR_REG0 0x304 | |
86 | #define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308 | |
87 | #define SUNXI_DMA_DDMA_BC_REG0 0x30C | |
88 | #define SUNXI_DMA_DDMA_PARA_REG0 0x318 | |
89 | ||
90 | #define SUNXI_DMA_DDMA_CFG_REG_LOADING (1 << 31) | |
91 | #define SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 (2 << 25) | |
10d069b7 | 92 | #define SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM (1 << 16) |
aeb4b0d3 PZ |
93 | #define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 (2 << 9) |
94 | #define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO (1 << 5) | |
95 | #define SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC (3 << 0) | |
96 | ||
97 | #define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0) | |
98 | #define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8) | |
99 | ||
100 | /* minimal "boot0" style NAND support for Allwinner A20 */ | |
101 | ||
aeb4b0d3 PZ |
102 | /* random seed used by linux */ |
103 | const uint16_t random_seed[128] = { | |
104 | 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, | |
105 | 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, | |
106 | 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, | |
107 | 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, | |
108 | 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, | |
109 | 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, | |
110 | 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, | |
111 | 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, | |
112 | 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, | |
113 | 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, | |
114 | 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, | |
115 | 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, | |
116 | 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, | |
117 | 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, | |
118 | 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, | |
119 | 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, | |
120 | }; | |
121 | ||
aeb4b0d3 PZ |
122 | #define MAX_RETRIES 10 |
123 | ||
124 | static int check_value_inner(int offset, int expected_bits, | |
125 | int max_number_of_retries, int negation) | |
126 | { | |
127 | int retries = 0; | |
128 | do { | |
129 | int val = readl(offset) & expected_bits; | |
130 | if (negation ? !val : val) | |
131 | return 1; | |
132 | mdelay(1); | |
133 | retries++; | |
134 | } while (retries < max_number_of_retries); | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | static inline int check_value(int offset, int expected_bits, | |
140 | int max_number_of_retries) | |
141 | { | |
142 | return check_value_inner(offset, expected_bits, | |
143 | max_number_of_retries, 0); | |
144 | } | |
145 | ||
146 | static inline int check_value_negated(int offset, int unexpected_bits, | |
147 | int max_number_of_retries) | |
148 | { | |
149 | return check_value_inner(offset, unexpected_bits, | |
150 | max_number_of_retries, 1); | |
151 | } | |
152 | ||
153 | void nand_init(void) | |
154 | { | |
155 | uint32_t val; | |
156 | ||
f62bfa56 HG |
157 | board_nand_init(); |
158 | ||
aeb4b0d3 PZ |
159 | val = readl(SUNXI_NFC_BASE + NFC_CTL); |
160 | /* enable and reset CTL */ | |
161 | writel(val | NFC_CTL_EN | NFC_CTL_RESET, | |
162 | SUNXI_NFC_BASE + NFC_CTL); | |
163 | ||
164 | if (!check_value_negated(SUNXI_NFC_BASE + NFC_CTL, | |
165 | NFC_CTL_RESET, MAX_RETRIES)) { | |
166 | printf("Couldn't initialize nand\n"); | |
167 | } | |
630cf2e7 HG |
168 | |
169 | /* reset NAND */ | |
ddd37fe8 | 170 | writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
630cf2e7 HG |
171 | writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET, |
172 | SUNXI_NFC_BASE + NFC_CMD); | |
173 | ||
ddd37fe8 | 174 | if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_CMD_INT_FLAG, |
630cf2e7 HG |
175 | MAX_RETRIES)) { |
176 | printf("Error timeout waiting for nand reset\n"); | |
177 | return; | |
178 | } | |
ddd37fe8 | 179 | writel(NFC_ST_CMD_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
aeb4b0d3 PZ |
180 | } |
181 | ||
f5916d18 | 182 | static int nand_read_page(int page_size, int ecc_strength, int ecc_page_size, |
c4adf9db | 183 | int addr_cycles, uint32_t real_addr, dma_addr_t dst) |
aeb4b0d3 PZ |
184 | { |
185 | uint32_t val; | |
008ac1df | 186 | int i, ecc_off = 0; |
aeb4b0d3 PZ |
187 | uint16_t ecc_mode = 0; |
188 | uint16_t rand_seed; | |
189 | uint32_t page; | |
190 | uint16_t column; | |
008ac1df | 191 | static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; |
aeb4b0d3 | 192 | |
008ac1df | 193 | for (i = 0; i < ARRAY_SIZE(strengths); i++) { |
f5916d18 | 194 | if (ecc_strength == strengths[i]) { |
008ac1df HG |
195 | ecc_mode = i; |
196 | break; | |
197 | } | |
aeb4b0d3 PZ |
198 | } |
199 | ||
008ac1df | 200 | /* HW ECC always request ECC bytes for 1024 bytes blocks */ |
f5916d18 | 201 | ecc_off = DIV_ROUND_UP(ecc_strength * fls(8 * 1024), 8); |
008ac1df HG |
202 | /* HW ECC always work with even numbers of ECC bytes */ |
203 | ecc_off += (ecc_off & 1); | |
204 | ecc_off += 4; /* prepad */ | |
aeb4b0d3 | 205 | |
f5916d18 HG |
206 | page = real_addr / page_size; |
207 | column = real_addr % page_size; | |
aeb4b0d3 | 208 | |
aeb4b0d3 PZ |
209 | /* clear ecc status */ |
210 | writel(0, SUNXI_NFC_BASE + NFC_ECC_ST); | |
211 | ||
212 | /* Choose correct seed */ | |
c4adf9db | 213 | rand_seed = random_seed[page % 128]; |
aeb4b0d3 PZ |
214 | |
215 | writel((rand_seed << 16) | NFC_ECC_RANDOM_EN | NFC_ECC_EN | |
216 | | NFC_ECC_PIPELINE | (ecc_mode << 12), | |
217 | SUNXI_NFC_BASE + NFC_ECC_CTL); | |
218 | ||
219 | val = readl(SUNXI_NFC_BASE + NFC_CTL); | |
220 | writel(val | NFC_CTL_RAM_METHOD, SUNXI_NFC_BASE + NFC_CTL); | |
221 | ||
c4adf9db BB |
222 | writel(page_size + (column / ecc_page_size) * ecc_off, |
223 | SUNXI_NFC_BASE + NFC_SPARE_AREA); | |
aeb4b0d3 | 224 | |
f5916d18 | 225 | flush_dcache_range(dst, ALIGN(dst + ecc_page_size, ARCH_DMA_MINALIGN)); |
2a43973f | 226 | |
aeb4b0d3 PZ |
227 | /* SUNXI_DMA */ |
228 | writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */ | |
229 | /* read from REG_IO_DATA */ | |
230 | writel(SUNXI_NFC_BASE + NFC_IO_DATA, | |
231 | SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0); | |
232 | /* read to RAM */ | |
10d069b7 | 233 | writel(dst, SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0); |
aeb4b0d3 PZ |
234 | writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC |
235 | | SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE, | |
236 | SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0); | |
f5916d18 | 237 | writel(ecc_page_size, |
aeb4b0d3 PZ |
238 | SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0); /* 1kB */ |
239 | writel(SUNXI_DMA_DDMA_CFG_REG_LOADING | |
240 | | SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 | |
10d069b7 | 241 | | SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM |
aeb4b0d3 PZ |
242 | | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 |
243 | | SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO | |
244 | | SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC, | |
245 | SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); | |
246 | ||
247 | writel((NFC_CMD_RNDOUTSTART << NFC_RANDOM_READ_CMD1_OFFSET) | |
248 | | (NFC_CMD_RNDOUT << NFC_RANDOM_READ_CMD0_OFFSET) | |
249 | | (NFC_CMD_READSTART | NFC_READ_CMD_OFFSET), SUNXI_NFC_BASE | |
250 | + NFC_RCMD_SET); | |
251 | writel(1, SUNXI_NFC_BASE + NFC_SECTOR_NUM); | |
252 | writel(((page & 0xFFFF) << 16) | column, | |
253 | SUNXI_NFC_BASE + NFC_ADDR_LOW); | |
254 | writel((page >> 16) & 0xFF, SUNXI_NFC_BASE + NFC_ADDR_HIGH); | |
ddd37fe8 | 255 | writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
aeb4b0d3 | 256 | writel(NFC_SEND_CMD1 | NFC_SEND_CMD2 | NFC_DATA_TRANS | |
f5916d18 HG |
257 | NFC_PAGE_CMD | NFC_WAIT_FLAG | |
258 | ((addr_cycles - 1) << NFC_ADDR_NUM_OFFSET) | | |
c4adf9db | 259 | NFC_SEND_ADR | NFC_DATA_SWAP_METHOD, |
aeb4b0d3 PZ |
260 | SUNXI_NFC_BASE + NFC_CMD); |
261 | ||
ddd37fe8 | 262 | if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG, |
aeb4b0d3 PZ |
263 | MAX_RETRIES)) { |
264 | printf("Error while initializing dma interrupt\n"); | |
f5916d18 | 265 | return -1; |
aeb4b0d3 | 266 | } |
ddd37fe8 | 267 | writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST); |
aeb4b0d3 PZ |
268 | |
269 | if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0, | |
270 | SUNXI_DMA_DDMA_CFG_REG_LOADING, MAX_RETRIES)) { | |
271 | printf("Error while waiting for dma transfer to finish\n"); | |
f5916d18 | 272 | return -1; |
aeb4b0d3 PZ |
273 | } |
274 | ||
2a43973f | 275 | invalidate_dcache_range(dst, |
f5916d18 | 276 | ALIGN(dst + ecc_page_size, ARCH_DMA_MINALIGN)); |
2a43973f | 277 | |
aeb4b0d3 | 278 | if (readl(SUNXI_NFC_BASE + NFC_ECC_ST)) |
f5916d18 HG |
279 | return -1; |
280 | ||
281 | return 0; | |
aeb4b0d3 PZ |
282 | } |
283 | ||
f5916d18 | 284 | static int nand_read_ecc(int page_size, int ecc_strength, int ecc_page_size, |
c4adf9db | 285 | int addr_cycles, uint32_t offs, uint32_t size, void *dest) |
aeb4b0d3 | 286 | { |
f5916d18 | 287 | void *end = dest + size; |
aeb4b0d3 | 288 | |
0a247554 | 289 | clrsetbits_le32(SUNXI_NFC_BASE + NFC_CTL, NFC_CTL_PAGE_SIZE_MASK, |
f5916d18 HG |
290 | NFC_CTL_PAGE_SIZE(page_size)); |
291 | ||
292 | for ( ;dest < end; dest += ecc_page_size, offs += ecc_page_size) { | |
293 | if (nand_read_page(page_size, ecc_strength, ecc_page_size, | |
c4adf9db | 294 | addr_cycles, offs, (dma_addr_t)dest)) |
f5916d18 | 295 | return -1; |
aeb4b0d3 | 296 | } |
f5916d18 HG |
297 | |
298 | return 0; | |
299 | } | |
300 | ||
c4adf9db | 301 | static int nand_read_buffer(uint32_t offs, unsigned int size, void *dest) |
f5916d18 | 302 | { |
2b8a01a9 HG |
303 | const struct { |
304 | int page_size; | |
305 | int ecc_strength; | |
306 | int ecc_page_size; | |
307 | int addr_cycles; | |
308 | } nand_configs[] = { | |
309 | { 8192, 40, 1024, 5 }, | |
310 | { 16384, 56, 1024, 5 }, | |
311 | { 8192, 24, 1024, 5 }, | |
cc19722f | 312 | { 4096, 24, 1024, 5 }, |
2b8a01a9 HG |
313 | }; |
314 | static int nand_config = -1; | |
315 | int i; | |
316 | ||
317 | if (nand_config == -1) { | |
318 | for (i = 0; i < ARRAY_SIZE(nand_configs); i++) { | |
319 | debug("nand: trying page %d ecc %d / %d addr %d: ", | |
320 | nand_configs[i].page_size, | |
321 | nand_configs[i].ecc_strength, | |
322 | nand_configs[i].ecc_page_size, | |
323 | nand_configs[i].addr_cycles); | |
324 | if (nand_read_ecc(nand_configs[i].page_size, | |
325 | nand_configs[i].ecc_strength, | |
326 | nand_configs[i].ecc_page_size, | |
327 | nand_configs[i].addr_cycles, | |
c4adf9db | 328 | offs, size, dest) == 0) { |
2b8a01a9 HG |
329 | debug("success\n"); |
330 | nand_config = i; | |
331 | return 0; | |
332 | } | |
333 | debug("failed\n"); | |
334 | } | |
335 | return -1; | |
336 | } | |
337 | ||
338 | return nand_read_ecc(nand_configs[nand_config].page_size, | |
339 | nand_configs[nand_config].ecc_strength, | |
340 | nand_configs[nand_config].ecc_page_size, | |
341 | nand_configs[nand_config].addr_cycles, | |
c4adf9db | 342 | offs, size, dest); |
f5916d18 HG |
343 | } |
344 | ||
345 | int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) | |
346 | { | |
c4adf9db | 347 | return nand_read_buffer(offs, size, dest); |
aeb4b0d3 PZ |
348 | } |
349 | ||
5d65c67b HG |
350 | void nand_deselect(void) |
351 | { | |
352 | struct sunxi_ccm_reg *const ccm = | |
353 | (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; | |
354 | ||
355 | clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0)); | |
356 | #ifdef CONFIG_MACH_SUN9I | |
357 | clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); | |
358 | #else | |
359 | clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); | |
360 | #endif | |
361 | clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1); | |
362 | } |