static vsf_err_t cfi_wait_busy(struct dal_info_t *info, uint64_t address) { uint32_t cur_status = 0, orig_status = 0; struct cfi_drv_param_t *param = (struct cfi_drv_param_t *)info->param; uint8_t data_width = param->nor_info.common_info.data_width / 8; cfi_read(info, (uint32_t)address, data_width, (uint8_t *)&orig_status, 1); do { cfi_read(info, 0, data_width, (uint8_t *)&cur_status, 1); interfaces->peripheral_commit(); if ((cur_status ^ orig_status) & 0x0040) { if (cur_status & 0x0020) { cfi_read(info, 0, data_width, (uint8_t *)&orig_status, 1); cfi_read(info, 0, data_width, (uint8_t *)&cur_status, 1); interfaces->peripheral_commit(); return ((cur_status ^ orig_status) & 0x0040) ? VSFERR_FAIL : VSFERR_NONE; } } else { break; } orig_status = cur_status; } while (1); return VSFERR_NONE; }
/* check for QRY. in: interleave,type,mode ret: table index, <0 for error */ static inline int qry_present(struct map_info *map, __u32 base, struct cfi_private *cfi) { int osf = cfi->interleave * cfi->device_type; // scale factor if (cfi_read(map,base+osf*0x10)==cfi_build_cmd('Q',map,cfi) && cfi_read(map,base+osf*0x11)==cfi_build_cmd('R',map,cfi) && cfi_read(map,base+osf*0x12)==cfi_build_cmd('Y',map,cfi)) return 1; // ok ! return 0; // nothing found }
static vsf_err_t cfi_drv_eraseall_nb_isready(struct dal_info_t *info) { uint32_t val1 = 0, val2 = 0; struct cfi_drv_param_t *param = (struct cfi_drv_param_t *)info->param; uint8_t data_width = param->nor_info.common_info.data_width / 8; cfi_read(info, 0x0000 << 1, data_width, (uint8_t *)&val1, 1); cfi_read(info, 0x0000 << 1, data_width, (uint8_t *)&val2, 1); interfaces->peripheral_commit(); return (((val1 ^ val2) & 0x0040) == 0) ? VSFERR_NONE : VSFERR_NOT_READY; }
static vsf_err_t cfi_drv_eraseblock_nb_isready(struct dal_info_t *info, uint64_t address) { uint32_t val1 = 0, val2 = 0; struct sst32hfxx_drv_param_t *param = (struct sst32hfxx_drv_param_t *)info->param; uint8_t data_width = param->nor_info.common_info.data_width / 8; REFERENCE_PARAMETER(address); cfi_read(info, 0x0000 << 1, data_width, (uint8_t *)&val1, 1); cfi_read(info, 0x0000 << 1, data_width, (uint8_t *)&val2, 1); interfaces->peripheral_commit(); return (((val1 ^ val2) & 0x0040) == 0) ? VSFERR_NONE : VSFERR_NOT_READY; }
static vsf_err_t cfi_drv_readblock_nb(struct dal_info_t *info, uint64_t address, uint8_t *buff) { uint32_t count, i, cur_count; struct cfi_drv_param_t *param = (struct cfi_drv_param_t *)info->param; uint8_t data_width = param->nor_info.common_info.data_width / 8; struct mal_info_t *mal_info = (struct mal_info_t *)info->extra; if (mal_info->read_page_size) { count = (uint32_t)mal_info->read_page_size / data_width; } else { count = (uint32_t)mal_info->capacity.block_size / data_width; } i = 0; while (i < count) { cur_count = (count > 1024) ? 1024 : count; cfi_read(info, (uint32_t)address, data_width, buff, cur_count); address += cur_count * data_width; buff += cur_count * data_width; i += cur_count; } return interfaces->peripheral_commit(); }
static int cfi_wait_ready(struct cfi_softc *sc, u_int ofs, u_int timeout) { int done, error; uint32_t st0 = 0, st = 0; done = 0; error = 0; timeout *= 10; while (!done && !error && timeout) { DELAY(100); timeout--; switch (sc->sc_cmdset) { case CFI_VEND_INTEL_ECS: case CFI_VEND_INTEL_SCS: st = cfi_read(sc, ofs); done = (st & CFI_INTEL_STATUS_WSMS); if (done) { /* NB: bit 0 is reserved */ st &= ~(CFI_INTEL_XSTATUS_RSVD | CFI_INTEL_STATUS_WSMS | CFI_INTEL_STATUS_RSVD); if (st & CFI_INTEL_STATUS_DPS) error = EPERM; else if (st & CFI_INTEL_STATUS_PSLBS) error = EIO; else if (st & CFI_INTEL_STATUS_ECLBS) error = ENXIO; else if (st) error = EACCES; } break; case CFI_VEND_AMD_SCS: case CFI_VEND_AMD_ECS: st0 = cfi_read(sc, ofs); st = cfi_read(sc, ofs); done = ((st & 0x40) == (st0 & 0x40)) ? 1 : 0; break; } } if (!done && !error) error = ETIMEDOUT; if (error) printf("\nerror=%d (st 0x%x st0 0x%x)\n", error, st, st0); return (error); }
static vsf_err_t cfi_drv_writeblock_nb_isready(struct dal_info_t *info, uint64_t address, uint8_t *buff) { uint32_t status = 0, verify_data; struct mal_info_t *mal_info = (struct mal_info_t *)info->extra; struct cfi_drv_param_t *param = (struct cfi_drv_param_t *)info->param; uint8_t data_width = param->nor_info.common_info.data_width / 8; uint32_t write_page_size = mal_info->write_page_size; cfi_read(info, (uint32_t)address + write_page_size - data_width, data_width, (uint8_t *)&status, 1); interfaces->peripheral_commit(); switch (data_width) { case 1: verify_data = buff[write_page_size - 1]; break; case 2: verify_data = ((uint16_t *)buff)[write_page_size / 2 - 1]; break; case 4: verify_data = ((uint32_t *)buff)[write_page_size / 4 - 1]; break; default: return VSFERR_FAIL; } if (verify_data == status) { return VSFERR_NONE; } else if ((status & 0x20) || (status & 0x02)) { cfi_read(info, (uint32_t)address + write_page_size - data_width, data_width, (uint8_t *)&status, 1); if (interfaces->peripheral_commit() || (verify_data != status)) { return VSFERR_FAIL; } return VSFERR_NONE; } return VSFERR_NOT_READY; }
static void cfi_disk_read(struct cfi_softc *sc, struct bio *bp) { long resid; KASSERT(sc->sc_width == 1 || sc->sc_width == 2 || sc->sc_width == 4, ("sc_width %d", sc->sc_width)); if (sc->sc_writing) { bp->bio_error = cfi_block_finish(sc); if (bp->bio_error) { bp->bio_flags |= BIO_ERROR; goto done; } } if (bp->bio_offset > sc->sc_size) { bp->bio_flags |= BIO_ERROR; bp->bio_error = EIO; goto done; } resid = bp->bio_bcount; if (sc->sc_width == 1) { uint8_t *dp = (uint8_t *)bp->bio_data; while (resid > 0 && bp->bio_offset < sc->sc_size) { *dp++ = cfi_read(sc, bp->bio_offset); bp->bio_offset += 1, resid -= 1; } } else if (sc->sc_width == 2) { uint16_t *dp = (uint16_t *)bp->bio_data; while (resid > 0 && bp->bio_offset < sc->sc_size) { *dp++ = cfi_read(sc, bp->bio_offset); bp->bio_offset += 2, resid -= 2; } } else { uint32_t *dp = (uint32_t *)bp->bio_data; while (resid > 0 && bp->bio_offset < sc->sc_size) { *dp++ = cfi_read(sc, bp->bio_offset); bp->bio_offset += 4, resid -= 4; } } bp->bio_resid = resid; done: biodone(bp); }
uint8_t cfi_read_qry(struct cfi_softc *sc, u_int ofs) { uint8_t val; cfi_write(sc, CFI_QRY_CMD_ADDR * sc->sc_width, CFI_QRY_CMD_DATA); val = cfi_read(sc, ofs * sc->sc_width); cfi_write(sc, 0, CFI_BCS_READ_ARRAY); return (val); }
static vsf_err_t cfi_drv_getinfo(struct dal_info_t *info) { uint32_t manufacturer_id = 0, device_id = 0; struct sst32hfxx_drv_info_t *pinfo = (struct sst32hfxx_drv_info_t *)info->info; struct sst32hfxx_drv_param_t *param = (struct sst32hfxx_drv_param_t *)info->param; uint8_t data_width = param->nor_info.common_info.data_width / 8; cfi_write_cmd(info, 0xAA, data_width, 0x5555 << 1); cfi_write_cmd(info, 0x55, data_width, 0x2AAA << 1); cfi_write_cmd(info, 0x90, data_width, 0x5555 << 1); cfi_read(info, 0x0000 << 1, data_width, (uint8_t *)&manufacturer_id, 1); cfi_read(info, 0x0001 << 1, data_width, (uint8_t *)&device_id, 1); interfaces->peripheral_commit(); pinfo->manufacturer_id = (uint8_t)manufacturer_id; pinfo->device_id = (uint16_t)device_id; cfi_write_cmd(info, 0xAA, data_width, 0x5555 << 1); cfi_write_cmd(info, 0x55, data_width, 0x2AAA << 1); cfi_write_cmd(info, 0xF0, data_width, 0x5555 << 1); return VSFERR_NONE; }
void cfi_test() { struct cfi *cfi; char test_buff[2] = {0x2c,0x04}; char read_buff[2]; int addr; int i; delay(5 * 1000); printf("cfi test ...\n"); cfi_init(0); cfi = &cfid[0]; addr = 0xf80000; for(i=0;i<256;i++) { cfi_erase(0, addr, cfi->sectsiz); cfi_write(0, addr + 0x01fe, 2, test_buff); cfi_write(0, addr + 0x01fc, 2, test_buff); addr += cfi->sectsiz; } addr = 0xf80000; for(i=0;i<256;i++) { cfi_read(0, addr + 0x01fe, 2, read_buff); if ((read_buff[0] != 0x2c) || (read_buff[1] != 0x04)) { printf("error block %d\n",i); } cfi_read(0, addr + 0x01fc, 2, read_buff); if ((read_buff[0] != 0x2c) || (read_buff[1] != 0x04)) { printf("error block %d\n",i); } addr += cfi->sectsiz; } printf("finish"); }
static vsf_err_t cfi_drv_getinfo(struct dal_info_t *info) { uint8_t cfi_info8[256]; uint16_t *cfi_info16 = (uint16_t *)cfi_info8; uint32_t *cfi_info32 = (uint32_t *)cfi_info8; uint32_t manufacturer_id = 0; struct cfi_drv_info_t *pinfo = (struct cfi_drv_info_t *)info->info; struct mal_info_t *cfi_mal_info = (struct mal_info_t *)info->extra; struct cfi_drv_param_t *param = (struct cfi_drv_param_t *)info->param; uint8_t data_width = param->nor_info.common_info.data_width / 8; cfi_write_cmd(info, 0xAA, data_width, 0x0555 << 1); cfi_write_cmd(info, 0x55, data_width, 0x02AA << 1); cfi_write_cmd(info, 0x90, data_width, 0x0555 << 1); cfi_read(info, 0x0000 << 1, data_width, (uint8_t *)&manufacturer_id, 1); cfi_read(info, 0x0001 << 1, data_width, (uint8_t *)&pinfo->device_id[0], 1); cfi_read(info, 0x000E << 1, data_width, (uint8_t *)&pinfo->device_id[1], 1); cfi_read(info, 0x000F << 1, data_width, (uint8_t *)&pinfo->device_id[2], 1); interfaces->peripheral_commit(); pinfo->manufacturer_id = (uint8_t)manufacturer_id; pinfo->device_id[0] = LE_TO_SYS_U16(pinfo->device_id[0]); pinfo->device_id[1] = LE_TO_SYS_U16(pinfo->device_id[1]); pinfo->device_id[2] = LE_TO_SYS_U16(pinfo->device_id[2]); cfi_write_cmd(info, 0xAA, data_width, 0x0555 << 1); cfi_write_cmd(info, 0x55, data_width, 0x02AA << 1); cfi_write_cmd(info, 0xF0, data_width, 0); cfi_write_cmd(info, 0x98, data_width, 0x0055 << 1); cfi_read(info, 0x0010 << 1, data_width, (uint8_t *)cfi_info8, sizeof(cfi_info8) / data_width); if (interfaces->peripheral_commit() || ((1 == data_width) && ((cfi_info8[0] != 'Q') || (cfi_info8[1] != 'R') || (cfi_info8[2] != 'Y'))) || ((2 == data_width) && ((cfi_info16[0] != 'Q') || (cfi_info16[1] != 'R') || (cfi_info16[2] != 'Y'))) || ((4 == data_width) && ((cfi_info32[0] != 'Q') || (cfi_info32[1] != 'R') || (cfi_info32[2] != 'Y')))) { return VSFERR_FAIL; } if (!cfi_mal_info->capacity.block_number || !cfi_mal_info->capacity.block_size) { switch (data_width) { case 1: cfi_mal_info->capacity.block_number = cfi_info8[0x1D] + 1; cfi_mal_info->capacity.block_size = ((uint64_t)1 << cfi_info8[0x17]) / (cfi_info8[0x1D] + 1); break; case 2: cfi_mal_info->capacity.block_number = cfi_info16[0x1D] + 1; cfi_mal_info->capacity.block_size = ((uint64_t)1 << cfi_info16[0x17]) / (cfi_info16[0x1D] + 1); break; case 4: cfi_mal_info->capacity.block_number = cfi_info32[0x1D] + 1; cfi_mal_info->capacity.block_size = ((uint64_t)1 << cfi_info32[0x17]) / (cfi_info32[0x1D] + 1); break; default: return VSFERR_FAIL; } } switch (data_width) { case 1: cfi_mal_info->write_page_size = 1 << cfi_info8[0x1A]; break; case 2: cfi_mal_info->write_page_size = 1 << cfi_info16[0x1A]; break; case 4: cfi_mal_info->write_page_size = 1 << cfi_info32[0x1A]; break; default: return VSFERR_FAIL; } cfi_mal_info->erase_page_size = (uint32_t)cfi_mal_info->capacity.block_size; cfi_write_cmd(info, 0xAA, data_width, 0x0555 << 1); cfi_write_cmd(info, 0x55, data_width, 0x02AA << 1); cfi_write_cmd(info, 0xF0, data_width, 0); return VSFERR_NONE; }