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.
22 /* Private global state */
23 static struct sflash sflash
;
25 /* Issue a serial flash command */
27 sflash_cmd (osl_t
* osh
, chipcregs_t
* cc
, uint opcode
)
29 W_REG (osh
, &cc
->flashcontrol
, SFLASH_START
| opcode
);
30 while (R_REG (osh
, &cc
->flashcontrol
) & SFLASH_BUSY
);
33 /* Initialize serial flash access */
35 sflash_init (sb_t
* sbh
, chipcregs_t
* cc
)
44 bzero (&sflash
, sizeof (sflash
));
46 sflash
.type
= sbh
->cccaps
& CC_CAP_FLASH_MASK
;
51 /* Probe for ST chips */
52 sflash_cmd (osh
, cc
, SFLASH_ST_DP
);
53 sflash_cmd (osh
, cc
, SFLASH_ST_RES
);
54 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
);
93 /* SST M25VF80 4 Mbit Serial Flash */
94 sflash
.blocksize
= 64 * 1024;
102 /* Probe for Atmel chips */
103 sflash_cmd (osh
, cc
, SFLASH_AT_STATUS
);
104 id
= R_REG (osh
, &cc
->flashdata
) & 0x3c;
108 /* Atmel AT45DB011 1Mbit Serial Flash */
109 sflash
.blocksize
= 256;
110 sflash
.numblocks
= 512;
113 /* Atmel AT45DB021 2Mbit Serial Flash */
114 sflash
.blocksize
= 256;
115 sflash
.numblocks
= 1024;
118 /* Atmel AT45DB041 4Mbit Serial Flash */
119 sflash
.blocksize
= 256;
120 sflash
.numblocks
= 2048;
123 /* Atmel AT45DB081 8Mbit Serial Flash */
124 sflash
.blocksize
= 256;
125 sflash
.numblocks
= 4096;
128 /* Atmel AT45DB161 16Mbit Serial Flash */
129 sflash
.blocksize
= 512;
130 sflash
.numblocks
= 4096;
133 /* Atmel AT45DB321 32Mbit Serial Flash */
134 sflash
.blocksize
= 512;
135 sflash
.numblocks
= 8192;
138 /* Atmel AT45DB642 64Mbit Serial Flash */
139 sflash
.blocksize
= 1024;
140 sflash
.numblocks
= 8192;
146 sflash
.size
= sflash
.blocksize
* sflash
.numblocks
;
147 return sflash
.size
? &sflash
: NULL
;
150 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
152 sflash_read (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
, uint len
, uchar
* buf
)
163 if ((offset
+ len
) > sflash
.size
)
166 if ((len
>= 4) && (offset
& 3))
167 cnt
= 4 - (offset
& 3);
168 else if ((len
>= 4) && ((uintptr
) buf
& 3))
169 cnt
= 4 - ((uintptr
) buf
& 3);
175 from
= (uint8
*) (uintptr
) OSL_UNCACHED (SB_FLASH2
+ offset
);
180 for (i
= 0; i
< cnt
; i
++)
182 *to
= R_REG (osh
, from
);
191 *(uint32
*) to
= R_REG (osh
, (uint32
*) from
);
200 /* Poll for command completion. Returns zero when complete. */
202 sflash_poll (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
)
210 if (offset
>= sflash
.size
)
216 /* Check for ST Write In Progress bit */
217 sflash_cmd (osh
, cc
, SFLASH_ST_RDSR
);
218 return R_REG (osh
, &cc
->flashdata
) & SFLASH_ST_WIP
;
220 /* Check for Atmel Ready bit */
221 sflash_cmd (osh
, cc
, SFLASH_AT_STATUS
);
222 return !(R_REG (osh
, &cc
->flashdata
) & SFLASH_AT_READY
);
228 /* Write len bytes starting at offset into buf. Returns number of bytes
229 * written. Caller should poll for completion.
232 sflash_write (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
, uint len
,
238 uint32 page
, byte
, mask
;
248 if ((offset
+ len
) > sflash
.size
)
255 is4712b0
= (sbh
->chip
== BCM4712_CHIP_ID
) && (sbh
->chiprev
== 3);
257 sflash_cmd (osh
, cc
, SFLASH_ST_WREN
);
261 W_REG (osh
, &cc
->flashaddress
, offset
);
262 W_REG (osh
, &cc
->flashdata
, *buf
++);
263 /* Set chip select */
264 OR_REG (osh
, &cc
->gpioout
, mask
);
265 /* Issue a page program with the first byte */
266 sflash_cmd (osh
, cc
, SFLASH_ST_PP
);
272 if ((offset
& 255) == 0)
274 /* Page boundary, drop cs and return */
275 AND_REG (osh
, &cc
->gpioout
, ~mask
);
276 if (!sflash_poll (sbh
, cc
, offset
))
278 /* Flash rejected command */
285 /* Write single byte */
286 sflash_cmd (osh
, cc
, *buf
++);
292 /* All done, drop cs if needed */
293 if ((offset
& 255) != 1)
296 AND_REG (osh
, &cc
->gpioout
, ~mask
);
297 if (!sflash_poll (sbh
, cc
, offset
))
299 /* Flash rejected command */
304 else if (sbh
->ccrev
>= 20)
306 W_REG (NULL
, &cc
->flashaddress
, offset
);
307 W_REG (NULL
, &cc
->flashdata
, *buf
++);
308 /* Issue a page program with CSA bit set */
309 sflash_cmd (osh
, cc
, SFLASH_ST_CSA
| SFLASH_ST_PP
);
315 if ((offset
& 255) == 0)
317 /* Page boundary, poll droping cs and return */
318 W_REG (NULL
, &cc
->flashcontrol
, 0);
319 if (!sflash_poll (sbh
, cc
, offset
))
321 /* Flash rejected command */
328 /* Write single byte */
329 sflash_cmd (osh
, cc
, SFLASH_ST_CSA
| *buf
++);
335 /* All done, drop cs if needed */
336 if ((offset
& 255) != 1)
339 W_REG (NULL
, &cc
->flashcontrol
, 0);
340 if (!sflash_poll (sbh
, cc
, offset
))
342 /* Flash rejected command */
350 W_REG (osh
, &cc
->flashaddress
, offset
);
351 W_REG (osh
, &cc
->flashdata
, *buf
);
353 sflash_cmd (osh
, cc
, SFLASH_ST_PP
);
357 mask
= sfl
->blocksize
- 1;
358 page
= (offset
& ~mask
) << 1;
359 byte
= offset
& mask
;
360 /* Read main memory page into buffer 1 */
361 if (byte
|| (len
< sfl
->blocksize
))
363 W_REG (osh
, &cc
->flashaddress
, page
);
364 sflash_cmd (osh
, cc
, SFLASH_AT_BUF1_LOAD
);
365 /* 250 us for AT45DB321B */
366 SPINWAIT (sflash_poll (sbh
, cc
, offset
), 1000);
367 ASSERT (!sflash_poll (sbh
, cc
, offset
));
369 /* Write into buffer 1 */
370 for (ret
= 0; (ret
< (int) len
) && (byte
< sfl
->blocksize
); ret
++)
372 W_REG (osh
, &cc
->flashaddress
, byte
++);
373 W_REG (osh
, &cc
->flashdata
, *buf
++);
374 sflash_cmd (osh
, cc
, SFLASH_AT_BUF1_WRITE
);
376 /* Write buffer 1 into main memory page */
377 W_REG (osh
, &cc
->flashaddress
, page
);
378 sflash_cmd (osh
, cc
, SFLASH_AT_BUF1_PROGRAM
);
385 /* Erase a region. Returns number of bytes scheduled for erasure.
386 * Caller should poll for completion.
389 sflash_erase (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
)
398 if (offset
>= sflash
.size
)
405 sflash_cmd (osh
, cc
, SFLASH_ST_WREN
);
406 W_REG (osh
, &cc
->flashaddress
, offset
);
407 sflash_cmd (osh
, cc
, SFLASH_ST_SE
);
408 return sfl
->blocksize
;
410 W_REG (osh
, &cc
->flashaddress
, offset
<< 1);
411 sflash_cmd (osh
, cc
, SFLASH_AT_PAGE_ERASE
);
412 return sfl
->blocksize
;
419 * writes the appropriate range of flash, a NULL buf simply erases
420 * the region of flash
423 sflash_commit (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
, uint len
,
427 uchar
*block
= NULL
, *cur_ptr
, *blk_ptr
;
428 uint blocksize
= 0, mask
, cur_offset
, cur_length
, cur_retlen
, remainder
;
429 uint blk_offset
, blk_len
, copied
;
437 /* Check address range */
442 if ((offset
+ len
) > sfl
->size
)
445 blocksize
= sfl
->blocksize
;
446 mask
= blocksize
- 1;
448 /* Allocate a block of mem */
449 if (!(block
= MALLOC (osh
, blocksize
)))
455 cur_offset
= offset
& ~mask
;
456 cur_length
= blocksize
;
459 remainder
= blocksize
- (offset
& mask
);
463 cur_retlen
= remainder
;
465 /* buf == NULL means erase only */
468 /* Copy existing data into holding block if necessary */
469 if ((offset
& mask
) || (len
< blocksize
))
471 blk_offset
= cur_offset
;
472 blk_len
= cur_length
;
475 /* Copy entire block */
479 sflash_read (sbh
, cc
, blk_offset
, blk_len
, blk_ptr
);
480 blk_offset
+= copied
;
486 /* Copy input data into holding block */
487 memcpy (cur_ptr
+ (offset
& mask
), buf
, cur_retlen
);
491 if ((ret
= sflash_erase (sbh
, cc
, (uint
) cur_offset
)) < 0)
493 while (sflash_poll (sbh
, cc
, (uint
) cur_offset
));
495 /* buf == NULL means erase only */
498 offset
+= cur_retlen
;
503 /* Write holding block */
504 while (cur_length
> 0)
506 if ((bytes
= sflash_write (sbh
, cc
,
509 (uchar
*) cur_ptr
)) < 0)
514 while (sflash_poll (sbh
, cc
, (uint
) cur_offset
));
520 offset
+= cur_retlen
;
528 MFREE (osh
, block
, blocksize
);
This page took 0.065542 seconds and 5 git commands to generate.