Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6db5506f authored by Richard Weinberger's avatar Richard Weinberger
Browse files

Merge tag 'cfi/for-5.4-rc1' of https://github.com/r-vignesh/linux into mtd/for-5.4

CFI core

* Kill useless initializer in mtd_do_chip_probe()
* Fix a rare write failure seen on some cfi_cmdset_0002 compliant
  Parallel NORs
* Bunch of cleanups for cfi_cmdset_0002 driver's write functions by
  Tokunori Ikegami <ikegami.t@gmail.com>
parents 560852a1 557c7590
Loading
Loading
Loading
Loading
+185 −112
Original line number Diff line number Diff line
@@ -61,7 +61,9 @@

static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
#if !FORCE_WORD_WRITE
static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
#endif
static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);
static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);
static void cfi_amdstd_sync (struct mtd_info *);
@@ -256,6 +258,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd)
}
#endif

#if !FORCE_WORD_WRITE
static void fixup_use_write_buffers(struct mtd_info *mtd)
{
	struct map_info *map = mtd->priv;
@@ -265,6 +268,7 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
		mtd->_write = cfi_amdstd_write_buffers;
	}
}
#endif /* !FORCE_WORD_WRITE */

/* Atmel chips don't use the same PRI format as AMD chips */
static void fixup_convert_atmel_pri(struct mtd_info *mtd)
@@ -1637,11 +1641,11 @@ static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
				   do_otp_lock, 1);
}

static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
static int __xipram do_write_oneword_once(struct map_info *map,
					  struct flchip *chip,
					  unsigned long adr, map_word datum,
				     int mode)
					  int mode, struct cfi_private *cfi)
{
	struct cfi_private *cfi = map->fldrv_priv;
	unsigned long timeo = jiffies + HZ;
	/*
	 * We use a 1ms + 1 jiffies generic timeout for writes (most devices
@@ -1654,42 +1658,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
	 */
	unsigned long uWriteTimeout = (HZ / 1000) + 1;
	int ret = 0;
	map_word oldd;
	int retry_cnt = 0;

	adr += chip->start;

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

	pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
		 __func__, adr, datum.x[0]);

	if (mode == FL_OTP_WRITE)
		otp_enter(map, chip, adr, map_bankwidth(map));

	/*
	 * Check for a NOP for the case when the datum to write is already
	 * present - it saves time and works around buggy chips that corrupt
	 * data at other locations when 0xff is written to a location that
	 * already contains 0xff.
	 */
	oldd = map_read(map, adr);
	if (map_word_equal(map, oldd, datum)) {
		pr_debug("MTD %s(): NOP\n",
		       __func__);
		goto op_done;
	}

	XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
	ENABLE_VPP(map);
	xip_disable(map, chip, adr);

 retry:
	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
	cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
@@ -1717,40 +1686,125 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
			continue;
		}

		/*
		 * We check "time_after" and "!chip_good" before checking
		 * "chip_good" to avoid the failure due to scheduling.
		 */
		if (time_after(jiffies, timeo) &&
		    !chip_ready(map, chip, adr)) {
		    !chip_good(map, chip, adr, datum)) {
			xip_enable(map, chip, adr);
			printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
			xip_disable(map, chip, adr);
			ret = -EIO;
			break;
		}

		if (chip_ready(map, chip, adr))
		if (chip_good(map, chip, adr, datum))
			break;

		/* Latency issues. Drop the lock, wait a while and retry */
		UDELAY(map, chip, adr, 1);
	}
	/* Did we succeed? */
	if (!chip_good(map, chip, adr, datum)) {
		/* reset on all failures. */
		cfi_check_err_status(map, chip, adr);
		map_write(map, CMD(0xF0), chip->start);
		/* FIXME - should have reset delay before continuing */

		if (++retry_cnt <= MAX_RETRIES)
			goto retry;
	return ret;
}

		ret = -EIO;
static int __xipram do_write_oneword_start(struct map_info *map,
					   struct flchip *chip,
					   unsigned long adr, int mode)
{
	int ret = 0;

	mutex_lock(&chip->mutex);

	ret = get_chip(map, chip, adr, mode);
	if (ret) {
		mutex_unlock(&chip->mutex);
		return ret;
	}
	xip_enable(map, chip, adr);
 op_done:

	if (mode == FL_OTP_WRITE)
		otp_enter(map, chip, adr, map_bankwidth(map));

	return ret;
}

static void __xipram do_write_oneword_done(struct map_info *map,
					   struct flchip *chip,
					   unsigned long adr, int mode)
{
	if (mode == FL_OTP_WRITE)
		otp_exit(map, chip, adr, map_bankwidth(map));

	chip->state = FL_READY;
	DISABLE_VPP(map);
	put_chip(map, chip, adr);

	mutex_unlock(&chip->mutex);
}

static int __xipram do_write_oneword_retry(struct map_info *map,
					   struct flchip *chip,
					   unsigned long adr, map_word datum,
					   int mode)
{
	struct cfi_private *cfi = map->fldrv_priv;
	int ret = 0;
	map_word oldd;
	int retry_cnt = 0;

	/*
	 * Check for a NOP for the case when the datum to write is already
	 * present - it saves time and works around buggy chips that corrupt
	 * data at other locations when 0xff is written to a location that
	 * already contains 0xff.
	 */
	oldd = map_read(map, adr);
	if (map_word_equal(map, oldd, datum)) {
		pr_debug("MTD %s(): NOP\n", __func__);
		return ret;
	}

	XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
	ENABLE_VPP(map);
	xip_disable(map, chip, adr);

 retry:
	ret = do_write_oneword_once(map, chip, adr, datum, mode, cfi);
	if (ret) {
		/* reset on all failures. */
		cfi_check_err_status(map, chip, adr);
		map_write(map, CMD(0xF0), chip->start);
		/* FIXME - should have reset delay before continuing */

		if (++retry_cnt <= MAX_RETRIES) {
			ret = 0;
			goto retry;
		}
	}
	xip_enable(map, chip, adr);

	return ret;
}

