]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
3a88179d PK |
2 | /* |
3 | * NAND boot for Freescale Integrated Flash Controller, NAND FCM | |
4 | * | |
5 | * Copyright 2011 Freescale Semiconductor, Inc. | |
6 | * Author: Dipen Dudhat <dipen.dudhat@freescale.com> | |
3a88179d PK |
7 | */ |
8 | ||
9 | #include <common.h> | |
10 | #include <asm/io.h> | |
0b66513b | 11 | #include <fsl_ifc.h> |
6ae3900a | 12 | #include <linux/mtd/rawnand.h> |
8f01397b SG |
13 | #ifdef CONFIG_CHAIN_OF_TRUST |
14 | #include <fsl_validate.h> | |
15 | #endif | |
3a88179d PK |
16 | |
17 | static inline int is_blank(uchar *addr, int page_size) | |
18 | { | |
19 | int i; | |
20 | ||
21 | for (i = 0; i < page_size; i++) { | |
22 | if (__raw_readb(&addr[i]) != 0xff) | |
23 | return 0; | |
24 | } | |
25 | ||
26 | /* | |
27 | * For the SPL, don't worry about uncorrectable errors | |
28 | * where the main area is all FFs but shouldn't be. | |
29 | */ | |
30 | return 1; | |
31 | } | |
32 | ||
33 | /* returns nonzero if entire page is blank */ | |
34 | static inline int check_read_ecc(uchar *buf, u32 *eccstat, | |
35 | unsigned int bufnum, int page_size) | |
36 | { | |
37 | u32 reg = eccstat[bufnum / 4]; | |
38 | int errors = (reg >> ((3 - bufnum % 4) * 8)) & 0xf; | |
39 | ||
40 | if (errors == 0xf) { /* uncorrectable */ | |
41 | /* Blank pages fail hw ECC checks */ | |
42 | if (is_blank(buf, page_size)) | |
43 | return 1; | |
44 | ||
45 | puts("ecc error\n"); | |
46 | for (;;) | |
47 | ; | |
48 | } | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
39b0bbbb JS |
53 | static inline struct fsl_ifc_runtime *runtime_regs_address(void) |
54 | { | |
55 | struct fsl_ifc regs = {(void *)CONFIG_SYS_IFC_ADDR, NULL}; | |
56 | int ver = 0; | |
57 | ||
58 | ver = ifc_in32(®s.gregs->ifc_rev); | |
59 | if (ver >= FSL_IFC_V2_0_0) | |
60 | regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_64KOFFSET; | |
61 | else | |
62 | regs.rregs = (void *)CONFIG_SYS_IFC_ADDR + IFC_RREGS_4KOFFSET; | |
63 | ||
64 | return regs.rregs; | |
65 | } | |
66 | ||
3a88179d PK |
67 | static inline void nand_wait(uchar *buf, int bufnum, int page_size) |
68 | { | |
39b0bbbb | 69 | struct fsl_ifc_runtime *ifc = runtime_regs_address(); |
3a88179d | 70 | u32 status; |
b2d5ac59 | 71 | u32 eccstat[8]; |
3a88179d PK |
72 | int bufperpage = page_size / 512; |
73 | int bufnum_end, i; | |
74 | ||
75 | bufnum *= bufperpage; | |
76 | bufnum_end = bufnum + bufperpage - 1; | |
77 | ||
78 | do { | |
1b4175d6 | 79 | status = ifc_in32(&ifc->ifc_nand.nand_evter_stat); |
3a88179d PK |
80 | } while (!(status & IFC_NAND_EVTER_STAT_OPC)); |
81 | ||
82 | if (status & IFC_NAND_EVTER_STAT_FTOER) { | |
83 | puts("flash time out error\n"); | |
84 | for (;;) | |
85 | ; | |
86 | } | |
87 | ||
88 | for (i = bufnum / 4; i <= bufnum_end / 4; i++) | |
1b4175d6 | 89 | eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]); |
3a88179d PK |
90 | |
91 | for (i = bufnum; i <= bufnum_end; i++) { | |
92 | if (check_read_ecc(buf, eccstat, i, page_size)) | |
93 | break; | |
94 | } | |
95 | ||
1b4175d6 | 96 | ifc_out32(&ifc->ifc_nand.nand_evter_stat, status); |
3a88179d PK |
97 | } |
98 | ||
99 | static inline int bad_block(uchar *marker, int port_size) | |
100 | { | |
101 | if (port_size == 8) | |
102 | return __raw_readb(marker) != 0xff; | |
103 | else | |
104 | return __raw_readw((u16 *)marker) != 0xffff; | |
105 | } | |
106 | ||
6609916e | 107 | int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst) |
3a88179d | 108 | { |
39b0bbbb JS |
109 | struct fsl_ifc_fcm *gregs = (void *)CONFIG_SYS_IFC_ADDR; |
110 | struct fsl_ifc_runtime *ifc = NULL; | |
3a88179d PK |
111 | uchar *buf = (uchar *)CONFIG_SYS_NAND_BASE; |
112 | int page_size; | |
113 | int port_size; | |
114 | int pages_per_blk; | |
115 | int blk_size; | |
116 | int bad_marker = 0; | |
591dd192 | 117 | int bufnum_mask, bufnum, ver = 0; |
3a88179d PK |
118 | |
119 | int csor, cspr; | |
120 | int pos = 0; | |
121 | int j = 0; | |
122 | ||
123 | int sram_addr; | |
124 | int pg_no; | |
6609916e | 125 | uchar *dst = vdst; |
3a88179d | 126 | |
39b0bbbb JS |
127 | ifc = runtime_regs_address(); |
128 | ||
3a88179d PK |
129 | /* Get NAND Flash configuration */ |
130 | csor = CONFIG_SYS_NAND_CSOR; | |
131 | cspr = CONFIG_SYS_NAND_CSPR; | |
132 | ||
133 | port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8; | |
134 | ||
71220f80 PK |
135 | if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_8K) { |
136 | page_size = 8192; | |
137 | bufnum_mask = 0x0; | |
138 | } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_4K) { | |
3a88179d PK |
139 | page_size = 4096; |
140 | bufnum_mask = 0x1; | |
71220f80 | 141 | } else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_2K) { |
3a88179d PK |
142 | page_size = 2048; |
143 | bufnum_mask = 0x3; | |
144 | } else { | |
145 | page_size = 512; | |
146 | bufnum_mask = 0xf; | |
147 | ||
148 | if (port_size == 8) | |
149 | bad_marker = 5; | |
150 | } | |
151 | ||
39b0bbbb | 152 | ver = ifc_in32(&gregs->ifc_rev); |
591dd192 PK |
153 | if (ver >= FSL_IFC_V2_0_0) |
154 | bufnum_mask = (bufnum_mask * 2) + 1; | |
155 | ||
3a88179d PK |
156 | pages_per_blk = |
157 | 32 << ((csor & CSOR_NAND_PB_MASK) >> CSOR_NAND_PB_SHIFT); | |
158 | ||
159 | blk_size = pages_per_blk * page_size; | |
160 | ||
161 | /* Open Full SRAM mapping for spare are access */ | |
1b4175d6 | 162 | ifc_out32(&ifc->ifc_nand.ncfgr, 0x0); |
3a88179d PK |
163 | |
164 | /* Clear Boot events */ | |
1b4175d6 | 165 | ifc_out32(&ifc->ifc_nand.nand_evter_stat, 0xffffffff); |
3a88179d PK |
166 | |
167 | /* Program FIR/FCR for Large/Small page */ | |
168 | if (page_size > 512) { | |
1b4175d6 PK |
169 | ifc_out32(&ifc->ifc_nand.nand_fir0, |
170 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | |
171 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | | |
172 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | | |
173 | (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | | |
174 | (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP4_SHIFT)); | |
175 | ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); | |
176 | ||
177 | ifc_out32(&ifc->ifc_nand.nand_fcr0, | |
178 | (NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | | |
179 | (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT)); | |
3a88179d | 180 | } else { |
1b4175d6 PK |
181 | ifc_out32(&ifc->ifc_nand.nand_fir0, |
182 | (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | | |
183 | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | | |
184 | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | | |
185 | (IFC_FIR_OP_BTRD << IFC_NAND_FIR0_OP3_SHIFT)); | |
186 | ifc_out32(&ifc->ifc_nand.nand_fir1, 0x0); | |
187 | ||
188 | ifc_out32(&ifc->ifc_nand.nand_fcr0, | |
189 | NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT); | |
3a88179d PK |
190 | } |
191 | ||
192 | /* Program FBCR = 0 for full page read */ | |
1b4175d6 | 193 | ifc_out32(&ifc->ifc_nand.nand_fbcr, 0); |
3a88179d PK |
194 | |
195 | /* Read and copy u-boot on SDRAM from NAND device, In parallel | |
196 | * check for Bad block if found skip it and read continue to | |
197 | * next Block | |
198 | */ | |
199 | while (pos < uboot_size) { | |
200 | int i = 0; | |
201 | do { | |
202 | pg_no = offs / page_size; | |
203 | bufnum = pg_no & bufnum_mask; | |
204 | sram_addr = bufnum * page_size * 2; | |
205 | ||
1b4175d6 PK |
206 | ifc_out32(&ifc->ifc_nand.row0, pg_no); |
207 | ifc_out32(&ifc->ifc_nand.col0, 0); | |
3a88179d | 208 | /* start read */ |
1b4175d6 PK |
209 | ifc_out32(&ifc->ifc_nand.nandseq_strt, |
210 | IFC_NAND_SEQ_STRT_FIR_STRT); | |
3a88179d PK |
211 | |
212 | /* wait for read to complete */ | |
213 | nand_wait(&buf[sram_addr], bufnum, page_size); | |
214 | ||
215 | /* | |
216 | * If either of the first two pages are marked bad, | |
217 | * continue to the next block. | |
218 | */ | |
219 | if (i++ < 2 && | |
220 | bad_block(&buf[sram_addr + page_size + bad_marker], | |
221 | port_size)) { | |
222 | puts("skipping\n"); | |
223 | offs = (offs + blk_size) & ~(blk_size - 1); | |
224 | pos &= ~(blk_size - 1); | |
225 | break; | |
226 | } | |
227 | ||
228 | for (j = 0; j < page_size; j++) | |
229 | dst[pos + j] = __raw_readb(&buf[sram_addr + j]); | |
230 | ||
231 | pos += page_size; | |
232 | offs += page_size; | |
233 | } while ((offs & (blk_size - 1)) && (pos < uboot_size)); | |
234 | } | |
6609916e PL |
235 | |
236 | return 0; | |
3a88179d PK |
237 | } |
238 | ||
239 | /* | |
240 | * Main entrypoint for NAND Boot. It's necessary that SDRAM is already | |
a187559e | 241 | * configured and available since this code loads the main U-Boot image |
3a88179d PK |
242 | * from NAND into SDRAM and starts from there. |
243 | */ | |
244 | void nand_boot(void) | |
245 | { | |
246 | __attribute__((noreturn)) void (*uboot)(void); | |
247 | /* | |
248 | * Load U-Boot image from NAND into RAM | |
249 | */ | |
6609916e PL |
250 | nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, |
251 | CONFIG_SYS_NAND_U_BOOT_SIZE, | |
252 | (uchar *)CONFIG_SYS_NAND_U_BOOT_DST); | |
3a88179d PK |
253 | |
254 | #ifdef CONFIG_NAND_ENV_DST | |
6609916e PL |
255 | nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, |
256 | (uchar *)CONFIG_NAND_ENV_DST); | |
3a88179d PK |
257 | |
258 | #ifdef CONFIG_ENV_OFFSET_REDUND | |
6609916e PL |
259 | nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, |
260 | (uchar *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); | |
3a88179d PK |
261 | #endif |
262 | #endif | |
263 | /* | |
264 | * Jump to U-Boot image | |
265 | */ | |
266 | #ifdef CONFIG_SPL_FLUSH_IMAGE | |
267 | /* | |
268 | * Clean d-cache and invalidate i-cache, to | |
269 | * make sure that no stale data is executed. | |
270 | */ | |
271 | flush_cache(CONFIG_SYS_NAND_U_BOOT_DST, CONFIG_SYS_NAND_U_BOOT_SIZE); | |
272 | #endif | |
8f01397b SG |
273 | |
274 | #ifdef CONFIG_CHAIN_OF_TRUST | |
275 | /* | |
276 | * U-Boot header is appended at end of U-boot image, so | |
277 | * calculate U-boot header address using U-boot header size. | |
278 | */ | |
279 | #define CONFIG_U_BOOT_HDR_ADDR \ | |
280 | ((CONFIG_SYS_NAND_U_BOOT_START + \ | |
281 | CONFIG_SYS_NAND_U_BOOT_SIZE) - \ | |
282 | CONFIG_U_BOOT_HDR_SIZE) | |
283 | spl_validate_uboot(CONFIG_U_BOOT_HDR_ADDR, | |
284 | CONFIG_SYS_NAND_U_BOOT_START); | |
285 | /* | |
286 | * In case of failure in validation, spl_validate_uboot would | |
287 | * not return back in case of Production environment with ITS=1. | |
288 | * Thus U-Boot will not start. | |
289 | * In Development environment (ITS=0 and SB_EN=1), the function | |
290 | * may return back in case of non-fatal failures. | |
291 | */ | |
292 | #endif | |
293 | ||
3a88179d PK |
294 | uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; |
295 | uboot(); | |
296 | } | |
8ab967b6 AW |
297 | |
298 | #ifndef CONFIG_SPL_NAND_INIT | |
299 | void nand_init(void) | |
300 | { | |
301 | } | |
302 | ||
303 | void nand_deselect(void) | |
304 | { | |
305 | } | |
306 | #endif |