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