public inbox for linux-mtd@lists.infradead.org
 help / color / mirror / Atom feed
* [RFC PATCH 3/3] Add panic_write function to the onenand driver
@ 2008-01-29 11:59 Richard Purdie
  2008-01-31  2:05 ` Kyungmin Park
  0 siblings, 1 reply; 3+ messages in thread
From: Richard Purdie @ 2008-01-29 11:59 UTC (permalink / raw)
  To: David Woodhouse, linux-mtd

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: Richard Purdie <rpurdie@rpsys.net>

---
 drivers/mtd/onenand/onenand_base.c |  102 +++++++++++++++++++++++++++++++++++++
 1 file changed, 102 insertions(+)

Index: kernel-hacking/drivers/mtd/onenand/onenand_base.c
===================================================================
--- kernel-hacking.orig/drivers/mtd/onenand/onenand_base.c	2008-01-29 09:16:55.000000000 +0000
+++ kernel-hacking/drivers/mtd/onenand/onenand_base.c	2008-01-29 10:51:01.000000000 +0000
@@ -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>
@@ -1285,6 +1286,106 @@ static int onenand_verify(struct mtd_inf
 
 #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 < 20; i++) {
+		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
+		if (interrupt & ONENAND_INT_MASTER)
+			break;
+		udelay(1000);
+	}
+}
+
+
+/**
+ * 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);
+
+		if (ret) {
+			printk(KERN_ERR "onenand_write_nolock: write filaed %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
@@ -2676,6 +2777,7 @@ int onenand_scan(struct mtd_info *mtd, i
 	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;

^ permalink raw reply	[flat|nested] 3+ messages in thread

* RE: [RFC PATCH 3/3] Add panic_write function to the onenand driver
  2008-01-29 11:59 [RFC PATCH 3/3] Add panic_write function to the onenand driver Richard Purdie
@ 2008-01-31  2:05 ` Kyungmin Park
  2008-01-31 10:03   ` Richard Purdie
  0 siblings, 1 reply; 3+ messages in thread
From: Kyungmin Park @ 2008-01-31  2:05 UTC (permalink / raw)
  To: 'Richard Purdie', 'David Woodhouse',
	'linux-mtd'

Hi,

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

Umm it's waiting with maximum 20msec. how about the increase the index to 20*1000 and use udelay(1).
If we use the busy-waiting, it uses the max 3msec in case of erase.

Anyway I think you don't use the onenand_wait since it has cond_resched(). It's better to use the existing function in case of
panic_write.
Okay try to think what's the better way.

> + */
> +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);

Reasonable!

> +
> +		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);

It has to check the return value and update the bufferram. If not, we can read the wrong data later.

Thank you,
Kyungmin Park

^ permalink raw reply	[flat|nested] 3+ messages in thread

* RE: [RFC PATCH 3/3] Add panic_write function to the onenand driver
  2008-01-31  2:05 ` Kyungmin Park
@ 2008-01-31 10:03   ` Richard Purdie
  0 siblings, 0 replies; 3+ messages in thread
From: Richard Purdie @ 2008-01-31 10:03 UTC (permalink / raw)
  To: kmpark; +Cc: 'linux-mtd', 'David Woodhouse'

On Thu, 2008-01-31 at 11:05 +0900, Kyungmin Park wrote:
> > +static void onenand_panic_wait(struct mtd_info *mtd)
> > +{
> > +	struct onenand_chip *this = mtd->priv;
> > +	unsigned int interrupt;
> > +	int i;
> > +
> > +	for (i = 0; i < 20; i++) {
> > +		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
> > +		if (interrupt & ONENAND_INT_MASTER)
> > +			break;
> > +		udelay(1000);
> > +	}
> > +}
> 
> Umm it's waiting with maximum 20msec. how about the increase the index to 20*1000 and use udelay(1).
> If we use the busy-waiting, it uses the max 3msec in case of erase.

I'm ok with changing it to use udelay(10). This function is going to be
rarely called so efficiency isn't a primary concern... 

> Anyway I think you don't use the onenand_wait since it has
> cond_resched(). It's better to use the existing function in case of
> panic_write.
> Okay try to think what's the better way.

Yes, I'm avoiding onenand_wait since we can't schedule in this context.
Other ideas welcome :)

> > + */
> > +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);
> 
> Reasonable!
> 
> > +
> > +		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);
> 
> It has to check the return value and update the bufferram. If not, we can read the wrong data later.

The key point with panic_write is there is unlikely to be any "later". I
don't mind putting the bufferram update calls back if you're happier
with that though.

Cheers,

Richard

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2008-01-31 10:04 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-01-29 11:59 [RFC PATCH 3/3] Add panic_write function to the onenand driver Richard Purdie
2008-01-31  2:05 ` Kyungmin Park
2008-01-31 10:03   ` Richard Purdie

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox