2 * Broadcom SiliconBackplane chipcommon serial flash interface
4 * Copyright 2004, 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.
21 /* Private global state */
22 static struct sflash sflash;
24 /* Issue a serial flash command */
26 sflash_cmd(chipcregs_t *cc, uint opcode)
28 W_REG(&cc->flashcontrol, SFLASH_START | opcode);
29 while (R_REG(&cc->flashcontrol) & SFLASH_BUSY);
32 /* Initialize serial flash access */
34 sflash_init(chipcregs_t *cc)
38 bzero(&sflash, sizeof(sflash));
40 sflash.type = R_REG(&cc->capabilities) & CAP_FLASH_MASK;
42 switch (sflash.type) {
44 /* Probe for ST chips */
45 sflash_cmd(cc, SFLASH_ST_DP);
46 sflash_cmd(cc, SFLASH_ST_RES);
47 id = R_REG(&cc->flashdata);
50 /* ST M25P20 2 Mbit Serial Flash */
51 sflash.blocksize = 64 * 1024;
55 /* ST M25P40 4 Mbit Serial Flash */
56 sflash.blocksize = 64 * 1024;
60 /* ST M25P80 8 Mbit Serial Flash */
61 sflash.blocksize = 64 * 1024;
62 sflash.numblocks = 16;
65 /* ST M25P16 16 Mbit Serial Flash */
66 sflash.blocksize = 64 * 1024;
67 sflash.numblocks = 32;
70 W_REG(&cc->flashaddress, 1);
71 sflash_cmd(cc, SFLASH_ST_RES);
72 id2 = R_REG(&cc->flashdata);
74 /* SST M25VF80 4 Mbit Serial Flash */
75 sflash.blocksize = 64 * 1024;
83 /* Probe for Atmel chips */
84 sflash_cmd(cc, SFLASH_AT_STATUS);
85 id = R_REG(&cc->flashdata) & 0x3c;
88 /* Atmel AT45DB161 16Mbit Serial Flash */
89 sflash.blocksize = 512;
90 sflash.numblocks = 4096;
93 /* Atmel AT45DB321 32Mbit Serial Flash */
94 sflash.blocksize = 512;
95 sflash.numblocks = 8192;
98 /* Atmel AT45DB642 64Mbit Serial Flash */
99 sflash.blocksize = 1024;
100 sflash.numblocks = 8192;
106 sflash.size = sflash.blocksize * sflash.numblocks;
107 return sflash.size ? &sflash : NULL;
110 /* Read len bytes starting at offset into buf. Returns number of bytes read. */
112 sflash_read(chipcregs_t *cc, uint offset, uint len, uchar *buf)
120 if ((offset + len) > sflash.size)
123 if ((len >= 4) && (offset & 3))
124 cnt = 4 - (offset & 3);
125 else if ((len >= 4) && ((uint32)buf & 3))
126 cnt = 4 - ((uint32)buf & 3);
130 from = (uint32 *)(CC_FLASH_BASE + offset);
134 bcopy(from, to, cnt);
146 /* Poll for command completion. Returns zero when complete. */
148 sflash_poll(chipcregs_t *cc, uint offset)
150 if (offset >= sflash.size)
153 switch (sflash.type) {
155 /* Check for ST Write In Progress bit */
156 sflash_cmd(cc, SFLASH_ST_RDSR);
157 return R_REG(&cc->flashdata) & SFLASH_ST_WIP;
159 /* Check for Atmel Ready bit */
160 sflash_cmd(cc, SFLASH_AT_STATUS);
161 return !(R_REG(&cc->flashdata) & SFLASH_AT_READY);
167 /* Write len bytes starting at offset into buf. Returns number of bytes
168 * written. Caller should poll for completion.
171 sflash_write(chipcregs_t *cc, uint offset, uint len, const uchar *buf)
175 uint32 page, byte, mask;
180 if ((offset + len) > sflash.size)
188 sflash_cmd(cc, SFLASH_ST_WREN);
189 W_REG(&cc->flashaddress, offset);
190 W_REG(&cc->flashdata, *buf);
192 sflash_cmd(cc, SFLASH_ST_PP);
195 mask = sfl->blocksize - 1;
196 page = (offset & ~mask) << 1;
197 byte = offset & mask;
198 /* Read main memory page into buffer 1 */
199 if (byte || len < sfl->blocksize) {
200 W_REG(&cc->flashaddress, page);
201 sflash_cmd(cc, SFLASH_AT_BUF1_LOAD);
202 /* 250 us for AT45DB321B */
203 SPINWAIT(sflash_poll(cc, offset), 1000);
204 ASSERT(!sflash_poll(cc, offset));
206 /* Write into buffer 1 */
207 for (ret = 0; ret < len && byte < sfl->blocksize; ret++) {
208 W_REG(&cc->flashaddress, byte++);
209 W_REG(&cc->flashdata, *buf++);
210 sflash_cmd(cc, SFLASH_AT_BUF1_WRITE);
212 /* Write buffer 1 into main memory page */
213 W_REG(&cc->flashaddress, page);
214 sflash_cmd(cc, SFLASH_AT_BUF1_PROGRAM);
221 /* Erase a region. Returns number of bytes scheduled for erasure.
222 * Caller should poll for completion.
225 sflash_erase(chipcregs_t *cc, uint offset)
229 if (offset >= sflash.size)
235 sflash_cmd(cc, SFLASH_ST_WREN);
236 W_REG(&cc->flashaddress, offset);
237 sflash_cmd(cc, SFLASH_ST_SE);
238 return sfl->blocksize;
240 W_REG(&cc->flashaddress, offset << 1);
241 sflash_cmd(cc, SFLASH_AT_PAGE_ERASE);
242 return sfl->blocksize;
249 * writes the appropriate range of flash, a NULL buf simply erases
250 * the region of flash
253 sflash_commit(chipcregs_t *cc, uint offset, uint len, const uchar *buf)
256 uchar *block = NULL, *cur_ptr, *blk_ptr;
257 uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
258 uint blk_offset, blk_len, copied;
261 /* Check address range */
266 if ((offset + len) > sfl->size)
269 blocksize = sfl->blocksize;
270 mask = blocksize - 1;
272 /* Allocate a block of mem */
273 if (!(block = MALLOC(blocksize)))
278 cur_offset = offset & ~mask;
279 cur_length = blocksize;
282 remainder = blocksize - (offset & mask);
286 cur_retlen = remainder;
288 /* buf == NULL means erase only */
290 /* Copy existing data into holding block if necessary */
291 if ((offset & mask) || (len < blocksize)) {
292 blk_offset = cur_offset;
293 blk_len = cur_length;
296 /* Copy entire block */
298 copied = sflash_read(cc, blk_offset, blk_len, blk_ptr);
299 blk_offset += copied;
305 /* Copy input data into holding block */
306 memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
310 if ((ret = sflash_erase(cc, (uint) cur_offset)) < 0)
312 while (sflash_poll(cc, (uint) cur_offset));
314 /* buf == NULL means erase only */
316 offset += cur_retlen;
321 /* Write holding block */
322 while (cur_length > 0) {
323 if ((bytes = sflash_write(cc,
326 (uchar *) cur_ptr)) < 0) {
330 while (sflash_poll(cc, (uint) cur_offset));
336 offset += cur_retlen;
343 MFREE(block, blocksize);