123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- /*
- * Broadcom SiliconBackplane chipcommon serial flash interface
- *
- * Copyright 2007, Broadcom Corporation
- * All Rights Reserved.
- *
- * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
- * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
- * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
- *
- * $Id$
- */
- #include <typedefs.h>
- #include <osl.h>
- #include "include/bcmutils.h"
- #include <sbutils.h>
- #include <sbconfig.h>
- #include <sbchipc.h>
- #include <bcmdevs.h>
- #include <sflash.h>
- /* Private global state */
- static struct sflash sflash;
- /* Issue a serial flash command */
- static INLINE void
- sflash_cmd(osl_t *osh, chipcregs_t *cc, uint opcode)
- {
- W_REG(osh, &cc->flashcontrol, SFLASH_START | opcode);
- while (R_REG(osh, &cc->flashcontrol) & SFLASH_BUSY);
- }
- /* Initialize serial flash access */
- struct sflash *
- sflash_init(sb_t *sbh, chipcregs_t *cc)
- {
- uint32 id, id2;
- osl_t *osh;
- ASSERT(sbh);
- osh = sb_osh(sbh);
- bzero(&sflash, sizeof(sflash));
- sflash.type = sbh->cccaps & CC_CAP_FLASH_MASK;
- switch (sflash.type) {
- case SFLASH_ST:
- /* Probe for ST chips */
- sflash_cmd(osh, cc, SFLASH_ST_DP);
- sflash_cmd(osh, cc, SFLASH_ST_RES);
- id = R_REG(osh, &cc->flashdata);
- switch (id) {
- case 0x11:
- /* ST M25P20 2 Mbit Serial Flash */
- sflash.blocksize = 64 * 1024;
- sflash.numblocks = 4;
- break;
- case 0x12:
- /* ST M25P40 4 Mbit Serial Flash */
- sflash.blocksize = 64 * 1024;
- sflash.numblocks = 8;
- break;
- case 0x13:
- /* ST M25P80 8 Mbit Serial Flash */
- sflash.blocksize = 64 * 1024;
- sflash.numblocks = 16;
- break;
- case 0x14:
- /* ST M25P16 16 Mbit Serial Flash */
- sflash.blocksize = 64 * 1024;
- sflash.numblocks = 32;
- break;
- case 0x15:
- /* ST M25P32 32 Mbit Serial Flash */
- sflash.blocksize = 64 * 1024;
- sflash.numblocks = 64;
- break;
- case 0x16:
- /* ST M25P64 64 Mbit Serial Flash */
- sflash.blocksize = 64 * 1024;
- sflash.numblocks = 128;
- break;
- case 0xbf:
- W_REG(osh, &cc->flashaddress, 1);
- sflash_cmd(osh, cc, SFLASH_ST_RES);
- id2 = R_REG(osh, &cc->flashdata);
- if (id2 == 0x44) {
- /* SST M25VF80 4 Mbit Serial Flash */
- sflash.blocksize = 64 * 1024;
- sflash.numblocks = 8;
- }
- break;
- }
- break;
- case SFLASH_AT:
- /* Probe for Atmel chips */
- sflash_cmd(osh, cc, SFLASH_AT_STATUS);
- id = R_REG(osh, &cc->flashdata) & 0x3c;
- switch (id) {
- case 0xc:
- /* Atmel AT45DB011 1Mbit Serial Flash */
- sflash.blocksize = 256;
- sflash.numblocks = 512;
- break;
- case 0x14:
- /* Atmel AT45DB021 2Mbit Serial Flash */
- sflash.blocksize = 256;
- sflash.numblocks = 1024;
- break;
- case 0x1c:
- /* Atmel AT45DB041 4Mbit Serial Flash */
- sflash.blocksize = 256;
- sflash.numblocks = 2048;
- break;
- case 0x24:
- /* Atmel AT45DB081 8Mbit Serial Flash */
- sflash.blocksize = 256;
- sflash.numblocks = 4096;
- break;
- case 0x2c:
- /* Atmel AT45DB161 16Mbit Serial Flash */
- sflash.blocksize = 512;
- sflash.numblocks = 4096;
- break;
- case 0x34:
- /* Atmel AT45DB321 32Mbit Serial Flash */
- sflash.blocksize = 512;
- sflash.numblocks = 8192;
- break;
- case 0x3c:
- /* Atmel AT45DB642 64Mbit Serial Flash */
- sflash.blocksize = 1024;
- sflash.numblocks = 8192;
- break;
- }
- break;
- }
- sflash.size = sflash.blocksize * sflash.numblocks;
- return sflash.size ? &sflash : NULL;
- }
- /* Read len bytes starting at offset into buf. Returns number of bytes read. */
- int
- sflash_read(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, uchar *buf)
- {
- uint8 *from, *to;
- int cnt, i;
- osl_t *osh;
- ASSERT(sbh);
- if (!len)
- return 0;
- if ((offset + len) > sflash.size)
- return -22;
- if ((len >= 4) && (offset & 3))
- cnt = 4 - (offset & 3);
- else if ((len >= 4) && ((uintptr)buf & 3))
- cnt = 4 - ((uintptr)buf & 3);
- else
- cnt = len;
- osh = sb_osh(sbh);
- from = (uint8 *)(uintptr)OSL_UNCACHED(SB_FLASH2 + offset);
- to = (uint8 *)buf;
- if (cnt < 4) {
- for (i = 0; i < cnt; i ++) {
- *to = R_REG(osh, from);
- from ++;
- to ++;
- }
- return cnt;
- }
- while (cnt >= 4) {
- *(uint32 *)to = R_REG(osh, (uint32 *)from);
- from += 4;
- to += 4;
- cnt -= 4;
- }
- return (len - cnt);
- }
- /* Poll for command completion. Returns zero when complete. */
- int
- sflash_poll(sb_t *sbh, chipcregs_t *cc, uint offset)
- {
- osl_t *osh;
- ASSERT(sbh);
- osh = sb_osh(sbh);
- if (offset >= sflash.size)
- return -22;
- switch (sflash.type) {
- case SFLASH_ST:
- /* Check for ST Write In Progress bit */
- sflash_cmd(osh, cc, SFLASH_ST_RDSR);
- return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP;
- case SFLASH_AT:
- /* Check for Atmel Ready bit */
- sflash_cmd(osh, cc, SFLASH_AT_STATUS);
- return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY);
- }
- return 0;
- }
- /* Write len bytes starting at offset into buf. Returns number of bytes
- * written. Caller should poll for completion.
- */
- int
- sflash_write(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
- {
- struct sflash *sfl;
- int ret = 0;
- bool is4712b0;
- uint32 page, byte, mask;
- osl_t *osh;
- ASSERT(sbh);
- osh = sb_osh(sbh);
- if (!len)
- return 0;
- if ((offset + len) > sflash.size)
- return -22;
- sfl = &sflash;
- switch (sfl->type) {
- case SFLASH_ST:
- is4712b0 = (sbh->chip == BCM4712_CHIP_ID) && (sbh->chiprev == 3);
- /* Enable writes */
- sflash_cmd(osh, cc, SFLASH_ST_WREN);
- if (is4712b0) {
- mask = 1 << 14;
- W_REG(osh, &cc->flashaddress, offset);
- W_REG(osh, &cc->flashdata, *buf++);
- /* Set chip select */
- OR_REG(osh, &cc->gpioout, mask);
- /* Issue a page program with the first byte */
- sflash_cmd(osh, cc, SFLASH_ST_PP);
- ret = 1;
- offset++;
- len--;
- while (len > 0) {
- if ((offset & 255) == 0) {
- /* Page boundary, drop cs and return */
- AND_REG(osh, &cc->gpioout, ~mask);
- if (!sflash_poll(sbh, cc, offset)) {
- /* Flash rejected command */
- return -11;
- }
- return ret;
- } else {
- /* Write single byte */
- sflash_cmd(osh, cc, *buf++);
- }
- ret++;
- offset++;
- len--;
- }
- /* All done, drop cs if needed */
- if ((offset & 255) != 1) {
- /* Drop cs */
- AND_REG(osh, &cc->gpioout, ~mask);
- if (!sflash_poll(sbh, cc, offset)) {
- /* Flash rejected command */
- return -12;
- }
- }
- } else if ( (sbh->ccrev >= 20) && (len != 1) ) {
- //} else if ( sbh->ccrev >= 20 ) { /* foxconn modified by EricHuang, 05/24/2007 */
- W_REG(NULL, &cc->flashaddress, offset);
- W_REG(NULL, &cc->flashdata, *buf++);
- /* Issue a page program with CSA bit set */
- sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP);
- ret = 1;
- offset++;
- len--;
- while (len > 0) {
- if ((offset & 255) == 0) {
- /* Page boundary, poll droping cs and return */
- W_REG(NULL, &cc->flashcontrol, 0);
- /* wklin added start, 06/08/2007 */
- W_REG(NULL, &cc->flashcontrol, 0);
- OSL_DELAY(1);
- /* wklin added end, 06/08/2007 */
- /* wklin rmeoved start, 06/08/2007 */
- #if 0
- if (!sflash_poll(sbh, cc, offset)) {
- /* Flash rejected command */
- return -11;
- }
- #endif
- /* wklin removed end, 06/08/2007 */
- return ret;
- } else {
- /* Write single byte */
- sflash_cmd(osh, cc, SFLASH_ST_CSA | *buf++);
- }
- ret++;
- offset++;
- len--;
- }
- /* All done, drop cs if needed */
- if ((offset & 255) != 1) {
- /* Drop cs, poll */
- W_REG(NULL, &cc->flashcontrol, 0);
- /* wklin added start, 06/08/2007 */
- W_REG(NULL, &cc->flashcontrol, 0);
- OSL_DELAY(1);
- /* wklin added end, 06/08/2007 */
- /* wklin removed start, 06/08/2007 */
- #if 0
- if (!sflash_poll(sbh, cc, offset)) {
- /* Flash rejected command */
- return -12;
- }
- #endif
- /* wklin removed end, 06/08/2007 */
- }
- } else {
- ret = 1;
- W_REG(osh, &cc->flashaddress, offset);
- W_REG(osh, &cc->flashdata, *buf);
- /* Page program */
- sflash_cmd(osh, cc, SFLASH_ST_PP);
- }
- break;
- case SFLASH_AT:
- mask = sfl->blocksize - 1;
- page = (offset & ~mask) << 1;
- byte = offset & mask;
- /* Read main memory page into buffer 1 */
- if (byte || (len < sfl->blocksize)) {
- W_REG(osh, &cc->flashaddress, page);
- sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD);
- /* 250 us for AT45DB321B */
- SPINWAIT(sflash_poll(sbh, cc, offset), 1000);
- ASSERT(!sflash_poll(sbh, cc, offset));
- }
- /* Write into buffer 1 */
- for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) {
- W_REG(osh, &cc->flashaddress, byte++);
- W_REG(osh, &cc->flashdata, *buf++);
- sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE);
- }
- /* Write buffer 1 into main memory page */
- W_REG(osh, &cc->flashaddress, page);
- sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM);
- break;
- }
- return ret;
- }
- /* Erase a region. Returns number of bytes scheduled for erasure.
- * Caller should poll for completion.
- */
- int
- sflash_erase(sb_t *sbh, chipcregs_t *cc, uint offset)
- {
- struct sflash *sfl;
- osl_t *osh;
- ASSERT(sbh);
- osh = sb_osh(sbh);
- if (offset >= sflash.size)
- return -22;
- sfl = &sflash;
- switch (sfl->type) {
- case SFLASH_ST:
- sflash_cmd(osh, cc, SFLASH_ST_WREN);
- W_REG(osh, &cc->flashaddress, offset);
- sflash_cmd(osh, cc, SFLASH_ST_SE);
- return sfl->blocksize;
- case SFLASH_AT:
- W_REG(osh, &cc->flashaddress, offset << 1);
- sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE);
- return sfl->blocksize;
- }
- return 0;
- }
- /*
- * writes the appropriate range of flash, a NULL buf simply erases
- * the region of flash
- */
- int
- sflash_commit(sb_t *sbh, chipcregs_t *cc, uint offset, uint len, const uchar *buf)
- {
- struct sflash *sfl;
- uchar *block = NULL, *cur_ptr, *blk_ptr;
- uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder;
- uint blk_offset, blk_len, copied;
- int bytes, ret = 0;
- osl_t *osh;
- ASSERT(sbh);
- osh = sb_osh(sbh);
- /* Check address range */
- if (len <= 0)
- return 0;
- sfl = &sflash;
- if ((offset + len) > sfl->size)
- return -1;
- blocksize = sfl->blocksize;
- mask = blocksize - 1;
- /* Allocate a block of mem */
- if (!(block = MALLOC(osh, blocksize)))
- return -1;
- while (len) {
- /* Align offset */
- cur_offset = offset & ~mask;
- cur_length = blocksize;
- cur_ptr = block;
- remainder = blocksize - (offset & mask);
- if (len < remainder)
- cur_retlen = len;
- else
- cur_retlen = remainder;
- /* buf == NULL means erase only */
- if (buf) {
- /* Copy existing data into holding block if necessary */
- if ((offset & mask) || (len < blocksize)) {
- blk_offset = cur_offset;
- blk_len = cur_length;
- blk_ptr = cur_ptr;
- /* Copy entire block */
- while (blk_len) {
- copied = sflash_read(sbh, cc, blk_offset, blk_len, blk_ptr);
- blk_offset += copied;
- blk_len -= copied;
- blk_ptr += copied;
- }
- }
- /* Copy input data into holding block */
- memcpy(cur_ptr + (offset & mask), buf, cur_retlen);
- }
- /* Erase block */
- if ((ret = sflash_erase(sbh, cc, (uint) cur_offset)) < 0)
- goto done;
- while (sflash_poll(sbh, cc, (uint) cur_offset));
- /* buf == NULL means erase only */
- if (!buf) {
- offset += cur_retlen;
- len -= cur_retlen;
- continue;
- }
- /* Write holding block */
- while (cur_length > 0) {
- if ((bytes = sflash_write(sbh, cc,
- (uint) cur_offset,
- (uint) cur_length,
- (uchar *) cur_ptr)) < 0) {
- ret = bytes;
- goto done;
- }
- while (sflash_poll(sbh, cc, (uint) cur_offset));
- cur_offset += bytes;
- cur_length -= bytes;
- cur_ptr += bytes;
- }
- offset += cur_retlen;
- len -= cur_retlen;
- buf += cur_retlen;
- }
- ret = len;
- done:
- if (block)
- MFREE(osh, block, blocksize);
- return ret;
- }
|