dcef7ddba82e14681988299503073c9976e33c25
[platform/kernel/u-boot.git] / board / lpc2292sodimm / flash.c
1 /*
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
43 static unsigned long command[5];
44 static unsigned long result[2];
45
46 flash_info_t flash_info[CFG_MAX_FLASH_BANKS];
47
48 extern void iap_entry(unsigned long * command, unsigned long * result);
49
50 /*-----------------------------------------------------------------------
51  *
52  */
53 int 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
67  * in flash. This function also assumes that prepare have been called 
68  * for the sector in question.
69  */
70 int 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  */
105 void 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
122 ulong 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;
144         }               
145         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,
166                  CFG_ENV_ADDR + CFG_ENV_SIZE - 1, 
167                  &flash_info[0]);
168
169         return size;
170 }
171
172 /*-----------------------------------------------------------------------
173  */
174 void 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
226 int 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
273 int 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
291 int 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):
295                         return flash_erase_sst(info, s_first, s_last);  
296                 case (PHILIPS_LPC2292 & FLASH_VENDMASK):
297                         return flash_erase_philips(info, s_first, s_last);  
298                 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
310 int 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
353
354         return ret;
355 }
356
357 int write_buff_philips (flash_info_t * info, 
358                         uchar * src, 
359                         ulong addr, 
360                         ulong cnt)
361 {
362         int first_copy_size;
363         int last_copy_size;
364         int first_block;
365         int last_block;
366         int nbr_mid_blocks;
367         uchar memmap_value;
368         ulong i;
369         uchar* src_org;
370         uchar* dst_org;
371         int ret = ERR_OK;
372
373         src_org = src;
374         dst_org = (uchar*)addr;
375
376         first_block = addr / 512;
377         last_block = (addr + cnt) / 512;
378         nbr_mid_blocks = last_block - first_block - 1;
379
380         first_copy_size = 512 - (addr % 512);
381         last_copy_size = (addr + cnt) % 512;
382
383 #if 0
384         printf("\ncopy first block: (1) %lX -> %lX 0x200 bytes, "
385                 "(2) %lX -> %lX 0x%X bytes, (3) %lX -> %lX 0x200 bytes\n",
386         (ulong)(first_block * 512),
387         (ulong)COPY_BUFFER_LOCATION,
388         (ulong)src,
389         (ulong)(COPY_BUFFER_LOCATION + 512 - first_copy_size),
390         first_copy_size,
391         (ulong)COPY_BUFFER_LOCATION,
392         (ulong)(first_block * 512));
393 #endif
394
395         /* copy first block */
396         memcpy((void*)COPY_BUFFER_LOCATION, 
397                 (void*)(first_block * 512), 512);
398         memcpy((void*)(COPY_BUFFER_LOCATION + 512 - first_copy_size), 
399                 src, first_copy_size);
400         copy_buffer_to_flash(info, first_block * 512);
401         src += first_copy_size;
402         addr += first_copy_size;
403
404         /* copy middle blocks */
405         for (i = 0; i < nbr_mid_blocks; i++) {
406 #if 0
407                 printf("copy middle block: %lX -> %lX 512 bytes, "
408                 "%lX -> %lX 512 bytes\n",
409                 (ulong)src,
410                 (ulong)COPY_BUFFER_LOCATION,
411                 (ulong)COPY_BUFFER_LOCATION,
412                 (ulong)addr);
413 #endif
414                 memcpy((void*)COPY_BUFFER_LOCATION, src, 512);
415                 copy_buffer_to_flash(info, addr);
416                 src += 512;
417                 addr += 512;    
418         }
419
420
421         if (last_copy_size > 0) {
422 #if 0
423                 printf("copy last block: (1) %lX -> %lX 0x200 bytes, "
424                 "(2) %lX -> %lX 0x%X bytes, (3) %lX -> %lX x200 bytes\n",
425                 (ulong)(last_block * 512),
426                 (ulong)COPY_BUFFER_LOCATION,
427                 (ulong)src,
428                 (ulong)(COPY_BUFFER_LOCATION),
429                 last_copy_size,
430                 (ulong)COPY_BUFFER_LOCATION,
431                 (ulong)addr);
432 #endif
433                 /* copy last block */
434                 memcpy((void*)COPY_BUFFER_LOCATION, 
435                         (void*)(last_block * 512), 512);
436                 memcpy((void*)COPY_BUFFER_LOCATION, 
437                         src, last_copy_size);
438                 copy_buffer_to_flash(info, addr);
439         }
440
441         /* verify write */
442         memmap_value = GET8(MEMMAP);
443
444         disable_interrupts();
445
446         PUT8(MEMMAP, 01);               /* we must make sure that initial 64
447                                                            bytes are taken from flash when we
448                                                            do the compare */
449
450         for (i = 0; i < cnt; i++) {
451                 if (*dst_org != *src_org){
452                         printf("Write failed. Byte %lX differs\n", i);
453                         ret = ERR_PROG_ERROR;
454                         break;
455                 }
456                 dst_org++;
457                 src_org++;
458         }
459
460         PUT8(MEMMAP, memmap_value);
461         enable_interrupts();
462
463         return ret;
464 }
465
466 int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
467 {
468         switch (info->flash_id & FLASH_VENDMASK) {
469                 case (SST_MANUFACT & FLASH_VENDMASK):
470                         return write_buff_sst(info, src, addr, cnt);  
471                 case (PHILIPS_LPC2292 & FLASH_VENDMASK):
472                         return write_buff_philips(info, src, addr, cnt);  
473                 default:
474                         return ERR_PROG_ERROR;
475         }
476         return ERR_PROG_ERROR;
477 }