]>
Commit | Line | Data |
---|---|---|
dc7c9a1a | 1 | /* |
dc7c9a1a WD |
2 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
3 | * | |
1a459660 | 4 | * SPDX-License-Identifier: GPL-2.0+ |
dc7c9a1a WD |
5 | */ |
6 | ||
7 | #include <common.h> | |
8 | #include <mpc8xx.h> | |
9 | ||
0e8d1586 | 10 | #ifndef CONFIG_ENV_ADDR |
6d0f6bcf | 11 | #define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET) |
dc7c9a1a WD |
12 | #endif |
13 | ||
8adc9052 | 14 | flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; |
dc7c9a1a WD |
15 | |
16 | /*----------------------------------------------------------------------- | |
17 | * Functions | |
18 | */ | |
8adc9052 WD |
19 | static int write_word(flash_info_t *info, ulong dest, ulong data); |
20 | ||
21 | #ifdef CONFIG_BOOT_8B | |
22 | static int my_in_8(unsigned char *addr); | |
23 | static void my_out_8(unsigned char *addr, int val); | |
dc7c9a1a | 24 | #endif |
8adc9052 WD |
25 | #ifdef CONFIG_BOOT_16B |
26 | static int my_in_be16(unsigned short *addr); | |
27 | static void my_out_be16(unsigned short *addr, int val); | |
dc7c9a1a | 28 | #endif |
8adc9052 WD |
29 | #ifdef CONFIG_BOOT_32B |
30 | static unsigned my_in_be32(unsigned *addr); | |
31 | static void my_out_be32(unsigned *addr, int val); | |
dc7c9a1a WD |
32 | #endif |
33 | /*----------------------------------------------------------------------- | |
34 | */ | |
35 | ||
8adc9052 | 36 | unsigned long flash_init(void) |
dc7c9a1a | 37 | { |
8adc9052 | 38 | volatile immap_t *immap = (immap_t *) CONFIG_SYS_IMMR; |
dc7c9a1a WD |
39 | volatile memctl8xx_t *memctl = &immap->im_memctl; |
40 | unsigned long size_b0, size_b1; | |
41 | int i; | |
42 | ||
8adc9052 WD |
43 | size_b0 = 0; |
44 | size_b1 = 0; | |
dc7c9a1a | 45 | /* Init: no FLASHes known */ |
8adc9052 | 46 | for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) |
dc7c9a1a | 47 | flash_info[i].flash_id = FLASH_UNKNOWN; |
8adc9052 | 48 | |
6d0f6bcf | 49 | #ifdef CONFIG_SYS_DOC_BASE |
dc7c9a1a | 50 | #ifndef CONFIG_FEL8xx_AT |
8adc9052 WD |
51 | /* 32k bytes */ |
52 | memctl->memc_or5 = (0xffff8000 | CONFIG_SYS_OR_TIMING_DOC); | |
6d0f6bcf | 53 | memctl->memc_br5 = CONFIG_SYS_DOC_BASE | 0x401; |
dc7c9a1a | 54 | #else |
8adc9052 WD |
55 | /* 32k bytes */ |
56 | memctl->memc_or3 = (0xffff8000 | CONFIG_SYS_OR_TIMING_DOC); | |
6d0f6bcf | 57 | memctl->memc_br3 = CONFIG_SYS_DOC_BASE | 0x401; |
dc7c9a1a WD |
58 | #endif |
59 | #endif | |
8adc9052 WD |
60 | #if defined(CONFIG_BOOT_8B) |
61 | size_b0 = 0x80000; /* 512 K */ | |
62 | ||
8bde7f77 WD |
63 | flash_info[0].flash_id = FLASH_MAN_AMD | FLASH_AM040; |
64 | flash_info[0].sector_count = 8; | |
65 | flash_info[0].size = 0x00080000; | |
8adc9052 | 66 | |
8bde7f77 WD |
67 | /* set up sector start address table */ |
68 | for (i = 0; i < flash_info[0].sector_count; i++) | |
8adc9052 WD |
69 | flash_info[0].start[i] = 0x40000000 + (i * 0x10000); |
70 | ||
8bde7f77 WD |
71 | /* protect all sectors */ |
72 | for (i = 0; i < flash_info[0].sector_count; i++) | |
8adc9052 WD |
73 | flash_info[0].protect[i] = 0x1; |
74 | ||
75 | #elif defined(CONFIG_BOOT_16B) | |
76 | size_b0 = 0x400000; /* 4MB , assume AMD29LV320B */ | |
77 | ||
8bde7f77 WD |
78 | flash_info[0].flash_id = FLASH_MAN_AMD | FLASH_AM320B; |
79 | flash_info[0].sector_count = 67; | |
80 | flash_info[0].size = 0x00400000; | |
8adc9052 | 81 | |
8bde7f77 | 82 | /* set up sector start address table */ |
8adc9052 | 83 | flash_info[0].start[0] = 0x40000000; |
8bde7f77 WD |
84 | flash_info[0].start[1] = 0x40000000 + 0x4000; |
85 | flash_info[0].start[2] = 0x40000000 + 0x6000; | |
86 | flash_info[0].start[3] = 0x40000000 + 0x8000; | |
8adc9052 WD |
87 | |
88 | for (i = 4; i < flash_info[0].sector_count; i++) { | |
89 | flash_info[0].start[i] = | |
90 | 0x40000000 + 0x10000 + ((i - 4) * 0x10000); | |
91 | } | |
92 | ||
8bde7f77 WD |
93 | /* protect all sectors */ |
94 | for (i = 0; i < flash_info[0].sector_count; i++) | |
8adc9052 | 95 | flash_info[0].protect[i] = 0x1; |
8bde7f77 | 96 | #endif |
dc7c9a1a | 97 | |
dc7c9a1a WD |
98 | #ifdef CONFIG_BOOT_32B |
99 | ||
100 | /* Static FLASH Bank configuration here - FIXME XXX */ | |
8adc9052 WD |
101 | size_b0 = flash_get_size((vu_long *) FLASH_BASE0_PRELIM, |
102 | &flash_info[0]); | |
dc7c9a1a WD |
103 | |
104 | if (flash_info[0].flash_id == FLASH_UNKNOWN) { | |
8adc9052 WD |
105 | printf("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", |
106 | size_b0, size_b0 << 20); | |
dc7c9a1a WD |
107 | } |
108 | ||
8adc9052 WD |
109 | size_b1 = flash_get_size((vu_long *) FLASH_BASE1_PRELIM, |
110 | &flash_info[1]); | |
dc7c9a1a WD |
111 | |
112 | if (size_b1 > size_b0) { | |
8adc9052 | 113 | printf("## ERROR: " |
dc7c9a1a | 114 | "Bank 1 (0x%08lx = %ld MB) > Bank 0 (0x%08lx = %ld MB)\n", |
8adc9052 WD |
115 | size_b1, size_b1 << 20, size_b0, size_b0 << 20); |
116 | flash_info[0].flash_id = FLASH_UNKNOWN; | |
117 | flash_info[1].flash_id = FLASH_UNKNOWN; | |
118 | flash_info[0].sector_count = -1; | |
119 | flash_info[1].sector_count = -1; | |
120 | flash_info[0].size = 0; | |
121 | flash_info[1].size = 0; | |
122 | ||
123 | return 0; | |
dc7c9a1a WD |
124 | } |
125 | ||
126 | /* Remap FLASH according to real size */ | |
8adc9052 WD |
127 | memctl->memc_or0 = CONFIG_SYS_OR_TIMING_FLASH | |
128 | (-size_b0 & OR_AM_MSK); | |
129 | memctl->memc_br0 = (CONFIG_SYS_FLASH_BASE & BR_BA_MSK) | | |
130 | BR_MS_GPCM | BR_V; | |
dc7c9a1a WD |
131 | |
132 | /* Re-do sizing to get full correct info */ | |
8adc9052 WD |
133 | size_b0 = flash_get_size((vu_long *) CONFIG_SYS_FLASH_BASE, |
134 | &flash_info[0]); | |
dc7c9a1a | 135 | |
8adc9052 | 136 | flash_get_offsets(CONFIG_SYS_FLASH_BASE, &flash_info[0]); |
dc7c9a1a | 137 | |
6d0f6bcf | 138 | #if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE |
dc7c9a1a WD |
139 | /* monitor protection ON by default */ |
140 | flash_protect(FLAG_PROTECT_SET, | |
8adc9052 WD |
141 | CONFIG_SYS_MONITOR_BASE, |
142 | CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, | |
143 | &flash_info[0]); | |
dc7c9a1a WD |
144 | #endif |
145 | ||
5a1aceb0 | 146 | #ifdef CONFIG_ENV_IS_IN_FLASH |
dc7c9a1a WD |
147 | /* ENV protection ON by default */ |
148 | flash_protect(FLAG_PROTECT_SET, | |
8adc9052 WD |
149 | CONFIG_ENV_ADDR, |
150 | CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]); | |
dc7c9a1a WD |
151 | #endif |
152 | ||
153 | if (size_b1) { | |
8adc9052 WD |
154 | memctl->memc_or1 = CONFIG_SYS_OR_TIMING_FLASH | |
155 | (-size_b1 & 0xFFFF8000); | |
156 | memctl->memc_br1 = ((CONFIG_SYS_FLASH_BASE + | |
157 | size_b0) & BR_BA_MSK) | BR_MS_GPCM | BR_V; | |
dc7c9a1a WD |
158 | |
159 | /* Re-do sizing to get full correct info */ | |
8adc9052 WD |
160 | size_b1 = flash_get_size((vu_long *)(CONFIG_SYS_FLASH_BASE + |
161 | size_b0), &flash_info[1]); | |
dc7c9a1a | 162 | |
8adc9052 WD |
163 | flash_get_offsets(CONFIG_SYS_FLASH_BASE + size_b0, |
164 | &flash_info[1]); | |
dc7c9a1a | 165 | |
6d0f6bcf | 166 | #if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE |
dc7c9a1a WD |
167 | /* monitor protection ON by default */ |
168 | flash_protect(FLAG_PROTECT_SET, | |
6d0f6bcf | 169 | CONFIG_SYS_MONITOR_BASE, |
8adc9052 | 170 | CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, |
dc7c9a1a WD |
171 | &flash_info[1]); |
172 | #endif | |
173 | ||
5a1aceb0 | 174 | #ifdef CONFIG_ENV_IS_IN_FLASH |
dc7c9a1a WD |
175 | /* ENV protection ON by default */ |
176 | flash_protect(FLAG_PROTECT_SET, | |
0e8d1586 | 177 | CONFIG_ENV_ADDR, |
8adc9052 | 178 | CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, |
dc7c9a1a WD |
179 | &flash_info[1]); |
180 | #endif | |
181 | } else { | |
8adc9052 | 182 | memctl->memc_br1 = 0; /* invalidate bank */ |
dc7c9a1a WD |
183 | |
184 | flash_info[1].flash_id = FLASH_UNKNOWN; | |
185 | flash_info[1].sector_count = -1; | |
186 | } | |
187 | ||
188 | flash_info[0].size = size_b0; | |
189 | flash_info[1].size = size_b1; | |
190 | ||
191 | ||
8adc9052 | 192 | #endif /* CONFIG_BOOT_32B */ |
dc7c9a1a | 193 | |
8adc9052 | 194 | return size_b0 + size_b1; |
dc7c9a1a | 195 | } |
dc7c9a1a | 196 | |
8adc9052 WD |
197 | |
198 | void flash_print_info(flash_info_t *info) | |
dc7c9a1a WD |
199 | { |
200 | int i; | |
201 | ||
202 | if (info->flash_id == FLASH_UNKNOWN) { | |
8adc9052 | 203 | printf("missing or unknown FLASH type\n"); |
dc7c9a1a WD |
204 | return; |
205 | } | |
206 | ||
207 | switch (info->flash_id & FLASH_VENDMASK) { | |
8adc9052 WD |
208 | case FLASH_MAN_AMD: |
209 | printf("AMD "); | |
210 | break; | |
211 | case FLASH_MAN_FUJ: | |
212 | printf("FUJITSU "); | |
213 | break; | |
214 | default: | |
215 | printf("Unknown Vendor "); | |
216 | break; | |
dc7c9a1a WD |
217 | } |
218 | ||
219 | switch (info->flash_id & FLASH_TYPEMASK) { | |
8adc9052 WD |
220 | case FLASH_AM400B: |
221 | printf("AM29LV400B (4 Mbit, bottom boot sect)\n"); | |
222 | break; | |
223 | case FLASH_AM400T: | |
224 | printf("AM29LV400T (4 Mbit, top boot sector)\n"); | |
225 | break; | |
226 | case FLASH_AM800B: | |
227 | printf("AM29LV800B (8 Mbit, bottom boot sect)\n"); | |
228 | break; | |
229 | case FLASH_AM800T: | |
230 | printf("AM29LV800T (8 Mbit, top boot sector)\n"); | |
231 | break; | |
232 | case FLASH_AM160B: | |
233 | printf("AM29LV160B (16 Mbit, bottom boot sect)\n"); | |
234 | break; | |
235 | case FLASH_AM160T: | |
236 | printf("AM29LV160T (16 Mbit, top boot sector)\n"); | |
237 | break; | |
238 | case FLASH_AM320B: | |
239 | printf("AM29LV320B (32 Mbit, bottom boot sect)\n"); | |
240 | break; | |
241 | case FLASH_AM320T: | |
242 | printf("AM29LV320T (32 Mbit, top boot sector)\n"); | |
243 | break; | |
244 | default: | |
245 | printf("Unknown Chip Type\n"); | |
246 | break; | |
dc7c9a1a WD |
247 | } |
248 | ||
8adc9052 | 249 | printf(" Size: %ld MB in %d Sectors\n", |
dc7c9a1a WD |
250 | info->size >> 20, info->sector_count); |
251 | ||
8adc9052 WD |
252 | printf(" Sector Start Addresses:"); |
253 | for (i = 0; i < info->sector_count; ++i) { | |
dc7c9a1a | 254 | if ((i % 5) == 0) |
8adc9052 WD |
255 | printf("\n "); |
256 | printf(" %08lX%s", | |
257 | info->start[i], info->protect[i] ? " (RO)" : " "); | |
dc7c9a1a | 258 | } |
8adc9052 | 259 | printf("\n"); |
dc7c9a1a WD |
260 | return; |
261 | } | |
262 | ||
dc7c9a1a WD |
263 | /* |
264 | * The following code cannot be run from FLASH! | |
265 | */ | |
dc7c9a1a | 266 | |
8adc9052 | 267 | int flash_erase(flash_info_t *info, int s_first, int s_last) |
dc7c9a1a | 268 | { |
8adc9052 WD |
269 | vu_long *addr = (vu_long *) (info->start[0]); |
270 | int flag, prot, sect, l_sect, in_mid, in_did; | |
dc7c9a1a WD |
271 | ulong start, now, last; |
272 | ||
273 | if ((s_first < 0) || (s_first > s_last)) { | |
8adc9052 WD |
274 | if (info->flash_id == FLASH_UNKNOWN) |
275 | printf("- missing\n"); | |
276 | else | |
277 | printf("- no sectors to erase\n"); | |
278 | ||
dc7c9a1a WD |
279 | return 1; |
280 | } | |
281 | ||
282 | if ((info->flash_id == FLASH_UNKNOWN) || | |
283 | (info->flash_id > FLASH_AMD_COMP)) { | |
8adc9052 WD |
284 | printf("Can't erase unknown flash type %08lx - aborted\n", |
285 | info->flash_id); | |
dc7c9a1a WD |
286 | return 1; |
287 | } | |
288 | ||
289 | prot = 0; | |
8adc9052 WD |
290 | for (sect = s_first; sect <= s_last; ++sect) { |
291 | if (info->protect[sect]) | |
dc7c9a1a | 292 | prot++; |
dc7c9a1a WD |
293 | } |
294 | ||
295 | if (prot) { | |
8adc9052 | 296 | printf("- Warning: %d protected sectors will not be erased!\n", |
dc7c9a1a WD |
297 | prot); |
298 | } else { | |
8adc9052 | 299 | printf("\n"); |
dc7c9a1a WD |
300 | } |
301 | ||
302 | l_sect = -1; | |
303 | ||
304 | /* Disable interrupts which might cause a timeout here */ | |
305 | flag = disable_interrupts(); | |
8adc9052 WD |
306 | |
307 | #if defined(CONFIG_BOOT_8B) | |
308 | my_out_8((unsigned char *)((ulong)addr + 0x555), 0xaa); | |
309 | my_out_8((unsigned char *)((ulong)addr + 0x2aa), 0x55); | |
310 | my_out_8((unsigned char *)((ulong)addr + 0x555), 0x90); | |
311 | ||
312 | in_mid = my_in_8((unsigned char *)addr); | |
313 | in_did = my_in_8((unsigned char *)((ulong)addr + 1)); | |
314 | ||
315 | printf(" man ID=0x%x, dev ID=0x%x.\n", in_mid, in_did); | |
316 | ||
317 | my_out_8((unsigned char *)addr, 0xf0); | |
dc7c9a1a | 318 | udelay(1); |
8adc9052 WD |
319 | |
320 | my_out_8((unsigned char *)((ulong)addr + 0x555), 0xaa); | |
321 | my_out_8((unsigned char *)((ulong)addr + 0x2aa), 0x55); | |
322 | my_out_8((unsigned char *)((ulong)addr + 0x555), 0x80); | |
323 | my_out_8((unsigned char *)((ulong)addr + 0x555), 0xaa); | |
324 | my_out_8((unsigned char *)((ulong)addr + 0x2aa), 0x55); | |
325 | ||
dc7c9a1a | 326 | /* Start erase on unprotected sectors */ |
8adc9052 | 327 | for (sect = s_first; sect <= s_last; sect++) { |
dc7c9a1a | 328 | if (info->protect[sect] == 0) { /* not protected */ |
8adc9052 | 329 | addr = (vu_long *) (info->start[sect]); |
8bde7f77 | 330 | /*addr[0] = 0x00300030; */ |
8adc9052 | 331 | my_out_8((unsigned char *)((ulong)addr), 0x30); |
dc7c9a1a WD |
332 | l_sect = sect; |
333 | } | |
334 | } | |
8adc9052 WD |
335 | #elif defined(CONFIG_BOOT_16B) |
336 | my_out_be16((unsigned short *)((ulong)addr + (0xaaa)), 0xaa); | |
337 | my_out_be16((unsigned short *)((ulong)addr + (0x554)), 0x55); | |
338 | my_out_be16((unsigned short *)((ulong)addr + (0xaaa)), 0x90); | |
339 | in_mid = my_in_be16((unsigned short *)addr); | |
340 | in_did = my_in_be16((unsigned short *)((ulong)addr + 2)); | |
341 | printf(" man ID=0x%x, dev ID=0x%x.\n", in_mid, in_did); | |
342 | my_out_be16((unsigned short *)addr, 0xf0); | |
dc7c9a1a | 343 | udelay(1); |
8adc9052 WD |
344 | my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0xaa); |
345 | my_out_be16((unsigned short *)((ulong)addr + 0x554), 0x55); | |
346 | my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0x80); | |
347 | my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0xaa); | |
348 | my_out_be16((unsigned short *)((ulong)addr + 0x554), 0x55); | |
dc7c9a1a | 349 | /* Start erase on unprotected sectors */ |
8adc9052 | 350 | for (sect = s_first; sect <= s_last; sect++) { |
dc7c9a1a | 351 | if (info->protect[sect] == 0) { /* not protected */ |
8adc9052 WD |
352 | addr = (vu_long *) (info->start[sect]); |
353 | my_out_be16((unsigned short *)((ulong)addr), 0x30); | |
dc7c9a1a WD |
354 | l_sect = sect; |
355 | } | |
356 | } | |
357 | ||
358 | #elif defined(CONFIG_BOOT_32B) | |
8adc9052 WD |
359 | my_out_be32((unsigned *)((ulong)addr + 0x1554), 0xaa); |
360 | my_out_be32((unsigned *)((ulong)addr + 0xaa8), 0x55); | |
361 | my_out_be32((unsigned *)((ulong)addr + 0x1554), 0x90); | |
362 | ||
363 | in_mid = my_in_be32((unsigned *)addr); | |
364 | in_did = my_in_be32((unsigned *)((ulong)addr + 4)); | |
365 | ||
366 | printf(" man ID=0x%x, dev ID=0x%x.\n", in_mid, in_did); | |
367 | ||
368 | my_out_be32((unsigned *) addr, 0xf0); | |
dc7c9a1a | 369 | udelay(1); |
8adc9052 WD |
370 | |
371 | my_out_be32((unsigned *)((ulong)addr + 0x1554), 0xaa); | |
372 | my_out_be32((unsigned *)((ulong)addr + 0xaa8), 0x55); | |
373 | my_out_be32((unsigned *)((ulong)addr + 0x1554), 0x80); | |
374 | my_out_be32((unsigned *)((ulong)addr + 0x1554), 0xaa); | |
375 | my_out_be32((unsigned *)((ulong)addr + 0xaa8), 0x55); | |
376 | ||
dc7c9a1a | 377 | /* Start erase on unprotected sectors */ |
8adc9052 | 378 | for (sect = s_first; sect <= s_last; sect++) { |
dc7c9a1a | 379 | if (info->protect[sect] == 0) { /* not protected */ |
8adc9052 WD |
380 | addr = (vu_long *) (info->start[sect]); |
381 | my_out_be32((unsigned *)((ulong)addr), 0x00300030); | |
dc7c9a1a WD |
382 | l_sect = sect; |
383 | } | |
384 | } | |
385 | ||
386 | #else | |
8adc9052 | 387 | #error CONFIG_BOOT_(size)B missing. |
dc7c9a1a WD |
388 | #endif |
389 | /* re-enable interrupts if necessary */ | |
390 | if (flag) | |
391 | enable_interrupts(); | |
392 | ||
393 | /* wait at least 80us - let's wait 1 ms */ | |
8adc9052 | 394 | udelay(1000); |
dc7c9a1a WD |
395 | |
396 | /* | |
397 | * We wait for the last triggered sector | |
398 | */ | |
399 | if (l_sect < 0) | |
400 | goto DONE; | |
401 | ||
8adc9052 WD |
402 | start = get_timer(0); |
403 | last = start; | |
404 | addr = (vu_long *) (info->start[l_sect]); | |
405 | #if defined(CONFIG_BOOT_8B) | |
406 | while ((my_in_8((unsigned char *) addr) & 0x80) != 0x80) | |
407 | #elif defined(CONFIG_BOOT_16B) | |
408 | while ((my_in_be16((unsigned short *) addr) & 0x0080) != 0x0080) | |
dc7c9a1a | 409 | #elif defined(CONFIG_BOOT_32B) |
8adc9052 | 410 | while ((my_in_be32((unsigned *) addr) & 0x00800080) != 0x00800080) |
dc7c9a1a | 411 | #else |
8adc9052 | 412 | #error CONFIG_BOOT_(size)B missing. |
dc7c9a1a WD |
413 | #endif |
414 | { | |
8adc9052 WD |
415 | now = get_timer(start); |
416 | if (now > CONFIG_SYS_FLASH_ERASE_TOUT) { | |
417 | printf("Timeout\n"); | |
dc7c9a1a WD |
418 | return 1; |
419 | } | |
420 | /* show that we're waiting */ | |
421 | if ((now - last) > 1000) { /* every second */ | |
8adc9052 | 422 | putc('.'); |
dc7c9a1a WD |
423 | last = now; |
424 | } | |
425 | } | |
426 | DONE: | |
427 | /* reset to read mode */ | |
8adc9052 WD |
428 | addr = (volatile unsigned long *) info->start[0]; |
429 | ||
430 | #if defined(CONFIG_BOOT_8B) | |
431 | my_out_8((unsigned char *) addr, 0xf0); | |
432 | #elif defined(CONFIG_BOOT_16B) | |
433 | my_out_be16((unsigned short *) addr, 0x00f0); | |
dc7c9a1a | 434 | #elif defined(CONFIG_BOOT_32B) |
8adc9052 | 435 | my_out_be32((unsigned *) addr, 0x00F000F0); /* reset bank */ |
dc7c9a1a | 436 | #else |
8adc9052 | 437 | #error CONFIG_BOOT_(size)B missing. |
dc7c9a1a | 438 | #endif |
8adc9052 | 439 | printf(" done\n"); |
dc7c9a1a WD |
440 | return 0; |
441 | } | |
442 | ||
8adc9052 | 443 | /* |
dc7c9a1a WD |
444 | * Copy memory to flash, returns: |
445 | * 0 - OK | |
446 | * 1 - write timeout | |
447 | * 2 - Flash not erased | |
448 | */ | |
449 | ||
8adc9052 | 450 | int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) |
dc7c9a1a WD |
451 | { |
452 | ulong cp, wp, data; | |
453 | int i, l, rc; | |
454 | ||
455 | wp = (addr & ~3); /* get lower word aligned address */ | |
456 | ||
457 | /* | |
458 | * handle unaligned start bytes | |
459 | */ | |
8adc9052 WD |
460 | l = addr - wp; |
461 | ||
462 | if (l != 0) { | |
dc7c9a1a | 463 | data = 0; |
8adc9052 WD |
464 | for (i = 0, cp = wp; i < l; ++i, ++cp) |
465 | data = (data << 8) | (*(uchar *) cp); | |
466 | ||
467 | for (; i < 4 && cnt > 0; ++i) { | |
dc7c9a1a WD |
468 | data = (data << 8) | *src++; |
469 | --cnt; | |
470 | ++cp; | |
471 | } | |
8adc9052 WD |
472 | for (; cnt == 0 && i < 4; ++i, ++cp) |
473 | data = (data << 8) | (*(uchar *) cp); | |
474 | ||
475 | rc = write_word(info, wp, data); | |
476 | ||
477 | if (rc != 0) | |
478 | return rc; | |
dc7c9a1a | 479 | |
dc7c9a1a WD |
480 | wp += 4; |
481 | } | |
482 | ||
483 | /* | |
484 | * handle word aligned part | |
485 | */ | |
486 | while (cnt >= 4) { | |
487 | data = 0; | |
8adc9052 | 488 | for (i = 0; i < 4; ++i) |
dc7c9a1a | 489 | data = (data << 8) | *src++; |
8adc9052 WD |
490 | |
491 | rc = write_word(info, wp, data); | |
492 | ||
493 | if (rc != 0) | |
494 | return rc; | |
495 | ||
496 | wp += 4; | |
dc7c9a1a WD |
497 | cnt -= 4; |
498 | } | |
499 | ||
8adc9052 WD |
500 | if (cnt == 0) |
501 | return 0; | |
dc7c9a1a WD |
502 | |
503 | /* | |
504 | * handle unaligned tail bytes | |
505 | */ | |
506 | data = 0; | |
8adc9052 | 507 | for (i = 0, cp = wp; i < 4 && cnt > 0; ++i, ++cp) { |
dc7c9a1a WD |
508 | data = (data << 8) | *src++; |
509 | --cnt; | |
510 | } | |
8adc9052 WD |
511 | for (; i < 4; ++i, ++cp) |
512 | data = (data << 8) | (*(uchar *) cp); | |
dc7c9a1a | 513 | |
8adc9052 | 514 | return write_word(info, wp, data); |
dc7c9a1a WD |
515 | } |
516 | ||
8adc9052 | 517 | /* |
dc7c9a1a WD |
518 | * Write a word to Flash, returns: |
519 | * 0 - OK | |
520 | * 1 - write timeout | |
521 | * 2 - Flash not erased | |
522 | */ | |
8adc9052 | 523 | static int write_word(flash_info_t *info, ulong dest, ulong data) |
dc7c9a1a | 524 | { |
8adc9052 | 525 | ulong addr = (ulong) (info->start[0]); |
48353247 | 526 | ulong start; |
dc7c9a1a WD |
527 | int flag; |
528 | ulong i; | |
8adc9052 | 529 | int data_short[2]; |
dc7c9a1a WD |
530 | |
531 | /* Check if Flash is (sufficiently) erased */ | |
8adc9052 WD |
532 | if (((ulong)*(ulong *)dest & data) != data) |
533 | return 2; | |
534 | ||
dc7c9a1a WD |
535 | /* Disable interrupts which might cause a timeout here */ |
536 | flag = disable_interrupts(); | |
537 | #if defined(CONFIG_BOOT_8B) | |
538 | #ifdef DEBUG | |
539 | { | |
8adc9052 WD |
540 | int in_mid, in_did; |
541 | ||
542 | my_out_8((unsigned char *) (addr + 0x555), 0xaa); | |
543 | my_out_8((unsigned char *) (addr + 0x2aa), 0x55); | |
544 | my_out_8((unsigned char *) (addr + 0x555), 0x90); | |
545 | ||
546 | in_mid = my_in_8((unsigned char *) addr); | |
547 | in_did = my_in_8((unsigned char *) (addr + 1)); | |
548 | ||
549 | printf(" man ID=0x%x, dev ID=0x%x.\n", in_mid, in_did); | |
550 | ||
551 | my_out_8((unsigned char *) addr, 0xf0); | |
552 | udelay(1); | |
dc7c9a1a WD |
553 | } |
554 | #endif | |
8adc9052 WD |
555 | { |
556 | int data_ch[4]; | |
557 | ||
558 | data_ch[0] = (int) ((data >> 24) & 0xff); | |
559 | data_ch[1] = (int) ((data >> 16) & 0xff); | |
560 | data_ch[2] = (int) ((data >> 8) & 0xff); | |
561 | data_ch[3] = (int) (data & 0xff); | |
562 | ||
563 | for (i = 0; i < 4; i++) { | |
564 | my_out_8((unsigned char *) (addr + 0x555), 0xaa); | |
565 | my_out_8((unsigned char *) (addr + 0x2aa), 0x55); | |
566 | my_out_8((unsigned char *) (addr + 0x555), 0xa0); | |
567 | my_out_8((unsigned char *) (dest + i), data_ch[i]); | |
568 | ||
569 | /* re-enable interrupts if necessary */ | |
570 | if (flag) | |
571 | enable_interrupts(); | |
572 | ||
573 | start = get_timer(0); | |
8adc9052 WD |
574 | while ((my_in_8((unsigned char *)(dest + i))) != |
575 | (data_ch[i])) { | |
576 | if (get_timer(start) > | |
577 | CONFIG_SYS_FLASH_WRITE_TOUT) { | |
578 | return 1; | |
579 | } | |
580 | } | |
581 | } /* for */ | |
582 | } | |
583 | #elif defined(CONFIG_BOOT_16B) | |
584 | data_short[0] = (int) (data >> 16) & 0xffff; | |
585 | data_short[1] = (int) data & 0xffff; | |
586 | for (i = 0; i < 2; i++) { | |
587 | my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0xaa); | |
588 | my_out_be16((unsigned short *)((ulong)addr + 0x554), 0x55); | |
589 | my_out_be16((unsigned short *)((ulong)addr + 0xaaa), 0xa0); | |
590 | my_out_be16((unsigned short *)(dest + (i * 2)), | |
591 | data_short[i]); | |
592 | ||
593 | /* re-enable interrupts if necessary */ | |
594 | if (flag) | |
595 | enable_interrupts(); | |
596 | ||
597 | start = get_timer(0); | |
8adc9052 WD |
598 | while ((my_in_be16((unsigned short *)(dest + (i * 2)))) != |
599 | (data_short[i])) { | |
600 | if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) | |
601 | return 1; | |
602 | } | |
dc7c9a1a | 603 | } |
8adc9052 | 604 | #elif defined(CONFIG_BOOT_32B) |
dc7c9a1a WD |
605 | addr[0x0555] = 0x00AA00AA; |
606 | addr[0x02AA] = 0x00550055; | |
607 | addr[0x0555] = 0x00A000A0; | |
608 | ||
609 | *((vu_long *)dest) = data; | |
610 | ||
611 | /* re-enable interrupts if necessary */ | |
612 | if (flag) | |
613 | enable_interrupts(); | |
614 | ||
615 | /* data polling for D7 */ | |
8adc9052 | 616 | start = get_timer(0); |
dc7c9a1a | 617 | while ((*((vu_long *)dest) & 0x00800080) != (data & 0x00800080)) { |
8adc9052 WD |
618 | if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) |
619 | return 1; | |
dc7c9a1a WD |
620 | } |
621 | #endif | |
8adc9052 | 622 | return 0; |
dc7c9a1a | 623 | } |
8adc9052 WD |
624 | |
625 | #ifdef CONFIG_BOOT_8B | |
626 | static int my_in_8(unsigned char *addr) | |
dc7c9a1a | 627 | { |
8adc9052 WD |
628 | int ret; |
629 | __asm__ __volatile__("lbz%U1%X1 %0,%1; eieio":"=r"(ret):"m"(*addr)); | |
630 | ||
631 | return ret; | |
dc7c9a1a WD |
632 | } |
633 | ||
8adc9052 | 634 | static void my_out_8(unsigned char *addr, int val) |
dc7c9a1a | 635 | { |
8adc9052 | 636 | __asm__ __volatile__("stb%U0%X0 %1,%0; eieio":"=m"(*addr):"r"(val)); |
dc7c9a1a WD |
637 | } |
638 | #endif | |
8adc9052 WD |
639 | #ifdef CONFIG_BOOT_16B |
640 | static int my_in_be16(unsigned short *addr) | |
dc7c9a1a | 641 | { |
8adc9052 WD |
642 | int ret; |
643 | __asm__ __volatile__("lhz%U1%X1 %0,%1; eieio":"=r"(ret):"m"(*addr)); | |
644 | ||
645 | return ret; | |
dc7c9a1a | 646 | } |
8adc9052 WD |
647 | |
648 | static void my_out_be16(unsigned short *addr, int val) | |
dc7c9a1a | 649 | { |
8adc9052 | 650 | __asm__ __volatile__("sth%U0%X0 %1,%0; eieio":"=m"(*addr):"r"(val)); |
dc7c9a1a WD |
651 | } |
652 | #endif | |
8adc9052 WD |
653 | #ifdef CONFIG_BOOT_32B |
654 | static unsigned my_in_be32(unsigned *addr) | |
dc7c9a1a | 655 | { |
8bde7f77 | 656 | unsigned ret; |
8adc9052 WD |
657 | __asm__ __volatile__("lwz%U1%X1 %0,%1; eieio":"=r"(ret):"m"(*addr)); |
658 | ||
659 | return ret; | |
dc7c9a1a | 660 | } |
8adc9052 WD |
661 | |
662 | static void my_out_be32(unsigned *addr, int val) | |
dc7c9a1a | 663 | { |
8adc9052 | 664 | __asm__ __volatile__("stw%U0%X0 %1,%0; eieio":"=m"(*addr):"r"(val)); |
dc7c9a1a WD |
665 | } |
666 | #endif |