/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "libflash.h" #include "libflash-priv.h" #include "ecc.h" #include "blocklevel.h" static const struct flash_info flash_info[] = { { 0xc22018, 0x01000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL12835F"}, { 0xc22019, 0x02000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL25635F"}, { 0xc2201a, 0x04000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MXxxL51235F"}, { 0xc2201b, 0x08000000, FL_ERASE_ALL | FL_CAN_4B, "Macronix MX66L1G45G"}, { 0xef4018, 0x01000000, FL_ERASE_ALL, "Winbond W25Q128BV" }, { 0xef4019, 0x02000000, FL_ERASE_ALL | FL_ERASE_64K | FL_CAN_4B | FL_ERASE_BULK, "Winbond W25Q256BV"}, { 0x20ba20, 0x04000000, FL_ERASE_4K | FL_ERASE_64K | FL_CAN_4B | FL_ERASE_BULK | FL_MICRON_BUGS, "Micron N25Qx512Ax" }, { 0x20ba19, 0x02000000, FL_ERASE_4K | FL_ERASE_64K | FL_CAN_4B | FL_ERASE_BULK | FL_MICRON_BUGS, "Micron N25Q256Ax" }, { 0x1940ef, 0x02000000, FL_ERASE_4K | FL_ERASE_64K | FL_CAN_4B | FL_ERASE_BULK | FL_MICRON_BUGS, "Micron N25Qx256Ax" }, { 0x4d5444, 0x02000000, FL_ERASE_ALL | FL_CAN_4B, "File Abstraction"}, { 0x55aa55, 0x00100000, FL_ERASE_ALL | FL_CAN_4B, "TEST_FLASH" }, { 0xaa55aa, 0x02000000, FL_ERASE_ALL | FL_CAN_4B, "EMULATED_FLASH"}, }; struct flash_chip { struct spi_flash_ctrl *ctrl; /* Controller */ struct flash_info info; /* Flash info */ uint32_t tsize; /* Corrected flash size */ uint32_t min_erase_mask; /* Minimum erase size */ bool mode_4b; /* Flash currently in 4b mode */ struct flash_req *cur_req; /* Current request */ void *smart_buf; /* Buffer for smart writes */ struct blocklevel_device bl; }; #ifndef __SKIBOOT__ bool libflash_debug; #endif int fl_read_stat(struct spi_flash_ctrl *ct, uint8_t *stat) { return ct->cmd_rd(ct, CMD_RDSR, false, 0, stat, 1); } static void fl_micron_status(struct spi_flash_ctrl *ct) { uint8_t flst; /* * After a success status on a write or erase, we * need to do that command or some chip variants will * lock */ ct->cmd_rd(ct, CMD_MIC_RDFLST, false, 0, &flst, 1); } /* Synchronous write completion, probably need a yield hook */ int fl_sync_wait_idle(struct spi_flash_ctrl *ct) { uint8_t stat; int rc; /* XXX Add timeout */ for (;;) { rc = fl_read_stat(ct, &stat); if (rc) return rc; if (!(stat & STAT_WIP)) { if (ct->finfo->flags & FL_MICRON_BUGS) fl_micron_status(ct); return 0; } } /* return FLASH_ERR_WIP_TIMEOUT; */ } /* Exported for internal use */ int fl_wren(struct spi_flash_ctrl *ct) { int i, rc; uint8_t stat; /* Some flashes need it to be hammered */ for (i = 0; i < 1000; i++) { rc = ct->cmd_wr(ct, CMD_WREN, false, 0, NULL, 0); if (rc) return rc; rc = fl_read_stat(ct, &stat); if (rc) return rc; if (stat & STAT_WIP) { FL_ERR("LIBFLASH: WREN has WIP status set !\n"); rc = fl_sync_wait_idle(ct); if (rc) return rc; continue; } if (stat & STAT_WEN) return 0; } return FLASH_ERR_WREN_TIMEOUT; } static int flash_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); struct spi_flash_ctrl *ct = c->ctrl; /* XXX Add sanity/bound checking */ /* * If the controller supports read and either we are in 3b mode * or we are in 4b *and* the controller supports it, then do a * high level read. */ if ((!c->mode_4b || ct->set_4b) && ct->read) return ct->read(ct, pos, buf, len); /* Otherwise, go manual if supported */ if (!ct->cmd_rd) return FLASH_ERR_CTRL_CMD_UNSUPPORTED; return ct->cmd_rd(ct, CMD_READ, true, pos, buf, len); } #define COPY_BUFFER_LENGTH 4096 /* * This provides a wrapper around flash_read on ECCed data * len is length of data without ECC attached */ int flash_read_corrected(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len, bool ecc) { struct ecc64 *bufecc; uint32_t copylen; int rc; uint8_t ret; if (!ecc) return flash_read(bl, pos, buf, len); /* Copy the buffer in chunks */ bufecc = malloc(ecc_buffer_size(COPY_BUFFER_LENGTH)); if (!bufecc) return FLASH_ERR_MALLOC_FAILED; while (len > 0) { /* What's left to copy? */ copylen = MIN(len, COPY_BUFFER_LENGTH); /* Read ECCed data from flash */ rc = flash_read(bl, pos, bufecc, ecc_buffer_size(copylen)); if (rc) goto err; /* Extract data from ECCed data */ ret = memcpy_from_ecc(buf, bufecc, copylen); if (ret) { rc = FLASH_ERR_ECC_INVALID; goto err; } /* Update for next copy */ len -= copylen; buf = (uint8_t *)buf + copylen; pos += ecc_buffer_size(copylen); } rc = 0; err: free(bufecc); return rc; } static void fl_get_best_erase(struct flash_chip *c, uint32_t dst, uint32_t size, uint32_t *chunk, uint8_t *cmd) { /* Smaller than 32k, use 4k */ if ((dst & 0x7fff) || (size < 0x8000)) { *chunk = 0x1000; *cmd = CMD_SE; return; } /* Smaller than 64k and 32k is supported, use it */ if ((c->info.flags & FL_ERASE_32K) && ((dst & 0xffff) || (size < 0x10000))) { *chunk = 0x8000; *cmd = CMD_BE32K; return; } /* If 64K is not supported, use whatever smaller size is */ if (!(c->info.flags & FL_ERASE_64K)) { if (c->info.flags & FL_ERASE_32K) { *chunk = 0x8000; *cmd = CMD_BE32K; } else { *chunk = 0x1000; *cmd = CMD_SE; } return; } /* Allright, let's go for 64K */ *chunk = 0x10000; *cmd = CMD_BE; } static int flash_erase(struct blocklevel_device *bl, uint64_t dst, uint64_t size) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); struct spi_flash_ctrl *ct = c->ctrl; uint32_t chunk; uint8_t cmd; int rc; /* Some sanity checking */ if (((dst + size) <= dst) || !size || (dst + size) > c->tsize) return FLASH_ERR_PARM_ERROR; /* Check boundaries fit erase blocks */ if ((dst | size) & c->min_erase_mask) return FLASH_ERR_ERASE_BOUNDARY; FL_DBG("LIBFLASH: Erasing 0x%" PRIx64"..0%" PRIx64 "...\n", dst, dst + size); /* Use controller erase if supported */ if (ct->erase) return ct->erase(ct, dst, size); /* Allright, loop as long as there's something to erase */ while(size) { /* How big can we make it based on alignent & size */ fl_get_best_erase(c, dst, size, &chunk, &cmd); /* Poke write enable */ rc = fl_wren(ct); if (rc) return rc; /* Send erase command */ rc = ct->cmd_wr(ct, cmd, true, dst, NULL, 0); if (rc) return rc; /* Wait for write complete */ rc = fl_sync_wait_idle(ct); if (rc) return rc; size -= chunk; dst += chunk; } return 0; } int flash_erase_chip(struct flash_chip *c) { struct spi_flash_ctrl *ct = c->ctrl; int rc; /* XXX TODO: Fallback to using normal erases */ if (!(c->info.flags & (FL_ERASE_CHIP|FL_ERASE_BULK))) return FLASH_ERR_CHIP_ER_NOT_SUPPORTED; FL_DBG("LIBFLASH: Erasing chip...\n"); /* Use controller erase if supported */ if (ct->erase) return ct->erase(ct, 0, 0xffffffff); rc = fl_wren(ct); if (rc) return rc; if (c->info.flags & FL_ERASE_CHIP) rc = ct->cmd_wr(ct, CMD_CE, false, 0, NULL, 0); else rc = ct->cmd_wr(ct, CMD_MIC_BULK_ERASE, false, 0, NULL, 0); if (rc) return rc; /* Wait for write complete */ return fl_sync_wait_idle(ct); } static int fl_wpage(struct flash_chip *c, uint32_t dst, const void *src, uint32_t size) { struct spi_flash_ctrl *ct = c->ctrl; int rc; if (size < 1 || size > 0x100) return FLASH_ERR_BAD_PAGE_SIZE; rc = fl_wren(ct); if (rc) return rc; rc = ct->cmd_wr(ct, CMD_PP, true, dst, src, size); if (rc) return rc; /* Wait for write complete */ return fl_sync_wait_idle(ct); } static int flash_write(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t size, bool verify) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); struct spi_flash_ctrl *ct = c->ctrl; uint32_t todo = size; uint32_t d = dst; const void *s = src; uint8_t vbuf[0x100]; int rc; /* Some sanity checking */ if (((dst + size) <= dst) || !size || (dst + size) > c->tsize) return FLASH_ERR_PARM_ERROR; FL_DBG("LIBFLASH: Writing to 0x%08x..0%08x...\n", dst, dst + size); /* * If the controller supports write and either we are in 3b mode * or we are in 4b *and* the controller supports it, then do a * high level write. */ if ((!c->mode_4b || ct->set_4b) && ct->write) { rc = ct->write(ct, dst, src, size); if (rc) return rc; goto writing_done; } /* Otherwise, go manual if supported */ if (!ct->cmd_wr) return FLASH_ERR_CTRL_CMD_UNSUPPORTED; /* Iterate for each page to write */ while(todo) { uint32_t chunk; /* Handle misaligned start */ chunk = 0x100 - (d & 0xff); if (chunk > todo) chunk = todo; rc = fl_wpage(c, d, s, chunk); if (rc) return rc; d += chunk; s += chunk; todo -= chunk; } writing_done: if (!verify) return 0; /* Verify */ FL_DBG("LIBFLASH: Verifying...\n"); while(size) { uint32_t chunk; chunk = sizeof(vbuf); if (chunk > size) chunk = size; rc = flash_read(bl, dst, vbuf, chunk); if (rc) return rc; if (memcmp(vbuf, src, chunk)) { FL_ERR("LIBFLASH: Miscompare at 0x%08x\n", dst); return FLASH_ERR_VERIFY_FAILURE; } dst += chunk; src += chunk; size -= chunk; } return 0; } int flash_write_corrected(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len, bool verify, bool ecc) { struct ecc64 *bufecc; uint32_t copylen, copylen_minus_ecc; int rc; uint8_t ret; if (!ecc) return flash_write(bl, pos, buf, len, verify); /* Copy the buffer in chunks */ bufecc = malloc(ecc_buffer_size(COPY_BUFFER_LENGTH)); if (!bufecc) return FLASH_ERR_MALLOC_FAILED; while (len > 0) { /* What's left to copy? */ copylen = MIN(len, COPY_BUFFER_LENGTH); copylen_minus_ecc = ecc_buffer_size_minus_ecc(copylen); /* Add the ecc byte to the data */ ret = memcpy_to_ecc(bufecc, buf, copylen_minus_ecc); if (ret) { rc = FLASH_ERR_ECC_INVALID; goto err; } /* Write ECCed data to the flash */ rc = flash_write(bl, pos, bufecc, copylen, verify); if (rc) goto err; /* Update for next copy */ len -= copylen_minus_ecc; buf = (uint8_t *)buf + copylen_minus_ecc; pos += copylen; } rc = 0; err: free(bufecc); return rc; } enum sm_comp_res { sm_no_change, sm_need_write, sm_need_erase, }; static enum sm_comp_res flash_smart_comp(struct flash_chip *c, const void *src, uint32_t offset, uint32_t size) { uint8_t *b = c->smart_buf + offset; const uint8_t *s = src; bool is_same = true; uint32_t i; /* SRC DEST NEED_ERASE * 0 1 0 * 1 1 0 * 0 0 0 * 1 0 1 */ for (i = 0; i < size; i++) { /* Any bit need to be set, need erase */ if (s[i] & ~b[i]) return sm_need_erase; if (is_same && (b[i] != s[i])) is_same = false; } return is_same ? sm_no_change : sm_need_write; } static int flash_smart_write(struct blocklevel_device *bl, uint64_t dst, const void *src, uint64_t size) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); uint32_t er_size = c->min_erase_mask + 1; uint32_t end = dst + size; int rc; /* Some sanity checking */ if (end <= dst || !size || end > c->tsize) { FL_DBG("LIBFLASH: Smart write param error\n"); return FLASH_ERR_PARM_ERROR; } FL_DBG("LIBFLASH: Smart writing to 0x%" PRIx64 "..0%" PRIx64 "...\n", dst, dst + size); /* As long as we have something to write ... */ while(dst < end) { uint32_t page, off, chunk; enum sm_comp_res sr; /* Figure out which erase page we are in and read it */ page = dst & ~c->min_erase_mask; off = dst & c->min_erase_mask; FL_DBG("LIBFLASH: reading page 0x%08x..0x%08x...", page, page + er_size); rc = flash_read(bl, page, c->smart_buf, er_size); if (rc) { FL_DBG(" error %d!\n", rc); return rc; } /* Locate the chunk of data we are working on */ chunk = er_size - off; if (size < chunk) chunk = size; /* Compare against what we are writing and ff */ sr = flash_smart_comp(c, src, off, chunk); switch(sr) { case sm_no_change: /* Identical, skip it */ FL_DBG(" same !\n"); break; case sm_need_write: /* Just needs writing over */ FL_DBG(" need write !\n"); rc = flash_write(bl, dst, src, chunk, true); if (rc) { FL_DBG("LIBFLASH: Write error %d !\n", rc); return rc; } break; case sm_need_erase: FL_DBG(" need erase !\n"); rc = flash_erase(bl, page, er_size); if (rc) { FL_DBG("LIBFLASH: erase error %d !\n", rc); return rc; } /* Then update the portion of the buffer and write the block */ memcpy(c->smart_buf + off, src, chunk); rc = flash_write(bl, page, c->smart_buf, er_size, true); if (rc) { FL_DBG("LIBFLASH: write error %d !\n", rc); return rc; } break; } dst += chunk; src += chunk; size -= chunk; } return 0; } int flash_smart_write_corrected(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t size, bool ecc) { struct ecc64 *buf; int rc; if (!ecc) return flash_smart_write(bl, dst, src, size); buf = malloc(ecc_buffer_size(size)); if (!buf) return FLASH_ERR_MALLOC_FAILED; rc = memcpy_to_ecc(buf, src, size); if (rc) { rc = FLASH_ERR_ECC_INVALID; goto out; } rc = flash_smart_write(bl, dst, buf, ecc_buffer_size(size)); out: free(buf); return rc; } static int fl_chip_id(struct spi_flash_ctrl *ct, uint8_t *id_buf, uint32_t *id_size) { int rc; uint8_t stat; /* Check initial status */ rc = fl_read_stat(ct, &stat); if (rc) return rc; /* If stuck writing, wait for idle */ if (stat & STAT_WIP) { FL_ERR("LIBFLASH: Flash in writing state ! Waiting...\n"); rc = fl_sync_wait_idle(ct); if (rc) return rc; } else FL_DBG("LIBFLASH: Init status: %02x\n", stat); /* Fallback to get ID manually */ rc = ct->cmd_rd(ct, CMD_RDID, false, 0, id_buf, 3); if (rc) return rc; *id_size = 3; return 0; } static int flash_identify(struct flash_chip *c) { struct spi_flash_ctrl *ct = c->ctrl; const struct flash_info *info = NULL; uint32_t iid, id_size; #define MAX_ID_SIZE 16 uint8_t id[MAX_ID_SIZE]; int rc, i; if (ct->chip_id) { /* High level controller interface */ id_size = MAX_ID_SIZE; rc = ct->chip_id(ct, id, &id_size); } else rc = fl_chip_id(ct, id, &id_size); if (rc) return rc; if (id_size < 3) return FLASH_ERR_CHIP_UNKNOWN; /* Convert to a dword for lookup */ iid = id[0]; iid = (iid << 8) | id[1]; iid = (iid << 8) | id[2]; FL_DBG("LIBFLASH: Flash ID: %02x.%02x.%02x (%06x)\n", id[0], id[1], id[2], iid); /* Lookup in flash_info */ for (i = 0; i < ARRAY_SIZE(flash_info); i++) { info = &flash_info[i]; if (info->id == iid) break; } if (!info || info->id != iid) return FLASH_ERR_CHIP_UNKNOWN; c->info = *info; c->tsize = info->size; ct->finfo = &c->info; /* * Let controller know about our settings and possibly * override them */ if (ct->setup) { rc = ct->setup(ct, &c->tsize); if (rc) return rc; } /* Calculate min erase granularity */ if (c->info.flags & FL_ERASE_4K) c->min_erase_mask = 0xfff; else if (c->info.flags & FL_ERASE_32K) c->min_erase_mask = 0x7fff; else if (c->info.flags & FL_ERASE_64K) c->min_erase_mask = 0xffff; else { /* No erase size ? oops ... */ FL_ERR("LIBFLASH: No erase sizes !\n"); return FLASH_ERR_CTRL_CONFIG_MISMATCH; } FL_DBG("LIBFLASH: Found chip %s size %dM erase granule: %dK\n", c->info.name, c->tsize >> 20, (c->min_erase_mask + 1) >> 10); return 0; } static int flash_set_4b(struct flash_chip *c, bool enable) { struct spi_flash_ctrl *ct = c->ctrl; int rc; /* Don't have low level interface, assume all is well */ if (!ct->cmd_wr) return 0; /* Some flash chips want this */ rc = fl_wren(ct); if (rc) { FL_ERR("LIBFLASH: Error %d enabling write for set_4b\n", rc); /* Ignore the error & move on (could be wrprotect chip) */ } /* Ignore error in case chip is write protected */ return ct->cmd_wr(ct, enable ? CMD_EN4B : CMD_EX4B, false, 0, NULL, 0); } int flash_force_4b_mode(struct flash_chip *c, bool enable_4b) { struct spi_flash_ctrl *ct = c->ctrl; int rc = FLASH_ERR_4B_NOT_SUPPORTED; /* * We only allow force 4b if both controller and flash do 4b * as this is mainly used if a 3rd party tries to directly * access a direct mapped read region */ if (enable_4b && !((c->info.flags & FL_CAN_4B) && ct->set_4b)) return rc; /* Only send to flash directly on controllers that implement * the low level callbacks */ if (ct->cmd_wr) { rc = flash_set_4b(c, enable_4b); if (rc) return rc; } /* Then inform the controller */ if (ct->set_4b) rc = ct->set_4b(ct, enable_4b); return rc; } static int flash_configure(struct flash_chip *c) { struct spi_flash_ctrl *ct = c->ctrl; int rc; /* Crop flash size if necessary */ if (c->tsize > 0x01000000 && !(c->info.flags & FL_CAN_4B)) { FL_ERR("LIBFLASH: Flash chip cropped to 16M, no 4b mode\n"); c->tsize = 0x01000000; } /* If flash chip > 16M, enable 4b mode */ if (c->tsize > 0x01000000) { FL_DBG("LIBFLASH: Flash >16MB, enabling 4B mode...\n"); /* Set flash to 4b mode if we can */ if (ct->cmd_wr) { rc = flash_set_4b(c, true); if (rc) { FL_ERR("LIBFLASH: Failed to set flash 4b mode\n"); return rc; } } /* Set controller to 4b mode if supported */ if (ct->set_4b) { FL_DBG("LIBFLASH: Enabling controller 4B mode...\n"); rc = ct->set_4b(ct, true); if (rc) { FL_ERR("LIBFLASH: Failed to set controller 4b mode\n"); return rc; } } } else { FL_DBG("LIBFLASH: Flash <=16MB, disabling 4B mode...\n"); /* * If flash chip supports 4b mode, make sure we disable * it in case it was left over by the previous user */ if (c->info.flags & FL_CAN_4B) { rc = flash_set_4b(c, false); if (rc) { FL_ERR("LIBFLASH: Failed to" " clear flash 4b mode\n"); return rc; } } /* Set controller to 3b mode if mode switch is supported */ if (ct->set_4b) { FL_DBG("LIBFLASH: Disabling controller 4B mode...\n"); rc = ct->set_4b(ct, false); if (rc) { FL_ERR("LIBFLASH: Failed to" " clear controller 4b mode\n"); return rc; } } } return 0; } static int flash_get_info(struct blocklevel_device *bl, const char **name, uint64_t *total_size, uint32_t *erase_granule) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); if (name) *name = c->info.name; if (total_size) *total_size = c->tsize; if (erase_granule) *erase_granule = c->min_erase_mask + 1; return 0; } int flash_init(struct spi_flash_ctrl *ctrl, struct blocklevel_device **bl, struct flash_chip **flash_chip) { struct flash_chip *c; int rc; if (!bl) return FLASH_ERR_PARM_ERROR; *bl = NULL; c = malloc(sizeof(struct flash_chip)); if (!c) return FLASH_ERR_MALLOC_FAILED; memset(c, 0, sizeof(*c)); c->ctrl = ctrl; rc = flash_identify(c); if (rc) { FL_ERR("LIBFLASH: Flash identification failed\n"); goto bail; } c->smart_buf = malloc(c->min_erase_mask + 1); if (!c->smart_buf) { FL_ERR("LIBFLASH: Failed to allocate smart buffer !\n"); rc = FLASH_ERR_MALLOC_FAILED; goto bail; } rc = flash_configure(c); if (rc) FL_ERR("LIBFLASH: Flash configuration failed\n"); bail: if (rc) { free(c); return rc; } /* The flash backend doesn't support reiniting it */ c->bl.keep_alive = true; c->bl.reacquire = NULL; c->bl.release = NULL; c->bl.read = &flash_read; c->bl.write = &flash_smart_write; c->bl.erase = &flash_erase; c->bl.get_info = &flash_get_info; c->bl.erase_mask = c->min_erase_mask; c->bl.flags = WRITE_NEED_ERASE; *bl = &(c->bl); if (flash_chip) *flash_chip = c; return 0; } void flash_exit(struct blocklevel_device *bl) { /* XXX Make sure we are idle etc... */ if (bl) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); free(c->smart_buf); free(c); } } void flash_exit_close(struct blocklevel_device *bl, void (*close)(struct spi_flash_ctrl *ctrl)) { if (bl) { struct flash_chip *c = container_of(bl, struct flash_chip, bl); close(c->ctrl); free(c); } }