Resync with broadcom drivers 5.100.138.20 and utilities.
[tomato.git] / release / src-rt / shared / flashutl.c
bloba2253ca424758c95b747b194cc3183135108a522
1 /*
2 * flashutl.c - Flash Read/write/Erase routines
4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * $Id: flashutl.c,v 1.52 2009-05-19 02:06:58 Exp $
21 #include <typedefs.h>
22 #include <osl.h>
24 #define DECLARE_FLASHES
25 #include <bcmutils.h>
26 #include <siutils.h>
27 #include <hndsoc.h>
28 #include <flash.h>
29 #include <sflash.h>
30 #include <flashutl.h>
31 #include <bcmnvram.h>
33 #define DPRINT(x)
35 #define ERR2 0x30 /* Mask for err UNUSED */
36 #define DONE 0x80 /* Mask for done */
37 #define WBUFSIZE 32 /* Write Buffer size */
38 #define FLASH_TRIES 700000 /* retry count */
39 #define CMD_ADDR ((unsigned long)0xFFFFFFFF)
41 /* 'which' param for block() */
42 #define BLOCK_BASE 0 /* Base of block */
43 #define BLOCK_LIM 1 /* Limit of block */
45 #define FLASH_ADDR(off) ((unsigned long)flashutl_base + (off))
47 /* Local vars */
48 static si_t *sih = NULL;
49 static chipcregs_t *cc = NULL;
51 /* Global vars */
52 uint8 *flashutl_base = NULL;
53 flash_desc_t *flashutl_desc = NULL;
54 flash_cmds_t *flashutl_cmd = NULL;
55 uint8 flashutl_wsz = sizeof(uint16);
57 static void scmd(uint16 cmd, unsigned long off);
58 static void cmd(uint16 cmd, unsigned long off);
59 static void flash_reset(void);
60 static int flash_poll(unsigned long off, uint16 data);
61 static unsigned long block(unsigned long addr, int which);
62 static int flash_eraseblk(unsigned long off);
63 static int flash_write(unsigned long off, uint8 *src, uint nbytes);
64 static uint16 INLINE flash_readword(unsigned long addr);
65 static void INLINE flash_writeword(unsigned long addr, uint16 data);
67 int sysFlashErase(uint off, unsigned int numbytes);
69 /* Read the flash ID and set the globals */
70 int
71 sysFlashInit(char *flash_str)
73 osl_t *osh;
74 uint32 fltype = PFLASH;
75 uint16 flash_vendid = 0;
76 uint16 flash_devid = 0;
77 int idx;
78 struct sflash *sflash;
81 * Check for serial flash.
83 sih = si_kattach(SI_OSH);
84 ASSERT(sih);
86 osh = si_osh(sih);
88 cc = (chipcregs_t *)si_setcoreidx(sih, SI_CC_IDX);
89 ASSERT(cc);
91 flashutl_base = (void *)OSL_UNCACHED((uintptr)SI_FLASH2);
92 /* Select SFLASH ? */
93 fltype = R_REG(osh, &cc->capabilities) & CC_CAP_FLASH_MASK;
94 if (fltype == SFLASH_ST || fltype == SFLASH_AT) {
95 if (sih->ccrev == 12)
96 flashutl_base = (void *)OSL_UNCACHED((uintptr)SI_FLASH2);
97 else
98 flashutl_base = (void *)OSL_CACHED((uintptr)SI_FLASH2);
99 sflash = sflash_init(sih, cc);
100 flashutl_cmd = &sflash_cmd_t;
101 flashutl_desc = &sflash_desc;
102 flashutl_desc->size = sflash->size;
103 if (flash_str)
104 sprintf(flash_str, "SFLASH %d kB", sflash->size/1024);
105 return (0);
108 flashutl_wsz = (R_REG(osh, &cc->flash_config) & CC_CFG_DS) ? sizeof(uint16) : sizeof(uint8);
109 ASSERT(flashutl_wsz == sizeof(uint8) || flashutl_wsz == sizeof(uint16));
112 * Parallel flash support
113 * Some flashes have different unlock addresses, try each it turn
115 for (idx = 0;
116 fltype == PFLASH && idx < ARRAYSIZE(flash_cmds);
117 idx ++) {
118 flashutl_cmd = &flash_cmds[idx];
119 if (flashutl_cmd->type == OLD)
120 continue;
122 if (flashutl_cmd->read_id) {
123 cmd(flashutl_cmd->read_id, CMD_ADDR);
124 /* Delay for turn around time */
125 OSL_DELAY(1);
128 #ifdef MIPSEB
129 #ifdef BCMHND74K
130 flash_vendid = flash_readword(FLASH_ADDR(0)^6);
131 flash_devid = flash_readword(FLASH_ADDR(2)^6);
132 #else /* !74K, bcm33xx */
133 flash_vendid = flash_readword(FLASH_ADDR(2));
134 flash_devid = flash_readword(FLASH_ADDR(0));
135 #endif /* BCMHND74K */
136 #else
137 flash_vendid = flash_readword(FLASH_ADDR(0));
138 flash_devid = flash_readword(FLASH_ADDR(2));
139 #endif /* MIPSEB */
141 /* Funky AMD, uses 3 byte device ID so use first byte (4th addr) to
142 * identify it is a 3-byte ID and use the next two bytes (5th & 6th addr)
143 * to form a word for unique identification of format xxyy, where
144 * xx = 5th addr and yy = 6th addr
146 if ((flash_vendid == 1) &&
147 ((flash_devid == 0x227e && flashutl_wsz == sizeof(uint16)) ||
148 (flash_devid == 0x7e && flashutl_wsz == sizeof(uint8)))) {
149 /* Get real devid */
150 uint16 flash_devid_5th;
151 #ifdef MIPSEB
152 #ifdef BCMHND74K
153 flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)^6) << 8;
154 flash_devid = (flash_readword(FLASH_ADDR(0x1e)^6) & 0xff) | flash_devid_5th;
155 #else /* !74K, bcm33xx */
156 flash_devid_5th = flash_readword(FLASH_ADDR(0x1e)) << 8;
157 flash_devid = (flash_readword(FLASH_ADDR(0x1c)) & 0xff) | flash_devid_5th;
158 #endif /* BCMHND74K */
159 #else
160 flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)) << 8;
161 flash_devid = (flash_readword(FLASH_ADDR(0x1e)) & 0xff) | flash_devid_5th;
162 #endif /* MIPSEB */
165 flashutl_desc = flashes;
166 while (flashutl_desc->mfgid != 0 &&
167 !(flashutl_desc->mfgid == flash_vendid &&
168 flashutl_desc->devid == flash_devid)) {
169 flashutl_desc++;
171 if (flashutl_desc->mfgid != 0)
172 break;
175 if (flashutl_desc->mfgid == 0) {
176 flashutl_desc = NULL;
177 flashutl_cmd = NULL;
178 } else {
179 flashutl_cmd = flash_cmds;
180 while (flashutl_cmd->type != 0 && flashutl_cmd->type != flashutl_desc->type)
181 flashutl_cmd++;
182 if (flashutl_cmd->type == 0)
183 flashutl_cmd = NULL;
186 if (flashutl_cmd != NULL) {
187 flash_reset();
190 if (flashutl_desc == NULL) {
191 if (flash_str)
192 sprintf(flash_str, "UNKNOWN 0x%x 0x%x", flash_vendid, flash_devid);
193 DPRINT(("Flash type UNKNOWN\n"));
194 return 1;
197 if (flash_str)
198 strcpy(flash_str, flashutl_desc->desc);
199 DPRINT(("Flash type \"%s\"\n", flashutl_desc->desc));
201 return 0;
204 static int
205 flash_eraseblk(unsigned long addr)
207 unsigned long a;
208 uint16 st;
210 a = (unsigned long)addr;
211 if (a >= flashutl_desc->size)
212 return 1;
214 a = block(a, BLOCK_BASE);
216 /* Ensure blocks are unlocked (for intel chips) */
217 if (flashutl_cmd->type == BSC) {
218 scmd((unsigned char)INTEL_UNLOCK1, a);
219 scmd((unsigned char)INTEL_UNLOCK2, a);
222 if (flashutl_cmd->pre_erase)
223 cmd(flashutl_cmd->pre_erase, CMD_ADDR);
224 if (flashutl_cmd->erase_block)
225 cmd(flashutl_cmd->erase_block, a);
226 if (flashutl_cmd->confirm)
227 scmd(flashutl_cmd->confirm, a);
229 if (flashutl_wsz == sizeof(uint8))
230 st = flash_poll(a, 0xff);
231 else
232 st = flash_poll(a, 0xffff);
234 flash_reset();
236 if (st) {
237 DPRINT(("Erase of block 0x%08lx-0x%08lx failed\n",
238 a, block((unsigned long)addr, BLOCK_LIM)));
239 return st;
242 DPRINT(("Erase of block 0x%08lx-0x%08lx done\n", a, block((unsigned long)addr, BLOCK_LIM)));
244 return 0;
247 static int
248 flash_write(unsigned long off, uint8 *src, uint nbytes)
250 uint8 *dest;
251 uint16 st, data;
252 uint i, len;
254 ASSERT(flashutl_desc != NULL);
256 if (off >= flashutl_desc->size)
257 return 1;
259 ASSERT(!(off & (flashutl_wsz - 1)));
261 dest = (uint8*)FLASH_ADDR(off);
262 st = 0;
264 while (nbytes) {
265 if ((flashutl_desc->type == SCS) &&
266 flashutl_cmd->write_buf &&
267 ((off & (WBUFSIZE - 1)) == 0)) {
268 /* issue write command */
269 if (flashutl_cmd->write_buf)
270 cmd(flashutl_cmd->write_buf, off);
271 if ((st = flash_poll(off, DONE)))
272 continue;
274 len = MIN(nbytes, WBUFSIZE);
276 #ifndef MIPSEB
277 /* write (length - 1) */
278 cmd(len / sizeof(uint16) - 1, off);
280 /* write data */
281 for (i = 0; i < len; i += sizeof(uint16),
282 dest += sizeof(uint16), src += sizeof(uint16))
283 *(uint16 *)dest = *(uint16 *)src;
284 #else
286 * BCM4710 endianness is word consistent but
287 * byte/short scrambled. This write buffer
288 * mechanism appears to be sensitive to the
289 * order of the addresses hence we need to
290 * unscramble them. We may also need to pad
291 * the source with two bytes of 0xffff in case
292 * an odd number of shorts are presented.
295 /* write (padded length - 1) */
296 cmd((ROUNDUP(len, sizeof(uint32)) / sizeof(uint16)) - 1, off);
298 /* write data (plus pad if necessary) */
299 for (i = 0; i < ROUNDUP(len, sizeof(uint32)); i += sizeof(uint32),
300 dest += sizeof(uint32), src += sizeof(uint32)) {
301 *((uint16 *)dest + 1) = ((i + sizeof(uint16)) < len) ?
302 *((uint16 *)src + 1) : 0xffff;
303 *(uint16 *)dest = *(uint16 *)src;
305 #endif /* MIPSEB */
307 /* write confirm */
308 if (flashutl_cmd->confirm)
309 cmd(flashutl_cmd->confirm, off);
311 if ((st = flash_poll(off, DONE)))
312 break;
313 } else {
314 /* issue write command */
315 if (flashutl_cmd->write_word)
316 cmd(flashutl_cmd->write_word, CMD_ADDR);
318 /* write data */
319 data = flash_readword((unsigned long)src);
320 flash_writeword((unsigned long)dest, data);
322 /* poll for done */
323 if ((st = flash_poll(off, data)))
324 break;
326 len = MIN(nbytes, flashutl_wsz);
327 dest += len;
328 src += len;
331 nbytes -= len;
332 off += len;
335 flash_reset();
337 return st;
340 static uint16 INLINE
341 flash_readword(unsigned long addr)
343 if (flashutl_wsz == sizeof(uint8))
344 return *(uint8*)addr;
345 else
346 return *(uint16*)addr;
349 static void INLINE
350 flash_writeword(unsigned long addr, uint16 data)
352 if (flashutl_wsz == sizeof(uint8))
353 *(uint8*)addr = (uint8)data;
354 else
355 *(uint16*)addr = data;
358 /* Writes a single command to the flash. */
359 static void
360 scmd(uint16 cmd, unsigned long off)
362 /* cmd |= cmd << 8; */
364 flash_writeword(FLASH_ADDR(off), cmd);
367 /* Writes a command to flash, performing an unlock if needed. */
368 static void
369 cmd(uint16 cmd, unsigned long off)
371 int i;
372 unlock_cmd_t *ul = NULL;
374 ASSERT(flashutl_cmd != NULL);
376 switch (flashutl_cmd->type) {
377 case AMD:
378 ul = &unlock_cmd_amd;
379 break;
380 case SST:
381 ul = &unlock_cmd_sst;
382 break;
383 default:
384 break;
387 if (flashutl_cmd->need_unlock) {
388 ASSERT(ul);
389 for (i = 0; i < UNLOCK_CMD_WORDS; i++)
390 flash_writeword(FLASH_ADDR(ul->addr[i]), ul->cmd[i]);
393 /* cmd |= cmd << 8; */
395 if (off == CMD_ADDR) {
396 switch (flashutl_cmd->type) {
397 case AMD:
398 off = AMD_CMD;
399 break;
400 case SST:
401 off = SST_CMD;
402 break;
403 default:
404 off = 0;
405 break;
409 #ifdef MIPSEB
410 #ifdef BCMHND74K
411 off ^= 6;
412 #else /* !74K, bcm33xx */
413 off ^= 2;
414 #endif /* BCMHND74K */
415 #endif /* MIPSEB */
417 flash_writeword(FLASH_ADDR(off), cmd);
420 static void
421 flash_reset()
423 ASSERT(flashutl_cmd != NULL);
425 if (flashutl_cmd->clear_csr)
426 scmd(flashutl_cmd->clear_csr, 0);
427 if (flashutl_cmd->read_array)
428 scmd(flashutl_cmd->read_array, 0);
431 static int
432 flash_poll(unsigned long off, uint16 data)
434 unsigned long addr;
435 int cnt = FLASH_TRIES;
436 uint16 st;
438 ASSERT(flashutl_desc != NULL);
440 if (flashutl_desc->type == AMD || flashutl_desc->type == SST) {
441 /* AMD style poll checkes the address being written */
442 addr = FLASH_ADDR(off);
443 while ((st = flash_readword(addr)) != data && cnt != 0) {
444 OSL_DELAY(10);
445 cnt--;
447 if (cnt == 0) {
448 DPRINT(("%s: timeout, off %lx, read 0x%x, expected 0x%x\n",
449 __FUNCTION__, off, st, data));
450 return -1;
452 } else {
453 /* INTEL style poll is at second word of the block being written */
454 addr = FLASH_ADDR(block(off, BLOCK_BASE)+sizeof(uint16));
455 while (((st = flash_readword(addr)) & DONE) == 0 && cnt != 0) {
456 OSL_DELAY(10);
457 cnt--;
459 if (cnt == 0) {
460 DPRINT(("%s: timeout, error status = 0x%x\n", __FUNCTION__, st));
461 return -1;
465 return 0;
468 static unsigned long
469 block(unsigned long addr, int which)
471 unsigned long b, l, sb;
472 uint* sblocks;
473 int i;
475 ASSERT(flashutl_desc != NULL);
477 ASSERT(addr < (unsigned long)flashutl_desc->size);
479 b = addr / flashutl_desc->bsize;
480 /* check for an address a full size block */
481 if (b >= flashutl_desc->ff && b <= flashutl_desc->lf) {
482 if (which == BLOCK_LIM) b++;
483 return (b * flashutl_desc->bsize);
486 /* search for the sub-block */
487 if (flashutl_desc->ff == 0) {
488 /* sub blocks are at the end of the flash */
489 sb = flashutl_desc->bsize * (flashutl_desc->lf + 1);
490 } else {
491 /* sub blocks are at the start of the flash */
492 sb = 0;
495 sblocks = flashutl_desc->subblocks;
496 for (i = 0; i < flashutl_desc->nsub; i++) {
497 b = sb + sblocks[i];
498 l = sb + sblocks[i+1];
499 if (addr >= b && addr < l) {
500 if (which == BLOCK_BASE)
501 return b;
502 else
503 return l;
507 return 0;
510 void
511 nvWrite(unsigned short *data, unsigned int len)
513 uint off = flashutl_desc->size - NVRAM_SPACE;
514 sysFlashWrite(off, (uchar*)data, len);
517 void
518 nvWriteChars(unsigned char *data, unsigned int len)
520 uint off = flashutl_desc->size - NVRAM_SPACE;
521 int err;
523 if (flashutl_cmd->type == SFLASH)
524 err = sflash_commit(sih, cc, off, len, data);
525 else /* PFLASH */
526 err = flash_write(off, data, len);
528 if (err)
529 DPRINT(("nvWriteChars failed\n"));
530 else
531 DPRINT(("nvWriteChars succeeded\n"));
535 sysFlashErase(uint off, unsigned int numbytes)
537 unsigned long end = off + numbytes;
538 int err = 0;
540 if (flashutl_cmd->type == SFLASH) {
541 err = sflash_commit(sih, cc, off, numbytes, NULL);
542 } else {
543 while (off < end) {
544 err = flash_eraseblk(off);
545 if (err)
546 break;
547 off = block(off, BLOCK_LIM);
551 if (err)
552 DPRINT(("Block erase at 0x%x failed\n", off));
553 else
554 DPRINT(("Done\n"));
556 return !err;
560 sysFlashWrite(uint off, uchar *src, uint numbytes)
562 int err;
564 DPRINT(("Writing 0x%x bytes to flash @0x%x ...\n", (unsigned int)numbytes, off));
566 if (flashutl_cmd->type == SFLASH)
567 err = sflash_commit(sih, cc, off, numbytes, src);
568 else {
569 if (!sysFlashErase(off, numbytes))
570 return 0;
571 err = flash_write(off, src, numbytes);
574 if (err)
575 DPRINT(("Flash write failed\n"));
576 else
577 DPRINT(("Flash write succeeded\n"));
579 return !err;
583 sysFlashRead(uint off, uchar *buf, uint numbytes)
585 uint read, total_read = 0;
587 if (flashutl_cmd->type == SFLASH) {
588 while (numbytes) {
589 read = sflash_read(sih, cc, off, numbytes, buf);
590 numbytes -= read;
591 buf += read;
592 off += read;
593 total_read += read;
595 } else {
596 ASSERT(!(off & (flashutl_wsz - 1)));
597 ASSERT(!(numbytes & (flashutl_wsz - 1)));
599 while (numbytes) {
600 flash_writeword((unsigned long)buf, flash_readword(FLASH_ADDR(off)));
601 numbytes -= flashutl_wsz;
602 buf += flashutl_wsz;
603 off += flashutl_wsz;
604 total_read += flashutl_wsz;
608 return (total_read);