static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
				     unsigned long adr, map_word datum,
				     int mode)
{
	int ret = 0;

	adr += chip->start;

	pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", __func__, adr,
		 datum.x[0]);

	ret = do_write_oneword_start(map, chip, adr, mode);
	if (ret)
		return ret;

	ret = do_write_oneword_retry(map, chip, adr, datum, mode);

	do_write_oneword_done(map, chip, adr, mode);

	return ret;
}
@@ -1879,6 +1933,78 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
	return 0;
}

#if !FORCE_WORD_WRITE
static int __xipram do_write_buffer_wait(struct map_info *map,
					 struct flchip *chip, unsigned long adr,
					 map_word datum)
{
	unsigned long timeo;
	unsigned long u_write_timeout;
	int ret = 0;

	/*
	 * Timeout is calculated according to CFI data, if available.
	 * See more comments in cfi_cmdset_0002().
	 */
	u_write_timeout = usecs_to_jiffies(chip->buffer_write_time_max);
	timeo = jiffies + u_write_timeout;

	for (;;) {
		if (chip->state != FL_WRITING) {
			/* Someone's suspended the write. Sleep */
			DECLARE_WAITQUEUE(wait, current);

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

		/*
		 * We check "time_after" and "!chip_good" before checking
		 * "chip_good" to avoid the failure due to scheduling.
		 */
		if (time_after(jiffies, timeo) &&
		    !chip_good(map, chip, adr, datum)) {
			ret = -EIO;
			break;
		}

		if (chip_good(map, chip, adr, datum))
			break;

		/* Latency issues. Drop the lock, wait a while and retry */
		UDELAY(map, chip, adr, 1);
	}

	return ret;
}

static void __xipram do_write_buffer_reset(struct map_info *map,
					   struct flchip *chip,
					   struct cfi_private *cfi)
{
	/*
	 * Recovery from write-buffer programming failures requires
	 * the write-to-buffer-reset sequence.  Since the last part
	 * of the sequence also works as a normal reset, we can run
	 * the same commands regardless of why we are here.
	 * See e.g.
	 * http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf
	 */
	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
			 cfi->device_type, NULL);
	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
			 cfi->device_type, NULL);
	cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi,
			 cfi->device_type, NULL);

	/* FIXME - should have reset delay before continuing */
}

/*
 * FIXME: interleaved mode not tested, and probably not supported!
@@ -1888,13 +2014,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
				    int len)
{
	struct cfi_private *cfi = map->fldrv_priv;
	unsigned long timeo = jiffies + HZ;
	/*
	 * Timeout is calculated according to CFI data, if available.
	 * See more comments in cfi_cmdset_0002().
	 */
	unsigned long uWriteTimeout =
				usecs_to_jiffies(chip->buffer_write_time_max);
	int ret = -EIO;
	unsigned long cmd_adr;
	int z, words;
@@ -1951,63 +2070,16 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
				adr, map_bankwidth(map),
				chip->word_write_time);

	timeo = jiffies + uWriteTimeout;

	for (;;) {
		if (chip->state != FL_WRITING) {
			/* Someone's suspended the write. Sleep */
			DECLARE_WAITQUEUE(wait, current);

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

		/*
		 * We check "time_after" and "!chip_good" before checking "chip_good" to avoid
		 * the failure due to scheduling.
		 */
		if (time_after(jiffies, timeo) &&
		    !chip_good(map, chip, adr, datum))
			break;

		if (chip_good(map, chip, adr, datum)) {
			xip_enable(map, chip, adr);
			goto op_done;
		}

		/* Latency issues. Drop the lock, wait a while and retry */
		UDELAY(map, chip, adr, 1);
	ret = do_write_buffer_wait(map, chip, adr, datum);
	if (ret) {
		cfi_check_err_status(map, chip, adr);
		do_write_buffer_reset(map, chip, cfi);
		pr_err("MTD %s(): software timeout, address:0x%.8lx.\n",
		       __func__, adr);
	}

	/*
	 * Recovery from write-buffer programming failures requires
	 * the write-to-buffer-reset sequence.  Since the last part
	 * of the sequence also works as a normal reset, we can run
	 * the same commands regardless of why we are here.
	 * See e.g.
	 * http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf
	 */
	cfi_check_err_status(map, chip, adr);
	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
			 cfi->device_type, NULL);
	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
			 cfi->device_type, NULL);
	cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi,
			 cfi->device_type, NULL);
	xip_enable(map, chip, adr);
	/* FIXME - should have reset delay before continuing */

	printk(KERN_WARNING "MTD %s(): software timeout, address:0x%.8lx.\n",
	       __func__, adr);

	ret = -EIO;
 op_done:
	chip->state = FL_READY;
	DISABLE_VPP(map);
	put_chip(map, chip, adr);
@@ -2091,6 +2163,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,

	return 0;
}
#endif /* !FORCE_WORD_WRITE */

/*
 * Wait for the flash chip to become ready to write data
+1 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,

struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
{
	struct mtd_info *mtd = NULL;
	struct mtd_info *mtd;
	struct cfi_private *cfi;

	/* First probe the map to see if we have CFI stuff there. */