in chips/cfi_cmdset_0020.c [420:604]
static int do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const u_char *buf, int len)
{
struct cfi_private *cfi = map->fldrv_priv;
map_word status, status_OK;
unsigned long cmd_adr, timeo;
DECLARE_WAITQUEUE(wait, current);
int wbufsize, z;
/* M58LW064A requires bus alignment for buffer wriets -- saw */
if (adr & (map_bankwidth(map)-1))
return -EINVAL;
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 */
status_OK = CMD(0x80);
timeo = jiffies + HZ;
retry:
#ifdef DEBUG_CFI_FEATURES
printk("%s: chip->state[%d]\n", __func__, chip->state);
#endif
mutex_lock(&chip->mutex);
/* Check that the chip's ready to talk to us.
* Later, we can actually think about interrupting it
* if it's in FL_ERASING state.
* Not just yet, 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;
}
ENABLE_VPP(map);
map_write(map, CMD(0xe8), cmd_adr);
chip->state = FL_WRITING_TO_BUFFER;
z = 0;
for (;;) {
status = map_read(map, cmd_adr);
if (map_word_andequal(map, status, status_OK, status_OK))
break;
mutex_unlock(&chip->mutex);
cfi_udelay(1);
mutex_lock(&chip->mutex);
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;
mutex_unlock(&chip->mutex);
cfi_udelay(chip->buffer_write_time);
mutex_lock(&chip->mutex);
timeo = jiffies + (HZ/2);
z = 0;
for (;;) {
if (chip->state != FL_WRITING) {
/* Someone's suspended the write. 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 / 2); /* FIXME */
mutex_lock(&chip->mutex);
continue;
}
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);
return 0;
}