2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright (C) 2009, Broadcom Corporation
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
12 * $Id: sflash.c,v 1.44.2.7 2010/02/18 02:08:55 Exp $
27 bool sflash_uncached
= FALSE
;
29 /* Private global state */
30 static struct sflash sflash
;
32 /* Issue a serial flash command */
34 sflash_cmd(osl_t
*osh
, chipcregs_t
*cc
, uint opcode
)
36 W_REG(osh
, &cc
->flashcontrol
, SFLASH_START
| opcode
);
37 while (R_REG(osh
, &cc
->flashcontrol
) & SFLASH_BUSY
);
40 /* Initialize serial flash access */
42 sflash_init(si_t
*sih
, chipcregs_t
*cc
)
47 static bool firsttime
= TRUE
;
53 bzero(&sflash
, sizeof(sflash
));
55 sflash
.type
= sih
->cccaps
& CC_CAP_FLASH_MASK
;
57 switch (sflash
.type
) {
59 /* Probe for ST chips */
60 name
= "ST compatible";
61 sflash_cmd(osh
, cc
, SFLASH_ST_DP
);
62 sflash_cmd(osh
, cc
, SFLASH_ST_RES
);
63 id
= R_REG(osh
, &cc
->flashdata
);
66 /* ST M25P20 2 Mbit Serial Flash */
67 sflash
.blocksize
= 64 * 1024;
71 /* ST M25P40 4 Mbit Serial Flash */
72 sflash
.blocksize
= 64 * 1024;
76 /* ST M25P80 8 Mbit Serial Flash */
77 sflash
.blocksize
= 64 * 1024;
78 sflash
.numblocks
= 16;
81 /* ST M25P16 16 Mbit Serial Flash */
82 sflash
.blocksize
= 64 * 1024;
83 sflash
.numblocks
= 32;
86 /* ST M25P32 32 Mbit Serial Flash */
87 sflash
.blocksize
= 64 * 1024;
88 sflash
.numblocks
= 64;
91 /* ST M25P64 64 Mbit Serial Flash */
92 sflash
.blocksize
= 64 * 1024;
93 sflash
.numblocks
= 128;
96 /* ST M25FL128 128 Mbit Serial Flash */
97 sflash
.blocksize
= 64 * 1024;
98 sflash
.numblocks
= 256;
101 W_REG(osh
, &cc
->flashaddress
, 1);
102 sflash_cmd(osh
, cc
, SFLASH_ST_RES
);
103 id2
= R_REG(osh
, &cc
->flashdata
);
105 /* SST M25VF80 4 Mbit Serial Flash */
107 sflash
.blocksize
= 64 * 1024;
108 sflash
.numblocks
= 8;
115 /* Probe for Atmel chips */
117 sflash_cmd(osh
, cc
, SFLASH_AT_STATUS
);
118 id
= R_REG(osh
, &cc
->flashdata
) & 0x3c;
121 /* Atmel AT45DB011 1Mbit Serial Flash */
122 sflash
.blocksize
= 256;
123 sflash
.numblocks
= 512;
126 /* Atmel AT45DB021 2Mbit Serial Flash */
127 sflash
.blocksize
= 256;
128 sflash
.numblocks
= 1024;
131 /* Atmel AT45DB041 4Mbit Serial Flash */
132 sflash
.blocksize
= 256;
133 sflash
.numblocks
= 2048;
136 /* Atmel AT45DB081 8Mbit Serial Flash */
137 sflash
.blocksize
= 256;
138 sflash
.numblocks
= 4096;
141 /* Atmel AT45DB161 16Mbit Serial Flash */
142 sflash
.blocksize
= 512;
143 sflash
.numblocks
= 4096;
146 /* Atmel AT45DB321 32Mbit Serial Flash */
147 sflash
.blocksize
= 512;
148 sflash
.numblocks
= 8192;
151 /* Atmel AT45DB642 64Mbit Serial Flash */
152 sflash
.blocksize
= 1024;
153 sflash
.numblocks
= 8192;
159 sflash
.size
= sflash
.blocksize
* sflash
.numblocks
;
162 printf("Found a %dMB %s serial flash\n",
163 sflash
.size
/ (1024 * 1024), name
);
166 if (((sih
->chip
== BCM4716_CHIP_ID
) || (sih
->chip
== BCM4748_CHIP_ID
)) &&
167 (sih
->chiprev
== 0)) {
168 if (sflash
.size
> (4 * 1024 * 1024)) {
169 sflash
.size
= 4 * 1024 * 1024;
171 printf("Using only 4MB in BCM4716a0\n");
175 return sflash
.size
? &sflash
: NULL
;
178 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
180 sflash_read(si_t
*sih
, chipcregs_t
*cc
, uint offset
, uint len
, uchar
*buf
)
191 if ((offset
+ len
) > sflash
.size
)
194 if ((len
>= 4) && (offset
& 3))
195 cnt
= 4 - (offset
& 3);
196 else if ((len
>= 4) && ((uintptr
)buf
& 3))
197 cnt
= 4 - ((uintptr
)buf
& 3);
203 if (sih
->ccrev
== 12)
204 from
= (uint8
*)OSL_UNCACHED(SI_FLASH2
+ offset
);
206 /* Read sflash using uncached addresses if the override exists.
207 * Otherwise default to reading thru' cache.
209 from
= (uint8
*)(sflash_uncached
? OSL_UNCACHED(SI_FLASH2
+ offset
) :
210 OSL_CACHED(SI_FLASH2
+ offset
));
214 for (i
= 0; i
< cnt
; i
++) {
215 /* Cannot use R_REG because in bigendian that will
216 * xor the address and we don't want that here.
226 *(uint32
*)to
= *(uint32
*)from
;
235 /* Poll for command completion. Returns zero when complete. */
237 sflash_poll(si_t
*sih
, chipcregs_t
*cc
, uint offset
)
245 if (offset
>= sflash
.size
)
248 switch (sflash
.type
) {
250 /* Check for ST Write In Progress bit */
251 sflash_cmd(osh
, cc
, SFLASH_ST_RDSR
);
252 return R_REG(osh
, &cc
->flashdata
) & SFLASH_ST_WIP
;
254 /* Check for Atmel Ready bit */
255 sflash_cmd(osh
, cc
, SFLASH_AT_STATUS
);
256 return !(R_REG(osh
, &cc
->flashdata
) & SFLASH_AT_READY
);
262 /* Write len bytes starting at offset into buf. Returns number of bytes
263 * written. Caller should poll for completion.
269 #define GET_BYTE(ptr) (*(uint8 *)((uint32)(ptr) ^ 7))
270 #else /* !74K, bcm33xx */
271 #define GET_BYTE(ptr) (*(uint8 *)((uint32)(ptr) ^ 3))
272 #endif /* BCMHND74K */
273 #else /* !IL_BIGENDIAN */
274 #define GET_BYTE(ptr) (*(ptr))
275 #endif /* IL_BIGENDIAN */
278 sflash_write(si_t
*sih
, chipcregs_t
*cc
, uint offset
, uint length
, const uchar
*buffer
)
281 uint off
= offset
, len
= length
;
282 const uint8
*buf
= buffer
;
284 int ret
= 0, ntry
= 0;
286 uint32 page
, byte
, mask
;
297 if ((off
+ len
) > sfl
->size
)
302 is4712b0
= (sih
->chip
== BCM4712_CHIP_ID
) && (sih
->chiprev
== 3);
304 retry
: sflash_cmd(osh
, cc
, SFLASH_ST_WREN
);
311 W_REG(osh
, &cc
->flashaddress
, off
);
312 data
= GET_BYTE(buf
);
314 W_REG(osh
, &cc
->flashdata
, data
);
315 /* Set chip select */
316 OR_REG(osh
, &cc
->gpioout
, mask
);
317 /* Issue a page program with the first byte */
318 sflash_cmd(osh
, cc
, SFLASH_ST_PP
);
323 if ((off
& 255) == 0) {
324 /* Page boundary, drop cs and return */
325 AND_REG(osh
, &cc
->gpioout
, ~mask
);
327 if (!sflash_poll(sih
, cc
, off
)) {
328 /* Flash rejected command */
329 if (ntry
<= ST_RETRIES
)
336 /* Write single byte */
337 data
= GET_BYTE(buf
);
339 sflash_cmd(osh
, cc
, data
);
345 /* All done, drop cs */
346 AND_REG(osh
, &cc
->gpioout
, ~mask
);
348 if (!sflash_poll(sih
, cc
, off
)) {
349 /* Flash rejected command */
350 if (ntry
<= ST_RETRIES
)
355 } else if (sih
->ccrev
>= 20) {
356 W_REG(osh
, &cc
->flashaddress
, off
);
357 data
= GET_BYTE(buf
);
359 W_REG(osh
, &cc
->flashdata
, data
);
360 /* Issue a page program with CSA bit set */
361 sflash_cmd(osh
, cc
, SFLASH_ST_CSA
| SFLASH_ST_PP
);
366 if ((off
& 255) == 0) {
367 /* Page boundary, poll droping cs and return */
368 W_REG(NULL
, &cc
->flashcontrol
, 0);
370 if (sflash_poll(sih
, cc
, off
) == 0) {
371 /* Flash rejected command */
372 SFL_MSG(("sflash: pp rejected, ntry: %d,"
373 " off: %d/%d, len: %d/%d, ret:"
374 "%d\n", ntry
, off
, offset
, len
,
376 if (ntry
<= ST_RETRIES
)
383 /* Write single byte */
384 data
= GET_BYTE(buf
);
386 sflash_cmd(osh
, cc
, SFLASH_ST_CSA
| data
);
392 /* All done, drop cs & poll */
393 W_REG(NULL
, &cc
->flashcontrol
, 0);
395 if (sflash_poll(sih
, cc
, off
) == 0) {
396 /* Flash rejected command */
397 SFL_MSG(("sflash: pp rejected, ntry: %d, off: %d/%d,"
398 " len: %d/%d, ret: %d\n",
399 ntry
, off
, offset
, len
, length
, ret
));
400 if (ntry
<= ST_RETRIES
)
407 W_REG(osh
, &cc
->flashaddress
, off
);
408 data
= GET_BYTE(buf
);
410 W_REG(osh
, &cc
->flashdata
, data
);
412 sflash_cmd(osh
, cc
, SFLASH_ST_PP
);
416 mask
= sfl
->blocksize
- 1;
417 page
= (off
& ~mask
) << 1;
419 /* Read main memory page into buffer 1 */
420 if (byte
|| (len
< sfl
->blocksize
)) {
421 W_REG(osh
, &cc
->flashaddress
, page
);
422 sflash_cmd(osh
, cc
, SFLASH_AT_BUF1_LOAD
);
423 /* 250 us for AT45DB321B */
424 SPINWAIT(sflash_poll(sih
, cc
, off
), 1000);
425 ASSERT(!sflash_poll(sih
, cc
, off
));
427 /* Write into buffer 1 */
428 for (ret
= 0; (ret
< (int)len
) && (byte
< sfl
->blocksize
); ret
++) {
429 W_REG(osh
, &cc
->flashaddress
, byte
++);
430 W_REG(osh
, &cc
->flashdata
, *buf
++);
431 sflash_cmd(osh
, cc
, SFLASH_AT_BUF1_WRITE
);
433 /* Write buffer 1 into main memory page */
434 W_REG(osh
, &cc
->flashaddress
, page
);
435 sflash_cmd(osh
, cc
, SFLASH_AT_BUF1_PROGRAM
);
442 /* Erase a region. Returns number of bytes scheduled for erasure.
443 * Caller should poll for completion.
446 sflash_erase(si_t
*sih
, chipcregs_t
*cc
, uint offset
)
456 if (offset
>= sfl
->size
)
461 sflash_cmd(osh
, cc
, SFLASH_ST_WREN
);
462 W_REG(osh
, &cc
->flashaddress
, offset
);
463 sflash_cmd(osh
, cc
, SFLASH_ST_SE
);
464 return sfl
->blocksize
;
466 W_REG(osh
, &cc
->flashaddress
, offset
<< 1);
467 sflash_cmd(osh
, cc
, SFLASH_AT_PAGE_ERASE
);
468 return sfl
->blocksize
;
475 * writes the appropriate range of flash, a NULL buf simply erases
476 * the region of flash
479 sflash_commit(si_t
*sih
, chipcregs_t
*cc
, uint offset
, uint len
, const uchar
*buf
)
482 uchar
*block
= NULL
, *cur_ptr
, *blk_ptr
;
483 uint blocksize
= 0, mask
, cur_offset
, cur_length
, cur_retlen
, remainder
;
484 uint blk_offset
, blk_len
, copied
;
492 /* Check address range */
497 if ((offset
+ len
) > sfl
->size
)
500 blocksize
= sfl
->blocksize
;
501 mask
= blocksize
- 1;
503 /* Allocate a block of mem */
504 if (!(block
= MALLOC(osh
, blocksize
)))
509 cur_offset
= offset
& ~mask
;
510 cur_length
= blocksize
;
513 remainder
= blocksize
- (offset
& mask
);
517 cur_retlen
= remainder
;
519 /* buf == NULL means erase only */
521 /* Copy existing data into holding block if necessary */
522 if ((offset
& mask
) || (len
< blocksize
)) {
523 blk_offset
= cur_offset
;
524 blk_len
= cur_length
;
527 /* Copy entire block */
529 copied
= sflash_read(sih
, cc
, blk_offset
, blk_len
, blk_ptr
);
530 blk_offset
+= copied
;
536 /* Copy input data into holding block */
537 memcpy(cur_ptr
+ (offset
& mask
), buf
, cur_retlen
);
541 if ((ret
= sflash_erase(sih
, cc
, (uint
) cur_offset
)) < 0)
543 while (sflash_poll(sih
, cc
, (uint
) cur_offset
));
545 /* buf == NULL means erase only */
547 offset
+= cur_retlen
;
552 /* Write holding block */
553 while (cur_length
> 0) {
554 if ((bytes
= sflash_write(sih
, cc
,
557 (uchar
*) cur_ptr
)) < 0) {
561 while (sflash_poll(sih
, cc
, (uint
) cur_offset
));
567 offset
+= cur_retlen
;
575 MFREE(osh
, block
, blocksize
);