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.
23 /* Private global state */
24 static struct sflash sflash
;
26 /* Issue a serial flash command */
28 sflash_cmd (osl_t
* osh
, chipcregs_t
* cc
, uint opcode
)
30 W_REG (osh
, &cc
->flashcontrol
, SFLASH_START
| opcode
);
31 while (R_REG (osh
, &cc
->flashcontrol
) & SFLASH_BUSY
);
34 /* Initialize serial flash access */
36 sflash_init (sb_t
* sbh
, chipcregs_t
* cc
)
45 bzero (&sflash
, sizeof (sflash
));
47 sflash
.type
= sbh
->cccaps
& CC_CAP_FLASH_MASK
;
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
);
59 /* ST M25P20 2 Mbit Serial Flash */
60 sflash
.blocksize
= 64 * 1024;
64 /* ST M25P40 4 Mbit Serial Flash */
65 sflash
.blocksize
= 64 * 1024;
69 /* ST M25P80 8 Mbit Serial Flash */
70 sflash
.blocksize
= 64 * 1024;
71 sflash
.numblocks
= 16;
74 /* ST M25P16 16 Mbit Serial Flash */
75 sflash
.blocksize
= 64 * 1024;
76 sflash
.numblocks
= 32;
79 /* ST M25P32 32 Mbit Serial Flash */
80 sflash
.blocksize
= 64 * 1024;
81 sflash
.numblocks
= 64;
84 /* ST M25P64 64 Mbit Serial Flash */
85 sflash
.blocksize
= 64 * 1024;
86 sflash
.numblocks
= 128;
89 W_REG (osh
, &cc
->flashaddress
, 1);
90 sflash_cmd (osh
, cc
, SFLASH_ST_RES
);
91 id2
= R_REG (osh
, &cc
->flashdata
);
94 /* SST M25VF80 4 Mbit Serial Flash */
95 sflash
.blocksize
= 64 * 1024;
103 /* Probe for Atmel chips */
104 sflash_cmd (osh
, cc
, SFLASH_AT_STATUS
);
105 id
= R_REG (osh
, &cc
->flashdata
) & 0x3c;
109 /* Atmel AT45DB011 1Mbit Serial Flash */
110 sflash
.blocksize
= 256;
111 sflash
.numblocks
= 512;
114 /* Atmel AT45DB021 2Mbit Serial Flash */
115 sflash
.blocksize
= 256;
116 sflash
.numblocks
= 1024;
119 /* Atmel AT45DB041 4Mbit Serial Flash */
120 sflash
.blocksize
= 256;
121 sflash
.numblocks
= 2048;
124 /* Atmel AT45DB081 8Mbit Serial Flash */
125 sflash
.blocksize
= 256;
126 sflash
.numblocks
= 4096;
129 /* Atmel AT45DB161 16Mbit Serial Flash */
130 sflash
.blocksize
= 512;
131 sflash
.numblocks
= 4096;
134 /* Atmel AT45DB321 32Mbit Serial Flash */
135 sflash
.blocksize
= 512;
136 sflash
.numblocks
= 8192;
139 /* Atmel AT45DB642 64Mbit Serial Flash */
140 sflash
.blocksize
= 1024;
141 sflash
.numblocks
= 8192;
147 sflash
.size
= sflash
.blocksize
* sflash
.numblocks
;
148 return sflash
.size
? &sflash
: NULL
;
151 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
153 sflash_read (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
, uint len
, uchar
* buf
)
164 if ((offset
+ len
) > sflash
.size
)
167 if ((len
>= 4) && (offset
& 3))
168 cnt
= 4 - (offset
& 3);
169 else if ((len
>= 4) && ((uintptr
) buf
& 3))
170 cnt
= 4 - ((uintptr
) buf
& 3);
176 from
= (uint8
*) (uintptr
) OSL_UNCACHED (SB_FLASH2
+ offset
);
181 for (i
= 0; i
< cnt
; i
++)
183 *to
= R_REG (osh
, from
);
192 *(uint32
*) to
= R_REG (osh
, (uint32
*) from
);
201 /* Poll for command completion. Returns zero when complete. */
203 sflash_poll (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
)
211 if (offset
>= sflash
.size
)
217 /* Check for ST Write In Progress bit */
218 sflash_cmd (osh
, cc
, SFLASH_ST_RDSR
);
219 return R_REG (osh
, &cc
->flashdata
) & SFLASH_ST_WIP
;
221 /* Check for Atmel Ready bit */
222 sflash_cmd (osh
, cc
, SFLASH_AT_STATUS
);
223 return !(R_REG (osh
, &cc
->flashdata
) & SFLASH_AT_READY
);
229 /* Write len bytes starting at offset into buf. Returns number of bytes
230 * written. Caller should poll for completion.
233 sflash_write (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
, uint len
,
239 uint32 page
, byte
, mask
;
249 if ((offset
+ len
) > sflash
.size
)
256 is4712b0
= (sbh
->chip
== BCM4712_CHIP_ID
) && (sbh
->chiprev
== 3);
258 sflash_cmd (osh
, cc
, SFLASH_ST_WREN
);
262 W_REG (osh
, &cc
->flashaddress
, offset
);
263 W_REG (osh
, &cc
->flashdata
, *buf
++);
264 /* Set chip select */
265 OR_REG (osh
, &cc
->gpioout
, mask
);
266 /* Issue a page program with the first byte */
267 sflash_cmd (osh
, cc
, SFLASH_ST_PP
);
273 if ((offset
& 255) == 0)
275 /* Page boundary, drop cs and return */
276 AND_REG (osh
, &cc
->gpioout
, ~mask
);
277 if (!sflash_poll (sbh
, cc
, offset
))
279 /* Flash rejected command */
286 /* Write single byte */
287 sflash_cmd (osh
, cc
, *buf
++);
293 /* All done, drop cs if needed */
294 if ((offset
& 255) != 1)
297 AND_REG (osh
, &cc
->gpioout
, ~mask
);
298 if (!sflash_poll (sbh
, cc
, offset
))
300 /* Flash rejected command */
305 else if (sbh
->ccrev
>= 20)
307 W_REG (NULL
, &cc
->flashaddress
, offset
);
308 W_REG (NULL
, &cc
->flashdata
, *buf
++);
309 /* Issue a page program with CSA bit set */
310 sflash_cmd (osh
, cc
, SFLASH_ST_CSA
| SFLASH_ST_PP
);
316 if ((offset
& 255) == 0)
318 /* Page boundary, poll droping cs and return */
319 W_REG (NULL
, &cc
->flashcontrol
, 0);
320 if (!sflash_poll (sbh
, cc
, offset
))
322 /* Flash rejected command */
329 /* Write single byte */
330 sflash_cmd (osh
, cc
, SFLASH_ST_CSA
| *buf
++);
336 /* All done, drop cs if needed */
337 if ((offset
& 255) != 1)
340 W_REG (NULL
, &cc
->flashcontrol
, 0);
341 if (!sflash_poll (sbh
, cc
, offset
))
343 /* Flash rejected command */
351 W_REG (osh
, &cc
->flashaddress
, offset
);
352 W_REG (osh
, &cc
->flashdata
, *buf
);
354 sflash_cmd (osh
, cc
, SFLASH_ST_PP
);
358 mask
= sfl
->blocksize
- 1;
359 page
= (offset
& ~mask
) << 1;
360 byte
= offset
& mask
;
361 /* Read main memory page into buffer 1 */
362 if (byte
|| (len
< sfl
->blocksize
))
364 W_REG (osh
, &cc
->flashaddress
, page
);
365 sflash_cmd (osh
, cc
, SFLASH_AT_BUF1_LOAD
);
366 /* 250 us for AT45DB321B */
367 SPINWAIT (sflash_poll (sbh
, cc
, offset
), 1000);
368 ASSERT (!sflash_poll (sbh
, cc
, offset
));
370 /* Write into buffer 1 */
371 for (ret
= 0; (ret
< (int) len
) && (byte
< sfl
->blocksize
); ret
++)
373 W_REG (osh
, &cc
->flashaddress
, byte
++);
374 W_REG (osh
, &cc
->flashdata
, *buf
++);
375 sflash_cmd (osh
, cc
, SFLASH_AT_BUF1_WRITE
);
377 /* Write buffer 1 into main memory page */
378 W_REG (osh
, &cc
->flashaddress
, page
);
379 sflash_cmd (osh
, cc
, SFLASH_AT_BUF1_PROGRAM
);
386 /* Erase a region. Returns number of bytes scheduled for erasure.
387 * Caller should poll for completion.
390 sflash_erase (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
)
399 if (offset
>= sflash
.size
)
406 sflash_cmd (osh
, cc
, SFLASH_ST_WREN
);
407 W_REG (osh
, &cc
->flashaddress
, offset
);
408 sflash_cmd (osh
, cc
, SFLASH_ST_SE
);
409 return sfl
->blocksize
;
411 W_REG (osh
, &cc
->flashaddress
, offset
<< 1);
412 sflash_cmd (osh
, cc
, SFLASH_AT_PAGE_ERASE
);
413 return sfl
->blocksize
;
420 * writes the appropriate range of flash, a NULL buf simply erases
421 * the region of flash
424 sflash_commit (sb_t
* sbh
, chipcregs_t
* cc
, uint offset
, uint len
,
428 uchar
*block
= NULL
, *cur_ptr
, *blk_ptr
;
429 uint blocksize
= 0, mask
, cur_offset
, cur_length
, cur_retlen
, remainder
;
430 uint blk_offset
, blk_len
, copied
;
438 /* Check address range */
443 if ((offset
+ len
) > sfl
->size
)
446 blocksize
= sfl
->blocksize
;
447 mask
= blocksize
- 1;
449 /* Allocate a block of mem */
450 if (!(block
= MALLOC (osh
, blocksize
)))
456 cur_offset
= offset
& ~mask
;
457 cur_length
= blocksize
;
460 remainder
= blocksize
- (offset
& mask
);
464 cur_retlen
= remainder
;
466 /* buf == NULL means erase only */
469 /* Copy existing data into holding block if necessary */
470 if ((offset
& mask
) || (len
< blocksize
))
472 blk_offset
= cur_offset
;
473 blk_len
= cur_length
;
476 /* Copy entire block */
480 sflash_read (sbh
, cc
, blk_offset
, blk_len
, blk_ptr
);
481 blk_offset
+= copied
;
487 /* Copy input data into holding block */
488 memcpy (cur_ptr
+ (offset
& mask
), buf
, cur_retlen
);
492 if ((ret
= sflash_erase (sbh
, cc
, (uint
) cur_offset
)) < 0)
494 while (sflash_poll (sbh
, cc
, (uint
) cur_offset
));
496 /* buf == NULL means erase only */
499 offset
+= cur_retlen
;
504 /* Write holding block */
505 while (cur_length
> 0)
507 if ((bytes
= sflash_write (sbh
, cc
,
510 (uchar
*) cur_ptr
)) < 0)
515 while (sflash_poll (sbh
, cc
, (uint
) cur_offset
));
521 offset
+= cur_retlen
;
529 MFREE (osh
, block
, blocksize
);
This page took 0.0722120000000001 seconds and 5 git commands to generate.