in chips/cfi_cmdset_0020.c [732:888]
static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
{
struct cfi_private *cfi = map->fldrv_priv;
map_word status, status_OK;
unsigned long 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);
timeo = jiffies + HZ;
retry:
mutex_lock(&chip->mutex);
/* 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);
/* Now erase */
map_write(map, CMD(0x20), adr);
map_write(map, CMD(0xD0), adr);
chip->state = FL_ERASING;
mutex_unlock(&chip->mutex);
msleep(1000);
mutex_lock(&chip->mutex);
/* FIXME. Use a timer to check this, and return immediately. */
/* Once the state machine's known to be working I'll do that */
timeo = jiffies + (HZ*20);
for (;;) {
if (chip->state != FL_ERASING) {
/* Someone's suspended the erase. Sleep */
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*20); /* FIXME */
mutex_lock(&chip->mutex);
continue;
}
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))) {
unsigned char 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;
} else if (chipstatus & 0x02) {
/* Protection bit set */
ret = -EROFS;
} else if (chipstatus & 0x8) {
/* Voltage */
printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus);
ret = -EIO;
} else if (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;
}
}
wake_up(&chip->wq);
mutex_unlock(&chip->mutex);
return ret;
}