Broadcom SDK and wireless driver: another attempt to update to ver. 5.10.147.0
[tomato.git] / release / src-rt / shared / flashutl.c
blob5ba8eebfcbe6693cbd4a52424f821534ce29fb0c
1 /*
2 * flashutl.c - Flash Read/write/Erase routines
4 * Copyright (C) 2009, 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.46.2.1 2009/09/11 20:23:52 Exp $
15 #include <typedefs.h>
16 #include <osl.h>
18 #define DECLARE_FLASHES
19 #include <bcmutils.h>
20 #include <siutils.h>
21 #include <hndsoc.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 si_t *sih = 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 sih = si_kattach(SI_OSH);
78 ASSERT(sih);
80 osh = si_osh(sih);
82 cc = (chipcregs_t *)si_setcoreidx(sih, SI_CC_IDX);
83 ASSERT(cc);
85 flashutl_base = (void *)OSL_UNCACHED((uintptr)SI_FLASH2);
86 /* Select SFLASH ? */
87 fltype = R_REG(osh, &cc->capabilities) & CC_CAP_FLASH_MASK;
88 if (fltype == SFLASH_ST || fltype == SFLASH_AT) {
89 if (sih->ccrev == 12)
90 flashutl_base = (void *)OSL_UNCACHED((uintptr)SI_FLASH2);
91 else
92 flashutl_base = (void *)OSL_CACHED((uintptr)SI_FLASH2);
93 sflash = sflash_init(sih, cc);
94 flashutl_cmd = &sflash_cmd_t;
95 flashutl_desc = &sflash_desc;
96 flashutl_desc->size = sflash->size;
97 if (flash_str)
98 sprintf(flash_str, "SFLASH %d kB", sflash->size/1024);
99 return (0);
102 flashutl_wsz = (R_REG(osh, &cc->flash_config) & CC_CFG_DS) ? sizeof(uint16) : sizeof(uint8);
103 ASSERT(flashutl_wsz == sizeof(uint8) || flashutl_wsz == sizeof(uint16));
106 * Parallel flash support
107 * Some flashes have different unlock addresses, try each it turn
109 for (idx = 0;
110 fltype == PFLASH && idx < ARRAYSIZE(flash_cmds);
111 idx ++) {
112 flashutl_cmd = &flash_cmds[idx];
113 if (flashutl_cmd->type == OLD)
114 continue;
116 if (flashutl_cmd->read_id) {
117 cmd(flashutl_cmd->read_id, CMD_ADDR);
118 /* Delay for turn around time */
119 OSL_DELAY(1);
122 #ifdef MIPSEB
123 #ifdef BCMHND74K
124 flash_vendid = flash_readword(FLASH_ADDR(0)^6);
125 flash_devid = flash_readword(FLASH_ADDR(2)^6);
126 #else /* !74K, bcm33xx */
127 flash_vendid = flash_readword(FLASH_ADDR(2));
128 flash_devid = flash_readword(FLASH_ADDR(0));
129 #endif /* BCMHND74K */
130 #else
131 flash_vendid = flash_readword(FLASH_ADDR(0));
132 flash_devid = flash_readword(FLASH_ADDR(2));
133 #endif /* MIPSEB */
135 /* Funky AMD, uses 3 byte device ID so use first byte (4th addr) to
136 * identify it is a 3-byte ID and use the next two bytes (5th & 6th addr)
137 * to form a word for unique identification of format xxyy, where
138 * xx = 5th addr and yy = 6th addr
140 if ((flash_vendid == 1) &&
141 ((flash_devid == 0x227e && flashutl_wsz == sizeof(uint16)) ||
142 (flash_devid == 0x7e && flashutl_wsz == sizeof(uint8)))) {
143 /* Get real devid */
144 uint16 flash_devid_5th;
145 #ifdef MIPSEB
146 #ifdef BCMHND74K
147 flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)^6) << 8;
148 flash_devid = (flash_readword(FLASH_ADDR(0x1e)^6) & 0xff) | flash_devid_5th;
149 #else /* !74K, bcm33xx */
150 flash_devid_5th = flash_readword(FLASH_ADDR(0x1e)) << 8;
151 flash_devid = (flash_readword(FLASH_ADDR(0x1c)) & 0xff) | flash_devid_5th;
152 #endif /* BCMHND74K */
153 #else
154 flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)) << 8;
155 flash_devid = (flash_readword(FLASH_ADDR(0x1e)) & 0xff) | flash_devid_5th;
156 #endif /* MIPSEB */
159 flashutl_desc = flashes;
160 while (flashutl_desc->mfgid != 0 &&
161 !(flashutl_desc->mfgid == flash_vendid &&
162 flashutl_desc->devid == flash_devid)) {
163 flashutl_desc++;
165 if (flashutl_desc->mfgid != 0)
166 break;
169 if (flashutl_desc->mfgid == 0) {
170 flashutl_desc = NULL;
171 flashutl_cmd = NULL;
172 } else {
173 flashutl_cmd = flash_cmds;
174 while (flashutl_cmd->type != 0 && flashutl_cmd->type != flashutl_desc->type)
175 flashutl_cmd++;
176 if (flashutl_cmd->type == 0)
177 flashutl_cmd = NULL;
180 if (flashutl_cmd != NULL) {
181 flash_reset();
184 if (flashutl_desc == NULL) {
185 if (flash_str)
186 sprintf(flash_str, "UNKNOWN 0x%x 0x%x", flash_vendid, flash_devid);
187 DPRINT(("Flash type UNKNOWN\n"));
188 return 1;
191 if (flash_str)
192 strcpy(flash_str, flashutl_desc->desc);
193 DPRINT(("Flash type \"%s\"\n", flashutl_desc->desc));
195 return 0;
198 static int
199 flash_eraseblk(unsigned long addr)
201 unsigned long a;
202 uint16 st;
204 a = (unsigned long)addr;
205 if (a >= flashutl_desc->size)
206 return 1;
208 a = block(a, BLOCK_BASE);
210 /* Ensure blocks are unlocked (for intel chips) */
211 if (flashutl_cmd->type == BSC) {
212 scmd((unsigned char)INTEL_UNLOCK1, a);
213 scmd((unsigned char)INTEL_UNLOCK2, a);
216 if (flashutl_cmd->pre_erase)
217 cmd(flashutl_cmd->pre_erase, CMD_ADDR);
218 if (flashutl_cmd->erase_block)
219 cmd(flashutl_cmd->erase_block, a);
220 if (flashutl_cmd->confirm)
221 scmd(flashutl_cmd->confirm, a);
223 if (flashutl_wsz == sizeof(uint8))
224 st = flash_poll(a, 0xff);
225 else
226 st = flash_poll(a, 0xffff);
228 flash_reset();
230 if (st) {
231 DPRINT(("Erase of block 0x%08lx-0x%08lx failed\n",
232 a, block((unsigned long)addr, BLOCK_LIM)));
233 return st;
236 DPRINT(("Erase of block 0x%08lx-0x%08lx done\n", a, block((unsigned long)addr, BLOCK_LIM)));
238 return 0;
241 static int
242 flash_write(unsigned long off, uint8 *src, uint nbytes)
244 uint8 *dest;
245 uint16 st, data;
246 uint i, len;
248 ASSERT(flashutl_desc != NULL);
250 if (off >= flashutl_desc->size)
251 return 1;
253 ASSERT(!(off & (flashutl_wsz - 1)));
255 dest = (uint8*)FLASH_ADDR(off);
256 st = 0;
258 while (nbytes) {
259 if ((flashutl_desc->type == SCS) &&
260 flashutl_cmd->write_buf &&
261 ((off & (WBUFSIZE - 1)) == 0)) {
262 /* issue write command */
263 if (flashutl_cmd->write_buf)
264 cmd(flashutl_cmd->write_buf, off);
265 if ((st = flash_poll(off, DONE)))
266 continue;
268 len = MIN(nbytes, WBUFSIZE);
270 #ifndef MIPSEB
271 /* write (length - 1) */
272 cmd(len / sizeof(uint16) - 1, off);
274 /* write data */
275 for (i = 0; i < len; i += sizeof(uint16),
276 dest += sizeof(uint16), src += sizeof(uint16))
277 *(uint16 *)dest = *(uint16 *)src;
278 #else
280 * BCM4710 endianness is word consistent but
281 * byte/short scrambled. This write buffer
282 * mechanism appears to be sensitive to the
283 * order of the addresses hence we need to
284 * unscramble them. We may also need to pad
285 * the source with two bytes of 0xffff in case
286 * an odd number of shorts are presented.
289 /* write (padded length - 1) */
290 cmd((ROUNDUP(len, sizeof(uint32)) / sizeof(uint16)) - 1, off);
292 /* write data (plus pad if necessary) */
293 for (i = 0; i < ROUNDUP(len, sizeof(uint32)); i += sizeof(uint32),
294 dest += sizeof(uint32), src += sizeof(uint32)) {
295 *((uint16 *)dest + 1) = ((i + sizeof(uint16)) < len) ?
296 *((uint16 *)src + 1) : 0xffff;
297 *(uint16 *)dest = *(uint16 *)src;
299 #endif /* MIPSEB */
301 /* write confirm */
302 if (flashutl_cmd->confirm)
303 cmd(flashutl_cmd->confirm, off);
305 if ((st = flash_poll(off, DONE)))
306 break;
307 } else {
308 /* issue write command */
309 if (flashutl_cmd->write_word)
310 cmd(flashutl_cmd->write_word, CMD_ADDR);
312 /* write data */
313 data = flash_readword((unsigned long)src);
314 flash_writeword((unsigned long)dest, data);
316 /* poll for done */
317 if ((st = flash_poll(off, data)))
318 break;
320 len = MIN(nbytes, flashutl_wsz);
321 dest += len;
322 src += len;
325 nbytes -= len;
326 off += len;
329 flash_reset();
331 return st;
334 static uint16 INLINE
335 flash_readword(unsigned long addr)
337 if (flashutl_wsz == sizeof(uint8))
338 return *(uint8*)addr;
339 else
340 return *(uint16*)addr;
343 static void INLINE
344 flash_writeword(unsigned long addr, uint16 data)
346 if (flashutl_wsz == sizeof(uint8))
347 *(uint8*)addr = (uint8)data;
348 else
349 *(uint16*)addr = data;
352 /* Writes a single command to the flash. */
353 static void
354 scmd(uint16 cmd, unsigned long off)
356 /* cmd |= cmd << 8; */
358 flash_writeword(FLASH_ADDR(off), cmd);
361 /* Writes a command to flash, performing an unlock if needed. */
362 static void
363 cmd(uint16 cmd, unsigned long off)
365 int i;
366 unlock_cmd_t *ul = NULL;
368 ASSERT(flashutl_cmd != NULL);
370 switch (flashutl_cmd->type) {
371 case AMD:
372 ul = &unlock_cmd_amd;
373 break;
374 case SST:
375 ul = &unlock_cmd_sst;
376 break;
377 default:
378 break;
381 if (flashutl_cmd->need_unlock) {
382 ASSERT(ul);
383 for (i = 0; i < UNLOCK_CMD_WORDS; i++)
384 flash_writeword(FLASH_ADDR(ul->addr[i]), ul->cmd[i]);
387 /* cmd |= cmd << 8; */
389 if (off == CMD_ADDR) {
390 switch (flashutl_cmd->type) {
391 case AMD:
392 off = AMD_CMD;
393 break;
394 case SST:
395 off = SST_CMD;
396 break;
397 default:
398 off = 0;
399 break;
403 #ifdef MIPSEB
404 #ifdef BCMHND74K
405 off ^= 6;
406 #else /* !74K, bcm33xx */
407 off ^= 2;
408 #endif /* BCMHND74K */
409 #endif /* MIPSEB */
411 flash_writeword(FLASH_ADDR(off), cmd);
414 static void
415 flash_reset()
417 ASSERT(flashutl_cmd != NULL);
419 if (flashutl_cmd->clear_csr)
420 scmd(flashutl_cmd->clear_csr, 0);
421 if (flashutl_cmd->read_array)
422 scmd(flashutl_cmd->read_array, 0);
425 static int
426 flash_poll(unsigned long off, uint16 data)
428 unsigned long addr;
429 int cnt = FLASH_TRIES;
430 uint16 st;
432 ASSERT(flashutl_desc != NULL);
434 if (flashutl_desc->type == AMD || flashutl_desc->type == SST) {
435 /* AMD style poll checkes the address being written */
436 addr = FLASH_ADDR(off);
437 while ((st = flash_readword(addr)) != data && cnt != 0) {
438 OSL_DELAY(10);
439 cnt--;
441 if (cnt == 0) {
442 DPRINT(("flash_poll: timeout, off %lx, read 0x%x, expected 0x%x\n",
443 off, st, data));
444 return -1;
446 } else {
447 /* INTEL style poll is at second word of the block being written */
448 addr = FLASH_ADDR(block(off, BLOCK_BASE)+sizeof(uint16));
449 while (((st = flash_readword(addr)) & DONE) == 0 && cnt != 0) {
450 OSL_DELAY(10);
451 cnt--;
453 if (cnt == 0) {
454 DPRINT(("flash_poll: timeout, error status = 0x%x\n", st));
455 return -1;
459 return 0;
462 static unsigned long
463 block(unsigned long addr, int which)
465 unsigned long b, l, sb;
466 uint* sblocks;
467 int i;
469 ASSERT(flashutl_desc != NULL);
471 ASSERT(addr < (unsigned long)flashutl_desc->size);
473 b = addr / flashutl_desc->bsize;
474 /* check for an address a full size block */
475 if (b >= flashutl_desc->ff && b <= flashutl_desc->lf) {
476 if (which == BLOCK_LIM) b++;
477 return (b * flashutl_desc->bsize);
480 /* search for the sub-block */
481 if (flashutl_desc->ff == 0) {
482 /* sub blocks are at the end of the flash */
483 sb = flashutl_desc->bsize * (flashutl_desc->lf + 1);
484 } else {
485 /* sub blocks are at the start of the flash */
486 sb = 0;
489 sblocks = flashutl_desc->subblocks;
490 for (i = 0; i < flashutl_desc->nsub; i++) {
491 b = sb + sblocks[i];
492 l = sb + sblocks[i+1];
493 if (addr >= b && addr < l) {
494 if (which == BLOCK_BASE)
495 return b;
496 else
497 return l;
501 return 0;
504 void
505 nvWrite(unsigned short *data, unsigned int len)
507 uint off = flashutl_desc->size - NVRAM_SPACE;
508 sysFlashWrite(off, (uchar*)data, len);
511 void
512 nvWriteChars(unsigned char *data, unsigned int len)
514 uint off = flashutl_desc->size - NVRAM_SPACE;
515 int err;
517 if (flashutl_cmd->type == SFLASH)
518 err = sflash_commit(sih, cc, off, len, data);
519 else /* PFLASH */
520 err = flash_write(off, data, len);
522 if (err)
523 DPRINT(("nvWriteChars failed\n"));
524 else
525 DPRINT(("nvWriteChars succeeded\n"));
529 sysFlashErase(uint off, unsigned int numbytes)
531 unsigned long end = off + numbytes;
532 int err = 0;
534 if (flashutl_cmd->type == SFLASH) {
535 err = sflash_commit(sih, cc, off, numbytes, NULL);
536 } else {
537 while (off < end) {
538 err = flash_eraseblk(off);
539 if (err)
540 break;
541 off = block(off, BLOCK_LIM);
545 if (err)
546 DPRINT(("Block erase at 0x%x failed\n", off));
547 else
548 DPRINT(("Done\n"));
550 return !err;
554 sysFlashWrite(uint off, uchar *src, uint numbytes)
556 int err;
558 DPRINT(("Writing 0x%x bytes to flash @0x%x ...\n", (unsigned int)numbytes, off));
560 if (flashutl_cmd->type == SFLASH)
561 err = sflash_commit(sih, cc, off, numbytes, src);
562 else {
563 if (!sysFlashErase(off, numbytes))
564 return 0;
565 err = flash_write(off, src, numbytes);
568 if (err)
569 DPRINT(("Flash write failed\n"));
570 else
571 DPRINT(("Flash write succeeded\n"));
573 return !err;
577 sysFlashRead(uint off, uchar *buf, uint numbytes)
579 uint read, total_read = 0;
581 if (flashutl_cmd->type == SFLASH) {
582 while (numbytes) {
583 read = sflash_read(sih, cc, off, numbytes, buf);
584 numbytes -= read;
585 buf += read;
586 off += read;
587 total_read += read;
589 } else {
590 ASSERT(!(off & (flashutl_wsz - 1)));
591 ASSERT(!(numbytes & (flashutl_wsz - 1)));
593 while (numbytes) {
594 flash_writeword((unsigned long)buf, flash_readword(FLASH_ADDR(off)));
595 numbytes -= flashutl_wsz;
596 buf += flashutl_wsz;
597 off += flashutl_wsz;
598 total_read += flashutl_wsz;
602 return (total_read);