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