/* This routine is made available to other mtd code via *inter_module_register.Itmustonlybeaccessedthrough *inter_module_getwhichwillbumptheusecountofthismodule.The *addressespassedbackincfiarevalidaslongastheusecountof *thismoduleisnon-zero,i.e.betweeninter_module_getand *inter_module_put.KeithOwens<kaos@ocs.com.au>29Oct2000.
*/ struct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary)
{ struct cfi_private *cfi = map->fldrv_priv; int i;
if (extp->MajorVersion != '1' ||
(extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
printk(KERN_ERR " Unknown ST Microelectronics" " Extended Query version %c.%c.\n",
extp->MajorVersion, extp->MinorVersion);
kfree(extp); return NULL;
}
/* Do some byteswapping if necessary */
extp->FeatureSupport = cfi32_to_cpu(map, extp->FeatureSupport);
extp->BlkStatusRegMask = cfi32_to_cpu(map,
extp->BlkStatusRegMask);
#ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */
cfi_tell_features(extp); #endif
/* Install our own private info structure */
cfi->cmdset_priv = extp;
}
if (offset != devsize) { /* Argh */
printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
kfree(mtd->eraseregions);
kfree(cfi->cmdset_priv);
kfree(mtd); return NULL;
}
for (i=0; i<mtd->numeraseregions;i++){
printk(KERN_DEBUG "%d: offset=0x%llx,size=0x%x,blocks=%d\n",
i, (unsignedlonglong)mtd->eraseregions[i].offset,
mtd->eraseregions[i].erasesize,
mtd->eraseregions[i].numblocks);
}
/* Check that the chip's ready to talk to us. *Ifit'sinFL_ERASINGstate,suspenditandmakeittalknow.
*/ switch (chip->state) { case FL_ERASING: if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) goto sleep; /* We don't support erase suspend */
map_write (map, CMD(0xb0), cmd_addr); /* If the flash has finished erasing, then 'erase suspend' *appearstomakesome(28F320)flashdevicesswitchto *'read'mode.Makesurethatweswitchto'readstatus' *modesowegettherightdata.--rmk
*/
map_write(map, CMD(0x70), cmd_addr);
chip->oldstate = FL_ERASING;
chip->state = FL_ERASE_SUSPENDING; // printk("Erase suspending at 0x%lx\n", cmd_addr); for (;;) {
status = map_read(map, cmd_addr); if (map_word_andequal(map, status, status_OK, status_OK)) break;
if (time_after(jiffies, timeo)) { /* Urgh */
map_write(map, CMD(0xd0), cmd_addr); /* make sure we're in 'read status' mode */
map_write(map, CMD(0x70), cmd_addr);
chip->state = FL_ERASING;
wake_up(&chip->wq);
mutex_unlock(&chip->mutex);
printk(KERN_ERR "Chip not ready after erase " "suspended: status = 0x%lx\n", status.x[0]); return -EIO;
}
case FL_CFI_QUERY: case FL_JEDEC_QUERY:
map_write(map, CMD(0x70), cmd_addr);
chip->state = FL_STATUS;
fallthrough; case FL_STATUS:
status = map_read(map, cmd_addr); if (map_word_andequal(map, status, status_OK, status_OK)) {
map_write(map, CMD(0xff), cmd_addr);
chip->state = FL_READY; break;
}
/* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) {
mutex_unlock(&chip->mutex);
printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); return -EIO;
}
/* Latency issues. Drop the lock, wait a while and retry */
mutex_unlock(&chip->mutex);
cfi_udelay(1); goto retry;
default:
sleep: /* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
mutex_unlock(&chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + HZ; goto retry;
}
map_copy_from(map, buf, adr, len);
if (suspended) {
chip->state = chip->oldstate; /* What if one interleaved chip has finished and the otherhasn't?Theoldcodewouldleavethefinished oneinREADYmode.That'sbad,andcaused-EROFS errorstobereturnedfromdo_erase_oneblockbecause that'stheonlybititcheckedforatthetime. Asthestatemachineappearstoexplicitlyallow sendingthe0x70(ReadStatus)commandtoanerasing chipandexpectingittobeignored,that'swhatwe
do. */
map_write(map, CMD(0xd0), cmd_addr);
map_write(map, CMD(0x70), cmd_addr);
}
/* Check that the chip's ready to talk to us. *Later,wecanactuallythinkaboutinterruptingit *ifit'sinFL_ERASINGstate. *Notjustyet,though.
*/ switch (chip->state) { case FL_READY: break;
case FL_CFI_QUERY: case FL_JEDEC_QUERY:
map_write(map, CMD(0x70), cmd_adr);
chip->state = FL_STATUS; #ifdef DEBUG_CFI_FEATURES
printk("%s: 1 status[%x]\n", __func__, map_read(map, cmd_adr)); #endif
fallthrough; case FL_STATUS:
status = map_read(map, cmd_adr); if (map_word_andequal(map, status, status_OK, status_OK)) break; /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) {
mutex_unlock(&chip->mutex);
printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n",
status.x[0], map_read(map, cmd_adr).x[0]); return -EIO;
}
/* Latency issues. Drop the lock, wait a while and retry */
mutex_unlock(&chip->mutex);
cfi_udelay(1); goto retry;
default: /* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
mutex_unlock(&chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + HZ; goto retry;
}
if (++z > 100) { /* Argh. Not ready for write to buffer */
DISABLE_VPP(map);
map_write(map, CMD(0x70), cmd_adr);
chip->state = FL_STATUS;
mutex_unlock(&chip->mutex);
printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); return -EIO;
}
}
/* Write length of data to come */
map_write(map, CMD(len/map_bankwidth(map)-1), cmd_adr );
/* Write data */ for (z = 0; z < len;
z += map_bankwidth(map), buf += map_bankwidth(map)) {
map_word d;
d = map_word_load(map, buf);
map_write(map, d, adr+z);
} /* GO GO GO */
map_write(map, CMD(0xd0), cmd_adr);
chip->state = FL_WRITING;
status = map_read(map, cmd_adr); if (map_word_andequal(map, status, status_OK, status_OK)) break;
/* OK Still waiting */ if (time_after(jiffies, timeo)) { /* clear status */
map_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */
map_write(map, CMD(0x70), adr);
chip->state = FL_STATUS;
DISABLE_VPP(map);
mutex_unlock(&chip->mutex);
printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); return -EIO;
}
/* Latency issues. Drop the lock, wait a while and retry */
mutex_unlock(&chip->mutex);
cfi_udelay(1);
z++;
mutex_lock(&chip->mutex);
} if (!z) {
chip->buffer_write_time--; if (!chip->buffer_write_time)
chip->buffer_write_time++;
} if (z > 1)
chip->buffer_write_time++;
/* Done and happy. */
DISABLE_VPP(map);
chip->state = FL_STATUS;
/* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ if (map_word_bitsset(map, status, CMD(0x3a))) { #ifdef DEBUG_CFI_FEATURES
printk("%s: 2 status[%lx]\n", __func__, status.x[0]); #endif /* clear status */
map_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */
map_write(map, CMD(0x70), adr);
wake_up(&chip->wq);
mutex_unlock(&chip->mutex); return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO;
}
wake_up(&chip->wq);
mutex_unlock(&chip->mutex);
return0;
}
staticint cfi_staa_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 = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; int ret; int chipnum; unsignedlong ofs;
chipnum = to >> cfi->chipshift;
ofs = to - (chipnum << cfi->chipshift);
/* Write buffer is worth it only if more than one word to write... */ while (len > 0) { /* 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, buf, size); if (ret) return ret;
if (!ECCBUF_SIZE) { /* We should fall back to a general writev implementation. *Untilthatiswritten,justbreak.
*/ return -EIO;
}
buffer = kmalloc(ECCBUF_SIZE, GFP_KERNEL); if (!buffer) return -ENOMEM;
for (i=0; i<count; i++) {
size_t elem_len = vecs[i].iov_len; void *elem_base = vecs[i].iov_base; if (!elem_len) /* FIXME: Might be unnecessary. Check that */ continue; if (buflen) { /* cut off head */ if (buflen + elem_len < ECCBUF_SIZE) { /* just accumulate */
memcpy(buffer+buflen, elem_base, elem_len);
buflen += elem_len; continue;
}
memcpy(buffer+buflen, elem_base, ECCBUF_SIZE-buflen);
ret = mtd_write(mtd, to, ECCBUF_SIZE, &thislen,
buffer);
totlen += thislen; if (ret || thislen != ECCBUF_SIZE) goto write_error;
elem_len -= thislen-buflen;
elem_base += thislen-buflen;
to += ECCBUF_SIZE;
} if (ECCBUF_DIV(elem_len)) { /* write clean aligned data */
ret = mtd_write(mtd, to, ECCBUF_DIV(elem_len),
&thislen, elem_base);
totlen += thislen; if (ret || thislen != ECCBUF_DIV(elem_len)) goto write_error;
to += thislen;
}
buflen = ECCBUF_MOD(elem_len); /* cut off tail */ if (buflen) {
memset(buffer, 0xff, ECCBUF_SIZE);
memcpy(buffer, elem_base + thislen, buflen);
}
} if (buflen) { /* flush last page, even if not full */ /* This is sometimes intended behaviour, really */
ret = mtd_write(mtd, to, buflen, &thislen, buffer);
totlen += thislen; if (ret || thislen != ECCBUF_SIZE) goto write_error;
}
write_error: if (retlen)
*retlen = totlen;
kfree(buffer); return ret;
}
staticinlineint do_erase_oneblock(struct map_info *map, struct flchip *chip, unsignedlong adr)
{ struct cfi_private *cfi = map->fldrv_priv;
map_word status, status_OK; unsignedlong timeo; int retries = 3;
DECLARE_WAITQUEUE(wait, current); int ret = 0;
adr += chip->start;
/* Let's determine this according to the interleave only once */
status_OK = CMD(0x80);
/* Check that the chip's ready to talk to us. */ switch (chip->state) { case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY:
map_write(map, CMD(0x70), adr);
chip->state = FL_STATUS;
fallthrough; case FL_STATUS:
status = map_read(map, adr); if (map_word_andequal(map, status, status_OK, status_OK)) break;
/* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) {
mutex_unlock(&chip->mutex);
printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); return -EIO;
}
/* Latency issues. Drop the lock, wait a while and retry */
mutex_unlock(&chip->mutex);
cfi_udelay(1); goto retry;
default: /* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
mutex_unlock(&chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + HZ; goto retry;
}
ENABLE_VPP(map); /* Clear the status register first */
map_write(map, CMD(0x50), adr);
status = map_read(map, adr); if (map_word_andequal(map, status, status_OK, status_OK)) break;
/* OK Still waiting */ if (time_after(jiffies, timeo)) {
map_write(map, CMD(0x70), adr);
chip->state = FL_STATUS;
printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]);
DISABLE_VPP(map);
mutex_unlock(&chip->mutex); return -EIO;
}
/* Latency issues. Drop the lock, wait a while and retry */
mutex_unlock(&chip->mutex);
cfi_udelay(1);
mutex_lock(&chip->mutex);
}
DISABLE_VPP(map);
ret = 0;
/* 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 lock bit */ if (map_word_bitsset(map, status, CMD(0x3a))) { unsignedchar chipstatus = status.x[0]; if (!map_word_equal(map, status, CMD(chipstatus))) { int i, w; for (w=0; w<map_words(map); w++) { for (i = 0; i<cfi_interleave(cfi); i++) {
chipstatus |= status.x[w] >> (cfi->device_type * 8);
}
}
printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n",
status.x[0], chipstatus);
} /* Reset the error bits */
map_write(map, CMD(0x50), adr);
map_write(map, CMD(0x70), adr);
if ((chipstatus & 0x30) == 0x30) {
printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus);
ret = -EIO;
} elseif (chipstatus & 0x02) { /* Protection bit set */
ret = -EROFS;
} elseif (chipstatus & 0x8) { /* Voltage */
printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus);
ret = -EIO;
} elseif (chipstatus & 0x20) { if (retries--) {
printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus);
timeo = jiffies + HZ;
chip->state = FL_STATUS;
mutex_unlock(&chip->mutex); goto retry;
}
printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus);
ret = -EIO;
}
}
/* Check that both start and end of the requested erase are *alignedwiththeerasesizeattheappropriateaddresses.
*/
i = 0;
/* Skip all erase regions which are ended before the start of therequestederase.Actually,tosaveonthecalculations, weskiptothefirsteraseregionwhichstartsafterthe startoftherequestederase,andthengobackone.
*/
while (i < mtd->numeraseregions && instr->addr >= regions[i].offset)
i++;
i--;
/* OK, now i is pointing at the erase region in which this eraserequeststarts.Checkthestartoftherequested eraserangeisalignedwiththeerasesizewhichisin effecthere.
*/
if (instr->addr & (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 *withtheeraseregionatthataddress.
*/
while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset)
i++;
/* As before, drop back one to point at the region in which theaddressactuallyfalls
*/
i--;
if ((instr->addr + instr->len) & (regions[i].erasesize-1)) return -EINVAL;
staticvoid cfi_staa_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;
DECLARE_WAITQUEUE(wait, current);
for (i=0; !ret && i<cfi->numchips; i++) {
chip = &cfi->chips[i];
retry:
mutex_lock(&chip->mutex);
switch(chip->state) { case FL_READY: case FL_STATUS: case FL_CFI_QUERY: case FL_JEDEC_QUERY:
chip->oldstate = chip->state;
chip->state = FL_SYNCING; /* No need to wake_up() on this state change - *asthewholepointisthatnobodycandoanything *withthechipnowanyway.
*/
fallthrough; case FL_SYNCING:
mutex_unlock(&chip->mutex); break;
default: /* Not an idle state */
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
/* Check that the chip's ready to talk to us. */ switch (chip->state) { case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY:
map_write(map, CMD(0x70), adr);
chip->state = FL_STATUS;
fallthrough; case FL_STATUS:
status = map_read(map, adr); if (map_word_andequal(map, status, status_OK, status_OK)) break;
/* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) {
mutex_unlock(&chip->mutex);
printk(KERN_ERR "waiting for chip to be ready timed out in lock\n"); return -EIO;
}
/* Latency issues. Drop the lock, wait a while and retry */
mutex_unlock(&chip->mutex);
cfi_udelay(1); goto retry;
default: /* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
mutex_unlock(&chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + HZ; goto retry;
}
/* Check that the chip's ready to talk to us. */ switch (chip->state) { case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY:
map_write(map, CMD(0x70), adr);
chip->state = FL_STATUS;
fallthrough; case FL_STATUS:
status = map_read(map, adr); if (map_word_andequal(map, status, status_OK, status_OK)) break;
/* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) {
mutex_unlock(&chip->mutex);
printk(KERN_ERR "waiting for chip to be ready timed out in unlock\n"); return -EIO;
}
/* Latency issues. Drop the lock, wait a while and retry */
mutex_unlock(&chip->mutex);
cfi_udelay(1); goto retry;
default: /* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
mutex_unlock(&chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + HZ; goto retry;
}
staticint cfi_staa_suspend(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];
mutex_lock(&chip->mutex);
switch(chip->state) { case FL_READY: case FL_STATUS: case FL_CFI_QUERY: case FL_JEDEC_QUERY:
chip->oldstate = chip->state;
chip->state = FL_PM_SUSPENDED; /* No need to wake_up() on this state change - *asthewholepointisthatnobodycandoanything *withthechipnowanyway.
*/ break;
case FL_PM_SUSPENDED: break;
default:
ret = -EAGAIN; break;
}
mutex_unlock(&chip->mutex);
}
/* Unlock the chips again */
if (ret) { for (i--; i >=0; i--) {
chip = &cfi->chips[i];
mutex_lock(&chip->mutex);
if (chip->state == FL_PM_SUSPENDED) { /* No need to force it into a known state here, becausewe'rereturningfailure,anditdidn't
get power cycled */
chip->state = chip->oldstate;
wake_up(&chip->wq);
}
mutex_unlock(&chip->mutex);
}
}
/* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) {
map_write(map, CMD(0xFF), 0);
chip->state = FL_READY;
wake_up(&chip->wq);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.