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);