]>
Commit | Line | Data |
---|---|---|
affae2bf WD |
1 | /* |
2 | * (C) Copyright 2000 | |
3 | * Marius Groeger <mgroeger@sysgo.de> | |
4 | * Sysgo Real-Time Solutions, GmbH <www.elinos.com> | |
5 | * | |
6 | * (C) Copyright 2000 | |
7 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | |
8 | * | |
9 | * Flash Routines for AM290[48]0B devices | |
10 | * | |
11 | *-------------------------------------------------------------------- | |
12 | * See file CREDITS for list of people who contributed to this | |
13 | * project. | |
14 | * | |
15 | * This program is free software; you can redistribute it and/or | |
16 | * modify it under the terms of the GNU General Public License as | |
17 | * published by the Free Software Foundation; either version 2 of | |
18 | * the License, or (at your option) any later version. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | * GNU General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU General Public License | |
26 | * along with this program; if not, write to the Free Software | |
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
28 | * MA 02111-1307 USA | |
29 | */ | |
30 | ||
31 | #include <common.h> | |
32 | #include <mpc8xx.h> | |
33 | ||
34 | /* flash hardware ids */ | |
35 | #define VENDOR_AMD 0x0001 | |
36 | #define AMD_29DL323C_B 0x2253 | |
37 | ||
38 | /* Define this to include autoselect sequence in flash_init(). Does NOT | |
39 | * work when executing from flash itself, so this should be turned | |
40 | * on only when debugging the RAM version. | |
41 | */ | |
42 | #undef WITH_AUTOSELECT | |
43 | ||
6d0f6bcf | 44 | flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* info for FLASH chips */ |
affae2bf WD |
45 | |
46 | #if 1 | |
47 | #define D(x) | |
48 | #else | |
49 | #define D(x) printf x | |
50 | #endif | |
51 | ||
52 | /*----------------------------------------------------------------------- | |
53 | * Functions | |
54 | */ | |
55 | ||
56 | static unsigned char write_ull(flash_info_t *info, | |
57 | unsigned long address, | |
58 | volatile unsigned long long data); | |
59 | ||
60 | /* from flash_asm.S */ | |
61 | extern void ull_write(unsigned long long volatile *address, | |
8bde7f77 | 62 | unsigned long long volatile *data); |
affae2bf | 63 | extern void ull_read(unsigned long long volatile *address, |
8bde7f77 | 64 | unsigned long long volatile *data); |
affae2bf WD |
65 | |
66 | /*----------------------------------------------------------------------- | |
67 | */ | |
68 | ||
69 | unsigned long flash_init (void) | |
70 | { | |
71 | int i; | |
72 | ulong addr; | |
73 | ||
74 | #ifdef WITH_AUTOSELECT | |
75 | { | |
76 | unsigned long long *f_addr = (unsigned long long *)PHYS_FLASH; | |
77 | unsigned long long f_command, vendor, device; | |
78 | /* Perform Autoselect */ | |
53677ef1 | 79 | f_command = 0x00AA00AA00AA00AAULL; |
affae2bf | 80 | ull_write(&f_addr[0x555], &f_command); |
53677ef1 | 81 | f_command = 0x0055005500550055ULL; |
affae2bf | 82 | ull_write(&f_addr[0x2AA], &f_command); |
53677ef1 | 83 | f_command = 0x0090009000900090ULL; |
affae2bf WD |
84 | ull_write(&f_addr[0x555], &f_command); |
85 | ull_read(&f_addr[0], &vendor); | |
86 | vendor &= 0xffff; | |
87 | ull_read(&f_addr[1], &device); | |
88 | device &= 0xffff; | |
53677ef1 | 89 | f_command = 0x00F000F000F000F0ULL; |
affae2bf WD |
90 | ull_write(&f_addr[0x555], &f_command); |
91 | if (vendor != VENDOR_AMD || device != AMD_29DL323C_B) | |
92 | return 0; | |
93 | } | |
94 | #endif | |
95 | ||
96 | /* Init: no FLASHes known */ | |
6d0f6bcf | 97 | for (i=0; i<CONFIG_SYS_MAX_FLASH_BANKS; ++i) { |
affae2bf WD |
98 | flash_info[i].flash_id = FLASH_UNKNOWN; |
99 | } | |
100 | ||
101 | /* 1st bank: 8 x 32 KB sectors */ | |
102 | flash_info[0].flash_id = VENDOR_AMD << 16 | AMD_29DL323C_B; | |
103 | flash_info[0].sector_count = 8; | |
104 | flash_info[0].size = flash_info[0].sector_count * 32 * 1024; | |
105 | addr = PHYS_FLASH; | |
106 | for(i = 0; i < flash_info[0].sector_count; i++) { | |
107 | flash_info[0].start[i] = addr; | |
108 | addr += flash_info[0].size / flash_info[0].sector_count; | |
109 | } | |
110 | /* 1st bank: 63 x 256 KB sectors */ | |
111 | flash_info[1].flash_id = VENDOR_AMD << 16 | AMD_29DL323C_B; | |
112 | flash_info[1].sector_count = 63; | |
113 | flash_info[1].size = flash_info[1].sector_count * 256 * 1024; | |
114 | for(i = 0; i < flash_info[1].sector_count; i++) { | |
115 | flash_info[1].start[i] = addr; | |
116 | addr += flash_info[1].size / flash_info[1].sector_count; | |
117 | } | |
118 | ||
119 | /* | |
120 | * protect monitor and environment sectors | |
121 | */ | |
122 | ||
6d0f6bcf | 123 | #if CONFIG_SYS_MONITOR_BASE >= PHYS_FLASH |
affae2bf | 124 | flash_protect(FLAG_PROTECT_SET, |
6d0f6bcf JCPV |
125 | CONFIG_SYS_MONITOR_BASE, |
126 | CONFIG_SYS_MONITOR_BASE+monitor_flash_len-1, | |
affae2bf WD |
127 | &flash_info[0]); |
128 | flash_protect(FLAG_PROTECT_SET, | |
6d0f6bcf JCPV |
129 | CONFIG_SYS_MONITOR_BASE, |
130 | CONFIG_SYS_MONITOR_BASE+monitor_flash_len-1, | |
affae2bf WD |
131 | &flash_info[1]); |
132 | #endif | |
133 | ||
0e8d1586 JCPV |
134 | #if defined(CONFIG_ENV_IS_IN_FLASH) && defined(CONFIG_ENV_ADDR) |
135 | # ifndef CONFIG_ENV_SIZE | |
136 | # define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE | |
affae2bf WD |
137 | # endif |
138 | flash_protect(FLAG_PROTECT_SET, | |
0e8d1586 JCPV |
139 | CONFIG_ENV_ADDR, |
140 | CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, | |
affae2bf WD |
141 | &flash_info[0]); |
142 | flash_protect(FLAG_PROTECT_SET, | |
0e8d1586 JCPV |
143 | CONFIG_ENV_ADDR, |
144 | CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, | |
affae2bf WD |
145 | &flash_info[1]); |
146 | #endif | |
147 | ||
148 | return flash_info[0].size + flash_info[1].size; | |
149 | } | |
150 | ||
151 | /*----------------------------------------------------------------------- | |
152 | */ | |
153 | void flash_print_info (flash_info_t *info) | |
154 | { | |
155 | int i; | |
156 | ||
157 | if (info->flash_id == FLASH_UNKNOWN) { | |
158 | printf ("missing or unknown FLASH type\n"); | |
159 | return; | |
160 | } | |
161 | ||
162 | switch (info->flash_id >> 16) { | |
163 | case VENDOR_AMD: | |
164 | printf ("AMD "); | |
165 | break; | |
166 | default: | |
167 | printf ("Unknown Vendor "); | |
168 | break; | |
169 | } | |
170 | ||
171 | switch (info->flash_id & FLASH_TYPEMASK) { | |
172 | case AMD_29DL323C_B: | |
173 | printf ("AM29DL323CB (32 Mbit)\n"); | |
174 | break; | |
175 | default: | |
176 | printf ("Unknown Chip Type\n"); | |
177 | break; | |
178 | } | |
179 | ||
180 | printf (" Size: %ld MB in %d Sectors\n", | |
181 | info->size >> 20, info->sector_count); | |
182 | ||
183 | printf (" Sector Start Addresses:"); | |
184 | for (i=0; i<info->sector_count; ++i) { | |
185 | if ((i % 5) == 0) | |
186 | printf ("\n "); | |
187 | printf (" %08lX%s", | |
188 | info->start[i], | |
189 | info->protect[i] ? " (RO)" : " " | |
190 | ); | |
191 | } | |
192 | printf ("\n"); | |
193 | return; | |
194 | } | |
195 | ||
196 | /*----------------------------------------------------------------------- | |
197 | */ | |
198 | ||
199 | int flash_erase (flash_info_t *info, int s_first, int s_last) | |
200 | { | |
201 | int flag, prot, sect, l_sect; | |
202 | ulong start; | |
203 | unsigned long long volatile *f_addr; | |
204 | unsigned long long volatile f_command; | |
205 | ||
206 | if ((s_first < 0) || (s_first > s_last)) { | |
207 | if (info->flash_id == FLASH_UNKNOWN) { | |
208 | printf ("- missing\n"); | |
209 | } else { | |
210 | printf ("- no sectors to erase\n"); | |
211 | } | |
212 | return 1; | |
213 | } | |
214 | ||
215 | prot = 0; | |
216 | for (sect = s_first; sect <= s_last; sect++) { | |
217 | if (info->protect[sect]) { | |
218 | prot++; | |
219 | } | |
220 | } | |
221 | if (prot) { | |
222 | printf ("- Warning: %d protected sectors will not be erased!\n", | |
223 | prot); | |
224 | } else { | |
225 | printf ("\n"); | |
226 | } | |
227 | ||
53677ef1 WD |
228 | f_addr = (unsigned long long *)info->start[0]; |
229 | f_command = 0x00AA00AA00AA00AAULL; | |
affae2bf | 230 | ull_write(&f_addr[0x555], &f_command); |
53677ef1 | 231 | f_command = 0x0055005500550055ULL; |
affae2bf | 232 | ull_write(&f_addr[0x2AA], &f_command); |
53677ef1 | 233 | f_command = 0x0080008000800080ULL; |
affae2bf | 234 | ull_write(&f_addr[0x555], &f_command); |
53677ef1 | 235 | f_command = 0x00AA00AA00AA00AAULL; |
affae2bf | 236 | ull_write(&f_addr[0x555], &f_command); |
53677ef1 | 237 | f_command = 0x0055005500550055ULL; |
affae2bf WD |
238 | ull_write(&f_addr[0x2AA], &f_command); |
239 | ||
240 | /* Disable interrupts which might cause a timeout here */ | |
241 | flag = disable_interrupts(); | |
242 | ||
243 | /* Start erase on unprotected sectors */ | |
244 | for (l_sect = -1, sect = s_first; sect<=s_last; sect++) { | |
245 | if (info->protect[sect] == 0) { /* not protected */ | |
246 | ||
53677ef1 | 247 | f_addr = |
affae2bf | 248 | (unsigned long long *)(info->start[sect]); |
53677ef1 | 249 | f_command = 0x0030003000300030ULL; |
affae2bf WD |
250 | ull_write(f_addr, &f_command); |
251 | l_sect = sect; | |
252 | } | |
253 | } | |
254 | ||
255 | /* re-enable interrupts if necessary */ | |
256 | if (flag) | |
257 | enable_interrupts(); | |
258 | ||
259 | start = get_timer (0); | |
260 | do | |
261 | { | |
6d0f6bcf | 262 | if (get_timer(start) > CONFIG_SYS_FLASH_ERASE_TOUT) |
affae2bf WD |
263 | { /* write reset command, command address is unimportant */ |
264 | /* this command turns the flash back to read mode */ | |
265 | f_addr = | |
266 | (unsigned long long *)(info->start[l_sect]); | |
53677ef1 | 267 | f_command = 0x00F000F000F000F0ULL; |
affae2bf WD |
268 | ull_write(f_addr, &f_command); |
269 | printf (" timeout\n"); | |
270 | return 1; | |
8bde7f77 | 271 | } |
affae2bf WD |
272 | } while(*f_addr != 0xFFFFFFFFFFFFFFFFULL); |
273 | ||
274 | printf (" done\n"); | |
275 | return 0; | |
276 | } | |
277 | ||
278 | /*----------------------------------------------------------------------- | |
279 | * Copy memory to flash, returns: | |
280 | * 0 - OK | |
281 | * 1 - write timeout | |
282 | * 2 - Flash not erased | |
283 | */ | |
284 | ||
285 | int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) | |
286 | { | |
287 | unsigned long cp, wp; | |
288 | unsigned long long data; | |
289 | int i, l, rc; | |
290 | ||
291 | wp = (addr & ~7); /* get lower long long aligned address */ | |
292 | ||
293 | /* | |
294 | * handle unaligned start bytes | |
295 | */ | |
296 | if ((l = addr - wp) != 0) { | |
297 | data = 0; | |
298 | for (i=0, cp=wp; i<l; ++i, ++cp) { | |
299 | data = (data << 8) | (*(uchar *)cp); | |
300 | } | |
301 | for (; i<8 && cnt>0; ++i) { | |
302 | data = (data << 8) | *src++; | |
303 | --cnt; | |
304 | ++cp; | |
305 | } | |
306 | for (; cnt==0 && i<8; ++i, ++cp) { | |
307 | data = (data << 8) | (*(uchar *)cp); | |
308 | } | |
309 | ||
310 | if ((rc = write_ull(info, wp, data)) != 0) { | |
311 | return rc; | |
312 | } | |
313 | wp += 4; | |
314 | } | |
315 | ||
316 | /* | |
317 | * handle long long aligned part | |
318 | */ | |
319 | while (cnt >= 8) { | |
320 | data = 0; | |
321 | for (i=0; i<8; ++i) { | |
322 | data = (data << 8) | *src++; | |
323 | } | |
324 | if ((rc = write_ull(info, wp, data)) != 0) { | |
325 | return rc; | |
326 | } | |
327 | wp += 8; | |
328 | cnt -= 8; | |
329 | } | |
330 | ||
331 | if (cnt == 0) { | |
332 | return ERR_OK; | |
333 | } | |
334 | ||
335 | /* | |
336 | * handle unaligned tail bytes | |
337 | */ | |
338 | data = 0; | |
339 | for (i=0, cp=wp; i<8 && cnt>0; ++i, ++cp) { | |
340 | data = (data << 8) | *src++; | |
341 | --cnt; | |
342 | } | |
343 | for (; i<8; ++i, ++cp) { | |
344 | data = (data << 8) | (*(uchar *)cp); | |
345 | } | |
346 | ||
347 | return write_ull(info, wp, data); | |
348 | } | |
349 | ||
350 | /*--------------------------------------------------------------------------- | |
351 | * | |
352 | * FUNCTION NAME: write_ull | |
353 | * | |
354 | * DESCRIPTION: writes 8 bytes to flash | |
355 | * | |
356 | * EXTERNAL EFFECT: nothing | |
357 | * | |
358 | * PARAMETERS: 32 bit long pointer to address, 64 bit long pointer to data | |
359 | * | |
53677ef1 | 360 | * RETURNS: 0 if OK, 1 if timeout, 4 if parameter error |
affae2bf WD |
361 | *--------------------------------------------------------------------------*/ |
362 | ||
363 | static unsigned char write_ull(flash_info_t *info, | |
364 | unsigned long address, | |
365 | volatile unsigned long long data) | |
366 | { | |
367 | static unsigned long long f_command; | |
368 | static unsigned long long *f_addr; | |
369 | ulong start; | |
370 | ||
371 | /* address muss be 8-aligned! */ | |
372 | if (address & 0x7) | |
373 | return ERR_ALIGN; | |
374 | ||
53677ef1 WD |
375 | f_addr = (unsigned long long *)info->start[0]; |
376 | f_command = 0x00AA00AA00AA00AAULL; | |
affae2bf | 377 | ull_write(&f_addr[0x555], &f_command); |
53677ef1 | 378 | f_command = 0x0055005500550055ULL; |
affae2bf | 379 | ull_write(&f_addr[0x2AA], &f_command); |
53677ef1 | 380 | f_command = 0x00A000A000A000A0ULL; |
affae2bf WD |
381 | ull_write(&f_addr[0x555], &f_command); |
382 | ||
53677ef1 WD |
383 | f_addr = (unsigned long long *)address; |
384 | f_command = data; | |
affae2bf WD |
385 | ull_write(f_addr, &f_command); |
386 | ||
387 | start = get_timer (0); | |
388 | do | |
389 | { | |
6d0f6bcf | 390 | if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) |
affae2bf WD |
391 | { |
392 | /* write reset command, command address is unimportant */ | |
393 | /* this command turns the flash back to read mode */ | |
53677ef1 WD |
394 | f_addr = (unsigned long long *)info->start[0]; |
395 | f_command = 0x00F000F000F000F0ULL; | |
affae2bf WD |
396 | ull_write(f_addr, &f_command); |
397 | return ERR_TIMOUT; | |
398 | } | |
399 | } while(*((unsigned long long *)address) != data); | |
400 | ||
401 | return 0; | |
402 | } |