📄 cfi_cmdset_0001.c
字号:
len -= n; ofs += n; buf += n; (*retlen) += n; if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } while(len >= map_bankwidth(map)) { map_word datum = map_word_load(map, buf); ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum, FL_WRITING); if (ret) return ret; ofs += map_bankwidth(map); buf += map_bankwidth(map); (*retlen) += map_bankwidth(map); len -= map_bankwidth(map); if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } if (len & (map_bankwidth(map)-1)) { map_word datum; datum = map_word_ff(map); datum = map_word_load_partial(map, datum, buf, 0, len); ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum, FL_WRITING); if (ret) return ret; (*retlen) += len; } return 0;}static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, unsigned long adr, const struct kvec **pvec, unsigned long *pvec_seek, int len){ struct cfi_private *cfi = map->fldrv_priv; map_word status, write_cmd, datum; unsigned long cmd_adr; int ret, wbufsize, word_gap, words; const struct kvec *vec; unsigned long vec_seek; wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; adr += chip->start; cmd_adr = adr & ~(wbufsize-1); /* Let's determine this according to the interleave only once */ write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9); spin_lock(chip->mutex); ret = get_chip(map, chip, cmd_adr, FL_WRITING); if (ret) { spin_unlock(chip->mutex); return ret; } XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); xip_disable(map, chip, cmd_adr); /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set [...], the device will not accept any more Write to Buffer commands". So we must check here and reset those bits if they're set. Otherwise we're just pissing in the wind */ if (chip->state != FL_STATUS) { map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; } status = map_read(map, cmd_adr); if (map_word_bitsset(map, status, CMD(0x30))) { xip_enable(map, chip, cmd_adr); printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]); xip_disable(map, chip, cmd_adr); map_write(map, CMD(0x50), cmd_adr); map_write(map, CMD(0x70), cmd_adr); } chip->state = FL_WRITING_TO_BUFFER; map_write(map, write_cmd, cmd_adr); ret = WAIT_TIMEOUT(map, chip, cmd_adr, 0); if (ret) { /* Argh. Not ready for write to buffer */ map_word Xstatus = map_read(map, cmd_adr); map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; status = map_read(map, cmd_adr); map_write(map, CMD(0x50), cmd_adr); map_write(map, CMD(0x70), cmd_adr); xip_enable(map, chip, cmd_adr); printk(KERN_ERR "%s: Chip not ready for buffer write. Xstatus = %lx, status = %lx\n", map->name, Xstatus.x[0], status.x[0]); goto out; } /* Figure out the number of words to write */ word_gap = (-adr & (map_bankwidth(map)-1)); words = (len - word_gap + map_bankwidth(map) - 1) / map_bankwidth(map); if (!word_gap) { words--; } else { word_gap = map_bankwidth(map) - word_gap; adr -= word_gap; datum = map_word_ff(map); } /* Write length of data to come */ map_write(map, CMD(words), cmd_adr ); /* Write data */ vec = *pvec; vec_seek = *pvec_seek; do { int n = map_bankwidth(map) - word_gap; if (n > vec->iov_len - vec_seek) n = vec->iov_len - vec_seek; if (n > len) n = len; if (!word_gap && len < map_bankwidth(map)) datum = map_word_ff(map); datum = map_word_load_partial(map, datum, vec->iov_base + vec_seek, word_gap, n); len -= n; word_gap += n; if (!len || word_gap == map_bankwidth(map)) { map_write(map, datum, adr); adr += map_bankwidth(map); word_gap = 0; } vec_seek += n; if (vec_seek == vec->iov_len) { vec++; vec_seek = 0; } } while (len); *pvec = vec; *pvec_seek = vec_seek; /* GO GO GO */ map_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, adr, len, chip->buffer_write_time); if (ret) { map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; xip_enable(map, chip, cmd_adr); printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name); goto out; } /* check for errors */ status = map_read(map, cmd_adr); if (map_word_bitsset(map, status, CMD(0x1a))) { unsigned long chipstatus = MERGESTATUS(status); /* reset status */ map_write(map, CMD(0x50), cmd_adr); map_write(map, CMD(0x70), cmd_adr); xip_enable(map, chip, cmd_adr); if (chipstatus & 0x02) { ret = -EROFS; } else if (chipstatus & 0x08) { printk(KERN_ERR "%s: buffer write error (bad VPP)\n", map->name); ret = -EIO; } else { printk(KERN_ERR "%s: buffer write error (status 0x%lx)\n", map->name, chipstatus); ret = -EINVAL; } goto out; } xip_enable(map, chip, cmd_adr); out: put_chip(map, chip, cmd_adr); spin_unlock(chip->mutex); return ret;}static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; int ret = 0; int chipnum; unsigned long ofs, vec_seek, i; size_t len = 0; for (i = 0; i < count; i++) len += vecs[i].iov_len; *retlen = 0; if (!len) return 0; chipnum = to >> cfi->chipshift; ofs = to - (chipnum << cfi->chipshift); vec_seek = 0; do { /* We must not cross write block boundaries */ int size = wbufsize - (ofs & (wbufsize-1)); if (size > len) size = len; ret = do_write_buffer(map, &cfi->chips[chipnum], ofs, &vecs, &vec_seek, size); if (ret) return ret; ofs += size; (*retlen) += size; len -= size; if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } /* Be nice and reschedule with the chip in a usable state for other processes. */ cond_resched(); } while (len); 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 kvec vec; vec.iov_base = (void *) buf; vec.iov_len = len; return cfi_intelext_writev(mtd, &vec, 1, to, retlen);}static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk){ struct cfi_private *cfi = map->fldrv_priv; map_word status; int retries = 3; int ret; adr += chip->start; retry: spin_lock(chip->mutex); ret = get_chip(map, chip, adr, FL_ERASING); if (ret) { spin_unlock(chip->mutex); return ret; } XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); xip_disable(map, chip, adr); /* Clear the status register first */ map_write(map, CMD(0x50), adr); /* Now erase */ map_write(map, CMD(0x20), adr); map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; chip->erase_suspended = 0; ret = INVAL_CACHE_AND_WAIT(map, chip, adr, adr, len, chip->erase_time); if (ret) { map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; xip_enable(map, chip, adr); printk(KERN_ERR "%s: block erase error: (status timeout)\n", map->name); goto out; } /* We've broken this before. It doesn't hurt to be safe */ map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; status = map_read(map, adr); /* check for errors */ if (map_word_bitsset(map, status, CMD(0x3a))) { unsigned long chipstatus = MERGESTATUS(status); /* Reset the error bits */ map_write(map, CMD(0x50), adr); map_write(map, CMD(0x70), adr); xip_enable(map, chip, adr); if ((chipstatus & 0x30) == 0x30) { printk(KERN_ERR "%s: block erase error: (bad command sequence, status 0x%lx)\n", map->name, chipstatus); ret = -EINVAL; } else if (chipstatus & 0x02) { /* Protection bit set */ ret = -EROFS; } else if (chipstatus & 0x8) { /* Voltage */ printk(KERN_ERR "%s: block erase error: (bad VPP)\n", map->name); ret = -EIO; } else if (chipstatus & 0x20 && retries--) { printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus); put_chip(map, chip, adr); spin_unlock(chip->mutex); goto retry; } else { printk(KERN_ERR "%s: block erase failed at 0x%08lx (status 0x%lx)\n", map->name, adr, chipstatus); ret = -EIO; } goto out; } xip_enable(map, chip, adr); out: put_chip(map, chip, adr); spin_unlock(chip->mutex); 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_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); if (ret) return ret; instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); return 0;}static void cfi_intelext_sync (struct mtd_info *mtd){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; int i; struct flchip *chip; int ret = 0; for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; spin_lock(chip->mutex); ret = get_chip(map, chip, chip->start, FL_SYNCING); if (!ret) { chip->oldstate = chip->state; chip->state = FL_SYNCING; /* No need to wake_up() on this state change - * as the whole point is that nobody can do anything * with the chip now anyway. */ } spin_unlock(chip->mutex); } /* Unlock the chips again */ for (i--; i >=0; i--) { chip = &cfi->chips[i]; spin_lock(chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; chip->oldstate = FL_READY; wake_up(&chip->wq); } spin_unlock(chip->mutex); }}#ifdef DEBUG_LOCK_BITSstatic int __xipram do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk){ struct cfi_private *cfi = map->fldrv_priv; int status, ofs_factor = cfi->interleave * cfi->device_type; adr += chip->start; xip_disable(map, chip, adr+(2*ofs_factor)); map_write(map, CMD(0x90), adr+(2*ofs_factor)); chip->state = FL_JEDEC_QUERY; status = cfi_read_query(map, adr+(2*ofs_factor)); xip_enable(map, chip, 0); printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", adr, status); return 0;}#endif#define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1)#define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2)static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk){ struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_intelext *extp = cfi->cmdset_priv; int udelay; int ret; adr += chip->start; spin_lock(chip->mutex); ret = get_chip(map, chip, adr, FL_LOCKING); if (ret) { spin_unlock(chip->mutex); return ret; } ENABLE_VPP(map); xip_disable(map, chip, adr); map_write(map, CMD(0x60), adr); if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { map_write(map, CMD(0xD0), adr); chip->state = FL_UNLOCKING; } else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -