shared ARM updates
[tomato.git] / release / src-rt-6.x.4708 / shared / flashutl.c
blob682b1b0b9fbf22adee9571efdcd536f8a62b7a29
1 /*
2 * flashutl.c - Flash Read/write/Erase routines
4 * Copyright (C) 2013, 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 401759 2013-05-13 16:08:08Z $
21 #include <bcm_cfg.h>
22 #include <typedefs.h>
23 #include <osl.h>
25 #define DECLARE_FLASHES
26 #include <bcmutils.h>
27 #include <siutils.h>
28 #include <hndsoc.h>
29 #include <flash.h>
30 #include <sflash.h>
31 #include <flashutl.h>
32 #include <bcmnvram.h>
34 #define DPRINT(x)
36 #define ERR2 0x30 /* Mask for err UNUSED */
37 #define DONE 0x80 /* Mask for done */
38 #define WBUFSIZE 32 /* Write Buffer size */
39 #define FLASH_TRIES 700000 /* retry count */
40 #define CMD_ADDR ((unsigned long)0xFFFFFFFF)
42 /* 'which' param for block() */
43 #define BLOCK_BASE 0 /* Base of block */
44 #define BLOCK_LIM 1 /* Limit of block */
46 #define FLASH_ADDR(off) ((unsigned long)flashutl_base + (off))
48 /* Local vars */
49 static si_t *sih = NULL;
50 static chipcregs_t *cc = NULL;
52 /* Global vars */
53 uint8 *flashutl_base = NULL;
54 flash_desc_t *flashutl_desc = NULL;
55 flash_cmds_t *flashutl_cmd = NULL;
56 uint8 flashutl_wsz = sizeof(uint16);
58 static void scmd(uint16 cmd, unsigned long off);
59 static void cmd(uint16 cmd, unsigned long off);
60 static void flash_reset(void);
61 static int flash_poll(unsigned long off, uint16 data);
62 static unsigned long block(unsigned long addr, int which);
63 static int flash_eraseblk(unsigned long off);
64 static int flash_write(unsigned long off, uint8 *src, uint nbytes);
65 static uint16 INLINE flash_readword(unsigned long addr);
66 static void INLINE flash_writeword(unsigned long addr, uint16 data);
68 int sysFlashErase(uint off, unsigned int numbytes);
70 /* Read the flash ID and set the globals */
71 int
72 sysFlashInit(char *flash_str)
74 osl_t *osh;
75 uint32 fltype = PFLASH;
76 uint16 flash_vendid = 0;
77 uint16 flash_devid = 0;
78 int idx;
79 struct sflash *sflash;
82 * Check for serial flash.
84 sih = si_kattach(SI_OSH);
85 ASSERT(sih);
87 osh = si_osh(sih);
89 cc = (chipcregs_t *)si_setcoreidx(sih, SI_CC_IDX);
90 ASSERT(cc);
92 flashutl_base = (void *)OSL_UNCACHED((uintptr)SI_FLASH2);
93 /* Select SFLASH ? */
94 fltype = R_REG(osh, &cc->capabilities) & CC_CAP_FLASH_MASK;
95 if (fltype == SFLASH_ST || fltype == SFLASH_AT) {
96 if (sih->ccrev == 12)
97 flashutl_base = (void *)OSL_UNCACHED((uintptr)SI_FLASH2);
98 else
99 flashutl_base = (void *)OSL_CACHED((uintptr)SI_FLASH2);
100 sflash = sflash_init(sih, cc);
101 flashutl_cmd = &sflash_cmd_t;
102 flashutl_desc = &sflash_desc;
103 flashutl_desc->size = sflash->size;
104 if (flash_str)
105 sprintf(flash_str, "SFLASH %d kB", sflash->size/1024);
106 return (0);
109 flashutl_wsz = (R_REG(osh, &cc->flash_config) & CC_CFG_DS) ? sizeof(uint16) : sizeof(uint8);
110 ASSERT(flashutl_wsz == sizeof(uint8) || flashutl_wsz == sizeof(uint16));
113 * Parallel flash support
114 * Some flashes have different unlock addresses, try each it turn
116 for (idx = 0;
117 fltype == PFLASH && idx < ARRAYSIZE(flash_cmds);
118 idx ++) {
119 flashutl_cmd = &flash_cmds[idx];
120 if (flashutl_cmd->type == OLD)
121 continue;
123 if (flashutl_cmd->read_id) {
124 cmd(flashutl_cmd->read_id, CMD_ADDR);
125 /* Delay for turn around time */
126 OSL_DELAY(1);
129 #ifdef MIPSEB
130 #ifdef BCMHND74K
131 flash_vendid = flash_readword(FLASH_ADDR(0)^6);
132 flash_devid = flash_readword(FLASH_ADDR(2)^6);
133 #else /* !74K, bcm33xx */
134 flash_vendid = flash_readword(FLASH_ADDR(2));
135 flash_devid = flash_readword(FLASH_ADDR(0));
136 #endif /* BCMHND74K */
137 #else
138 flash_vendid = flash_readword(FLASH_ADDR(0));
139 flash_devid = flash_readword(FLASH_ADDR(2));
140 #endif /* MIPSEB */
142 /* Funky AMD, uses 3 byte device ID so use first byte (4th addr) to
143 * identify it is a 3-byte ID and use the next two bytes (5th & 6th addr)
144 * to form a word for unique identification of format xxyy, where
145 * xx = 5th addr and yy = 6th addr
147 if ((flash_vendid == 1) &&
148 ((flash_devid == 0x227e && flashutl_wsz == sizeof(uint16)) ||
149 (flash_devid == 0x7e && flashutl_wsz == sizeof(uint8)))) {
150 /* Get real devid */
151 uint16 flash_devid_5th;
152 #ifdef MIPSEB
153 #ifdef BCMHND74K
154 flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)^6) << 8;
155 flash_devid = (flash_readword(FLASH_ADDR(0x1e)^6) & 0xff) | flash_devid_5th;
156 #else /* !74K, bcm33xx */
157 flash_devid_5th = flash_readword(FLASH_ADDR(0x1e)) << 8;
158 flash_devid = (flash_readword(FLASH_ADDR(0x1c)) & 0xff) | flash_devid_5th;
159 #endif /* BCMHND74K */
160 #else
161 flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)) << 8;
162 flash_devid = (flash_readword(FLASH_ADDR(0x1e)) & 0xff) | flash_devid_5th;
163 #endif /* MIPSEB */
166 flashutl_desc = flashes;
167 while (flashutl_desc->mfgid != 0 &&
168 !(flashutl_desc->mfgid == flash_vendid &&
169 flashutl_desc->devid == flash_devid)) {
170 flashutl_desc++;
172 if (flashutl_desc->mfgid != 0)
173 break;
176 if (flashutl_desc->mfgid == 0) {
177 flashutl_desc = NULL;
178 flashutl_cmd = NULL;
179 } else {
180 flashutl_cmd = flash_cmds;
181 while (flashutl_cmd->type != 0 && flashutl_cmd->type != flashutl_desc->type)
182 flashutl_cmd++;
183 if (flashutl_cmd->type == 0)
184 flashutl_cmd = NULL;
187 if (flashutl_cmd != NULL) {
188 flash_reset();
191 if (flashutl_desc == NULL) {
192 if (flash_str)
193 sprintf(flash_str, "UNKNOWN 0x%x 0x%x", flash_vendid, flash_devid);
194 DPRINT(("Flash type UNKNOWN\n"));
195 return 1;
198 if (flash_str)
199 strcpy(flash_str, flashutl_desc->desc);
200 DPRINT(("Flash type \"%s\"\n", flashutl_desc->desc));
202 return 0;
205 static int
206 flash_eraseblk(unsigned long addr)
208 unsigned long a;
209 uint16 st;
211 a = (unsigned long)addr;
212 if (a >= flashutl_desc->size)
213 return 1;
215 a = block(a, BLOCK_BASE);
217 /* Ensure blocks are unlocked (for intel chips) */
218 if (flashutl_cmd->type == BSC) {
219 scmd((unsigned char)INTEL_UNLOCK1, a);
220 scmd((unsigned char)INTEL_UNLOCK2, a);
223 if (flashutl_cmd->pre_erase)
224 cmd(flashutl_cmd->pre_erase, CMD_ADDR);
225 if (flashutl_cmd->erase_block)
226 cmd(flashutl_cmd->erase_block, a);
227 if (flashutl_cmd->confirm)
228 scmd(flashutl_cmd->confirm, a);
230 if (flashutl_wsz == sizeof(uint8))
231 st = flash_poll(a, 0xff);
232 else
233 st = flash_poll(a, 0xffff);
235 flash_reset();
237 if (st) {
238 DPRINT(("Erase of block 0x%08lx-0x%08lx failed\n",
239 a, block((unsigned long)addr, BLOCK_LIM)));
240 return st;
243 DPRINT(("Erase of block 0x%08lx-0x%08lx done\n", a, block((unsigned long)addr, BLOCK_LIM)));
245 return 0;
248 static int
249 flash_write(unsigned long off, uint8 *src, uint nbytes)
251 uint8 *dest;
252 uint16 st, data;
253 uint i, len;
255 ASSERT(flashutl_desc != NULL);
257 if (off >= flashutl_desc->size)
258 return 1;
260 ASSERT(!(off & (flashutl_wsz - 1)));
262 dest = (uint8*)FLASH_ADDR(off);
263 st = 0;
265 while (nbytes) {
266 if ((flashutl_desc->type == SCS) &&
267 flashutl_cmd->write_buf &&
268 ((off & (WBUFSIZE - 1)) == 0)) {
269 /* issue write command */
270 if (flashutl_cmd->write_buf)
271 cmd(flashutl_cmd->write_buf, off);
272 if ((st = flash_poll(off, DONE)))
273 continue;
275 len = MIN(nbytes, WBUFSIZE);
277 #ifndef MIPSEB
278 /* write (length - 1) */
279 cmd(len / sizeof(uint16) - 1, off);
281 /* write data */
282 for (i = 0; i < len; i += sizeof(uint16),
283 dest += sizeof(uint16), src += sizeof(uint16))
284 *(uint16 *)dest = *(uint16 *)src;
285 #else
287 * BCM4710 endianness is word consistent but
288 * byte/short scrambled. This write buffer
289 * mechanism appears to be sensitive to the
290 * order of the addresses hence we need to
291 * unscramble them. We may also need to pad
292 * the source with two bytes of 0xffff in case
293 * an odd number of shorts are presented.
296 /* write (padded length - 1) */
297 cmd((ROUNDUP(len, sizeof(uint32)) / sizeof(uint16)) - 1, off);
299 /* write data (plus pad if necessary) */
300 for (i = 0; i < ROUNDUP(len, sizeof(uint32)); i += sizeof(uint32),
301 dest += sizeof(uint32), src += sizeof(uint32)) {
302 *((uint16 *)dest + 1) = ((i + sizeof(uint16)) < len) ?
303 *((uint16 *)src + 1) : 0xffff;
304 *(uint16 *)dest = *(uint16 *)src;
306 #endif /* MIPSEB */
308 /* write confirm */
309 if (flashutl_cmd->confirm)
310 cmd(flashutl_cmd->confirm, off);
312 if ((st = flash_poll(off, DONE)))
313 break;
314 } else {
315 /* issue write command */
316 if (flashutl_cmd->write_word)
317 cmd(flashutl_cmd->write_word, CMD_ADDR);
319 /* write data */
320 data = flash_readword((unsigned long)src);
321 flash_writeword((unsigned long)dest, data);
323 /* poll for done */
324 if ((st = flash_poll(off, data)))
325 break;
327 len = MIN(nbytes, flashutl_wsz);
328 dest += len;
329 src += len;
332 nbytes -= len;
333 off += len;
336 flash_reset();
338 return st;
341 static uint16 INLINE
342 flash_readword(unsigned long addr)
344 if (flashutl_wsz == sizeof(uint8))
345 return *(uint8*)addr;
346 else
347 return *(uint16*)addr;
350 static void INLINE
351 flash_writeword(unsigned long addr, uint16 data)
353 if (flashutl_wsz == sizeof(uint8))
354 *(uint8*)addr = (uint8)data;
355 else
356 *(uint16*)addr = data;
359 /* Writes a single command to the flash. */
360 static void
361 scmd(uint16 cmd, unsigned long off)
363 /* cmd |= cmd << 8; */
365 flash_writeword(FLASH_ADDR(off), cmd);
368 /* Writes a command to flash, performing an unlock if needed. */
369 static void
370 cmd(uint16 cmd, unsigned long off)
372 int i;
373 unlock_cmd_t *ul = NULL;
375 ASSERT(flashutl_cmd != NULL);
377 switch (flashutl_cmd->type) {
378 case AMD:
379 ul = &unlock_cmd_amd;
380 break;
381 case SST:
382 ul = &unlock_cmd_sst;
383 break;
384 default:
385 break;
388 if (flashutl_cmd->need_unlock) {
389 ASSERT(ul);
390 for (i = 0; i < UNLOCK_CMD_WORDS; i++)
391 flash_writeword(FLASH_ADDR(ul->addr[i]), ul->cmd[i]);
394 /* cmd |= cmd << 8; */
396 if (off == CMD_ADDR) {
397 switch (flashutl_cmd->type) {
398 case AMD:
399 off = AMD_CMD;
400 break;
401 case SST:
402 off = SST_CMD;
403 break;
404 default:
405 off = 0;
406 break;
410 #ifdef MIPSEB
411 #ifdef BCMHND74K
412 off ^= 6;
413 #else /* !74K, bcm33xx */
414 off ^= 2;
415 #endif /* BCMHND74K */
416 #endif /* MIPSEB */
418 flash_writeword(FLASH_ADDR(off), cmd);
421 static void
422 flash_reset()
424 ASSERT(flashutl_cmd != NULL);
426 if (flashutl_cmd->clear_csr)
427 scmd(flashutl_cmd->clear_csr, 0);
428 if (flashutl_cmd->read_array)
429 scmd(flashutl_cmd->read_array, 0);
432 static int
433 flash_poll(unsigned long off, uint16 data)
435 unsigned long addr;
436 int cnt = FLASH_TRIES;
437 uint16 st;
439 ASSERT(flashutl_desc != NULL);
441 if (flashutl_desc->type == AMD || flashutl_desc->type == SST) {
442 /* AMD style poll checkes the address being written */
443 addr = FLASH_ADDR(off);
444 while ((st = flash_readword(addr)) != data && cnt != 0) {
445 OSL_DELAY(10);
446 cnt--;
448 if (cnt == 0) {
449 DPRINT(("%s: timeout, off %lx, read 0x%x, expected 0x%x\n",
450 __FUNCTION__, off, st, data));
451 return -1;
453 } else {
454 /* INTEL style poll is at second word of the block being written */
455 addr = FLASH_ADDR(block(off, BLOCK_BASE)+sizeof(uint16));
456 while (((st = flash_readword(addr)) & DONE) == 0 && cnt != 0) {
457 OSL_DELAY(10);
458 cnt--;
460 if (cnt == 0) {
461 DPRINT(("%s: timeout, error status = 0x%x\n", __FUNCTION__, st));
462 return -1;
466 return 0;
469 static unsigned long
470 block(unsigned long addr, int which)
472 unsigned long b, l, sb;
473 uint* sblocks;
474 int i;
476 ASSERT(flashutl_desc != NULL);
478 ASSERT(addr < (unsigned long)flashutl_desc->size);
480 b = addr / flashutl_desc->bsize;
481 /* check for an address a full size block */
482 if (b >= flashutl_desc->ff && b <= flashutl_desc->lf) {
483 if (which == BLOCK_LIM) b++;
484 return (b * flashutl_desc->bsize);
487 /* search for the sub-block */
488 if (flashutl_desc->ff == 0) {
489 /* sub blocks are at the end of the flash */
490 sb = flashutl_desc->bsize * (flashutl_desc->lf + 1);
491 } else {
492 /* sub blocks are at the start of the flash */
493 sb = 0;
496 sblocks = flashutl_desc->subblocks;
497 for (i = 0; i < flashutl_desc->nsub; i++) {
498 b = sb + sblocks[i];
499 l = sb + sblocks[i+1];
500 if (addr >= b && addr < l) {
501 if (which == BLOCK_BASE)
502 return b;
503 else
504 return l;
508 return 0;
511 void
512 nvWrite(unsigned short *data, unsigned int len)
514 uint off = flashutl_desc->size - NVRAM_SPACE;
515 sysFlashWrite(off, (uchar*)data, len);
518 void
519 nvWriteChars(unsigned char *data, unsigned int len)
521 uint off = flashutl_desc->size - NVRAM_SPACE;
522 int err;
524 if (flashutl_cmd->type == SFLASH)
525 err = sflash_commit(sih, cc, off, len, data);
526 else /* PFLASH */
527 err = flash_write(off, data, len);
529 if (err)
530 DPRINT(("nvWriteChars failed\n"));
531 else
532 DPRINT(("nvWriteChars succeeded\n"));
536 sysFlashErase(uint off, unsigned int numbytes)
538 unsigned long end = off + numbytes;
539 int err = 0;
541 if (flashutl_cmd->type == SFLASH) {
542 err = sflash_commit(sih, cc, off, numbytes, NULL);
543 } else {
544 while (off < end) {
545 err = flash_eraseblk(off);
546 if (err)
547 break;
548 off = block(off, BLOCK_LIM);
552 if (err)
553 DPRINT(("Block erase at 0x%x failed\n", off));
554 else
555 DPRINT(("Done\n"));
557 return !err;
561 sysFlashWrite(uint off, uchar *src, uint numbytes)
563 int err;
565 DPRINT(("Writing 0x%x bytes to flash @0x%x ...\n", (unsigned int)numbytes, off));
567 if (flashutl_cmd->type == SFLASH)
568 err = sflash_commit(sih, cc, off, numbytes, src);
569 else {
570 if (!sysFlashErase(off, numbytes))
571 return 0;
572 err = flash_write(off, src, numbytes);
575 if (err)
576 DPRINT(("Flash write failed\n"));
577 else
578 DPRINT(("Flash write succeeded\n"));
580 return !err;
584 sysFlashRead(uint off, uchar *buf, uint numbytes)
586 uint read, total_read = 0;
588 if (flashutl_cmd->type == SFLASH) {
589 while (numbytes) {
590 read = sflash_read(sih, cc, off, numbytes, buf);
591 numbytes -= read;
592 buf += read;
593 off += read;
594 total_read += read;
596 } else {
597 ASSERT(!(off & (flashutl_wsz - 1)));
598 ASSERT(!(numbytes & (flashutl_wsz - 1)));
600 while (numbytes) {
601 flash_writeword((unsigned long)buf, flash_readword(FLASH_ADDR(off)));
602 numbytes -= flashutl_wsz;
603 buf += flashutl_wsz;
604 off += flashutl_wsz;
605 total_read += flashutl_wsz;
609 return (total_read);