📄 cfi_cmdset_0001.c
字号:
return -EIO; } } /* Write length of data to come */ cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr); /* Write data */ for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { if (cfi_buswidth_is_1()) { map->write8(map, *((__u8*)buf)++, adr+z); } else if (cfi_buswidth_is_2()) { map->write16(map, *((__u16*)buf)++, adr+z); } else if (cfi_buswidth_is_4()) { map->write32(map, *((__u32*)buf)++, adr+z); } else if (cfi_buswidth_is_8()) { map->write64(map, *((__u64*)buf)++, adr+z); } else { DISABLE_VPP(map); cfi_write(map, CMD(0xff), adr); return -EINVAL; } } /* GO GO GO */ cfi_write(map, CMD(0xd0), cmd_adr); timeo = cfi->cfiq->BufWriteTimeoutMax * 1000; for (;;) { status = cfi_read(map, cmd_adr); if ((status & status_OK) == status_OK) break; if (timeo-- < 0) { printk("Waiting for chip to be ready timed out in bufwrite\n"); cfi_write(map, CMD(0xff), adr); return -EIO; } } /* Done and happy. */ DISABLE_VPP(map); /* check for lock bit */ if (status & CMD(0x02)) { /* clear status */ cfi_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */ cfi_write(map, CMD(0x70), adr); cfi_write(map, CMD(0xff), adr); return -EROFS; } cfi_write(map, CMD(0xff), adr); return 0;}static int cfi_intelext_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; int ret = 0; int chipnum; unsigned long ofs; *retlen = 0; if (!len) return 0; chipnum = to >> cfi->chipshift; ofs = to - (chipnum << cfi->chipshift);#ifdef CONFIG_MSG_PROGRESS progress_bar(0, 100);#endif /* If it's not bus-aligned, do the first word write */ if (ofs & (CFIDEV_BUSWIDTH-1)) { size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1); if (local_len > len) local_len = len; ret = cfi_intelext_write_words(mtd, to, local_len, retlen, buf); if (ret) return ret; ofs += local_len; buf += local_len; len -= local_len; if (ofs >> cfi->chipshift) { chipnum++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } /* Write buffer is worth it only if more than one word to write... */ while (len > CFIDEV_BUSWIDTH) { /* We must not cross write block boundaries */ int size = wbufsize - (ofs & (wbufsize-1)); if (size > len ) size = len & ~(CFIDEV_BUSWIDTH - 1); ret = do_write_buffer(map, &cfi->chips[chipnum], ofs, buf, size); if (ret) return ret; ofs += size; buf += size; (*retlen) += size; len -= size; if (ofs >> cfi->chipshift) { chipnum++; ofs = 0; if (chipnum == cfi->numchips) return 0; }#ifdef CONFIG_MSG_PROGRESS if (!(*retlen % 0x20000)) progress_bar(*retlen, len + *retlen);#endif } /* ... and write the remaining bytes */ if (len > 0) { size_t local_retlen; ret = cfi_intelext_write_words(mtd, ofs + (chipnum << cfi->chipshift), len, &local_retlen, buf); if (ret) return ret; (*retlen) += local_retlen; }#ifdef CONFIG_MSG_PROGRESS progress_bar(100, 100);#endif return 0;}typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk);static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, loff_t ofs, size_t len, void *thunk){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long adr; int chipnum, ret = 0; int i, first; struct mtd_erase_region_info *regions = mtd->eraseregions; if (ofs > mtd->size) return -EINVAL; if ((len + ofs) > mtd->size) return -EINVAL; /* Check that both start and end of the requested erase are * aligned with the erasesize at the appropriate addresses. */ i = 0; /* Skip all erase regions which are ended before the start of the requested erase. Actually, to save on the calculations, we skip to the first erase region which starts after the start of the requested erase, and then go back one. */ while (i < mtd->numeraseregions && ofs >= regions[i].offset) i++; i--; /* OK, now i is pointing at the erase region in which this erase request starts. Check the start of the requested erase range is aligned with the erase size which is in effect here. */ if (ofs & (regions[i].erasesize-1)) return -EINVAL; /* Remember the erase region we start on */ first = i; /* Next, check that the end of the requested erase is aligned * with the erase region at that address. */ while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset) i++; /* As before, drop back one to point at the region in which the address actually falls */ i--; if ((ofs + len) & (regions[i].erasesize-1)) return -EINVAL; chipnum = ofs >> cfi->chipshift; adr = ofs - (chipnum << cfi->chipshift); i=first;#ifdef CONFIG_MSG_PROGRESS progress_bar(adr - ofs, (len + adr) - ofs);#endif while(len) { ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); if (ret) return ret; adr += regions[i].erasesize; len -= regions[i].erasesize; if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) i++; if (adr >> cfi->chipshift) { adr = 0; chipnum++; if (chipnum >= cfi->numchips) break; }#ifdef CONFIG_MSG_PROGRESS progress_bar(adr - ofs, (len + adr) - ofs);#endif }#ifdef CONFIG_MSG_PROGRESS progress_bar(100, 100);#endif return 0;}static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk){ struct cfi_private *cfi = map->fldrv_priv; cfi_word status, status_OK; unsigned long timeo; int ret = 0; adr += chip->start; /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); timeo = 0x4000 * 1000; ENABLE_VPP(map); /* Clear the status register first */ cfi_write(map, CMD(0x50), adr); /* Now erase */ cfi_write(map, CMD(0x20), adr); cfi_write(map, CMD(0xD0), adr); while (timeo > 0) { status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; timeo--; } DISABLE_VPP(map); /* We've broken this before. It doesn't hurt to be safe */ cfi_write(map, CMD(0x70), adr); status = cfi_read(map, adr); ret = 0; /* check for lock bit */ if ((timeo <= 0) || (status & CMD(0x3a))) { unsigned char chipstatus = status; if (status != CMD(status & 0xff)) { int i; for (i = 1; i < CFIDEV_INTERLEAVE; i++) { chipstatus |= status >> (cfi->device_type * 8); } printk("Status is not identical for all chips: 0x%llx, Mergint to give 0x%02x\n", (__u64)status, chipstatus); } /* Reset the error bits */ cfi_write(map, CMD(0x50), adr); cfi_write(map, CMD(0x70), adr); printk("\n"); if ((chipstatus & 0x30) == 0x30) { printk("Chip reports improper command sequence: status 0x%llx\n", (__u64)status); ret = -EIO; } else if (chipstatus & 0x02) { /* Protection bit set */ printk("Protection bit set: status 0x%llx\n", (__u64)status); ret = -EROFS; } else if (chipstatus & 0x8) { /* Voltage */ printk("Chip reports voltage low on erase: status 0x%llx\n", (__u64)status); ret = -EIO; } else if (chipstatus & 0x20) { printk("Chip erase failed at 0x%08lx: status 0x%llx\n", adr, (__u64)status); ret = -EIO; } } cfi_write(map, CMD(0xff), adr); return ret;}int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr){ unsigned long ofs, len; int ret; ofs = instr->addr; len = instr->len; ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0); return ret;}#ifdef DEBUG_LOCK_BITSstatic int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk){ struct cfi_private *cfi = map->fldrv_priv; int ofs_factor = cfi->interleave * cfi->device_type; cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); printk("block status register for 0x%08lx is %x\n", adr, cfi_read_query(map, adr+(2*ofs_factor))); cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); return 0;}#endif#define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1)#define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2)static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk){ struct cfi_private *cfi = map->fldrv_priv; cfi_word status, status_OK; unsigned long timeo = 0x10000; adr += chip->start; /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); ENABLE_VPP(map); cfi_write(map, CMD(0x60), adr); if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { cfi_write(map, CMD(0x01), adr); } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { cfi_write(map, CMD(0xD0), adr); } else { /* bug */ } for (;;) { status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; if (timeo-- == 0) { cfi_write(map, CMD(0x70), adr); printk("waiting for unlock to complete timed out. Xstatus = %llx, status = %llx.\n", (__u64)status, (__u64)cfi_read(map, adr)); cfi_write(map, CMD(0xff), adr); DISABLE_VPP(map); return -EIO; } } /* Done */ DISABLE_VPP(map); cfi_write(map, CMD(0xff), adr); return 0;}static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len){ int ret;#ifdef DEBUG_LOCK_BITS printk(__FUNCTION__ ": lock status before, ofs=0x%08llx, len=0x%08X\n", ofs, len); cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0);#endif ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);#ifdef DEBUG_LOCK_BITS printk(__FUNCTION__ ": lock status after, ret=%d\n", ret); cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0);#endif return ret;}static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len){ int ret;#ifdef DEBUG_LOCK_BITS printk(__FUNCTION__ ": lock status before, ofs=0x%08llx, len=0x%08X\n", ofs, len); cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0);#endif ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK);#ifdef DEBUG_LOCK_BITS printk(__FUNCTION__ ": lock status after, ret=%d\n", ret); cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0);#endif return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -