BCM WL 6.30.102.9 (r366174)
[tomato.git] / release / src-rt / shared / sflash.c
blobd78faa6ed7457af252e866a427bbe18c8309e38d
1 /*
2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2011, 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 300516 2011-12-04 17:39:44Z $
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 <sflash.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 struct sflash sflash;
41 /* Issue a serial flash command */
42 static INLINE void
43 sflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode)
45 W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode);
46 while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY);
49 static bool firsttime = TRUE;
51 /* Initialize serial flash access */
52 struct sflash *
53 sflash_init(si_t *sih, chipcregs_t *cc)
55 uint32 id, id2;
56 const char *name = "";
57 osl_t *osh;
59 ASSERT(sih);
61 osh = si_osh(sih);
63 bzero(&sflash, sizeof(sflash));
65 sflash.type = sih->cccaps & CC_CAP_FLASH_MASK;
67 switch (sflash.type) {
68 case SFLASH_ST:
69 /* Probe for ST chips */
70 name = "ST compatible";
71 sflash_cmd(osh, cc, SFLASH_ST_DP);
72 W_REG(osh, &cc->flashaddress, 0);
73 sflash_cmd(osh, cc, SFLASH_ST_RES);
74 id = R_REG(osh, &cc->flashdata);
75 sflash.blocksize = 64 * 1024;
76 switch (id) {
77 case 0x11:
78 /* ST M25P20 2 Mbit Serial Flash */
79 sflash.numblocks = 4;
80 break;
81 case 0x12:
82 /* ST M25P40 4 Mbit Serial Flash */
83 sflash.numblocks = 8;
84 break;
85 case 0x13:
86 sflash_cmd(osh, cc, SFLASH_MXIC_RDID);
87 id = R_REG(osh, &cc->flashdata);
88 if (id == SFLASH_MXIC_MFID) {
89 /* MXIC MX25L8006E 8 Mbit Serial Flash */
90 sflash.blocksize = 4 * 1024;
91 sflash.numblocks = 16 * 16;
92 } else {
93 /* ST M25P80 8 Mbit Serial Flash */
94 sflash.numblocks = 16;
96 break;
97 case 0x14:
98 /* ST M25P16 16 Mbit Serial Flash */
99 sflash.numblocks = 32;
100 break;
101 case 0x15:
102 /* ST M25P32 32 Mbit Serial Flash */
103 sflash.numblocks = 64;
104 break;
105 case 0x16:
106 /* ST M25P64 64 Mbit Serial Flash */
107 sflash.numblocks = 128;
108 break;
109 case 0x17:
110 /* ST M25FL128 128 Mbit Serial Flash */
111 sflash.numblocks = 256;
112 break;
113 case 0xbf:
114 /* All of the following flashes are SST with
115 * 4KB subsectors. Others should be added but
116 * We'll have to revamp the way we identify them
117 * since RES is not eough to disambiguate them.
119 name = "SST";
120 sflash.blocksize = 4 * 1024;
121 W_REG(osh, &cc->flashaddress, 1);
122 sflash_cmd(osh, cc, SFLASH_ST_RES);
123 id2 = R_REG(osh, &cc->flashdata);
124 switch (id2) {
125 case 1:
126 /* SST25WF512 512 Kbit Serial Flash */
127 sflash.numblocks = 16;
128 break;
129 case 0x48:
130 /* SST25VF512 512 Kbit Serial Flash */
131 sflash.numblocks = 16;
132 break;
133 case 2:
134 /* SST25WF010 1 Mbit Serial Flash */
135 sflash.numblocks = 32;
136 break;
137 case 0x49:
138 /* SST25VF010 1 Mbit Serial Flash */
139 sflash.numblocks = 32;
140 break;
141 case 3:
142 /* SST25WF020 2 Mbit Serial Flash */
143 sflash.numblocks = 64;
144 break;
145 case 0x43:
146 /* SST25VF020 2 Mbit Serial Flash */
147 sflash.numblocks = 64;
148 break;
149 case 4:
150 /* SST25WF040 4 Mbit Serial Flash */
151 sflash.numblocks = 128;
152 break;
153 case 0x44:
154 /* SST25VF040 4 Mbit Serial Flash */
155 sflash.numblocks = 128;
156 break;
157 case 0x8d:
158 /* SST25VF040B 4 Mbit Serial Flash */
159 sflash.numblocks = 128;
160 break;
161 case 5:
162 /* SST25WF080 8 Mbit Serial Flash */
163 sflash.numblocks = 256;
164 break;
165 case 0x8e:
166 /* SST25VF080B 8 Mbit Serial Flash */
167 sflash.numblocks = 256;
168 break;
169 case 0x41:
170 /* SST25VF016 16 Mbit Serial Flash */
171 sflash.numblocks = 512;
172 break;
173 case 0x4a:
174 /* SST25VF032 32 Mbit Serial Flash */
175 sflash.numblocks = 1024;
176 break;
177 case 0x4b:
178 /* SST25VF064 64 Mbit Serial Flash */
179 sflash.numblocks = 2048;
180 break;
182 break;
184 break;
186 case SFLASH_AT:
187 /* Probe for Atmel chips */
188 name = "Atmel";
189 sflash_cmd(osh, cc, SFLASH_AT_STATUS);
190 id = R_REG(osh, &cc->flashdata) & 0x3c;
191 switch (id) {
192 case 0xc:
193 /* Atmel AT45DB011 1Mbit Serial Flash */
194 sflash.blocksize = 256;
195 sflash.numblocks = 512;
196 break;
197 case 0x14:
198 /* Atmel AT45DB021 2Mbit Serial Flash */
199 sflash.blocksize = 256;
200 sflash.numblocks = 1024;
201 break;
202 case 0x1c:
203 /* Atmel AT45DB041 4Mbit Serial Flash */
204 sflash.blocksize = 256;
205 sflash.numblocks = 2048;
206 break;
207 case 0x24:
208 /* Atmel AT45DB081 8Mbit Serial Flash */
209 sflash.blocksize = 256;
210 sflash.numblocks = 4096;
211 break;
212 case 0x2c:
213 /* Atmel AT45DB161 16Mbit Serial Flash */
214 sflash.blocksize = 512;
215 sflash.numblocks = 4096;
216 break;
217 case 0x34:
218 /* Atmel AT45DB321 32Mbit Serial Flash */
219 sflash.blocksize = 512;
220 sflash.numblocks = 8192;
221 break;
222 case 0x3c:
223 /* Atmel AT45DB642 64Mbit Serial Flash */
224 sflash.blocksize = 1024;
225 sflash.numblocks = 8192;
226 break;
228 break;
231 sflash.size = sflash.blocksize * sflash.numblocks;
233 if (firsttime)
234 printf("Found an %s serial flash with %d %dKB blocks; total size %dMB\n",
235 name, sflash.numblocks, sflash.blocksize / 1024,
236 sflash.size / (1024 * 1024));
238 firsttime = FALSE;
239 return sflash.size ? &sflash : NULL;
242 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
244 sflash_read(si_t *sih, chipcregs_t *cc, uint offset, uint len, uchar *buf)
246 uint8 *from, *to;
247 int cnt, i;
249 ASSERT(sih);
251 if (!len)
252 return 0;
254 if ((offset + len) > sflash.size)
255 return -22;
257 if ((len >= 4) && (offset & 3))
258 cnt = 4 - (offset & 3);
259 else if ((len >= 4) && ((uintptr)buf & 3))
260 cnt = 4 - ((uintptr)buf & 3);
261 else
262 cnt = len;
264 if (sih->ccrev == 12)
265 from = (uint8 *)OSL_UNCACHED((void *)SI_FLASH2 + offset);
266 else
267 from = (uint8 *)OSL_CACHED((void *)SI_FLASH2 + offset);
268 to = (uint8 *)buf;
270 if (cnt < 4) {
271 for (i = 0; i < cnt; i ++) {
272 /* Cannot use R_REG because in bigendian that will
273 * xor the address and we don't want that here.
275 *to = *from;
276 from ++;
277 to ++;
279 return cnt;
282 while (cnt >= 4) {
283 *(uint32 *)to = *(uint32 *)from;
284 from += 4;
285 to += 4;
286 cnt -= 4;
289 return (len - cnt);
292 /* Poll for command completion. Returns zero when complete. */
294 sflash_poll(si_t *sih, chipcregs_t *cc, uint offset)
296 osl_t *osh;
298 ASSERT(sih);
300 osh = si_osh(sih);
302 if (offset >= sflash.size)
303 return -22;
305 switch (sflash.type) {
306 case SFLASH_ST:
307 /* Check for ST Write In Progress bit */
308 sflash_cmd(osh, cc, SFLASH_ST_RDSR);
309 return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP;
310 case SFLASH_AT:
311 /* Check for Atmel Ready bit */
312 sflash_cmd(osh, cc, SFLASH_AT_STATUS);
313 return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY);
316 return 0;
319 /* Write len bytes starting at offset into buf. Returns number of bytes
320 * written. Caller should poll for completion.
322 #define ST_RETRIES 3
324 #ifdef IL_BIGENDIAN
325 #ifdef BCMHND74K
326 #define GET_BYTE(ptr) (*(uint8 *)((uint32)(ptr) ^ 7))
327 #else /* !74K, bcm33xx */
328 #define GET_BYTE(ptr) (*(uint8 *)((uint32)(ptr) ^ 3))
329 #endif /* BCMHND74K */
330 #else /* !IL_BIGENDIAN */
331 #define GET_BYTE(ptr) (*(ptr))
332 #endif /* IL_BIGENDIAN */
335 sflash_write(si_t *sih, chipcregs_t *cc, uint offset, uint length, const uchar *buffer)
337 struct sflash *sfl;
338 uint off = offset, len = length;
339 const uint8 *buf = buffer;
340 uint8 data;
341 int ret = 0, ntry = 0;
342 bool is4712b0;
343 uint32 page, byte, mask;
344 osl_t *osh;
346 ASSERT(sih);
348 osh = si_osh(sih);
350 if (!len)
351 return 0;
353 sfl = &sflash;
354 if ((off + len) > sfl->size)
355 return -22;
357 switch (sfl->type) {
358 case SFLASH_ST:
359 is4712b0 = (CHIPID(sih->chip) == BCM4712_CHIP_ID) && (CHIPREV(sih->chiprev) == 3);
360 /* Enable writes */
361 retry: sflash_cmd(osh, cc, SFLASH_ST_WREN);
362 off = offset;
363 len = length;
364 buf = buffer;
365 ntry++;
366 if (is4712b0) {
367 mask = 1 << 14;
368 W_REG(osh, &cc->flashaddress, off);
369 data = GET_BYTE(buf);
370 buf++;
371 W_REG(osh, &cc->flashdata, data);
372 /* Set chip select */
373 OR_REG(osh, &cc->gpioout, mask);
374 /* Issue a page program with the first byte */
375 sflash_cmd(osh, cc, SFLASH_ST_PP);
376 ret = 1;
377 off++;
378 len--;
379 while (len > 0) {
380 if ((off & 255) == 0) {
381 /* Page boundary, drop cs and return */
382 AND_REG(osh, &cc->gpioout, ~mask);
383 OSL_DELAY(1);
384 if (!sflash_poll(sih, cc, off)) {
385 /* Flash rejected command */
386 if (ntry <= ST_RETRIES)
387 goto retry;
388 else
389 return -11;
391 return ret;
392 } else {
393 /* Write single byte */
394 data = GET_BYTE(buf);
395 buf++;
396 sflash_cmd(osh, cc, data);
398 ret++;
399 off++;
400 len--;
402 /* All done, drop cs */
403 AND_REG(osh, &cc->gpioout, ~mask);
404 OSL_DELAY(1);
405 if (!sflash_poll(sih, cc, off)) {
406 /* Flash rejected command */
407 if (ntry <= ST_RETRIES)
408 goto retry;
409 else
410 return -12;
412 } else if (sih->ccrev >= 20) {
413 W_REG(osh, &cc->flashaddress, off);
414 data = GET_BYTE(buf);
415 buf++;
416 W_REG(osh, &cc->flashdata, data);
417 /* Issue a page program with CSA bit set */
418 sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP);
419 ret = 1;
420 off++;
421 len--;
422 while (len > 0) {
423 if ((off & 255) == 0) {
424 /* Page boundary, poll droping cs and return */
425 W_REG(NULL, &cc->flashcontrol, 0);
426 OSL_DELAY(1);
427 if (sflash_poll(sih, cc, off) == 0) {
428 /* Flash rejected command */
429 SFL_MSG(("sflash: pp rejected, ntry: %d,"
430 " off: %d/%d, len: %d/%d, ret:"
431 "%d\n", ntry, off, offset, len,
432 length, ret));
433 if (ntry <= ST_RETRIES)
434 goto retry;
435 else
436 return -11;
438 return ret;
439 } else {
440 /* Write single byte */
441 data = GET_BYTE(buf);
442 buf++;
443 sflash_cmd(osh, cc, SFLASH_ST_CSA | data);
445 ret++;
446 off++;
447 len--;
449 /* All done, drop cs & poll */
450 W_REG(NULL, &cc->flashcontrol, 0);
451 OSL_DELAY(1);
452 if (sflash_poll(sih, cc, off) == 0) {
453 /* Flash rejected command */
454 SFL_MSG(("sflash: pp rejected, ntry: %d, off: %d/%d,"
455 " len: %d/%d, ret: %d\n",
456 ntry, off, offset, len, length, ret));
457 if (ntry <= ST_RETRIES)
458 goto retry;
459 else
460 return -12;
462 } else {
463 ret = 1;
464 W_REG(osh, &cc->flashaddress, off);
465 data = GET_BYTE(buf);
466 buf++;
467 W_REG(osh, &cc->flashdata, data);
468 /* Page program */
469 sflash_cmd(osh, cc, SFLASH_ST_PP);
471 break;
472 case SFLASH_AT:
473 mask = sfl->blocksize - 1;
474 page = (off & ~mask) << 1;
475 byte = off & mask;
476 /* Read main memory page into buffer 1 */
477 if (byte || (len < sfl->blocksize)) {
478 W_REG(osh, &cc->flashaddress, page);
479 sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD);
480 /* 250 us for AT45DB321B */
481 SPINWAIT(sflash_poll(sih, cc, off), 1000);
482 ASSERT(!sflash_poll(sih, cc, off));
484 /* Write into buffer 1 */
485 for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) {
486 W_REG(osh, &cc->flashaddress, byte++);
487 W_REG(osh, &cc->flashdata, *buf++);
488 sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE);
490 /* Write buffer 1 into main memory page */
491 W_REG(osh, &cc->flashaddress, page);
492 sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM);
493 break;
496 return ret;
499 /* Erase a region. Returns number of bytes scheduled for erasure.
500 * Caller should poll for completion.
503 sflash_erase(si_t *sih, chipcregs_t *cc, uint offset)
505 struct sflash *sfl;
506 osl_t *osh;
508 ASSERT(sih);
510 osh = si_osh(sih);
512 sfl = &sflash;
513 if (offset >= sfl->size)
514 return -22;
516 switch (sfl->type) {
517 case SFLASH_ST:
518 sflash_cmd(osh, cc, SFLASH_ST_WREN);
519 W_REG(osh, &cc->flashaddress, offset);
520 /* Newer flashes have "sub-sectors" which can be erased independently
521 * with a new command: ST_SSE. The ST_SE command erases 64KB just as
522 * before.
524 sflash_cmd(osh, cc, (sfl->blocksize < (64 * 1024)) ? SFLASH_ST_SSE : SFLASH_ST_SE);
525 return sfl->blocksize;
526 case SFLASH_AT:
527 W_REG(osh, &cc->flashaddress, offset << 1);
528 sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE);
529 return sfl->blocksize;
532 return 0;
536 * writes the appropriate range of flash, a NULL buf simply erases
537 * the region of flash
540 sflash_commit(si_t *sih, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
542 struct sflash *sfl;
543 uchar *block = NULL, *cur_ptr, *blk_ptr;
544 uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
545 uint blk_offset, blk_len, copied;
546 int bytes, ret = 0;
547 osl_t *osh;
549 ASSERT(sih);
551 osh = si_osh(sih);
553 /* Check address range */
554 if (len <= 0)
555 return 0;
557 sfl = &sflash;
558 if ((offset + len) > sfl->size)
559 return -1;
561 blocksize = sfl->blocksize;
562 mask = blocksize - 1;
564 /* Allocate a block of mem */
565 if (!(block = MALLOC(osh, blocksize)))
566 return -1;
568 while (len) {
569 /* Align offset */
570 cur_offset = offset & ~mask;
571 cur_length = blocksize;
572 cur_ptr = block;
574 remainder = blocksize - (offset & mask);
575 if (len < remainder)
576 cur_retlen = len;
577 else
578 cur_retlen = remainder;
580 /* buf == NULL means erase only */
581 if (buf) {
582 /* Copy existing data into holding block if necessary */
583 if ((offset & mask) || (len < blocksize)) {
584 blk_offset = cur_offset;
585 blk_len = cur_length;
586 blk_ptr = cur_ptr;
588 /* Copy entire block */
589 while (blk_len) {
590 copied = sflash_read(sih, cc, blk_offset, blk_len, blk_ptr);
591 blk_offset += copied;
592 blk_len -= copied;
593 blk_ptr += copied;
597 /* Copy input data into holding block */
598 memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
601 /* Erase block */
602 if ((ret = sflash_erase(sih, cc, (uint) cur_offset)) < 0)
603 goto done;
604 while (sflash_poll(sih, cc, (uint) cur_offset));
606 /* buf == NULL means erase only */
607 if (!buf) {
608 offset += cur_retlen;
609 len -= cur_retlen;
610 continue;
613 /* Write holding block */
614 while (cur_length > 0) {
615 if ((bytes = sflash_write(sih, cc,
616 (uint) cur_offset,
617 (uint) cur_length,
618 (uchar *) cur_ptr)) < 0) {
619 ret = bytes;
620 goto done;
622 while (sflash_poll(sih, cc, (uint) cur_offset));
623 cur_offset += bytes;
624 cur_length -= bytes;
625 cur_ptr += bytes;
628 offset += cur_retlen;
629 len -= cur_retlen;
630 buf += cur_retlen;
633 ret = len;
634 done:
635 if (block)
636 MFREE(osh, block, blocksize);
637 return ret;