blob: 55aaabfe6f6e2bbe59bdc51036aeac246ea93e63 [file] [log] [blame]
Gary Jennejohn7968bb52007-01-24 12:16:56 +01001/*
2 * (C) Copyright 2006 Embedded Artists AB <www.embeddedartists.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17 * MA 02111-1307 USA
18 */
19
20#include <common.h>
21#include <asm/arch/hardware.h>
22
23/* IAP commands use 32 bytes at the top of CPU internal sram, we
24 use 512 bytes below that */
25#define COPY_BUFFER_LOCATION 0x40003de0
26
27#define IAP_LOCATION 0x7ffffff1
28#define IAP_CMD_PREPARE 50
29#define IAP_CMD_COPY 51
30#define IAP_CMD_ERASE 52
31#define IAP_CMD_CHECK 53
32#define IAP_CMD_ID 54
33#define IAP_CMD_VERSION 55
34#define IAP_CMD_COMPARE 56
35
36#define IAP_RET_CMD_SUCCESS 0
37
38#define SST_BASEADDR 0x80000000
39#define SST_ADDR1 ((volatile ushort*)(SST_BASEADDR + (0x5555 << 1)))
40#define SST_ADDR2 ((volatile ushort*)(SST_BASEADDR + (0x2AAA << 1)))
41
42
43static unsigned long command[5];
44static unsigned long result[2];
45
46flash_info_t flash_info[CFG_MAX_FLASH_BANKS];
47
48extern void iap_entry(unsigned long * command, unsigned long * result);
49
50/*-----------------------------------------------------------------------
51 *
52 */
53int get_flash_sector(flash_info_t * info, ulong flash_addr)
54{
55 int i;
56
57 for(i=1; i < (info->sector_count); i++) {
58 if (flash_addr < (info->start[i]))
59 break;
60 }
61
62 return (i-1);
63}
64
65/*-----------------------------------------------------------------------
66 * This function assumes that flash_addr is aligned on 512 bytes boundary
Wolfgang Denk62e8a972007-01-30 00:50:40 +010067 * in flash. This function also assumes that prepare have been called
Gary Jennejohn7968bb52007-01-24 12:16:56 +010068 * for the sector in question.
69 */
70int copy_buffer_to_flash(flash_info_t * info, ulong flash_addr)
71{
72 int first_sector;
73 int last_sector;
74
75 first_sector = get_flash_sector(info, flash_addr);
76 last_sector = get_flash_sector(info, flash_addr + 512 - 1);
77
78 /* prepare sectors for write */
79 command[0] = IAP_CMD_PREPARE;
80 command[1] = first_sector;
81 command[2] = last_sector;
82 iap_entry(command, result);
83 if (result[0] != IAP_RET_CMD_SUCCESS) {
84 printf("IAP prepare failed\n");
85 return ERR_PROG_ERROR;
86 }
87
88 command[0] = IAP_CMD_COPY;
89 command[1] = flash_addr;
90 command[2] = COPY_BUFFER_LOCATION;
91 command[3] = 512;
92 command[4] = CFG_SYS_CLK_FREQ >> 10;
93 iap_entry(command, result);
94 if (result[0] != IAP_RET_CMD_SUCCESS) {
95 printf("IAP copy failed\n");
96 return 1;
97 }
98
99 return 0;
100}
101
102/*-----------------------------------------------------------------------
103 *
104 */
105void write_word_sst(ulong addr, ushort data)
106{
107 ushort tmp;
108
109 *SST_ADDR1 = 0x00AA;
110 *SST_ADDR2 = 0x0055;
111 *SST_ADDR1 = 0x00A0;
112 *((volatile ushort*)addr) = data;
113 /* do data polling */
114 do {
115 tmp = *((volatile ushort*)addr);
116 } while (tmp != data);
117}
118
119/*-----------------------------------------------------------------------
120 */
121
122ulong flash_init (void)
123{
124 int j, k;
125 ulong size = 0;
126 ulong flashbase = 0;
127
128 flash_info[0].flash_id = (PHILIPS_LPC2292 & FLASH_VENDMASK);
129 flash_info[0].size = 0x003E000; /* 256 - 8 KB */
130 flash_info[0].sector_count = 17;
131 memset (flash_info[0].protect, 0, 17);
132 flashbase = 0x00000000;
133 for (j = 0, k = 0; j < 8; j++, k++) {
134 flash_info[0].start[k] = flashbase;
135 flashbase += 0x00002000;
136 }
137 for (j = 0; j < 2; j++, k++) {
138 flash_info[0].start[k] = flashbase;
139 flashbase += 0x00010000;
140 }
141 for (j = 0; j < 7; j++, k++) {
142 flash_info[0].start[k] = flashbase;
143 flashbase += 0x00002000;
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100144 }
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100145 size += flash_info[0].size;
146
147 flash_info[1].flash_id = (SST_MANUFACT & FLASH_VENDMASK);
148 flash_info[1].size = 0x00200000; /* 2 MB */
149 flash_info[1].sector_count = 512;
150 memset (flash_info[1].protect, 0, 512);
151 flashbase = SST_BASEADDR;
152 for (j=0; j<512; j++) {
153 flash_info[1].start[j] = flashbase;
154 flashbase += 0x1000; /* 4 KB sectors */
155 }
156 size += flash_info[1].size;
157
158 /* Protect monitor and environment sectors */
159 flash_protect (FLAG_PROTECT_SET,
160 0x0,
161 0x0 + monitor_flash_len - 1,
162 &flash_info[0]);
163
164 flash_protect (FLAG_PROTECT_SET,
165 CFG_ENV_ADDR,
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100166 CFG_ENV_ADDR + CFG_ENV_SIZE - 1,
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100167 &flash_info[0]);
168
169 return size;
170}
171
172/*-----------------------------------------------------------------------
173 */
174void flash_print_info (flash_info_t * info)
175{
176 int i;
177 int erased = 0;
178 unsigned long j;
179 unsigned long count;
180 unsigned char *p;
181
182 switch (info->flash_id & FLASH_VENDMASK) {
183 case (SST_MANUFACT & FLASH_VENDMASK):
184 printf("SST: ");
185 break;
186 case (PHILIPS_LPC2292 & FLASH_VENDMASK):
187 printf("Philips: ");
188 break;
189 default:
190 printf ("Unknown Vendor ");
191 break;
192 }
193
194 printf (" Size: %ld KB in %d Sectors\n",
195 info->size >> 10, info->sector_count);
196
197 printf (" Sector Start Addresses:");
198 for (i = 0; i < info->sector_count; i++) {
199 if ((i % 5) == 0) {
200 printf ("\n ");
201 }
202 if (i < (info->sector_count - 1)) {
203 count = info->start[i+1] - info->start[i];
204 }
205 else {
206 count = info->start[0] + info->size - info->start[i];
207 }
208 p = (unsigned char*)(info->start[i]);
209 erased = 1;
210 for (j = 0; j < count; j++) {
211 if (*p != 0xFF) {
212 erased = 0;
213 break;
214 }
215 p++;
216 }
217 printf (" %08lX%s%s", info->start[i], info->protect[i] ? " RO" : " ",
218 erased ? " E" : " ");
219 }
220 printf ("\n");
221}
222
223/*-----------------------------------------------------------------------
224 */
225
226int flash_erase_philips (flash_info_t * info, int s_first, int s_last)
227{
228 int flag;
229 int prot;
230 int sect;
231
232 prot = 0;
233 for (sect = s_first; sect <= s_last; ++sect) {
234 if (info->protect[sect]) {
235 prot++;
236 }
237 }
238 if (prot)
239 return ERR_PROTECTED;
240
241
242 flag = disable_interrupts();
243
244 printf ("Erasing %d sectors starting at sector %2d.\n"
245 "This make take some time ... ",
246 s_last - s_first + 1, s_first);
247
248 command[0] = IAP_CMD_PREPARE;
249 command[1] = s_first;
250 command[2] = s_last;
251 iap_entry(command, result);
252 if (result[0] != IAP_RET_CMD_SUCCESS) {
253 printf("IAP prepare failed\n");
254 return ERR_PROTECTED;
255 }
256
257 command[0] = IAP_CMD_ERASE;
258 command[1] = s_first;
259 command[2] = s_last;
260 command[3] = CFG_SYS_CLK_FREQ >> 10;
261 iap_entry(command, result);
262 if (result[0] != IAP_RET_CMD_SUCCESS) {
263 printf("IAP erase failed\n");
264 return ERR_PROTECTED;
265 }
266
267 if (flag)
268 enable_interrupts();
269
270 return ERR_OK;
271}
272
273int flash_erase_sst (flash_info_t * info, int s_first, int s_last)
274{
275 int i;
276
277 for (i = s_first; i <= s_last; i++) {
278 *SST_ADDR1 = 0x00AA;
279 *SST_ADDR2 = 0x0055;
280 *SST_ADDR1 = 0x0080;
281 *SST_ADDR1 = 0x00AA;
282 *SST_ADDR2 = 0x0055;
283 *((volatile ushort*)(info->start[i])) = 0x0030;
284 /* wait for erase to finish */
285 udelay(25000);
286 }
287
288 return ERR_OK;
289}
290
291int flash_erase (flash_info_t * info, int s_first, int s_last)
292{
293 switch (info->flash_id & FLASH_VENDMASK) {
294 case (SST_MANUFACT & FLASH_VENDMASK):
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100295 return flash_erase_sst(info, s_first, s_last);
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100296 case (PHILIPS_LPC2292 & FLASH_VENDMASK):
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100297 return flash_erase_philips(info, s_first, s_last);
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100298 default:
299 return ERR_PROTECTED;
300 }
301 return ERR_PROTECTED;
302}
303
304/*-----------------------------------------------------------------------
305 * Copy memory to flash.
306 *
307 * cnt is in bytes
308 */
309
310int write_buff_sst (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
311{
312 ushort tmp;
313 ulong i;
314 uchar* src_org;
315 uchar* dst_org;
316 ulong cnt_org = cnt;
317 int ret = ERR_OK;
318
319 src_org = src;
320 dst_org = (uchar*)addr;
321
322 if (addr & 1) { /* if odd address */
323 tmp = *((uchar*)(addr - 1)); /* little endian */
324 tmp |= (*src << 8);
325 write_word_sst(addr - 1, tmp);
326 addr += 1;
327 cnt -= 1;
328 src++;
329 }
330 while (cnt > 1) {
331 tmp = ((*(src+1)) << 8) + (*src); /* little endian */
332 write_word_sst(addr, tmp);
333 addr += 2;
334 src += 2;
335 cnt -= 2;
336 }
337 if (cnt > 0) {
338 tmp = (*((uchar*)(addr + 1))) << 8;
339 tmp |= *src;
340 write_word_sst(addr, tmp);
341 }
342
343 for (i = 0; i < cnt_org; i++) {
344 if (*dst_org != *src_org) {
345 printf("Write failed. Byte %lX differs\n", i);
346 ret = ERR_PROG_ERROR;
347 break;
348 }
349 dst_org++;
350 src_org++;
351 }
352
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100353 return ret;
354}
355
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100356int write_buff_philips (flash_info_t * info,
357 uchar * src,
358 ulong addr,
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100359 ulong cnt)
360{
361 int first_copy_size;
362 int last_copy_size;
363 int first_block;
364 int last_block;
365 int nbr_mid_blocks;
366 uchar memmap_value;
367 ulong i;
368 uchar* src_org;
369 uchar* dst_org;
370 int ret = ERR_OK;
371
372 src_org = src;
373 dst_org = (uchar*)addr;
374
375 first_block = addr / 512;
376 last_block = (addr + cnt) / 512;
377 nbr_mid_blocks = last_block - first_block - 1;
378
379 first_copy_size = 512 - (addr % 512);
380 last_copy_size = (addr + cnt) % 512;
381
382#if 0
383 printf("\ncopy first block: (1) %lX -> %lX 0x200 bytes, "
384 "(2) %lX -> %lX 0x%X bytes, (3) %lX -> %lX 0x200 bytes\n",
385 (ulong)(first_block * 512),
386 (ulong)COPY_BUFFER_LOCATION,
387 (ulong)src,
388 (ulong)(COPY_BUFFER_LOCATION + 512 - first_copy_size),
389 first_copy_size,
390 (ulong)COPY_BUFFER_LOCATION,
391 (ulong)(first_block * 512));
392#endif
393
394 /* copy first block */
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100395 memcpy((void*)COPY_BUFFER_LOCATION,
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100396 (void*)(first_block * 512), 512);
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100397 memcpy((void*)(COPY_BUFFER_LOCATION + 512 - first_copy_size),
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100398 src, first_copy_size);
399 copy_buffer_to_flash(info, first_block * 512);
400 src += first_copy_size;
401 addr += first_copy_size;
402
403 /* copy middle blocks */
404 for (i = 0; i < nbr_mid_blocks; i++) {
405#if 0
406 printf("copy middle block: %lX -> %lX 512 bytes, "
407 "%lX -> %lX 512 bytes\n",
408 (ulong)src,
409 (ulong)COPY_BUFFER_LOCATION,
410 (ulong)COPY_BUFFER_LOCATION,
411 (ulong)addr);
412#endif
413 memcpy((void*)COPY_BUFFER_LOCATION, src, 512);
414 copy_buffer_to_flash(info, addr);
415 src += 512;
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100416 addr += 512;
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100417 }
418
419
420 if (last_copy_size > 0) {
421#if 0
422 printf("copy last block: (1) %lX -> %lX 0x200 bytes, "
423 "(2) %lX -> %lX 0x%X bytes, (3) %lX -> %lX x200 bytes\n",
424 (ulong)(last_block * 512),
425 (ulong)COPY_BUFFER_LOCATION,
426 (ulong)src,
427 (ulong)(COPY_BUFFER_LOCATION),
428 last_copy_size,
429 (ulong)COPY_BUFFER_LOCATION,
430 (ulong)addr);
431#endif
432 /* copy last block */
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100433 memcpy((void*)COPY_BUFFER_LOCATION,
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100434 (void*)(last_block * 512), 512);
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100435 memcpy((void*)COPY_BUFFER_LOCATION,
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100436 src, last_copy_size);
437 copy_buffer_to_flash(info, addr);
438 }
439
440 /* verify write */
441 memmap_value = GET8(MEMMAP);
442
443 disable_interrupts();
444
445 PUT8(MEMMAP, 01); /* we must make sure that initial 64
446 bytes are taken from flash when we
447 do the compare */
448
449 for (i = 0; i < cnt; i++) {
450 if (*dst_org != *src_org){
451 printf("Write failed. Byte %lX differs\n", i);
452 ret = ERR_PROG_ERROR;
453 break;
454 }
455 dst_org++;
456 src_org++;
457 }
458
459 PUT8(MEMMAP, memmap_value);
460 enable_interrupts();
461
462 return ret;
463}
464
465int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
466{
467 switch (info->flash_id & FLASH_VENDMASK) {
468 case (SST_MANUFACT & FLASH_VENDMASK):
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100469 return write_buff_sst(info, src, addr, cnt);
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100470 case (PHILIPS_LPC2292 & FLASH_VENDMASK):
Wolfgang Denk62e8a972007-01-30 00:50:40 +0100471 return write_buff_philips(info, src, addr, cnt);
Gary Jennejohn7968bb52007-01-24 12:16:56 +0100472 default:
473 return ERR_PROG_ERROR;
474 }
475 return ERR_PROG_ERROR;
476}