static int __xipram do_write_buffer()

in chips/cfi_cmdset_0001.c [1706:1870]


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;
	unsigned long initial_adr;
	int initial_len = len;

	wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
	adr += chip->start;
	initial_adr = adr;
	cmd_adr = adr & ~(wbufsize-1);

	/* Sharp LH28F640BF chips need the first address for the
	 * Page Buffer Program command. See Table 5 of
	 * LH28F320BF, LH28F640BF, LH28F128BF Series (Appendix FUM00701) */
	if (is_LH28F640BF(cfi))
		cmd_adr = adr;

	/* Let's determine this according to the interleave only once */
	write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9);

	mutex_lock(&chip->mutex);
	ret = get_chip(map, chip, cmd_adr, FL_WRITING);
	if (ret) {
		mutex_unlock(&chip->mutex);
		return ret;
	}

	XIP_INVAL_CACHED_RANGE(map, initial_adr, initial_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, 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 = DIV_ROUND_UP(len - word_gap, 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,
				   initial_adr, initial_len,
				   chip->buffer_write_time,
				   chip->buffer_write_time_max);
	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:	DISABLE_VPP(map);
	put_chip(map, chip, cmd_adr);
	mutex_unlock(&chip->mutex);
	return ret;
}