2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2012, Broadcom Corporation. All Rights Reserved.
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.
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 $
30 #include <hndsflash.h>
33 #define SFL_MSG(args) printf args
38 /* Private global state */
39 static hndsflash_t ccsflash
;
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 */
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 */
62 ccsflash_init(si_t
*sih
)
66 const char *name
= "";
71 /* No sflash for NorthStar */
75 if ((cc
= (chipcregs_t
*)si_setcoreidx(sih
, SI_CC_IDX
)) == NULL
)
78 if (!firsttime
&& ccsflash
.size
!= 0)
83 bzero(&ccsflash
, sizeof(ccsflash
));
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
) {
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;
106 /* ST M25P20 2 Mbit Serial Flash */
107 ccsflash
.numblocks
= 4;
110 /* ST M25P40 4 Mbit Serial Flash */
111 ccsflash
.numblocks
= 8;
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;
121 /* ST M25P80 8 Mbit Serial Flash */
122 ccsflash
.numblocks
= 16;
126 /* ST M25P16 16 Mbit Serial Flash */
127 ccsflash
.numblocks
= 32;
130 /* ST M25P32 32 Mbit Serial Flash */
131 ccsflash
.numblocks
= 64;
134 /* ST M25P64 64 Mbit Serial Flash */
135 ccsflash
.numblocks
= 128;
138 /* ST M25FL128 128 Mbit Serial Flash */
139 ccsflash
.numblocks
= 256;
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.
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
);
154 /* SST25WF512 512 Kbit Serial Flash */
155 ccsflash
.numblocks
= 16;
158 /* SST25VF512 512 Kbit Serial Flash */
159 ccsflash
.numblocks
= 16;
162 /* SST25WF010 1 Mbit Serial Flash */
163 ccsflash
.numblocks
= 32;
166 /* SST25VF010 1 Mbit Serial Flash */
167 ccsflash
.numblocks
= 32;
170 /* SST25WF020 2 Mbit Serial Flash */
171 ccsflash
.numblocks
= 64;
174 /* SST25VF020 2 Mbit Serial Flash */
175 ccsflash
.numblocks
= 64;
178 /* SST25WF040 4 Mbit Serial Flash */
179 ccsflash
.numblocks
= 128;
182 /* SST25VF040 4 Mbit Serial Flash */
183 ccsflash
.numblocks
= 128;
186 /* SST25VF040B 4 Mbit Serial Flash */
187 ccsflash
.numblocks
= 128;
190 /* SST25WF080 8 Mbit Serial Flash */
191 ccsflash
.numblocks
= 256;
194 /* SST25VF080B 8 Mbit Serial Flash */
195 ccsflash
.numblocks
= 256;
198 /* SST25VF016 16 Mbit Serial Flash */
199 ccsflash
.numblocks
= 512;
202 /* SST25VF032 32 Mbit Serial Flash */
203 ccsflash
.numblocks
= 1024;
206 /* SST25VF064 64 Mbit Serial Flash */
207 ccsflash
.numblocks
= 2048;
215 /* Probe for Atmel chips */
217 ccsflash_cmd(osh
, cc
, SFLASH_AT_STATUS
);
218 id
= R_REG(osh
, &cc
->flashdata
) & 0x3c;
221 /* Atmel AT45DB011 1Mbit Serial Flash */
222 ccsflash
.blocksize
= 256;
223 ccsflash
.numblocks
= 512;
226 /* Atmel AT45DB021 2Mbit Serial Flash */
227 ccsflash
.blocksize
= 256;
228 ccsflash
.numblocks
= 1024;
231 /* Atmel AT45DB041 4Mbit Serial Flash */
232 ccsflash
.blocksize
= 256;
233 ccsflash
.numblocks
= 2048;
236 /* Atmel AT45DB081 8Mbit Serial Flash */
237 ccsflash
.blocksize
= 256;
238 ccsflash
.numblocks
= 4096;
241 /* Atmel AT45DB161 16Mbit Serial Flash */
242 ccsflash
.blocksize
= 512;
243 ccsflash
.numblocks
= 4096;
246 /* Atmel AT45DB321 32Mbit Serial Flash */
247 ccsflash
.blocksize
= 512;
248 ccsflash
.numblocks
= 8192;
251 /* Atmel AT45DB642 64Mbit Serial Flash */
252 ccsflash
.blocksize
= 1024;
253 ccsflash
.numblocks
= 8192;
259 ccsflash
.size
= ccsflash
.blocksize
* ccsflash
.numblocks
;
260 ccsflash
.phybase
= SI_FLASH2
;
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));
268 return ccsflash
.size
? &ccsflash
: NULL
;
271 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
273 ccsflash_read(hndsflash_t
*sfl
, uint offset
, uint len
, const uchar
*buf
)
275 si_t
*sih
= sfl
->sih
;
284 if ((offset
+ len
) > sfl
->size
)
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);
294 if (sih
->ccrev
== 12)
295 from
= (uint8
*)OSL_UNCACHED((void *)SI_FLASH2
+ offset
);
297 from
= (uint8
*)OSL_CACHED((void *)SI_FLASH2
+ offset
);
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.
313 *(uint32
*)to
= *(uint32
*)from
;
322 /* Poll for command completion. Returns zero when complete. */
324 ccsflash_poll(hndsflash_t
*sfl
, uint offset
)
326 si_t
*sih
= sfl
->sih
;
327 chipcregs_t
*cc
= (chipcregs_t
*)sfl
->core
;
334 if (offset
>= sfl
->size
)
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
;
343 /* Check for Atmel Ready bit */
344 ccsflash_cmd(osh
, cc
, SFLASH_AT_STATUS
);
345 return !(R_REG(osh
, &cc
->flashdata
) & SFLASH_AT_READY
);
351 /* Write len bytes starting at offset into buf. Returns number of bytes
352 * written. Caller should poll for completion.
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 */
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
;
374 int ret
= 0, ntry
= 0;
376 uint32 page
, byte
, mask
;
386 if ((off
+ len
) > sfl
->size
)
391 is4712b0
= (CHIPID(sih
->chip
) == BCM4712_CHIP_ID
) && (CHIPREV(sih
->chiprev
) == 3);
393 retry
: ccsflash_cmd(osh
, cc
, SFLASH_ST_WREN
);
400 W_REG(osh
, &cc
->flashaddress
, off
);
401 data
= GET_BYTE(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
);
412 if ((off
& 255) == 0) {
413 /* Page boundary, drop cs and return */
414 AND_REG(osh
, &cc
->gpioout
, ~mask
);
416 if (!ccsflash_poll(sfl
, off
)) {
417 /* Flash rejected command */
418 if (ntry
<= ST_RETRIES
)
425 /* Write single byte */
426 data
= GET_BYTE(buf
);
428 ccsflash_cmd(osh
, cc
, data
);
434 /* All done, drop cs */
435 AND_REG(osh
, &cc
->gpioout
, ~mask
);
437 if (!ccsflash_poll(sfl
, off
)) {
438 /* Flash rejected command */
439 if (ntry
<= ST_RETRIES
)
444 } else if (sih
->ccrev
>= 20) {
445 W_REG(osh
, &cc
->flashaddress
, off
);
446 data
= GET_BYTE(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
);
455 if ((off
& 255) == 0) {
456 /* Page boundary, poll droping cs and return */
457 W_REG(NULL
, &cc
->flashcontrol
, 0);
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
,
465 if (ntry
<= ST_RETRIES
)
472 /* Write single byte */
473 data
= GET_BYTE(buf
);
475 ccsflash_cmd(osh
, cc
, SFLASH_ST_CSA
| data
);
481 /* All done, drop cs & poll */
482 W_REG(NULL
, &cc
->flashcontrol
, 0);
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
)
496 W_REG(osh
, &cc
->flashaddress
, off
);
497 data
= GET_BYTE(buf
);
499 W_REG(osh
, &cc
->flashdata
, data
);
501 ccsflash_cmd(osh
, cc
, SFLASH_ST_PP
);
505 mask
= sfl
->blocksize
- 1;
506 page
= (off
& ~mask
) << 1;
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
);
531 /* Erase a region. Returns number of bytes scheduled for erasure.
532 * Caller should poll for completion.
535 ccsflash_erase(hndsflash_t
*sfl
, uint offset
)
537 si_t
*sih
= sfl
->sih
;
538 chipcregs_t
*cc
= (chipcregs_t
*)sfl
->core
;
545 if (offset
>= sfl
->size
)
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
556 ccsflash_cmd(osh
, cc
,
557 (sfl
->blocksize
< (64 * 1024)) ? SFLASH_ST_SSE
: SFLASH_ST_SE
);
558 return sfl
->blocksize
;
560 W_REG(osh
, &cc
->flashaddress
, offset
<< 1);
561 ccsflash_cmd(osh
, cc
, SFLASH_AT_PAGE_ERASE
);
562 return sfl
->blocksize
;
569 * writes the appropriate range of flash, a NULL buf simply erases
570 * the region of flash
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
;
586 /* Check address range */
590 if ((offset
+ len
) > sfl
->size
)
593 blocksize
= sfl
->blocksize
;
594 mask
= blocksize
- 1;
596 /* Allocate a block of mem */
597 if (!(block
= MALLOC(osh
, blocksize
)))
602 cur_offset
= offset
& ~mask
;
603 cur_length
= blocksize
;
606 remainder
= blocksize
- (offset
& mask
);
610 cur_retlen
= remainder
;
612 /* buf == NULL means erase only */
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
;
620 /* Copy entire block */
622 copied
= ccsflash_read(sfl
, blk_offset
, blk_len
, blk_ptr
);
623 blk_offset
+= copied
;
629 /* Copy input data into holding block */
630 memcpy(cur_ptr
+ (offset
& mask
), buf
, cur_retlen
);
634 if ((ret
= ccsflash_erase(sfl
, (uint
) cur_offset
)) < 0)
636 while (ccsflash_poll(sfl
, (uint
) cur_offset
));
638 /* buf == NULL means erase only */
640 offset
+= cur_retlen
;
645 /* Write holding block */
646 while (cur_length
> 0) {
647 if ((bytes
= ccsflash_write(sfl
,
650 (uchar
*) cur_ptr
)) < 0) {
654 while (ccsflash_poll(sfl
, (uint
) cur_offset
));
660 offset
+= cur_retlen
;
668 MFREE(osh
, block
, blocksize
);