TomatoVPN 1.27vpn3.6 release
[tomato.git] / release / src / shared / flashutl.c
blob919b974bfda34eb491ce9204d79064fe85a0cccc
1 /*
2 * flashutl.c - Flash Read/write/Erase routines
4 * Copyright 2005, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
12 * $Id: flashutl.c,v 1.8 2005/03/07 08:35:32 kanki Exp $
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
19 #include <typedefs.h>
21 #define DECLARE_FLASHES
22 #include <sbutils.h>
23 #include <sbconfig.h>
24 #include <flash.h>
25 #include <sflash.h>
26 #include <flashutl.h>
27 #include <bcmnvram.h>
28 #include <bcmutils.h>
29 #include <osl.h>
31 #define DPRINT(x)
33 #define ERR2 0x30
34 #define DONE 0x80
35 #define WBUFSIZE 32
36 #define FLASH_TRIES 4000000 /* retry count */
37 #define CMD_ADDR ((unsigned long)0xFFFFFFFF)
39 /* 'which' param for block() */
40 #define BLOCK_BASE 0
41 #define BLOCK_LIM 1
43 #define FLASH_ADDR(off) ((unsigned long)flashutl_base + (off))
45 static chipcregs_t *cc;
47 /* Global vars */
48 char* flashutl_base = NULL;
49 flash_desc_t* flashutl_desc = NULL;
50 flash_cmds_t* flashutl_cmd = NULL;
52 static void scmd(uint16 cmd, unsigned long off);
53 static void cmd(uint16 cmd, unsigned long off);
54 static void flash_reset(void);
55 static int flash_poll(unsigned long off, uint16 data);
56 static unsigned long block(unsigned long addr, int which);
57 static int flash_erase(void);
58 static int flash_eraseblk(unsigned long off);
59 static int flash_write(unsigned long off, uint16 *src, uint nbytes);
60 static unsigned long flash_block_base(unsigned long off);
61 static unsigned long flash_block_lim(unsigned long off);
62 static chipcregs_t *cc;
64 /* Read the flash ID and set the globals */
65 int
66 sysFlashInit(char *flash_str)
68 uint32 fltype = PFLASH;
69 uint16 flash_vendid = 0;
70 uint16 flash_devid = 0;
71 uint16* flash = (uint16*)0xbfc00000;
72 int idx;
73 struct sflash *sflash;
74 void *sbh;
77 * Check for serial flash.
79 sbh = sb_kattach();
80 ASSERT(sbh);
81 cc = (chipcregs_t *) sb_setcore(sbh, SB_CC, 0);
83 if (cc) {
84 flash = (uint16*)0xbc000000;
85 fltype = R_REG(&cc->capabilities) & CAP_FLASH_MASK;
86 /* Select SFLASH ? */
87 if (fltype == SFLASH_ST || fltype == SFLASH_AT) {
88 sflash = sflash_init(cc);
89 flashutl_cmd = &sflash_cmd_t;
90 flashutl_desc = &sflash_desc;
91 flashutl_desc->size = sflash->size;
92 if (flash_str)
93 sprintf(flash_str, "SFLASH %d kB", sflash->size/1024);
94 return(0);
98 flashutl_base = (uint8*)flash;
101 * Parallel flash support
102 * Some flashes have different unlock addresses, try each it turn
104 idx = sizeof(flash_cmds)/sizeof(flash_cmds_t) - 2;
105 flashutl_cmd = &flash_cmds[idx--];
106 while((fltype == PFLASH) && flashutl_cmd->type) {
108 if (flashutl_cmd->read_id)
109 cmd(flashutl_cmd->read_id, CMD_ADDR);
111 #ifdef MIPSEB
112 flash_vendid = *(flash + 1);
113 flash_devid = *flash;
114 #else
115 flash_vendid = *flash;
116 flash_devid = *(flash + 1);
117 #endif
119 /* Funky AMD */
120 if ((flash_vendid == 1) && (flash_devid == 0x227e)) {
121 /* Get real devid */
122 #ifdef MIPSEB
123 flash_devid = *(flash+0xe);
124 #else
125 flash_devid = *(flash+0xf);
126 #endif
129 flashutl_desc = flashes;
130 while (flashutl_desc->mfgid != 0 &&
131 !(flashutl_desc->mfgid == flash_vendid &&
132 flashutl_desc->devid == flash_devid)) {
133 flashutl_desc++;
135 if (flashutl_desc->mfgid != 0)
136 break;
138 flashutl_cmd = &flash_cmds[idx--];
141 if (flashutl_desc->mfgid == 0) {
142 flashutl_desc = NULL;
143 flashutl_cmd = NULL;
144 } else {
145 flashutl_cmd = flash_cmds;
146 while (flashutl_cmd->type != 0 && flashutl_cmd->type != flashutl_desc->type)
147 flashutl_cmd++;
148 if (flashutl_cmd->type == 0)
149 flashutl_cmd = NULL;
152 if (flashutl_cmd != NULL) {
153 flash_reset();
156 if (flashutl_desc == NULL) {
157 if (flash_str)
158 sprintf(flash_str, "UNKNOWN 0x%x 0x%x", flash_vendid, flash_devid);
159 DPRINT(("Flash type UNKNOWN\n"));
160 return 1;
163 if (flash_str)
164 strcpy(flash_str, flashutl_desc->desc);
165 DPRINT(("Flash type \"%s\"\n", flashutl_desc->desc));
167 return 0;
170 static int
171 flash_erase()
173 unsigned long size = flashutl_desc->size;
174 unsigned long addr;
175 int err = 0;
177 for (addr = 0; addr < size; addr = block(addr, BLOCK_LIM)) {
178 err = flash_eraseblk(addr);
179 if (err) break;
182 return err;
185 static int
186 flash_eraseblk(unsigned long addr)
188 unsigned long a;
189 uint16 st;
191 a = (unsigned long)addr;
192 if (a >= flashutl_desc->size)
193 return 1;
195 a = block(a, BLOCK_BASE);
197 /* Ensure blocks are unlocked (for intel chips)*/
198 if (flashutl_cmd->type == BSC) {
199 scmd((unsigned char)INTEL_UNLOCK1, a);
200 scmd((unsigned char)INTEL_UNLOCK2, a);
203 if (flashutl_cmd->pre_erase)
204 cmd(flashutl_cmd->pre_erase, CMD_ADDR);
205 if (flashutl_cmd->erase_block)
206 cmd(flashutl_cmd->erase_block, a);
207 if (flashutl_cmd->confirm)
208 scmd(flashutl_cmd->confirm, a);
210 st = flash_poll(a, 0xffff);
212 flash_reset();
214 if (st) {
215 DPRINT(("Erase of block 0x%08lx-0x%08lx failed\n",
216 a, block((unsigned long)addr, BLOCK_LIM)));
217 return st;
220 DPRINT(("Erase of block 0x%08lx-0x%08lx done", a, block((unsigned long)addr, BLOCK_LIM)));
222 return 0;
225 static int
226 flash_write(unsigned long off, uint16 *src, uint nbytes)
228 uint16* dest;
229 uint16 st, data;
230 uint i, len;
231 uint cnts=0,k=0;
233 ASSERT(flashutl_desc != NULL);
235 if (off >= flashutl_desc->size)
236 return 1;
238 dest = (uint16*)FLASH_ADDR(off);
239 st = 0;
241 while (nbytes) {
242 if((nbytes % 40000)==0)
244 Cled(k);
245 // OSL_DELAY(10);
246 for(cnts=0;cnts<10000;cnts++);
247 if(k==1)k=0;else k=1;
250 if ((flashutl_desc->type == SCS) &&
251 flashutl_cmd->write_buf &&
252 ((off & (WBUFSIZE - 1)) == 0)) {
253 /* issue write command */
254 if (flashutl_cmd->write_buf)
255 cmd(flashutl_cmd->write_buf, off);
256 if ((st = flash_poll(off, DONE)))
257 continue;
259 len = MIN(nbytes, WBUFSIZE);
261 #ifndef MIPSEB
262 /* write (length - 1) */
263 cmd((len / 2) - 1, off);
265 /* write data */
266 for (i = 0; i < len; i += 2, dest++, src++)
267 *dest = *src;
268 #else
270 * BCM4710 endianness is word consistent but
271 * byte/short scrambled. This write buffer
272 * mechanism appears to be sensitive to the
273 * order of the addresses hence we need to
274 * unscramble them. We may also need to pad
275 * the source with two bytes of 0xffff in case
276 * an odd number of shorts are presented.
279 /* write (padded length - 1) */
280 cmd((ROUNDUP(len, 4) / 2) - 1, off);
282 /* write data (plus pad if necessary) */
283 for (i = 0; i < ROUNDUP(len, 4); i += 4, dest += 2, src += 2) {
284 *(dest + 1) = ((i + 2) < len) ? *(src + 1) : 0xffff;
285 *dest = *src;
287 #endif
289 /* write confirm */
290 if (flashutl_cmd->confirm)
291 cmd(flashutl_cmd->confirm, off);
293 if ((st = flash_poll(off, DONE)))
294 break;
295 } else {
296 /* issue write command */
297 if (flashutl_cmd->write_word)
298 cmd(flashutl_cmd->write_word, CMD_ADDR);
300 /* write data */
301 len = MIN(nbytes, 2);
302 data = *src++;
303 *dest++ = data;
305 /* poll for done */
306 if ((st = flash_poll(off, data)))
307 break;
310 nbytes -= len;
311 off += len;
314 flash_reset();
316 return st;
319 static unsigned long
320 flash_block_base(unsigned long off)
322 return block(off, BLOCK_BASE);
325 static unsigned long
326 flash_block_lim(unsigned long off)
328 return block(off, BLOCK_LIM);
331 /* Writes a single command to the flash. */
332 static void
333 scmd(uint16 cmd, unsigned long off)
335 ASSERT(flashutl_base != NULL);
337 /* cmd |= cmd << 8; */
339 *(uint16*)(flashutl_base + off) = cmd;
342 /* Writes a command to flash, performing an unlock if needed. */
343 static void
344 cmd(uint16 cmd, unsigned long off)
346 int i;
347 unlock_cmd_t *ul=NULL;
348 unsigned long cmd_off;
350 ASSERT(flashutl_cmd != NULL);
352 switch (flashutl_cmd->type) {
353 case AMD:
354 ul = &unlock_cmd_amd;
355 cmd_off = AMD_CMD;
356 break;
357 case SST:
358 ul = &unlock_cmd_sst;
359 cmd_off = SST_CMD;
360 break;
361 default:
362 cmd_off = 0;
363 break;
366 if (flashutl_cmd->need_unlock) {
367 for (i = 0; i < UNLOCK_CMD_WORDS; i++)
368 *(uint16*)(flashutl_base + ul->addr[i]) = ul->cmd[i];
371 /* cmd |= cmd << 8; */
373 if (off == CMD_ADDR)
374 off = cmd_off;
376 #ifdef MIPSEB
377 off ^= 2;
378 #endif
380 *(uint16*)(flashutl_base + off) = cmd;
383 static void
384 flash_reset()
386 ASSERT(flashutl_desc != NULL);
388 if (flashutl_cmd->clear_csr)
389 scmd(flashutl_cmd->clear_csr, 0);
390 if (flashutl_cmd->read_array)
391 scmd(flashutl_cmd->read_array, 0);
394 static int
395 flash_poll(unsigned long off, uint16 data)
397 volatile uint16* addr;
398 int cnt = FLASH_TRIES;
399 uint16 st;
401 ASSERT(flashutl_desc != NULL);
403 if (flashutl_desc->type == AMD || flashutl_desc->type == SST) {
404 /* AMD style poll checkes the address being written */
405 addr = (volatile uint16*)FLASH_ADDR(off);
406 while ((st = *addr) != data && cnt != 0)
407 cnt--;
408 if (cnt == 0) {
409 DPRINT(("flash_poll: timeout, read 0x%x, expected 0x%x\n", st, data));
410 return -1;
412 } else {
413 /* INTEL style poll is at second word of the block being written */
414 addr = (volatile uint16*)FLASH_ADDR(block(off, BLOCK_BASE));
415 addr++;
416 while (((st = *addr) & DONE) == 0 && cnt != 0)
417 cnt--;
418 if (cnt == 0) {
419 DPRINT(("flash_poll: timeout, error status = 0x%x\n", st));
420 return -1;
424 return 0;
427 static unsigned long
428 block(unsigned long addr, int which)
430 unsigned long b, l, sb;
431 uint* sblocks;
432 int i;
434 ASSERT(flashutl_desc != NULL);
435 ASSERT(addr < (unsigned long)flashutl_desc->size);
437 b = addr / flashutl_desc->bsize;
438 /* check for an address a full size block */
439 if (b >= flashutl_desc->ff && b <= flashutl_desc->lf) {
440 if (which == BLOCK_LIM) b++;
441 return (b * flashutl_desc->bsize);
444 /* search for the sub-block */
445 if (flashutl_desc->ff == 0) {
446 /* sub blocks are at the end of the flash */
447 sb = flashutl_desc->bsize * (flashutl_desc->lf + 1);
448 } else {
449 /* sub blocks are at the start of the flash */
450 sb = 0;
453 sblocks = flashutl_desc->subblocks;
454 for (i = 0; i < flashutl_desc->nsub; i++) {
455 b = sb + sblocks[i];
456 l = sb + sblocks[i+1];
457 if (addr >= b && addr < l) {
458 if (which == BLOCK_BASE)
459 return b;
460 else
461 return l;
465 ASSERT(1);
466 return 0;
469 void
470 nvWrite(unsigned short *data, unsigned int len)
472 uint off = flashutl_desc->size - NVRAM_SPACE;
473 sysFlashWrite(off, (uchar*)data, len);
477 sysFlashErase(uint off, unsigned int numbytes)
479 unsigned long end = off + numbytes;
480 int err = 0;
481 int k=0,i=1;
483 if (flashutl_cmd->type == SFLASH) {
484 err = sflash_commit(cc, off, numbytes, NULL);
485 } else {
486 ASSERT(!(off & 1));
487 while (off < end) {
488 Cled(i);
489 //delayUs(10);
490 if(i)i=0;else i=1;
491 err = flash_eraseblk(off);
492 if (err)
493 break;
494 off = flash_block_lim(off);
498 if (err)
499 DPRINT(("Block erase at 0x%x failed\n", off));
500 else
501 DPRINT(("Done\n"));
503 return !err;
507 sysFlashWrite(uint off, uchar *src, uint numbytes)
509 int err;
511 DPRINT(("Writing 0x%x bytes to flash off @0x%x ...\n", (unsigned int)numbytes, off));
513 if (flashutl_cmd->type == SFLASH)
514 err = sflash_commit(cc, off, numbytes, src);
515 else {
516 ASSERT(!(off & 1));
517 if (!sysFlashErase(off, numbytes))
518 return 0;
519 err = flash_write(off, (uint16*)src, numbytes);
522 if (err)
523 DPRINT(("Flash write failed\n"));
524 else
525 DPRINT(("Flash write succeeded\n"));
527 return !err;
530 int
531 sysFlashRead(uint off, uchar *buf, uint numbytes)
533 uint read, total_read=0;
534 uint16 *src, *dst;
536 if (flashutl_cmd->type == SFLASH) {
537 while (numbytes) {
538 read = sflash_read(cc, off, numbytes, buf);
539 numbytes -= read;
540 buf += read;
541 off += read;
542 total_read += read;
544 } else {
545 ASSERT(!(off & 1));
546 ASSERT(!(numbytes & 1));
548 src = (uint16*)(flashutl_base + off);
549 dst = (uint16*)buf;
551 while(numbytes) {
552 *dst++ = *src++;
553 numbytes-=2;
554 total_read+=2;
558 return(total_read);