2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright 2007, 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.
17 #include "include/bcmutils.h"
24 /* Private global state */
25 static struct sflash sflash
;
27 /* Issue a serial flash command */
29 sflash_cmd(osl_t
*osh
, chipcregs_t
*cc
, uint opcode
)
31 W_REG(osh
, &cc
->flashcontrol
, SFLASH_START
| opcode
);
32 while (R_REG(osh
, &cc
->flashcontrol
) & SFLASH_BUSY
);
35 /* Initialize serial flash access */
37 sflash_init(sb_t
*sbh
, chipcregs_t
*cc
)
46 bzero(&sflash
, sizeof(sflash
));
48 sflash
.type
= sbh
->cccaps
& CC_CAP_FLASH_MASK
;
50 switch (sflash
.type
) {
52 /* Probe for ST chips */
53 sflash_cmd(osh
, cc
, SFLASH_ST_DP
);
54 sflash_cmd(osh
, cc
, SFLASH_ST_RES
);
55 id
= R_REG(osh
, &cc
->flashdata
);
58 /* ST M25P20 2 Mbit Serial Flash */
59 sflash
.blocksize
= 64 * 1024;
63 /* ST M25P40 4 Mbit Serial Flash */
64 sflash
.blocksize
= 64 * 1024;
68 /* ST M25P80 8 Mbit Serial Flash */
69 sflash
.blocksize
= 64 * 1024;
70 sflash
.numblocks
= 16;
73 /* ST M25P16 16 Mbit Serial Flash */
74 sflash
.blocksize
= 64 * 1024;
75 sflash
.numblocks
= 32;
78 /* ST M25P32 32 Mbit Serial Flash */
79 sflash
.blocksize
= 64 * 1024;
80 sflash
.numblocks
= 64;
83 /* ST M25P64 64 Mbit Serial Flash */
84 sflash
.blocksize
= 64 * 1024;
85 sflash
.numblocks
= 128;
88 W_REG(osh
, &cc
->flashaddress
, 1);
89 sflash_cmd(osh
, cc
, SFLASH_ST_RES
);
90 id2
= R_REG(osh
, &cc
->flashdata
);
92 /* SST M25VF80 4 Mbit Serial Flash */
93 sflash
.blocksize
= 64 * 1024;
101 /* Probe for Atmel chips */
102 sflash_cmd(osh
, cc
, SFLASH_AT_STATUS
);
103 id
= R_REG(osh
, &cc
->flashdata
) & 0x3c;
106 /* Atmel AT45DB011 1Mbit Serial Flash */
107 sflash
.blocksize
= 256;
108 sflash
.numblocks
= 512;
111 /* Atmel AT45DB021 2Mbit Serial Flash */
112 sflash
.blocksize
= 256;
113 sflash
.numblocks
= 1024;
116 /* Atmel AT45DB041 4Mbit Serial Flash */
117 sflash
.blocksize
= 256;
118 sflash
.numblocks
= 2048;
121 /* Atmel AT45DB081 8Mbit Serial Flash */
122 sflash
.blocksize
= 256;
123 sflash
.numblocks
= 4096;
126 /* Atmel AT45DB161 16Mbit Serial Flash */
127 sflash
.blocksize
= 512;
128 sflash
.numblocks
= 4096;
131 /* Atmel AT45DB321 32Mbit Serial Flash */
132 sflash
.blocksize
= 512;
133 sflash
.numblocks
= 8192;
136 /* Atmel AT45DB642 64Mbit Serial Flash */
137 sflash
.blocksize
= 1024;
138 sflash
.numblocks
= 8192;
144 sflash
.size
= sflash
.blocksize
* sflash
.numblocks
;
145 return sflash
.size
? &sflash
: NULL
;
148 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
150 sflash_read(sb_t
*sbh
, chipcregs_t
*cc
, uint offset
, uint len
, uchar
*buf
)
161 if ((offset
+ len
) > sflash
.size
)
164 if ((len
>= 4) && (offset
& 3))
165 cnt
= 4 - (offset
& 3);
166 else if ((len
>= 4) && ((uintptr
)buf
& 3))
167 cnt
= 4 - ((uintptr
)buf
& 3);
173 from
= (uint8
*)(uintptr
)OSL_UNCACHED(SB_FLASH2
+ offset
);
177 for (i
= 0; i
< cnt
; i
++) {
178 *to
= R_REG(osh
, from
);
186 *(uint32
*)to
= R_REG(osh
, (uint32
*)from
);
195 /* Poll for command completion. Returns zero when complete. */
197 sflash_poll(sb_t
*sbh
, chipcregs_t
*cc
, uint offset
)
205 if (offset
>= sflash
.size
)
208 switch (sflash
.type
) {
210 /* Check for ST Write In Progress bit */
211 sflash_cmd(osh
, cc
, SFLASH_ST_RDSR
);
212 return R_REG(osh
, &cc
->flashdata
) & SFLASH_ST_WIP
;
214 /* Check for Atmel Ready bit */
215 sflash_cmd(osh
, cc
, SFLASH_AT_STATUS
);
216 return !(R_REG(osh
, &cc
->flashdata
) & SFLASH_AT_READY
);
222 /* Write len bytes starting at offset into buf. Returns number of bytes
223 * written. Caller should poll for completion.
226 sflash_write(sb_t
*sbh
, chipcregs_t
*cc
, uint offset
, uint len
, const uchar
*buf
)
231 uint32 page
, byte
, mask
;
241 if ((offset
+ len
) > sflash
.size
)
247 is4712b0
= (sbh
->chip
== BCM4712_CHIP_ID
) && (sbh
->chiprev
== 3);
249 sflash_cmd(osh
, cc
, SFLASH_ST_WREN
);
252 W_REG(osh
, &cc
->flashaddress
, offset
);
253 W_REG(osh
, &cc
->flashdata
, *buf
++);
254 /* Set chip select */
255 OR_REG(osh
, &cc
->gpioout
, mask
);
256 /* Issue a page program with the first byte */
257 sflash_cmd(osh
, cc
, SFLASH_ST_PP
);
262 if ((offset
& 255) == 0) {
263 /* Page boundary, drop cs and return */
264 AND_REG(osh
, &cc
->gpioout
, ~mask
);
265 if (!sflash_poll(sbh
, cc
, offset
)) {
266 /* Flash rejected command */
271 /* Write single byte */
272 sflash_cmd(osh
, cc
, *buf
++);
278 /* All done, drop cs if needed */
279 if ((offset
& 255) != 1) {
281 AND_REG(osh
, &cc
->gpioout
, ~mask
);
282 if (!sflash_poll(sbh
, cc
, offset
)) {
283 /* Flash rejected command */
287 } else if ( (sbh
->ccrev
>= 20) && (len
!= 1) ) {
288 //} else if ( sbh->ccrev >= 20 ) { /* foxconn modified by EricHuang, 05/24/2007 */
289 W_REG(NULL
, &cc
->flashaddress
, offset
);
290 W_REG(NULL
, &cc
->flashdata
, *buf
++);
291 /* Issue a page program with CSA bit set */
292 sflash_cmd(osh
, cc
, SFLASH_ST_CSA
| SFLASH_ST_PP
);
297 if ((offset
& 255) == 0) {
298 /* Page boundary, poll droping cs and return */
299 W_REG(NULL
, &cc
->flashcontrol
, 0);
300 /* wklin added start, 06/08/2007 */
301 W_REG(NULL
, &cc
->flashcontrol
, 0);
303 /* wklin added end, 06/08/2007 */
304 /* wklin rmeoved start, 06/08/2007 */
306 if (!sflash_poll(sbh
, cc
, offset
)) {
307 /* Flash rejected command */
311 /* wklin removed end, 06/08/2007 */
314 /* Write single byte */
315 sflash_cmd(osh
, cc
, SFLASH_ST_CSA
| *buf
++);
321 /* All done, drop cs if needed */
322 if ((offset
& 255) != 1) {
324 W_REG(NULL
, &cc
->flashcontrol
, 0);
325 /* wklin added start, 06/08/2007 */
326 W_REG(NULL
, &cc
->flashcontrol
, 0);
328 /* wklin added end, 06/08/2007 */
329 /* wklin removed start, 06/08/2007 */
331 if (!sflash_poll(sbh
, cc
, offset
)) {
332 /* Flash rejected command */
336 /* wklin removed end, 06/08/2007 */
340 W_REG(osh
, &cc
->flashaddress
, offset
);
341 W_REG(osh
, &cc
->flashdata
, *buf
);
343 sflash_cmd(osh
, cc
, SFLASH_ST_PP
);
347 mask
= sfl
->blocksize
- 1;
348 page
= (offset
& ~mask
) << 1;
349 byte
= offset
& mask
;
350 /* Read main memory page into buffer 1 */
351 if (byte
|| (len
< sfl
->blocksize
)) {
352 W_REG(osh
, &cc
->flashaddress
, page
);
353 sflash_cmd(osh
, cc
, SFLASH_AT_BUF1_LOAD
);
354 /* 250 us for AT45DB321B */
355 SPINWAIT(sflash_poll(sbh
, cc
, offset
), 1000);
356 ASSERT(!sflash_poll(sbh
, cc
, offset
));
358 /* Write into buffer 1 */
359 for (ret
= 0; (ret
< (int)len
) && (byte
< sfl
->blocksize
); ret
++) {
360 W_REG(osh
, &cc
->flashaddress
, byte
++);
361 W_REG(osh
, &cc
->flashdata
, *buf
++);
362 sflash_cmd(osh
, cc
, SFLASH_AT_BUF1_WRITE
);
364 /* Write buffer 1 into main memory page */
365 W_REG(osh
, &cc
->flashaddress
, page
);
366 sflash_cmd(osh
, cc
, SFLASH_AT_BUF1_PROGRAM
);
373 /* Erase a region. Returns number of bytes scheduled for erasure.
374 * Caller should poll for completion.
377 sflash_erase(sb_t
*sbh
, chipcregs_t
*cc
, uint offset
)
386 if (offset
>= sflash
.size
)
392 sflash_cmd(osh
, cc
, SFLASH_ST_WREN
);
393 W_REG(osh
, &cc
->flashaddress
, offset
);
394 sflash_cmd(osh
, cc
, SFLASH_ST_SE
);
395 return sfl
->blocksize
;
397 W_REG(osh
, &cc
->flashaddress
, offset
<< 1);
398 sflash_cmd(osh
, cc
, SFLASH_AT_PAGE_ERASE
);
399 return sfl
->blocksize
;
406 * writes the appropriate range of flash, a NULL buf simply erases
407 * the region of flash
410 sflash_commit(sb_t
*sbh
, chipcregs_t
*cc
, uint offset
, uint len
, const uchar
*buf
)
413 uchar
*block
= NULL
, *cur_ptr
, *blk_ptr
;
414 uint blocksize
= 0, mask
, cur_offset
, cur_length
, cur_retlen
, remainder
;
415 uint blk_offset
, blk_len
, copied
;
423 /* Check address range */
428 if ((offset
+ len
) > sfl
->size
)
431 blocksize
= sfl
->blocksize
;
432 mask
= blocksize
- 1;
434 /* Allocate a block of mem */
435 if (!(block
= MALLOC(osh
, blocksize
)))
440 cur_offset
= offset
& ~mask
;
441 cur_length
= blocksize
;
444 remainder
= blocksize
- (offset
& mask
);
448 cur_retlen
= remainder
;
450 /* buf == NULL means erase only */
452 /* Copy existing data into holding block if necessary */
453 if ((offset
& mask
) || (len
< blocksize
)) {
454 blk_offset
= cur_offset
;
455 blk_len
= cur_length
;
458 /* Copy entire block */
460 copied
= sflash_read(sbh
, cc
, blk_offset
, blk_len
, blk_ptr
);
461 blk_offset
+= copied
;
467 /* Copy input data into holding block */
468 memcpy(cur_ptr
+ (offset
& mask
), buf
, cur_retlen
);
472 if ((ret
= sflash_erase(sbh
, cc
, (uint
) cur_offset
)) < 0)
474 while (sflash_poll(sbh
, cc
, (uint
) cur_offset
));
476 /* buf == NULL means erase only */
478 offset
+= cur_retlen
;
483 /* Write holding block */
484 while (cur_length
> 0) {
485 if ((bytes
= sflash_write(sbh
, cc
,
488 (uchar
*) cur_ptr
)) < 0) {
492 while (sflash_poll(sbh
, cc
, (uint
) cur_offset
));
498 offset
+= cur_retlen
;
506 MFREE(osh
, block
, blocksize
);
This page took 0.073252 seconds and 5 git commands to generate.