static int do_write_buffer()

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;
}