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