From 092f7b58e7206d134bbcafabd91d1813e72804e5 Mon Sep 17 00:00:00 2001
From: Atle Nissestad <atle@nissestad.no>
Date: Thu, 15 Nov 2007 19:06:46 +0100
Subject: [PATCH] nios2: Merged MTD CFI-driver patch from Mictrotronix 2.6.11 port

This patch is required to get flash erase (and thus writing) to work properly with the Avalon bus.
It should really be deuglified, but it works for now.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>

diff --git a/linux-2.6.x/drivers/mtd/chips/cfi_cmdset_0002.c b/linux-2.6.x/drivers/mtd/chips/cfi_cmdset_0002.c
index 702ae4c..3cb3037 100644
--- a/linux-2.6.x/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/linux-2.6.x/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -445,6 +445,7 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
  * correctly and is therefore not done	(particulary with interleaved chips
  * as each chip must be checked independantly of the others).
  */
+#ifndef CONFIG_NIOS2
 static int __xipram chip_ready(struct map_info *map, unsigned long addr)
 {
 	map_word d, t;
@@ -454,6 +455,28 @@ static int __xipram chip_ready(struct map_info *map, unsigned long addr)
 
 	return map_word_equal(map, d, t);
 }
+#else
+/* On avalon bus, there is no such a luxury of toggling bit ... 
+ * Use the polling bit, if we know the datum ...
+ */ 
+static int __xipram chip_ready_dq7(struct map_info *map, unsigned long addr, map_word datum)
+{
+	map_word d;
+
+	d = map_read(map, addr);
+
+	return map_word_equal(map, d, datum);
+}
+
+static int __xipram erase_is_done(struct map_info *map, unsigned long addr)
+{
+	map_word d;
+
+	d = map_read(map, addr);
+
+	return (d.x[0] & 0x80);
+}
+#endif
 
 /*
  * Return true if the chip is ready and has the correct value.
@@ -474,10 +497,16 @@ static int __xipram chip_good(struct map_info *map, unsigned long addr, map_word
 {
 	map_word oldd, curd;
 
+#ifndef CONFIG_NIOS2
 	oldd = map_read(map, addr);
+#endif
 	curd = map_read(map, addr);
 
+#ifdef CONFIG_NIOS2
+	return
+#else
 	return	map_word_equal(map, oldd, curd) &&
+#endif
 		map_word_equal(map, curd, expected);
 }
 
@@ -495,8 +524,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
 
 	case FL_STATUS:
 		for (;;) {
+#ifndef CONFIG_NIOS2
 			if (chip_ready(map, adr))
 				break;
+#endif
 
 			if (time_after(jiffies, timeo)) {
 				printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
@@ -516,6 +547,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
 		return 0;
 
 	case FL_ERASING:
+#ifdef CONFIG_NIOS2
+		/* Because of Avalon bus, it is impossible to tell if a
+		 * sector is suspended or not, better avoid erase suspending
+		 */
+		 	goto sleep;
+#endif
 		if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */
 			goto sleep;
 
@@ -537,8 +574,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
 		chip->state = FL_ERASE_SUSPENDING;
 		chip->erase_suspended = 1;
 		for (;;) {
+#ifndef CONFIG_NIOS2
 			if (chip_ready(map, adr))
 				break;
+#endif
 
 			if (time_after(jiffies, timeo)) {
 				/* Should have suspended the erase by now.
@@ -990,7 +1029,11 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
 	 * depending of the conditions.	 The ' + 1' is to avoid having a
 	 * timeout of 0 jiffies if HZ is smaller than 1000.
 	 */
+#ifdef CONFIG_NIOS2
+	unsigned long uWriteTimeout = ( HZ / 1000 ) + 2;
+#else
 	unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+#endif
 	int ret = 0;
 	map_word oldd;
 	int retry_cnt = 0;
@@ -1051,14 +1094,22 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
 			continue;
 		}
 
+#ifdef CONFIG_NIOS2
+		if (time_after(jiffies, timeo) && !chip_ready_dq7(map, adr, datum)){
+#else
 		if (time_after(jiffies, timeo) && !chip_ready(map, adr)){
+#endif
 			xip_enable(map, chip, adr);
 			printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
 			xip_disable(map, chip, adr);
 			break;
 		}
 
+#ifdef CONFIG_NIOS2
+		if (chip_ready_dq7(map, adr, datum))
+#else
 		if (chip_ready(map, adr))
+#endif
 			break;
 
 		/* Latency issues. Drop the lock, wait a while and retry */
@@ -1237,7 +1288,11 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
 	struct cfi_private *cfi = map->fldrv_priv;
 	unsigned long timeo = jiffies + HZ;
 	/* see comments in do_write_oneword() regarding uWriteTimeo. */
+#ifdef CONFIG_NIOS2
+	unsigned long uWriteTimeout = ( HZ / 1000 ) + 2;
+#else
 	unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+#endif
 	int ret = -EIO;
 	unsigned long cmd_adr;
 	int z, words;
@@ -1312,10 +1367,18 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
 			continue;
 		}
 
+#ifdef CONFIG_NIOS2
+		if (time_after(jiffies, timeo) && !chip_ready_dq7(map, adr, datum))
+#else
 		if (time_after(jiffies, timeo) && !chip_ready(map, adr))
+#endif
 			break;
 
+#ifdef CONFIG_NIOS2
+		if (chip_ready_dq7(map, adr, datum)) {
+#else
 		if (chip_ready(map, adr)) {
+#endif
 			xip_enable(map, chip, adr);
 			goto op_done;
 		}
@@ -1485,7 +1548,11 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
 			chip->erase_suspended = 0;
 		}
 
+#ifdef CONFIG_NIOS2
+		if (erase_is_done(map, adr))
+#else
 		if (chip_ready(map, adr))
+#endif
 			break;
 
 		if (time_after(jiffies, timeo)) {
@@ -1521,6 +1588,9 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 	unsigned long timeo = jiffies + HZ;
 	DECLARE_WAITQUEUE(wait, current);
 	int ret = 0;
+#ifdef CONFIG_NIOS2
+	int altera_retried = 0;
+#endif
 
 	adr += chip->start;
 
@@ -1534,6 +1604,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
 	       __func__, adr );
 
+#ifdef CONFIG_NIOS2
+/* the erase sometimes needs a second try on altera platforms */
+altera_retry:
+#endif
 	XIP_INVAL_CACHED_RANGE(map, adr, len);
 	ENABLE_VPP(map);
 	xip_disable(map, chip, adr);
@@ -1573,7 +1647,11 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 			chip->erase_suspended = 0;
 		}
 
+#ifdef CONFIG_NIOS2
+		if (erase_is_done(map, adr)) {
+#else
 		if (chip_ready(map, adr)) {
+#endif
 			xip_enable(map, chip, adr);
 			break;
 		}
@@ -1588,6 +1666,17 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 		/* Latency issues. Drop the lock, wait a while and retry */
 		UDELAY(map, chip, adr, 1000000/HZ);
 	}
+
+#ifdef CONFIG_NIOS2
+	/* give altera's platform a second chance */
+	if (!altera_retried) {
+		altera_retried=1;
+		/* reset on all failures. */
+		map_write( map, CMD(0xF0), chip->start );
+		goto altera_retry;
+	}
+#endif
+
 	/* Did we succeed? */
 	if (!chip_good(map, adr, map_word_ff(map))) {
 		/* reset on all failures. */
-- 
1.5.3.3

