cosmetics
[tomato.git] / release / src / shared / flashutl.c
blob9b22fd8d85c4f250f2dc4ba7889d345bad8773fd
1 /*
2 * flashutl.c - Flash Read/write/Erase routines
4 * Copyright 2004, 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$
15 #include <typedefs.h>
16 #include <osl.h>
18 #define DECLARE_FLASHES
19 #include <bcmutils.h>
20 #include <sbutils.h>
21 #include <sbconfig.h>
22 #include <flash.h>
23 #include <sflash.h>
24 #include <flashutl.h>
25 #include <bcmnvram.h>
27 #define DPRINT(x)
29 #define ERR2 0x30 /* Mask for err UNUSED */
30 #define DONE 0x80 /* Mask for done */
31 #define WBUFSIZE 32 /* Write Buffer size */
32 #define FLASH_TRIES 4000000 /* retry count */
33 #define CMD_ADDR ((unsigned long)0xFFFFFFFF)
35 /* 'which' param for block() */
36 #define BLOCK_BASE 0 /* Base of block */
37 #define BLOCK_LIM 1 /* Limit of block */
39 #define FLASH_ADDR(off) ((unsigned long)flashutl_base + (off))
41 /* Local vars */
42 static sb_t *sbh = NULL;
43 static chipcregs_t *cc = NULL;
45 /* Global vars */
46 uint8 *flashutl_base = NULL;
47 flash_desc_t *flashutl_desc = NULL;
48 flash_cmds_t *flashutl_cmd = NULL;
49 uint8 flashutl_wsz = sizeof(uint16);
51 static void scmd(uint16 cmd, unsigned long off);
52 static void cmd(uint16 cmd, unsigned long off);
53 static void flash_reset(void);
54 static int flash_poll(unsigned long off, uint16 data);
55 static unsigned long block(unsigned long addr, int which);
56 static int flash_eraseblk(unsigned long off);
57 static int flash_write(unsigned long off, uint8 *src, uint nbytes);
58 static uint16 INLINE flash_readword(unsigned long addr);
59 static void INLINE flash_writeword(unsigned long addr, uint16 data);
61 int sysFlashErase(uint off, unsigned int numbytes);
63 /* Read the flash ID and set the globals */
64 int
65 sysFlashInit(char *flash_str)
67 osl_t *osh;
68 uint32 fltype = PFLASH;
69 uint16 flash_vendid = 0;
70 uint16 flash_devid = 0;
71 int idx;
72 struct sflash *sflash;
75 * Check for serial flash.
77 sbh = sb_kattach(SB_OSH);
78 ASSERT(sbh);
80 osh = sb_osh(sbh);
82 flashutl_base = (uint8*)OSL_UNCACHED(SB_FLASH1);
83 flashutl_wsz = sizeof(uint16);
84 cc = (chipcregs_t *)sb_setcore(sbh, SB_CC, 0);
85 if (cc) {
86 flashutl_base = (uint8*)OSL_UNCACHED(SB_FLASH2);
87 flashutl_wsz = (R_REG(osh, &cc->flash_config) & CC_CFG_DS) ?
88 sizeof(uint16) : sizeof(uint8);
89 /* Select SFLASH ? */
90 fltype = R_REG(osh, &cc->capabilities) & CC_CAP_FLASH_MASK;
91 if (fltype == SFLASH_ST || fltype == SFLASH_AT) {
92 sflash = sflash_init(sbh, cc);
93 flashutl_cmd = &sflash_cmd_t;
94 flashutl_desc = &sflash_desc;
95 flashutl_desc->size = sflash->size;
96 if (flash_str)
97 sprintf(flash_str, "SFLASH %d kB", sflash->size/1024);
98 return (0);
102 ASSERT(flashutl_wsz == sizeof(uint8) || flashutl_wsz == sizeof(uint16));
105 * Parallel flash support
106 * Some flashes have different unlock addresses, try each it turn
108 for (idx = 0;
109 fltype == PFLASH && idx < ARRAYSIZE(flash_cmds);
110 idx ++) {
111 flashutl_cmd = &flash_cmds[idx];
112 if (flashutl_cmd->type == OLD)
113 continue;
115 if (flashutl_cmd->read_id)
116 cmd(flashutl_cmd->read_id, CMD_ADDR);
118 #ifdef MIPSEB
119 flash_vendid = flash_readword(FLASH_ADDR(2));
120 flash_devid = flash_readword(FLASH_ADDR(0));
121 #else
122 flash_vendid = flash_readword(FLASH_ADDR(0));
123 flash_devid = flash_readword(FLASH_ADDR(2));
124 #endif /* MIPSEB */
126 /* Funky AMD, uses 3 byte device ID so use first byte (4th addr) to
127 * identify it is a 3-byte ID and use the next two bytes (5th & 6th addr)
128 * to form a word for unique identification of format xxyy, where
129 * xx = 5th addr and yy = 6th addr
131 if ((flash_vendid == 1) && (flash_devid == 0x227e)) {
132 /* Get real devid */
133 uint16 flash_devid_5th;
134 #ifdef MIPSEB
135 flash_devid_5th = flash_readword(FLASH_ADDR(0x1e)) << 8;
136 flash_devid = (flash_readword(FLASH_ADDR(0x1c)) & 0xff) | flash_devid_5th;
137 #else
138 flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)) << 8;
139 flash_devid = (flash_readword(FLASH_ADDR(0x1e)) & 0xff) | flash_devid_5th;
140 #endif /* MIPSEB */
143 flashutl_desc = flashes;
144 while (flashutl_desc->mfgid != 0 &&
145 !(flashutl_desc->mfgid == flash_vendid &&
146 flashutl_desc->devid == flash_devid)) {
147 flashutl_desc++;
149 if (flashutl_desc->mfgid != 0)
150 break;
153 if (flashutl_desc->mfgid == 0) {
154 flashutl_desc = NULL;
155 flashutl_cmd = NULL;
156 } else {
157 flashutl_cmd = flash_cmds;
158 while (flashutl_cmd->type != 0 && flashutl_cmd->type != flashutl_desc->type)
159 flashutl_cmd++;
160 if (flashutl_cmd->type == 0)
161 flashutl_cmd = NULL;
164 if (flashutl_cmd != NULL) {
165 flash_reset();
168 if (flashutl_desc == NULL) {
169 if (flash_str)
170 sprintf(flash_str, "UNKNOWN 0x%x 0x%x", flash_vendid, flash_devid);
171 DPRINT(("Flash type UNKNOWN\n"));
172 return 1;
175 if (flash_str)
176 strcpy(flash_str, flashutl_desc->desc);
177 DPRINT(("Flash type \"%s\"\n", flashutl_desc->desc));
179 return 0;
182 static int
183 flash_eraseblk(unsigned long addr)
185 unsigned long a;
186 uint16 st;
188 a = (unsigned long)addr;
189 if (a >= flashutl_desc->size)
190 return 1;
192 a = block(a, BLOCK_BASE);
194 /* Ensure blocks are unlocked (for intel chips) */
195 if (flashutl_cmd->type == BSC) {
196 scmd((unsigned char)INTEL_UNLOCK1, a);
197 scmd((unsigned char)INTEL_UNLOCK2, a);
200 if (flashutl_cmd->pre_erase)
201 cmd(flashutl_cmd->pre_erase, CMD_ADDR);
202 if (flashutl_cmd->erase_block)
203 cmd(flashutl_cmd->erase_block, a);
204 if (flashutl_cmd->confirm)
205 scmd(flashutl_cmd->confirm, a);
207 if (flashutl_wsz == sizeof(uint8))
208 st = flash_poll(a, 0xff);
209 else
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\n", a, block((unsigned long)addr, BLOCK_LIM)));
222 return 0;
225 static int
226 flash_write(unsigned long off, uint8 *src, uint nbytes)
228 uint8 *dest;
229 uint16 st, data;
230 uint i, len;
232 ASSERT(flashutl_desc != NULL);
234 if (off >= flashutl_desc->size)
235 return 1;
237 ASSERT(!(off & (flashutl_wsz - 1)));
239 dest = (uint8*)FLASH_ADDR(off);
240 st = 0;
242 while (nbytes) {
243 if ((flashutl_desc->type == SCS) &&
244 flashutl_cmd->write_buf &&
245 ((off & (WBUFSIZE - 1)) == 0)) {
246 /* issue write command */
247 if (flashutl_cmd->write_buf)
248 cmd(flashutl_cmd->write_buf, off);
249 if ((st = flash_poll(off, DONE)))
250 continue;
252 len = MIN(nbytes, WBUFSIZE);
254 #ifndef MIPSEB
255 /* write (length - 1) */
256 cmd(len / sizeof(uint16) - 1, off);
258 /* write data */
259 for (i = 0; i < len; i += sizeof(uint16),
260 dest += sizeof(uint16), src += sizeof(uint16))
261 *(uint16 *)dest = *(uint16 *)src;
262 #else
264 * BCM4710 endianness is word consistent but
265 * byte/short scrambled. This write buffer
266 * mechanism appears to be sensitive to the
267 * order of the addresses hence we need to
268 * unscramble them. We may also need to pad
269 * the source with two bytes of 0xffff in case
270 * an odd number of shorts are presented.
273 /* write (padded length - 1) */
274 cmd((ROUNDUP(len, sizeof(uint32)) / sizeof(uint16)) - 1, off);
276 /* write data (plus pad if necessary) */
277 for (i = 0; i < ROUNDUP(len, sizeof(uint32)); i += sizeof(uint32),
278 dest += sizeof(uint32), src += sizeof(uint32)) {
279 *((uint16 *)dest + 1) = ((i + sizeof(uint16)) < len) ?
280 *((uint16 *)src + 1) : 0xffff;
281 *(uint16 *)dest = *(uint16 *)src;
283 #endif /* MIPSEB */
285 /* write confirm */
286 if (flashutl_cmd->confirm)
287 cmd(flashutl_cmd->confirm, off);
289 if ((st = flash_poll(off, DONE)))
290 break;
291 } else {
292 /* issue write command */
293 if (flashutl_cmd->write_word)
294 cmd(flashutl_cmd->write_word, CMD_ADDR);
296 /* write data */
297 data = flash_readword((unsigned long)src);
298 flash_writeword((unsigned long)dest, data);
300 /* poll for done */
301 if ((st = flash_poll(off, data)))
302 break;
304 len = MIN(nbytes, flashutl_wsz);
305 dest += len;
306 src += len;
309 nbytes -= len;
310 off += len;
313 flash_reset();
315 return st;
318 static uint16 INLINE
319 flash_readword(unsigned long addr)
321 if (flashutl_wsz == sizeof(uint8))
322 return *(uint8*)addr;
323 else
324 return *(uint16*)addr;
327 static void INLINE
328 flash_writeword(unsigned long addr, uint16 data)
330 if (flashutl_wsz == sizeof(uint8))
331 *(uint8*)addr = (uint8)data;
332 else
333 *(uint16*)addr = data;
336 /* Writes a single command to the flash. */
337 static void
338 scmd(uint16 cmd, unsigned long off)
340 /* cmd |= cmd << 8; */
342 flash_writeword(FLASH_ADDR(off), cmd);
345 /* Writes a command to flash, performing an unlock if needed. */
346 static void
347 cmd(uint16 cmd, unsigned long off)
349 int i;
350 unlock_cmd_t *ul = NULL;
352 ASSERT(flashutl_cmd != NULL);
354 switch (flashutl_cmd->type) {
355 case AMD:
356 ul = &unlock_cmd_amd;
357 break;
358 case SST:
359 ul = &unlock_cmd_sst;
360 break;
361 default:
362 break;
365 if (flashutl_cmd->need_unlock) {
366 ASSERT(ul);
367 for (i = 0; i < UNLOCK_CMD_WORDS; i++)
368 flash_writeword(FLASH_ADDR(ul->addr[i]), ul->cmd[i]);
371 /* cmd |= cmd << 8; */
373 if (off == CMD_ADDR) {
374 switch (flashutl_cmd->type) {
375 case AMD:
376 off = AMD_CMD;
377 break;
378 case SST:
379 off = SST_CMD;
380 break;
381 default:
382 off = 0;
383 break;
387 #ifdef MIPSEB
388 off ^= 2;
389 #endif /* MIPSEB */
391 flash_writeword(FLASH_ADDR(off), cmd);
394 static void
395 flash_reset()
397 ASSERT(flashutl_desc != NULL);
399 if (flashutl_cmd->clear_csr)
400 scmd(flashutl_cmd->clear_csr, 0);
401 if (flashutl_cmd->read_array)
402 scmd(flashutl_cmd->read_array, 0);
405 static int
406 flash_poll(unsigned long off, uint16 data)
408 unsigned long addr;
409 int cnt = FLASH_TRIES;
410 uint16 st;
412 ASSERT(flashutl_desc != NULL);
414 if (flashutl_desc->type == AMD || flashutl_desc->type == SST) {
415 /* AMD style poll checkes the address being written */
416 addr = FLASH_ADDR(off);
417 while ((st = flash_readword(addr)) != data && cnt != 0)
418 cnt--;
419 if (cnt == 0) {
420 DPRINT(("flash_poll: timeout, off %lx, read 0x%x, expected 0x%x\n",
421 off, st, data));
422 return -1;
424 } else {
425 /* INTEL style poll is at second word of the block being written */
426 addr = FLASH_ADDR(block(off, BLOCK_BASE)+sizeof(uint16));
427 while (((st = flash_readword(addr)) & DONE) == 0 && cnt != 0)
428 cnt--;
429 if (cnt == 0) {
430 DPRINT(("flash_poll: timeout, error status = 0x%x\n", st));
431 return -1;
435 return 0;
438 static unsigned long
439 block(unsigned long addr, int which)
441 unsigned long b, l, sb;
442 uint* sblocks;
443 int i;
445 ASSERT(flashutl_desc != NULL);
446 ASSERT(addr < (unsigned long)flashutl_desc->size);
448 b = addr / flashutl_desc->bsize;
449 /* check for an address a full size block */
450 if (b >= flashutl_desc->ff && b <= flashutl_desc->lf) {
451 if (which == BLOCK_LIM) b++;
452 return (b * flashutl_desc->bsize);
455 /* search for the sub-block */
456 if (flashutl_desc->ff == 0) {
457 /* sub blocks are at the end of the flash */
458 sb = flashutl_desc->bsize * (flashutl_desc->lf + 1);
459 } else {
460 /* sub blocks are at the start of the flash */
461 sb = 0;
464 sblocks = flashutl_desc->subblocks;
465 for (i = 0; i < flashutl_desc->nsub; i++) {
466 b = sb + sblocks[i];
467 l = sb + sblocks[i+1];
468 if (addr >= b && addr < l) {
469 if (which == BLOCK_BASE)
470 return b;
471 else
472 return l;
476 return 0;
479 void
480 nvWrite(unsigned short *data, unsigned int len)
482 uint off = flashutl_desc->size - NVRAM_SPACE;
483 sysFlashWrite(off, (uchar*)data, len);
486 void
487 nvWriteChars(unsigned char *data, unsigned int len)
489 uint off = flashutl_desc->size - NVRAM_SPACE;
490 int err;
492 if (flashutl_cmd->type == SFLASH)
493 err = sflash_commit(sbh, cc, off, len, data);
494 else /* PFLASH */
495 err = flash_write(off, data, len);
497 if (err)
498 DPRINT(("nvWriteChars failed\n"));
499 else
500 DPRINT(("nvWriteChars succeeded\n"));
504 sysFlashErase(uint off, unsigned int numbytes)
506 unsigned long end = off + numbytes;
507 int err = 0;
509 if (flashutl_cmd->type == SFLASH) {
510 err = sflash_commit(sbh, cc, off, numbytes, NULL);
511 } else {
512 while (off < end) {
513 err = flash_eraseblk(off);
514 if (err)
515 break;
516 off = block(off, BLOCK_LIM);
520 if (err)
521 DPRINT(("Block erase at 0x%x failed\n", off));
522 else
523 DPRINT(("Done\n"));
525 return !err;
529 sysFlashWrite(uint off, uchar *src, uint numbytes)
531 int err;
533 DPRINT(("Writing 0x%x bytes to flash @0x%x ...\n", (unsigned int)numbytes, off));
535 if (flashutl_cmd->type == SFLASH)
536 err = sflash_commit(sbh, cc, off, numbytes, src);
537 else {
538 if (!sysFlashErase(off, numbytes))
539 return 0;
540 err = flash_write(off, src, numbytes);
543 if (err)
544 DPRINT(("Flash write failed\n"));
545 else
546 DPRINT(("Flash write succeeded\n"));
548 return !err;
551 int
552 sysFlashRead(uint off, uchar *buf, uint numbytes)
554 uint read, total_read = 0;
556 if (flashutl_cmd->type == SFLASH) {
557 while (numbytes) {
558 read = sflash_read(sbh, cc, off, numbytes, buf);
559 numbytes -= read;
560 buf += read;
561 off += read;
562 total_read += read;
564 } else {
565 ASSERT(!(off & (flashutl_wsz - 1)));
566 ASSERT(!(numbytes & (flashutl_wsz - 1)));
568 while (numbytes) {
569 flash_writeword((unsigned long)buf, flash_readword(FLASH_ADDR(off)));
570 numbytes -= flashutl_wsz;
571 buf += flashutl_wsz;
572 off += flashutl_wsz;
573 total_read += flashutl_wsz;
577 return (total_read);