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

Commit 6c77fd64 authored by Richard Purdie's avatar Richard Purdie Committed by David Woodhouse
Browse files

[MTD] onenand: Add panic_write function to the onenand driver



Implement the panic_write function for the onenand driver. This waits
for any active command to complete/timeout, performs the write, waits
for it to complete and then returns.

Signed-off-by: default avatarRichard Purdie <rpurdie@rpsys.net>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 621e4f8e
Loading
Loading
Loading
Loading
+108 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/mtd/mtd.h>
@@ -1284,6 +1285,112 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,

#define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)

static void onenand_panic_wait(struct mtd_info *mtd)
{
	struct onenand_chip *this = mtd->priv;
	unsigned int interrupt;
	int i;
	
	for (i = 0; i < 2000; i++) {
		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
		if (interrupt & ONENAND_INT_MASTER)
			break;
		udelay(10);
	}
}

/**
 * onenand_panic_write - [MTD Interface] write buffer to FLASH in a panic context
 * @param mtd		MTD device structure
 * @param to		offset to write to
 * @param len		number of bytes to write
 * @param retlen	pointer to variable to store the number of written bytes
 * @param buf		the data to write
 *
 * Write with ECC
 */
static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
			 size_t *retlen, const u_char *buf)
{
	struct onenand_chip *this = mtd->priv;
	int column, subpage;
	int written = 0;
	int ret = 0;

	if (this->state == FL_PM_SUSPENDED)
		return -EBUSY;

	/* Wait for any existing operation to clear */
	onenand_panic_wait(mtd);

	DEBUG(MTD_DEBUG_LEVEL3, "onenand_panic_write: to = 0x%08x, len = %i\n",
	      (unsigned int) to, (int) len);

	/* Initialize retlen, in case of early exit */
	*retlen = 0;

	/* Do not allow writes past end of device */
	if (unlikely((to + len) > mtd->size)) {
		printk(KERN_ERR "onenand_panic_write: Attempt write to past end of device\n");
		return -EINVAL;
	}

	/* Reject writes, which are not page aligned */
        if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {
                printk(KERN_ERR "onenand_panic_write: Attempt to write not page aligned data\n");
                return -EINVAL;
        }

	column = to & (mtd->writesize - 1);

	/* Loop until all data write */
	while (written < len) {
		int thislen = min_t(int, mtd->writesize - column, len - written);
		u_char *wbuf = (u_char *) buf;

		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);

		/* Partial page write */
		subpage = thislen < mtd->writesize;
		if (subpage) {
			memset(this->page_buf, 0xff, mtd->writesize);
			memcpy(this->page_buf + column, buf, thislen);
			wbuf = this->page_buf;
		}

		this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
		this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize);

		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);

		onenand_panic_wait(mtd);

		/* In partial page write we don't update bufferram */
		onenand_update_bufferram(mtd, to, !ret && !subpage);
		if (ONENAND_IS_2PLANE(this)) {
			ONENAND_SET_BUFFERRAM1(this);
			onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
		}

		if (ret) {
			printk(KERN_ERR "onenand_panic_write: write failed %d\n", ret);
			break;
		}

		written += thislen;

		if (written == len)
			break;

		column = 0;
		to += thislen;
		buf += thislen;
	}

	*retlen = written;
	return ret;
}

/**
 * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
 * @param mtd		MTD device structure
@@ -2673,6 +2780,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
	mtd->write = onenand_write;
	mtd->read_oob = onenand_read_oob;
	mtd->write_oob = onenand_write_oob;
	mtd->panic_write = onenand_panic_write;
#ifdef CONFIG_MTD_ONENAND_OTP
	mtd->get_fact_prot_info = onenand_get_fact_prot_info;
	mtd->read_fact_prot_reg = onenand_read_fact_prot_reg;