lzma-loader and ntools
[tomato.git] / release / src-rt-6.x.4708 / shared / ccsflash.c
blob2eecc121e6234f88dca75d5f06c7c9fd7135465b
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2012, 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: sflash.c 345824 2012-07-19 06:29:12Z $
21 #include <bcm_cfg.h>
22 #include <typedefs.h>
23 #include <osl.h>
24 #include <bcmutils.h>
25 #include <siutils.h>
26 #include <hndsoc.h>
27 #include <sbhndcpu.h>
28 #include <sbchipc.h>
29 #include <bcmdevs.h>
30 #include <hndsflash.h>
32 #ifdef BCMDBG
33 #define SFL_MSG(args) printf args
34 #else
35 #define SFL_MSG(args)
36 #endif /* BCMDBG */
38 /* Private global state */
39 static hndsflash_t ccsflash;
41 /* Prototype */
42 hndsflash_t *ccsflash_init(si_t *sih);
43 static int ccsflash_poll(hndsflash_t *sfl, uint offset);
44 static int ccsflash_read(hndsflash_t *sfl, uint offset, uint len, const uchar *buf);
45 static int ccsflash_write(hndsflash_t *sfl, uint offset, uint len, const uchar *buf);
46 static int ccsflash_erase(hndsflash_t *sfl, uint offset);
47 static int ccsflash_commit(hndsflash_t *sfl, uint offset, uint len, const uchar *buf);
50 /* Issue a serial flash command */
51 static INLINE void
52 ccsflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode)
54 W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode);
55 while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY);
58 static bool firsttime = TRUE;
60 /* Initialize serial flash access */
61 hndsflash_t *
62 ccsflash_init(si_t *sih)
64 chipcregs_t *cc;
65 uint32 id, id2;
66 const char *name = "";
67 osl_t *osh;
69 ASSERT(sih);
71 /* No sflash for NorthStar */
72 if (sih->ccrev == 42)
73 return NULL;
75 if ((cc = (chipcregs_t *)si_setcoreidx(sih, SI_CC_IDX)) == NULL)
76 return NULL;
78 if (!firsttime && ccsflash.size != 0)
79 return &ccsflash;
81 osh = si_osh(sih);
83 bzero(&ccsflash, sizeof(ccsflash));
84 ccsflash.sih = sih;
85 ccsflash.core = (void *)cc;
86 ccsflash.read = ccsflash_read;
87 ccsflash.write = ccsflash_write;
88 ccsflash.erase = ccsflash_erase;
89 ccsflash.commit = ccsflash_commit;
90 ccsflash.poll = ccsflash_poll;
93 ccsflash.type = sih->cccaps & CC_CAP_FLASH_MASK;
95 switch (ccsflash.type) {
96 case SFLASH_ST:
97 /* Probe for ST chips */
98 name = "ST compatible";
99 ccsflash_cmd(osh, cc, SFLASH_ST_DP);
100 W_REG(osh, &cc->flashaddress, 0);
101 ccsflash_cmd(osh, cc, SFLASH_ST_RES);
102 id = R_REG(osh, &cc->flashdata);
103 ccsflash.blocksize = 64 * 1024;
104 switch (id) {
105 case 0x11:
106 /* ST M25P20 2 Mbit Serial Flash */
107 ccsflash.numblocks = 4;
108 break;
109 case 0x12:
110 /* ST M25P40 4 Mbit Serial Flash */
111 ccsflash.numblocks = 8;
112 break;
113 case 0x13:
114 ccsflash_cmd(osh, cc, SFLASH_MXIC_RDID);
115 id = R_REG(osh, &cc->flashdata);
116 if (id == SFLASH_MXIC_MFID) {
117 /* MXIC MX25L8006E 8 Mbit Serial Flash */
118 ccsflash.blocksize = 4 * 1024;
119 ccsflash.numblocks = 16 * 16;
120 } else {
121 /* ST M25P80 8 Mbit Serial Flash */
122 ccsflash.numblocks = 16;
124 break;
125 case 0x14:
126 /* ST M25P16 16 Mbit Serial Flash */
127 ccsflash.numblocks = 32;
128 break;
129 case 0x15:
130 /* ST M25P32 32 Mbit Serial Flash */
131 ccsflash.numblocks = 64;
132 break;
133 case 0x16:
134 /* ST M25P64 64 Mbit Serial Flash */
135 ccsflash.numblocks = 128;
136 break;
137 case 0x17:
138 /* ST M25FL128 128 Mbit Serial Flash */
139 ccsflash.numblocks = 256;
140 break;
141 case 0xbf:
142 /* All of the following flashes are SST with
143 * 4KB subsectors. Others should be added but
144 * We'll have to revamp the way we identify them
145 * since RES is not eough to disambiguate them.
147 name = "SST";
148 ccsflash.blocksize = 4 * 1024;
149 W_REG(osh, &cc->flashaddress, 1);
150 ccsflash_cmd(osh, cc, SFLASH_ST_RES);
151 id2 = R_REG(osh, &cc->flashdata);
152 switch (id2) {
153 case 1:
154 /* SST25WF512 512 Kbit Serial Flash */
155 ccsflash.numblocks = 16;
156 break;
157 case 0x48:
158 /* SST25VF512 512 Kbit Serial Flash */
159 ccsflash.numblocks = 16;
160 break;
161 case 2:
162 /* SST25WF010 1 Mbit Serial Flash */
163 ccsflash.numblocks = 32;
164 break;
165 case 0x49:
166 /* SST25VF010 1 Mbit Serial Flash */
167 ccsflash.numblocks = 32;
168 break;
169 case 3:
170 /* SST25WF020 2 Mbit Serial Flash */
171 ccsflash.numblocks = 64;
172 break;
173 case 0x43:
174 /* SST25VF020 2 Mbit Serial Flash */
175 ccsflash.numblocks = 64;
176 break;
177 case 4:
178 /* SST25WF040 4 Mbit Serial Flash */
179 ccsflash.numblocks = 128;
180 break;
181 case 0x44:
182 /* SST25VF040 4 Mbit Serial Flash */
183 ccsflash.numblocks = 128;
184 break;
185 case 0x8d:
186 /* SST25VF040B 4 Mbit Serial Flash */
187 ccsflash.numblocks = 128;
188 break;
189 case 5:
190 /* SST25WF080 8 Mbit Serial Flash */
191 ccsflash.numblocks = 256;
192 break;
193 case 0x8e:
194 /* SST25VF080B 8 Mbit Serial Flash */
195 ccsflash.numblocks = 256;
196 break;
197 case 0x41:
198 /* SST25VF016 16 Mbit Serial Flash */
199 ccsflash.numblocks = 512;
200 break;
201 case 0x4a:
202 /* SST25VF032 32 Mbit Serial Flash */
203 ccsflash.numblocks = 1024;
204 break;
205 case 0x4b:
206 /* SST25VF064 64 Mbit Serial Flash */
207 ccsflash.numblocks = 2048;
208 break;
210 break;
212 break;
214 case SFLASH_AT:
215 /* Probe for Atmel chips */
216 name = "Atmel";
217 ccsflash_cmd(osh, cc, SFLASH_AT_STATUS);
218 id = R_REG(osh, &cc->flashdata) & 0x3c;
219 switch (id) {
220 case 0xc:
221 /* Atmel AT45DB011 1Mbit Serial Flash */
222 ccsflash.blocksize = 256;
223 ccsflash.numblocks = 512;
224 break;
225 case 0x14:
226 /* Atmel AT45DB021 2Mbit Serial Flash */
227 ccsflash.blocksize = 256;
228 ccsflash.numblocks = 1024;
229 break;
230 case 0x1c:
231 /* Atmel AT45DB041 4Mbit Serial Flash */
232 ccsflash.blocksize = 256;
233 ccsflash.numblocks = 2048;
234 break;
235 case 0x24:
236 /* Atmel AT45DB081 8Mbit Serial Flash */
237 ccsflash.blocksize = 256;
238 ccsflash.numblocks = 4096;
239 break;
240 case 0x2c:
241 /* Atmel AT45DB161 16Mbit Serial Flash */
242 ccsflash.blocksize = 512;
243 ccsflash.numblocks = 4096;
244 break;
245 case 0x34:
246 /* Atmel AT45DB321 32Mbit Serial Flash */
247 ccsflash.blocksize = 512;
248 ccsflash.numblocks = 8192;
249 break;
250 case 0x3c:
251 /* Atmel AT45DB642 64Mbit Serial Flash */
252 ccsflash.blocksize = 1024;
253 ccsflash.numblocks = 8192;
254 break;
256 break;
259 ccsflash.size = ccsflash.blocksize * ccsflash.numblocks;
260 ccsflash.phybase = SI_FLASH2;
262 if (firsttime)
263 printf("Found an %s serial flash with %d %dKB blocks; total size %dMB\n",
264 name, ccsflash.numblocks, ccsflash.blocksize / 1024,
265 ccsflash.size / (1024 * 1024));
267 firsttime = FALSE;
268 return ccsflash.size ? &ccsflash : NULL;
271 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
272 static int
273 ccsflash_read(hndsflash_t *sfl, uint offset, uint len, const uchar *buf)
275 si_t *sih = sfl->sih;
276 uint8 *from, *to;
277 int cnt, i;
279 ASSERT(sih);
281 if (!len)
282 return 0;
284 if ((offset + len) > sfl->size)
285 return -22;
287 if ((len >= 4) && (offset & 3))
288 cnt = 4 - (offset & 3);
289 else if ((len >= 4) && ((uintptr)buf & 3))
290 cnt = 4 - ((uintptr)buf & 3);
291 else
292 cnt = len;
294 if (sih->ccrev == 12)
295 from = (uint8 *)OSL_UNCACHED((void *)SI_FLASH2 + offset);
296 else
297 from = (uint8 *)OSL_CACHED((void *)SI_FLASH2 + offset);
298 to = (uint8 *)buf;
300 if (cnt < 4) {
301 for (i = 0; i < cnt; i ++) {
302 /* Cannot use R_REG because in bigendian that will
303 * xor the address and we don't want that here.
305 *to = *from;
306 from ++;
307 to ++;
309 return cnt;
312 while (cnt >= 4) {
313 *(uint32 *)to = *(uint32 *)from;
314 from += 4;
315 to += 4;
316 cnt -= 4;
319 return (len - cnt);
322 /* Poll for command completion. Returns zero when complete. */
323 static int
324 ccsflash_poll(hndsflash_t *sfl, uint offset)
326 si_t *sih = sfl->sih;
327 chipcregs_t *cc = (chipcregs_t *)sfl->core;
328 osl_t *osh;
330 ASSERT(sih);
332 osh = si_osh(sih);
334 if (offset >= sfl->size)
335 return -22;
337 switch (sfl->type) {
338 case SFLASH_ST:
339 /* Check for ST Write In Progress bit */
340 ccsflash_cmd(osh, cc, SFLASH_ST_RDSR);
341 return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP;
342 case SFLASH_AT:
343 /* Check for Atmel Ready bit */
344 ccsflash_cmd(osh, cc, SFLASH_AT_STATUS);
345 return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY);
348 return 0;
351 /* Write len bytes starting at offset into buf. Returns number of bytes
352 * written. Caller should poll for completion.
354 #define ST_RETRIES 3
356 #ifdef IL_BIGENDIAN
357 #ifdef BCMHND74K
358 #define GET_BYTE(ptr) (*(uint8 *)((uint32)(ptr) ^ 7))
359 #else /* !74K, bcm33xx */
360 #define GET_BYTE(ptr) (*(uint8 *)((uint32)(ptr) ^ 3))
361 #endif /* BCMHND74K */
362 #else /* !IL_BIGENDIAN */
363 #define GET_BYTE(ptr) (*(ptr))
364 #endif /* IL_BIGENDIAN */
366 static int
367 ccsflash_write(hndsflash_t *sfl, uint offset, uint length, const uchar *buffer)
369 si_t *sih = sfl->sih;
370 chipcregs_t *cc = (chipcregs_t *)sfl->core;
371 uint off = offset, len = length;
372 const uint8 *buf = buffer;
373 uint8 data;
374 int ret = 0, ntry = 0;
375 bool is4712b0;
376 uint32 page, byte, mask;
377 osl_t *osh;
379 ASSERT(sih);
381 osh = si_osh(sih);
383 if (!len)
384 return 0;
386 if ((off + len) > sfl->size)
387 return -22;
389 switch (sfl->type) {
390 case SFLASH_ST:
391 is4712b0 = (CHIPID(sih->chip) == BCM4712_CHIP_ID) && (CHIPREV(sih->chiprev) == 3);
392 /* Enable writes */
393 retry: ccsflash_cmd(osh, cc, SFLASH_ST_WREN);
394 off = offset;
395 len = length;
396 buf = buffer;
397 ntry++;
398 if (is4712b0) {
399 mask = 1 << 14;
400 W_REG(osh, &cc->flashaddress, off);
401 data = GET_BYTE(buf);
402 buf++;
403 W_REG(osh, &cc->flashdata, data);
404 /* Set chip select */
405 OR_REG(osh, &cc->gpioout, mask);
406 /* Issue a page program with the first byte */
407 ccsflash_cmd(osh, cc, SFLASH_ST_PP);
408 ret = 1;
409 off++;
410 len--;
411 while (len > 0) {
412 if ((off & 255) == 0) {
413 /* Page boundary, drop cs and return */
414 AND_REG(osh, &cc->gpioout, ~mask);
415 OSL_DELAY(1);
416 if (!ccsflash_poll(sfl, off)) {
417 /* Flash rejected command */
418 if (ntry <= ST_RETRIES)
419 goto retry;
420 else
421 return -11;
423 return ret;
424 } else {
425 /* Write single byte */
426 data = GET_BYTE(buf);
427 buf++;
428 ccsflash_cmd(osh, cc, data);
430 ret++;
431 off++;
432 len--;
434 /* All done, drop cs */
435 AND_REG(osh, &cc->gpioout, ~mask);
436 OSL_DELAY(1);
437 if (!ccsflash_poll(sfl, off)) {
438 /* Flash rejected command */
439 if (ntry <= ST_RETRIES)
440 goto retry;
441 else
442 return -12;
444 } else if (sih->ccrev >= 20) {
445 W_REG(osh, &cc->flashaddress, off);
446 data = GET_BYTE(buf);
447 buf++;
448 W_REG(osh, &cc->flashdata, data);
449 /* Issue a page program with CSA bit set */
450 ccsflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP);
451 ret = 1;
452 off++;
453 len--;
454 while (len > 0) {
455 if ((off & 255) == 0) {
456 /* Page boundary, poll droping cs and return */
457 W_REG(NULL, &cc->flashcontrol, 0);
458 OSL_DELAY(1);
459 if (ccsflash_poll(sfl, off) == 0) {
460 /* Flash rejected command */
461 SFL_MSG(("sflash: pp rejected, ntry: %d,"
462 " off: %d/%d, len: %d/%d, ret:"
463 "%d\n", ntry, off, offset, len,
464 length, ret));
465 if (ntry <= ST_RETRIES)
466 goto retry;
467 else
468 return -11;
470 return ret;
471 } else {
472 /* Write single byte */
473 data = GET_BYTE(buf);
474 buf++;
475 ccsflash_cmd(osh, cc, SFLASH_ST_CSA | data);
477 ret++;
478 off++;
479 len--;
481 /* All done, drop cs & poll */
482 W_REG(NULL, &cc->flashcontrol, 0);
483 OSL_DELAY(1);
484 if (ccsflash_poll(sfl, off) == 0) {
485 /* Flash rejected command */
486 SFL_MSG(("ccsflash: pp rejected, ntry: %d, off: %d/%d,"
487 " len: %d/%d, ret: %d\n",
488 ntry, off, offset, len, length, ret));
489 if (ntry <= ST_RETRIES)
490 goto retry;
491 else
492 return -12;
494 } else {
495 ret = 1;
496 W_REG(osh, &cc->flashaddress, off);
497 data = GET_BYTE(buf);
498 buf++;
499 W_REG(osh, &cc->flashdata, data);
500 /* Page program */
501 ccsflash_cmd(osh, cc, SFLASH_ST_PP);
503 break;
504 case SFLASH_AT:
505 mask = sfl->blocksize - 1;
506 page = (off & ~mask) << 1;
507 byte = off & mask;
508 /* Read main memory page into buffer 1 */
509 if (byte || (len < sfl->blocksize)) {
510 W_REG(osh, &cc->flashaddress, page);
511 ccsflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD);
512 /* 250 us for AT45DB321B */
513 SPINWAIT(ccsflash_poll(sfl, off), 1000);
514 ASSERT(!ccsflash_poll(sfl, off));
516 /* Write into buffer 1 */
517 for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) {
518 W_REG(osh, &cc->flashaddress, byte++);
519 W_REG(osh, &cc->flashdata, *buf++);
520 ccsflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE);
522 /* Write buffer 1 into main memory page */
523 W_REG(osh, &cc->flashaddress, page);
524 ccsflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM);
525 break;
528 return ret;
531 /* Erase a region. Returns number of bytes scheduled for erasure.
532 * Caller should poll for completion.
534 static int
535 ccsflash_erase(hndsflash_t *sfl, uint offset)
537 si_t *sih = sfl->sih;
538 chipcregs_t *cc = (chipcregs_t *)sfl->core;
539 osl_t *osh;
541 ASSERT(sih);
543 osh = si_osh(sih);
545 if (offset >= sfl->size)
546 return -22;
548 switch (sfl->type) {
549 case SFLASH_ST:
550 ccsflash_cmd(osh, cc, SFLASH_ST_WREN);
551 W_REG(osh, &cc->flashaddress, offset);
552 /* Newer flashes have "sub-sectors" which can be erased independently
553 * with a new command: ST_SSE. The ST_SE command erases 64KB just as
554 * before.
556 ccsflash_cmd(osh, cc,
557 (sfl->blocksize < (64 * 1024)) ? SFLASH_ST_SSE : SFLASH_ST_SE);
558 return sfl->blocksize;
559 case SFLASH_AT:
560 W_REG(osh, &cc->flashaddress, offset << 1);
561 ccsflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE);
562 return sfl->blocksize;
565 return 0;
569 * writes the appropriate range of flash, a NULL buf simply erases
570 * the region of flash
572 static int
573 ccsflash_commit(hndsflash_t *sfl, uint offset, uint len, const uchar *buf)
575 si_t *sih = sfl->sih;
576 uchar *block = NULL, *cur_ptr, *blk_ptr;
577 uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
578 uint blk_offset, blk_len, copied;
579 int bytes, ret = 0;
580 osl_t *osh;
582 ASSERT(sih);
584 osh = si_osh(sih);
586 /* Check address range */
587 if (len <= 0)
588 return 0;
590 if ((offset + len) > sfl->size)
591 return -1;
593 blocksize = sfl->blocksize;
594 mask = blocksize - 1;
596 /* Allocate a block of mem */
597 if (!(block = MALLOC(osh, blocksize)))
598 return -1;
600 while (len) {
601 /* Align offset */
602 cur_offset = offset & ~mask;
603 cur_length = blocksize;
604 cur_ptr = block;
606 remainder = blocksize - (offset & mask);
607 if (len < remainder)
608 cur_retlen = len;
609 else
610 cur_retlen = remainder;
612 /* buf == NULL means erase only */
613 if (buf) {
614 /* Copy existing data into holding block if necessary */
615 if ((offset & mask) || (len < blocksize)) {
616 blk_offset = cur_offset;
617 blk_len = cur_length;
618 blk_ptr = cur_ptr;
620 /* Copy entire block */
621 while (blk_len) {
622 copied = ccsflash_read(sfl, blk_offset, blk_len, blk_ptr);
623 blk_offset += copied;
624 blk_len -= copied;
625 blk_ptr += copied;
629 /* Copy input data into holding block */
630 memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
633 /* Erase block */
634 if ((ret = ccsflash_erase(sfl, (uint) cur_offset)) < 0)
635 goto done;
636 while (ccsflash_poll(sfl, (uint) cur_offset));
638 /* buf == NULL means erase only */
639 if (!buf) {
640 offset += cur_retlen;
641 len -= cur_retlen;
642 continue;
645 /* Write holding block */
646 while (cur_length > 0) {
647 if ((bytes = ccsflash_write(sfl,
648 (uint) cur_offset,
649 (uint) cur_length,
650 (uchar *) cur_ptr)) < 0) {
651 ret = bytes;
652 goto done;
654 while (ccsflash_poll(sfl, (uint) cur_offset));
655 cur_offset += bytes;
656 cur_length -= bytes;
657 cur_ptr += bytes;
660 offset += cur_retlen;
661 len -= cur_retlen;
662 buf += cur_retlen;
665 ret = len;
666 done:
667 if (block)
668 MFREE(osh, block, blocksize);
669 return ret;