/* Poll for command completion. Returns zero when complete. */ int sflash_poll(si_t *sih, chipcregs_t *cc, uint offset) { osl_t *osh; ASSERT(sih); osh = si_osh(sih); 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->sflashdata) & SFLASH_ST_WIP; case SFLASH_AT: /* Check for Atmel Ready bit */ sflash_cmd(osh, cc, SFLASH_AT_STATUS); return !(R_REG(osh, &cc->sflashdata) & SFLASH_AT_READY); } return 0; }
/* Erase a region. Returns number of bytes scheduled for erasure. * Caller should poll for completion. */ int sflash_erase(si_t *sih, chipcregs_t *cc, uint offset) { struct sflash *sfl; osl_t *osh; ASSERT(sih); osh = si_osh(sih); sfl = &sflash; if (offset >= sfl->size) return -22; switch (sfl->type) { case SFLASH_ST: sflash_cmd(osh, cc, SFLASH_ST_WREN); W_REG(osh, &cc->flashaddress, offset); /* Newer flashes have "sub-sectors" which can be erased independently * with a new command: ST_SSE. The ST_SE command erases 64KB just as * before. */ sflash_cmd(osh, cc, (sfl->blocksize < (64 * 1024)) ? SFLASH_ST_SSE : 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; }
/* 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; }
/* Poll for command completion. Returns zero when complete. */ int sflash_poll(chipcregs_t *cc, uint offset) { if (offset >= sflash.size) return -22; switch (sflash.type) { case SFLASH_ST: /* Check for ST Write In Progress bit */ sflash_cmd(cc, SFLASH_ST_RDSR); return R_REG(NULL, &cc->flashdata) & SFLASH_ST_WIP; case SFLASH_AT: /* Check for Atmel Ready bit */ sflash_cmd(cc, SFLASH_AT_STATUS); return !(R_REG(NULL, &cc->flashdata) & SFLASH_AT_READY); } return 0; }
/* Erase a region. Returns number of bytes scheduled for erasure. * Caller should poll for completion. */ int sflash_erase(chipcregs_t *cc, uint offset) { struct sflash *sfl; if (offset >= sflash.size) return -22; sfl = &sflash; switch (sfl->type) { case SFLASH_ST: sflash_cmd(cc, SFLASH_ST_WREN); W_REG(NULL, &cc->flashaddress, offset); sflash_cmd(cc, SFLASH_ST_SE); return sfl->blocksize; case SFLASH_AT: W_REG(NULL, &cc->flashaddress, offset << 1); sflash_cmd(cc, SFLASH_AT_PAGE_ERASE); return sfl->blocksize; } return 0; }
/* Initialize serial flash access */ struct sflash * sflash_init(si_t *sih, chipcregs_t *cc) { uint32 id, id2; char *name = ""; osl_t *osh; ASSERT(sih); osh = si_osh(sih); bzero(&sflash, sizeof(sflash)); sflash.type = sih->cccaps & CC_CAP_FLASH_MASK; switch (sflash.type) { case SFLASH_ST: /* Probe for ST chips */ name = "ST compatible"; sflash_cmd(osh, cc, SFLASH_ST_DP); W_REG(osh, &cc->flashaddress, 0); sflash_cmd(osh, cc, SFLASH_ST_RES); id = R_REG(osh, &cc->flashdata); sflash.blocksize = 64 * 1024; switch (id) { case 0x11: /* ST M25P20 2 Mbit Serial Flash */ sflash.numblocks = 4; break; case 0x12: /* ST M25P40 4 Mbit Serial Flash */ sflash.numblocks = 8; break; case 0x13: /* ST M25P80 8 Mbit Serial Flash */ sflash.numblocks = 16; break; case 0x14: /* ST M25P16 16 Mbit Serial Flash */ sflash.numblocks = 32; break; case 0x15: /* ST M25P32 32 Mbit Serial Flash */ sflash.numblocks = 64; break; case 0x16: /* ST M25P64 64 Mbit Serial Flash */ sflash.numblocks = 128; break; case 0x17: /* ST M25FL128 128 Mbit Serial Flash */ sflash.numblocks = 256; break; case 0xbf: /* All of the following flashes are SST with * 4KB subsectors. Others should be added but * We'll have to revamp the way we identify them * since RES is not eough to disambiguate them. */ name = "SST"; sflash.blocksize = 4 * 1024; W_REG(osh, &cc->flashaddress, 1); sflash_cmd(osh, cc, SFLASH_ST_RES); id2 = R_REG(osh, &cc->flashdata); switch (id2) { case 1: /* SST25WF512 512 Kbit Serial Flash */ sflash.numblocks = 16; break; case 0x48: /* SST25VF512 512 Kbit Serial Flash */ sflash.numblocks = 16; break; case 2: /* SST25WF010 1 Mbit Serial Flash */ sflash.numblocks = 32; break; case 0x49: /* SST25VF010 1 Mbit Serial Flash */ sflash.numblocks = 32; break; case 3: /* SST25WF020 2 Mbit Serial Flash */ sflash.numblocks = 64; break; case 0x43: /* SST25VF020 2 Mbit Serial Flash */ sflash.numblocks = 64; break; case 4: /* SST25WF040 4 Mbit Serial Flash */ sflash.numblocks = 128; break; case 0x44: /* SST25VF040 4 Mbit Serial Flash */ sflash.numblocks = 128; break; case 0x8d: /* SST25VF040B 4 Mbit Serial Flash */ sflash.numblocks = 128; break; case 5: /* SST25WF080 8 Mbit Serial Flash */ sflash.numblocks = 256; break; case 0x8e: /* SST25VF080B 8 Mbit Serial Flash */ sflash.numblocks = 256; break; case 0x41: /* SST25VF016 16 Mbit Serial Flash */ sflash.numblocks = 512; break; case 0x4a: /* SST25VF032 32 Mbit Serial Flash */ sflash.numblocks = 1024; break; case 0x4b: /* SST25VF064 64 Mbit Serial Flash */ sflash.numblocks = 2048; break; } break; } break; case SFLASH_AT: /* Probe for Atmel chips */ name = "Atmel"; 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; if (firsttime) printf("Found an %s serial flash with %d %dKB blocks; total size %dMB\n", name, sflash.numblocks, sflash.blocksize / 1024, sflash.size / (1024 * 1024)); firsttime = FALSE; return sflash.size ? &sflash : NULL; }
int sflash_write(si_t *sih, chipcregs_t *cc, uint offset, uint length, const uchar *buffer) { struct sflash *sfl; uint off = offset, len = length; const uint8 *buf = buffer; uint8 data; int ret = 0, ntry = 0; bool is4712b0; uint32 page, byte, mask; osl_t *osh; ASSERT(sih); osh = si_osh(sih); if (!len) return 0; sfl = &sflash; if ((off + len) > sfl->size) return -22; switch (sfl->type) { case SFLASH_ST: is4712b0 = (CHIPID(sih->chip) == BCM4712_CHIP_ID) && (CHIPREV(sih->chiprev) == 3); /* Enable writes */ retry: sflash_cmd(osh, cc, SFLASH_ST_WREN); off = offset; len = length; buf = buffer; ntry++; if (is4712b0) { mask = 1 << 14; W_REG(osh, &cc->flashaddress, off); data = GET_BYTE(buf); buf++; W_REG(osh, &cc->flashdata, data); /* 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; off++; len--; while (len > 0) { if ((off & 255) == 0) { /* Page boundary, drop cs and return */ AND_REG(osh, &cc->gpioout, ~mask); OSL_DELAY(1); if (!sflash_poll(sih, cc, off)) { /* Flash rejected command */ if (ntry <= ST_RETRIES) goto retry; else return -11; } return ret; } else { /* Write single byte */ data = GET_BYTE(buf); buf++; sflash_cmd(osh, cc, data); } ret++; off++; len--; } /* All done, drop cs */ AND_REG(osh, &cc->gpioout, ~mask); OSL_DELAY(1); if (!sflash_poll(sih, cc, off)) { /* Flash rejected command */ if (ntry <= ST_RETRIES) goto retry; else return -12; } } else if (sih->ccrev >= 20) { W_REG(osh, &cc->flashaddress, off); data = GET_BYTE(buf); buf++; W_REG(osh, &cc->flashdata, data); /* Issue a page program with CSA bit set */ sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP); ret = 1; off++; len--; while (len > 0) { if ((off & 255) == 0) { /* Page boundary, poll droping cs and return */ W_REG(NULL, &cc->flashcontrol, 0); OSL_DELAY(1); if (sflash_poll(sih, cc, off) == 0) { /* Flash rejected command */ SFL_MSG(("sflash: pp rejected, ntry: %d," " off: %d/%d, len: %d/%d, ret:" "%d\n", ntry, off, offset, len, length, ret)); if (ntry <= ST_RETRIES) goto retry; else return -11; } return ret; } else { /* Write single byte */ data = GET_BYTE(buf); buf++; sflash_cmd(osh, cc, SFLASH_ST_CSA | data); } ret++; off++; len--; } /* All done, drop cs & poll */ W_REG(NULL, &cc->flashcontrol, 0); OSL_DELAY(1); if (sflash_poll(sih, cc, off) == 0) { /* Flash rejected command */ SFL_MSG(("sflash: pp rejected, ntry: %d, off: %d/%d," " len: %d/%d, ret: %d\n", ntry, off, offset, len, length, ret)); if (ntry <= ST_RETRIES) goto retry; else return -12; } } else { ret = 1; W_REG(osh, &cc->flashaddress, off); data = GET_BYTE(buf); buf++; W_REG(osh, &cc->flashdata, data); /* Page program */ sflash_cmd(osh, cc, SFLASH_ST_PP); } break; case SFLASH_AT: mask = sfl->blocksize - 1; page = (off & ~mask) << 1; byte = off & 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(sih, cc, off), 1000); ASSERT(!sflash_poll(sih, cc, off)); } /* 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; }
/* Initialize serial flash access */ struct sflash * sflash_init(si_t *sih, chipcregs_t *cc) { uint32 id; osl_t *osh; uint32 mem_type, mem_cap; ASSERT(sih); osh = si_osh(sih); bzero(&sflash, sizeof(sflash)); sflash.type = sih->cccaps & CC_CAP_FLASH_MASK; switch (sflash.type) { case SFLASH_ST: /* Probe for ST chips */ sflash_cmd(osh, cc, (SFLASH_ST_RDID | SFLASH_ST_CSA)); sflash_cmd(osh, cc, (SFLASH_ST_RDID_READ | SFLASH_ST_CSA)); id = R_REG(osh, &cc->sflashdata); sflash_cmd(osh, cc, (SFLASH_ST_RDID_READ | SFLASH_ST_CSA)); mem_type = R_REG(osh, &cc->sflashdata); sflash_cmd(osh, cc, (SFLASH_ST_RDID_READ | SFLASH_ST_CSA)); mem_cap = R_REG(osh, &cc->sflashdata); switch (id) { case 0x20: /* Micron */ switch(mem_type) { case 0x20: /* M25P series */ case 0xba: /* N25Q series */ switch (mem_cap) { case 0x12: /* ST M25P20 2 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 4; break; case 0x13: /* ST M25P40 4 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 8; break; case 0x14: /* ST M25P80 8 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 16; break; case 0x15: /* ST M25P16 16 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 32; break; case 0x16: /* ST M25P32 32 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 64; break; case 0x17: /* ST M25P64 64 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 128; break; case 0x18: /* ST M25P 128 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 256; break; } break; } break; case 0x01: /* Spansion */ if ((mem_type == 0x20) && (mem_cap == 0x18)) { /* ST S25FL128P00M 128 Mbit */ sflash.blocksize = 64 * 1024; sflash.numblocks = 256; } else if ((mem_type == 0x02) && (mem_cap == 0x15)) { /* ST S25FL032P */ sflash.blocksize = 64 * 1024; sflash.numblocks = 64; } break; case 0xbf: /* SST */ if ((mem_type == 0x25) && (mem_cap == 0x8e)) { /* SST 25VF80 */ sflash.blocksize = 64 * 1024; sflash.numblocks = 8; } break; } /* All done, drop cs & read */ W_REG(osh, &cc->sflashcontrol, 0); break; case SFLASH_AT: /* Probe for Atmel chips */ sflash_cmd(osh, cc, SFLASH_AT_STATUS); id = R_REG(osh, &cc->sflashdata) & 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; }
int sflash_write(si_t *sih, chipcregs_t *cc, uint offset, uint length, const uchar *buffer) { struct sflash *sfl; uint off = offset, len = length; #if SFLASH_ST_PAGE_MODE_WRITE int tryn = 0; #else /* !SFLASH_ST_PAGE_MODE_WRITE */ uint quot = 0, remain = 0, wlen = 0; uint32 reg_val = 0; #endif /* SFLASH_ST_PAGE_MODE_WRITE */ const uchar *buf = buffer; int ret = 0; uint32 page, byte, mask; osl_t *osh; ASSERT(sih); osh = si_osh(sih); if (!len) return 0; sfl = &sflash; if ((off + len) > sfl->size) return -22; switch (sfl->type) { case SFLASH_ST: #if SFLASH_ST_PAGE_MODE_WRITE /* Enable writes */ retry: sflash_cmd(osh, cc, SFLASH_ST_WREN); off = offset; len = length; buf = buffer; tryn++; if (sih->ccrev >= 20) { W_REG(osh, &cc->sflashaddress, off); W_REG(osh, &cc->sflashdata, *buf++); /* Issue a page program with CSA bit set */ sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP); ret = 1; off++; len--; while (len > 0) { if ((off & 255) == 0) { /* Page boundary, poll droping cs and return */ W_REG(osh, &cc->sflashcontrol, 0); OSL_DELAY(1); if (sflash_poll(sih, cc, offset) != 0) { /* Flash rejected command */ SFL_MSG(("sflash: pp rejected, tryn: %d," " off: %d/%d, len: %d/%d, ret:" "%d\n", tryn, off, offset, len, length, ret)); if (tryn <= ST_RETRIES) goto retry; else return -11; } return ret; } else { /* Write single byte */ sflash_cmd(osh, cc, SFLASH_ST_CSA | *buf++); } ret++; off++; len--; } /* All done, drop cs & poll */ W_REG(osh, &cc->sflashcontrol, 0); OSL_DELAY(1); if (sflash_poll(sih, cc, offset) != 0) { /* Flash rejected command */ SFL_MSG(("sflash: pp rejected, tryn: %d, off: %d/%d," " len: %d/%d, ret: %d\n", tryn, off, offset, len, length, ret)); if (tryn <= ST_RETRIES) goto retry; else return -12; } } else { ret = 1; W_REG(osh, &cc->sflashaddress, off); W_REG(osh, &cc->sflashdata, *buf); /* Page program */ sflash_cmd(osh, cc, SFLASH_ST_PP); } #else /* !SFLASH_ST_PAGE_MODE_WRITE */ off = offset; len = length; buf = buffer; if (sih->ccrev >= 20) { ret = 0; while (len > 0) { /* Enable writes */ sflash_cmd(osh, cc, SFLASH_ST_WREN); /* Drop cs before starting a page program */ W_REG(osh, &cc->sflashcontrol, 0); W_REG(osh, &cc->sflashaddress, off); quot = (len / 4); remain = (len % 4); if (quot != 0) { /* len >= 4 bytes */ wlen = 4; reg_val = (*buf << 24); buf++; reg_val |= (*buf << 16); buf++; reg_val |= (*buf << 8); buf++; reg_val |= (*buf); buf++; W_REG(osh, &cc->sflashdata, reg_val); /* Issue a page program with CSA bit set : opcode+3 addres & 4 data bytes */ sflash_cmd(osh, cc, (SFLASH_ST_CSA | SFLASH_ST_PP3A4D)); } else { /* len < 4 bytes */ wlen = 1; W_REG(osh, &cc->sflashdata, *buf++); /* Issue a page program with CSA bit set : opcode+3 addres & 1 data bytes */ sflash_cmd(osh, cc, (SFLASH_ST_CSA | SFLASH_ST_PP)); } ret += wlen; off += wlen; len -= wlen; /* A page program done(1 or 4 data bytes), drop cs & poll */ W_REG(osh, &cc->sflashcontrol, 0); while (sflash_poll(sih, cc, offset) != 0) { /* Poll until command completion */ } /* Page boundary and return for 256 bytes write */ if ((off & 255) == 0) { return ret; } } } else { /* Enable writes */ sflash_cmd(osh, cc, SFLASH_ST_WREN); ret = 1; W_REG(osh, &cc->sflashaddress, off); W_REG(osh, &cc->sflashdata, *buf); /* Page program */ sflash_cmd(osh, cc, SFLASH_ST_PP); } #endif /* SFLASH_ST_PAGE_MODE_WRITE */ break; case SFLASH_AT: mask = sfl->blocksize - 1; page = (off & ~mask) << 1; byte = off & mask; /* Read main memory page into buffer 1 */ if (byte || (len < sfl->blocksize)) { W_REG(osh, &cc->sflashaddress, page); sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD); /* 250 us for AT45DB321B */ SPINWAIT(sflash_poll(sih, cc, offset), 1000); ASSERT(!sflash_poll(sih, cc, offset)); } /* Write into buffer 1 */ for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { W_REG(osh, &cc->sflashaddress, byte++); W_REG(osh, &cc->sflashdata, *buf++); sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE); } /* Write buffer 1 into main memory page */ W_REG(osh, &cc->sflashaddress, page); sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM); break; } return ret; }
/* Initialize serial flash access */ struct sflash * sflash_init(chipcregs_t *cc) { uint32 id, id2; bzero(&sflash, sizeof(sflash)); sflash.type = R_REG(NULL, &cc->capabilities) & CC_CAP_FLASH_MASK; switch (sflash.type) { case SFLASH_ST: /* Probe for ST chips */ sflash_cmd(cc, SFLASH_ST_DP); sflash_cmd(cc, SFLASH_ST_RES); id = R_REG(NULL, &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(NULL, &cc->flashaddress, 1); sflash_cmd(cc, SFLASH_ST_RES); id2 = R_REG(NULL, &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(cc, SFLASH_AT_STATUS); id = R_REG(NULL, &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; }
/* Write len bytes starting at offset into buf. Returns number of bytes * written. Caller should poll for completion. */ int sflash_write(chipcregs_t *cc, uint offset, uint len, const uchar *buf) { struct sflash *sfl; int ret = 0; bool is4712b0; uint32 page, byte, mask; if (!len) return 0; if ((offset + len) > sflash.size) return -22; sfl = &sflash; switch (sfl->type) { case SFLASH_ST: mask = R_REG(NULL, &cc->chipid); is4712b0 = (((mask & CID_ID_MASK) == BCM4712_CHIP_ID) && ((mask & CID_REV_MASK) == (3 << CID_REV_SHIFT))); /* Enable writes */ sflash_cmd(cc, SFLASH_ST_WREN); if (is4712b0) { mask = 1 << 14; W_REG(NULL, &cc->flashaddress, offset); W_REG(NULL, &cc->flashdata, *buf++); /* Set chip select */ OR_REG(NULL, &cc->gpioout, mask); /* Issue a page program with the first byte */ sflash_cmd(cc, SFLASH_ST_PP); ret = 1; offset++; len--; while (len > 0) { if ((offset & 255) == 0) { /* Page boundary, drop cs and return */ AND_REG(NULL, &cc->gpioout, ~mask); if (!sflash_poll(cc, offset)) { /* Flash rejected command */ return -11; } return ret; } else { /* Write single byte */ sflash_cmd(cc, *buf++); } ret++; offset++; len--; } /* All done, drop cs if needed */ if ((offset & 255) != 1) { /* Drop cs */ AND_REG(NULL, &cc->gpioout, ~mask); if (!sflash_poll(cc, offset)) { /* Flash rejected command */ return -12; } } } else { ret = 1; W_REG(NULL, &cc->flashaddress, offset); W_REG(NULL, &cc->flashdata, *buf); /* Page program */ sflash_cmd(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(NULL, &cc->flashaddress, page); sflash_cmd(cc, SFLASH_AT_BUF1_LOAD); /* 250 us for AT45DB321B */ SPINWAIT(sflash_poll(cc, offset), 1000); ASSERT(!sflash_poll(cc, offset)); } /* Write into buffer 1 */ for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { W_REG(NULL, &cc->flashaddress, byte++); W_REG(NULL, &cc->flashdata, *buf++); sflash_cmd(cc, SFLASH_AT_BUF1_WRITE); } /* Write buffer 1 into main memory page */ W_REG(NULL, &cc->flashaddress, page); sflash_cmd(cc, SFLASH_AT_BUF1_PROGRAM); break; } return ret; }
/* Initialize serial flash access */ struct sflash * sflash_init(si_t *sih, chipcregs_t *cc) { uint32 id, id2; char *name = ""; osl_t *osh; static bool firsttime = TRUE; ASSERT(sih); osh = si_osh(sih); bzero(&sflash, sizeof(sflash)); sflash.type = sih->cccaps & CC_CAP_FLASH_MASK; switch (sflash.type) { case SFLASH_ST: /* Probe for ST chips */ name = "ST compatible"; 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 0x17: /* ST M25FL128 128 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 256; 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 */ name = "SST"; sflash.blocksize = 64 * 1024; sflash.numblocks = 8; } break; } break; case SFLASH_AT: /* Probe for Atmel chips */ name = "Atmel"; 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; if (firsttime) printf("Found a %dMB %s serial flash\n", sflash.size / (1024 * 1024), name); /* 4716A0 hack */ if (((sih->chip == BCM4716_CHIP_ID) || (sih->chip == BCM4748_CHIP_ID)) && (sih->chiprev == 0)) { if (sflash.size > (4 * 1024 * 1024)) { sflash.size = 4 * 1024 * 1024; if (firsttime) printf("Using only 4MB in BCM4716a0\n"); } } firsttime = FALSE; return sflash.size ? &sflash : NULL; }