]>
Commit | Line | Data |
---|---|---|
1cb8e980 WD |
1 | /* |
2 | * (C) Copyright 2002 | |
3 | * Sysgo Real-Time Solutions, GmbH <www.elinos.com> | |
4 | * Alex Zuepke <azu@sysgo.de> | |
5 | * | |
6 | * See file CREDITS for list of people who contributed to this | |
7 | * project. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License as | |
11 | * published by the Free Software Foundation; either version 2 of | |
12 | * the License, or (at your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
22 | * MA 02111-1307 USA | |
23 | */ | |
24 | ||
25 | #include <common.h> | |
26 | ||
e86e5a07 | 27 | ulong myflush (void); |
1cb8e980 WD |
28 | |
29 | ||
30 | #define FLASH_BANK_SIZE PHYS_FLASH_SIZE | |
e86e5a07 | 31 | #define MAIN_SECT_SIZE 0x10000 /* 64 KB */ |
1cb8e980 | 32 | |
6d0f6bcf | 33 | flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; |
1cb8e980 WD |
34 | |
35 | ||
36 | #define CMD_READ_ARRAY 0x000000F0 | |
37 | #define CMD_UNLOCK1 0x000000AA | |
38 | #define CMD_UNLOCK2 0x00000055 | |
39 | #define CMD_ERASE_SETUP 0x00000080 | |
40 | #define CMD_ERASE_CONFIRM 0x00000030 | |
41 | #define CMD_PROGRAM 0x000000A0 | |
42 | #define CMD_UNLOCK_BYPASS 0x00000020 | |
43 | ||
6d0f6bcf JCPV |
44 | #define MEM_FLASH_ADDR1 (*(volatile u16 *)(CONFIG_SYS_FLASH_BASE + (0x00000555 << 1))) |
45 | #define MEM_FLASH_ADDR2 (*(volatile u16 *)(CONFIG_SYS_FLASH_BASE + (0x000002AA << 1))) | |
1cb8e980 WD |
46 | |
47 | #define BIT_ERASE_DONE 0x00000080 | |
48 | #define BIT_RDY_MASK 0x00000080 | |
49 | #define BIT_PROGRAM_ERROR 0x00000020 | |
e86e5a07 | 50 | #define BIT_TIMEOUT 0x80000000 /* our flag */ |
1cb8e980 WD |
51 | |
52 | #define READY 1 | |
53 | #define ERR 2 | |
54 | #define TMO 4 | |
55 | ||
56 | /*----------------------------------------------------------------------- | |
57 | */ | |
58 | ||
e86e5a07 | 59 | ulong flash_init (void) |
1cb8e980 | 60 | { |
e86e5a07 WD |
61 | int i, j; |
62 | ulong size = 0; | |
1cb8e980 | 63 | |
6d0f6bcf | 64 | for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { |
e86e5a07 WD |
65 | ulong flashbase = 0; |
66 | ||
67 | flash_info[i].flash_id = | |
1cb8e980 | 68 | #if defined(CONFIG_AMD_LV400) |
e86e5a07 WD |
69 | (AMD_MANUFACT & FLASH_VENDMASK) | |
70 | (AMD_ID_LV400B & FLASH_TYPEMASK); | |
1cb8e980 | 71 | #elif defined(CONFIG_AMD_LV800) |
e86e5a07 WD |
72 | (AMD_MANUFACT & FLASH_VENDMASK) | |
73 | (AMD_ID_LV800B & FLASH_TYPEMASK); | |
1cb8e980 WD |
74 | #else |
75 | #error "Unknown flash configured" | |
76 | #endif | |
e86e5a07 | 77 | flash_info[i].size = FLASH_BANK_SIZE; |
6d0f6bcf JCPV |
78 | flash_info[i].sector_count = CONFIG_SYS_MAX_FLASH_SECT; |
79 | memset (flash_info[i].protect, 0, CONFIG_SYS_MAX_FLASH_SECT); | |
e86e5a07 WD |
80 | if (i == 0) |
81 | flashbase = PHYS_FLASH_1; | |
82 | else | |
83 | panic ("configured too many flash banks!\n"); | |
84 | for (j = 0; j < flash_info[i].sector_count; j++) { | |
85 | if (j <= 3) { | |
86 | /* 1st one is 16 KB */ | |
87 | if (j == 0) { | |
88 | flash_info[i].start[j] = | |
89 | flashbase + 0; | |
90 | } | |
91 | ||
92 | /* 2nd and 3rd are both 8 KB */ | |
93 | if ((j == 1) || (j == 2)) { | |
94 | flash_info[i].start[j] = | |
95 | flashbase + 0x4000 + (j - | |
96 | 1) * | |
97 | 0x2000; | |
98 | } | |
99 | ||
100 | /* 4th 32 KB */ | |
101 | if (j == 3) { | |
102 | flash_info[i].start[j] = | |
103 | flashbase + 0x8000; | |
104 | } | |
105 | } else { | |
106 | flash_info[i].start[j] = | |
107 | flashbase + (j - 3) * MAIN_SECT_SIZE; | |
108 | } | |
1cb8e980 | 109 | } |
e86e5a07 | 110 | size += flash_info[i].size; |
1cb8e980 | 111 | } |
1cb8e980 | 112 | |
e86e5a07 | 113 | flash_protect (FLAG_PROTECT_SET, |
6d0f6bcf JCPV |
114 | CONFIG_SYS_FLASH_BASE, |
115 | CONFIG_SYS_FLASH_BASE + monitor_flash_len - 1, | |
e86e5a07 | 116 | &flash_info[0]); |
1cb8e980 | 117 | |
e86e5a07 | 118 | flash_protect (FLAG_PROTECT_SET, |
0e8d1586 JCPV |
119 | CONFIG_ENV_ADDR, |
120 | CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]); | |
1cb8e980 | 121 | |
e86e5a07 | 122 | return size; |
1cb8e980 WD |
123 | } |
124 | ||
125 | /*----------------------------------------------------------------------- | |
126 | */ | |
e86e5a07 | 127 | void flash_print_info (flash_info_t * info) |
1cb8e980 | 128 | { |
e86e5a07 WD |
129 | int i; |
130 | ||
131 | switch (info->flash_id & FLASH_VENDMASK) { | |
132 | case (AMD_MANUFACT & FLASH_VENDMASK): | |
133 | puts ("AMD: "); | |
134 | break; | |
135 | default: | |
136 | puts ("Unknown Vendor "); | |
137 | break; | |
1cb8e980 | 138 | } |
1cb8e980 | 139 | |
e86e5a07 WD |
140 | switch (info->flash_id & FLASH_TYPEMASK) { |
141 | case (AMD_ID_LV400B & FLASH_TYPEMASK): | |
142 | puts ("1x Amd29LV400BB (4Mbit)\n"); | |
143 | break; | |
144 | case (AMD_ID_LV800B & FLASH_TYPEMASK): | |
145 | puts ("1x Amd29LV800BB (8Mbit)\n"); | |
146 | break; | |
147 | default: | |
148 | puts ("Unknown Chip Type\n"); | |
149 | goto Done; | |
150 | break; | |
151 | } | |
152 | ||
153 | printf (" Size: %ld MB in %d Sectors\n", | |
154 | info->size >> 20, info->sector_count); | |
155 | ||
156 | puts (" Sector Start Addresses:"); | |
157 | for (i = 0; i < info->sector_count; i++) { | |
158 | if ((i % 5) == 0) { | |
159 | puts ("\n "); | |
160 | } | |
161 | printf (" %08lX%s", info->start[i], | |
162 | info->protect[i] ? " (RO)" : " "); | |
163 | } | |
164 | puts ("\n"); | |
165 | ||
166 | Done: ; | |
1cb8e980 WD |
167 | } |
168 | ||
169 | /*----------------------------------------------------------------------- | |
170 | */ | |
171 | ||
e86e5a07 | 172 | int flash_erase (flash_info_t * info, int s_first, int s_last) |
1cb8e980 | 173 | { |
e86e5a07 WD |
174 | ushort result; |
175 | int iflag, cflag, prot, sect; | |
176 | int rc = ERR_OK; | |
177 | int chip; | |
1cb8e980 | 178 | |
e86e5a07 | 179 | /* first look for protection bits */ |
1cb8e980 | 180 | |
e86e5a07 WD |
181 | if (info->flash_id == FLASH_UNKNOWN) |
182 | return ERR_UNKNOWN_FLASH_TYPE; | |
1cb8e980 | 183 | |
e86e5a07 WD |
184 | if ((s_first < 0) || (s_first > s_last)) { |
185 | return ERR_INVAL; | |
1cb8e980 | 186 | } |
1cb8e980 | 187 | |
e86e5a07 WD |
188 | if ((info->flash_id & FLASH_VENDMASK) != |
189 | (AMD_MANUFACT & FLASH_VENDMASK)) { | |
190 | return ERR_UNKNOWN_FLASH_VENDOR; | |
191 | } | |
1cb8e980 | 192 | |
e86e5a07 WD |
193 | prot = 0; |
194 | for (sect = s_first; sect <= s_last; ++sect) { | |
195 | if (info->protect[sect]) { | |
196 | prot++; | |
1cb8e980 | 197 | } |
1cb8e980 | 198 | } |
e86e5a07 WD |
199 | if (prot) |
200 | return ERR_PROTECTED; | |
201 | ||
202 | /* | |
203 | * Disable interrupts which might cause a timeout | |
204 | * here. Remember that our exception vectors are | |
205 | * at address 0 in the flash, and we don't want a | |
206 | * (ticker) exception to happen while the flash | |
207 | * chip is in programming mode. | |
208 | */ | |
209 | cflag = icache_status (); | |
210 | icache_disable (); | |
211 | iflag = disable_interrupts (); | |
212 | ||
213 | /* Start erase on unprotected sectors */ | |
214 | for (sect = s_first; sect <= s_last && !ctrlc (); sect++) { | |
215 | printf ("Erasing sector %2d ... ", sect); | |
216 | ||
217 | /* arm simple, non interrupt dependent timer */ | |
218 | reset_timer_masked (); | |
219 | ||
220 | if (info->protect[sect] == 0) { /* not protected */ | |
221 | vu_short *addr = (vu_short *) (info->start[sect]); | |
222 | ||
223 | MEM_FLASH_ADDR1 = CMD_UNLOCK1; | |
224 | MEM_FLASH_ADDR2 = CMD_UNLOCK2; | |
225 | MEM_FLASH_ADDR1 = CMD_ERASE_SETUP; | |
226 | ||
227 | MEM_FLASH_ADDR1 = CMD_UNLOCK1; | |
228 | MEM_FLASH_ADDR2 = CMD_UNLOCK2; | |
229 | *addr = CMD_ERASE_CONFIRM; | |
230 | ||
231 | /* wait until flash is ready */ | |
232 | chip = 0; | |
233 | ||
234 | do { | |
235 | result = *addr; | |
236 | ||
237 | /* check timeout */ | |
238 | if (get_timer_masked () > | |
6d0f6bcf | 239 | CONFIG_SYS_FLASH_ERASE_TOUT) { |
e86e5a07 WD |
240 | MEM_FLASH_ADDR1 = CMD_READ_ARRAY; |
241 | chip = TMO; | |
242 | break; | |
243 | } | |
244 | ||
245 | if (!chip | |
246 | && (result & 0xFFFF) & BIT_ERASE_DONE) | |
247 | chip = READY; | |
248 | ||
249 | if (!chip | |
250 | && (result & 0xFFFF) & BIT_PROGRAM_ERROR) | |
251 | chip = ERR; | |
252 | ||
253 | } while (!chip); | |
254 | ||
255 | MEM_FLASH_ADDR1 = CMD_READ_ARRAY; | |
256 | ||
257 | if (chip == ERR) { | |
258 | rc = ERR_PROG_ERROR; | |
259 | goto outahere; | |
260 | } | |
261 | if (chip == TMO) { | |
262 | rc = ERR_TIMOUT; | |
263 | goto outahere; | |
264 | } | |
265 | ||
266 | puts ("ok.\n"); | |
267 | } else { /* it was protected */ | |
268 | ||
269 | puts ("protected!\n"); | |
270 | } | |
1cb8e980 | 271 | } |
1cb8e980 | 272 | |
e86e5a07 WD |
273 | if (ctrlc ()) |
274 | puts ("User Interrupt!\n"); | |
1cb8e980 | 275 | |
e86e5a07 WD |
276 | outahere: |
277 | /* allow flash to settle - wait 10 ms */ | |
278 | udelay_masked (10000); | |
1cb8e980 | 279 | |
e86e5a07 WD |
280 | if (iflag) |
281 | enable_interrupts (); | |
1cb8e980 | 282 | |
e86e5a07 WD |
283 | if (cflag) |
284 | icache_enable (); | |
1cb8e980 | 285 | |
e86e5a07 | 286 | return rc; |
1cb8e980 WD |
287 | } |
288 | ||
289 | /*----------------------------------------------------------------------- | |
290 | * Copy memory to flash | |
291 | */ | |
292 | ||
8412d814 | 293 | static int write_hword (flash_info_t * info, ulong dest, ushort data) |
1cb8e980 | 294 | { |
e86e5a07 WD |
295 | vu_short *addr = (vu_short *) dest; |
296 | ushort result; | |
297 | int rc = ERR_OK; | |
298 | int cflag, iflag; | |
299 | int chip; | |
300 | ||
301 | /* | |
302 | * Check if Flash is (sufficiently) erased | |
303 | */ | |
1cb8e980 | 304 | result = *addr; |
e86e5a07 WD |
305 | if ((result & data) != data) |
306 | return ERR_NOT_ERASED; | |
307 | ||
308 | ||
309 | /* | |
310 | * Disable interrupts which might cause a timeout | |
311 | * here. Remember that our exception vectors are | |
312 | * at address 0 in the flash, and we don't want a | |
313 | * (ticker) exception to happen while the flash | |
314 | * chip is in programming mode. | |
315 | */ | |
316 | cflag = icache_status (); | |
317 | icache_disable (); | |
318 | iflag = disable_interrupts (); | |
319 | ||
320 | MEM_FLASH_ADDR1 = CMD_UNLOCK1; | |
321 | MEM_FLASH_ADDR2 = CMD_UNLOCK2; | |
322 | MEM_FLASH_ADDR1 = CMD_PROGRAM; | |
323 | *addr = data; | |
1cb8e980 | 324 | |
e86e5a07 WD |
325 | /* arm simple, non interrupt dependent timer */ |
326 | reset_timer_masked (); | |
1cb8e980 | 327 | |
e86e5a07 WD |
328 | /* wait until flash is ready */ |
329 | chip = 0; | |
330 | do { | |
1cb8e980 WD |
331 | result = *addr; |
332 | ||
e86e5a07 | 333 | /* check timeout */ |
6d0f6bcf | 334 | if (get_timer_masked () > CONFIG_SYS_FLASH_ERASE_TOUT) { |
e86e5a07 WD |
335 | chip = ERR | TMO; |
336 | break; | |
337 | } | |
338 | if (!chip && ((result & 0x80) == (data & 0x80))) | |
1cb8e980 | 339 | chip = READY; |
1cb8e980 | 340 | |
e86e5a07 WD |
341 | if (!chip && ((result & 0xFFFF) & BIT_PROGRAM_ERROR)) { |
342 | result = *addr; | |
343 | ||
344 | if ((result & 0x80) == (data & 0x80)) | |
345 | chip = READY; | |
346 | else | |
347 | chip = ERR; | |
348 | } | |
349 | ||
350 | } while (!chip); | |
1cb8e980 | 351 | |
e86e5a07 | 352 | *addr = CMD_READ_ARRAY; |
1cb8e980 | 353 | |
e86e5a07 WD |
354 | if (chip == ERR || *addr != data) |
355 | rc = ERR_PROG_ERROR; | |
1cb8e980 | 356 | |
e86e5a07 WD |
357 | if (iflag) |
358 | enable_interrupts (); | |
1cb8e980 | 359 | |
e86e5a07 WD |
360 | if (cflag) |
361 | icache_enable (); | |
1cb8e980 | 362 | |
e86e5a07 | 363 | return rc; |
1cb8e980 WD |
364 | } |
365 | ||
366 | /*----------------------------------------------------------------------- | |
367 | * Copy memory to flash. | |
368 | */ | |
369 | ||
e86e5a07 | 370 | int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) |
1cb8e980 | 371 | { |
e86e5a07 WD |
372 | ulong cp, wp; |
373 | int l; | |
374 | int i, rc; | |
375 | ushort data; | |
376 | ||
377 | wp = (addr & ~1); /* get lower word aligned address */ | |
378 | ||
379 | /* | |
380 | * handle unaligned start bytes | |
381 | */ | |
382 | if ((l = addr - wp) != 0) { | |
383 | data = 0; | |
384 | for (i = 0, cp = wp; i < l; ++i, ++cp) { | |
385 | data = (data >> 8) | (*(uchar *) cp << 8); | |
386 | } | |
387 | for (; i < 2 && cnt > 0; ++i) { | |
388 | data = (data >> 8) | (*src++ << 8); | |
389 | --cnt; | |
390 | ++cp; | |
391 | } | |
392 | for (; cnt == 0 && i < 2; ++i, ++cp) { | |
393 | data = (data >> 8) | (*(uchar *) cp << 8); | |
394 | } | |
1cb8e980 | 395 | |
e86e5a07 WD |
396 | if ((rc = write_hword (info, wp, data)) != 0) { |
397 | return (rc); | |
398 | } | |
399 | wp += 2; | |
1cb8e980 | 400 | } |
e86e5a07 WD |
401 | |
402 | /* | |
403 | * handle word aligned part | |
404 | */ | |
405 | while (cnt >= 2) { | |
406 | data = *((vu_short *) src); | |
407 | if ((rc = write_hword (info, wp, data)) != 0) { | |
408 | return (rc); | |
409 | } | |
410 | src += 2; | |
411 | wp += 2; | |
412 | cnt -= 2; | |
1cb8e980 | 413 | } |
e86e5a07 WD |
414 | |
415 | if (cnt == 0) { | |
416 | return ERR_OK; | |
1cb8e980 WD |
417 | } |
418 | ||
e86e5a07 WD |
419 | /* |
420 | * handle unaligned tail bytes | |
421 | */ | |
422 | data = 0; | |
423 | for (i = 0, cp = wp; i < 2 && cnt > 0; ++i, ++cp) { | |
424 | data = (data >> 8) | (*src++ << 8); | |
425 | --cnt; | |
1cb8e980 | 426 | } |
e86e5a07 WD |
427 | for (; i < 2; ++i, ++cp) { |
428 | data = (data >> 8) | (*(uchar *) cp << 8); | |
1cb8e980 | 429 | } |
e86e5a07 WD |
430 | |
431 | return write_hword (info, wp, data); | |
1cb8e980 | 432 | } |