mtd/drivers/mtd/chips cfi_cmdset_0002.c,1.66,1.67

Thayne Harbaugh tharbaugh at lnxi.com
Tue Apr 15 18:32:16 EDT 2003


Update of /home/cvs/mtd/drivers/mtd/chips
In directory phoenix.infradead.org:/tmp/cvs-serv12986

Modified Files:
	cfi_cmdset_0002.c 
Log Message:
Error information and status history on all write/erase failures.
Handle interleaved chips correctly (this might change slightly in the
future - see the FIXME note that was added).
Thanks to Alexander Wild for help debugging interleaved chips.


Index: cfi_cmdset_0002.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/chips/cfi_cmdset_0002.c,v
retrieving revision 1.66
retrieving revision 1.67
diff -u -r1.66 -r1.67
--- cfi_cmdset_0002.c	11 Apr 2003 22:25:17 -0000	1.66
+++ cfi_cmdset_0002.c	15 Apr 2003 22:32:13 -0000	1.67
@@ -51,6 +51,7 @@
 	module: THIS_MODULE
 };
 
+
 struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
@@ -458,7 +459,7 @@
 {
 	unsigned long timeo = jiffies + HZ;
 	unsigned int oldstatus, status, prev_oldstatus, prev_status;
-	unsigned int dq6, dq5;	
+	unsigned int dq6;
 	struct cfi_private *cfi = map->fldrv_priv;
     /* We use a 1ms + 1 jiffies generic timeout for writes (most devices have
        a max write time of a few hundreds usec). However, we should use the
@@ -499,8 +500,10 @@
 	chip->state = FL_WRITING;
 
 	adr += chip->start;
-	ENABLE_VPP(map);
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n",
+	       __func__, adr, datum );
 
+	ENABLE_VPP(map);
 	if (fast) { /* Unlock bypass */
 		cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL);
 	}
@@ -509,8 +512,6 @@
 	        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
 	        cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
 	}
-
-	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.2x\n", __func__, datum );
 	cfi_write(map, datum, adr);
 
 	cfi_spin_unlock(chip->mutex);
@@ -544,16 +545,38 @@
 	 * - Thayne Harbaugh
 	 */
 	dq6 = CMD(1<<6);
-	dq5 = CMD(1<<5);
 	/* See comment above for timeout value. */
 	timeo = jiffies + uWriteTimeout; 
 		
 	oldstatus = cfi_read(map, adr);
 	status = cfi_read(map, adr);
-	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.2x 0x%.2x\n", __func__, oldstatus, status );
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+	       __func__, oldstatus, status );
 
+	/*
+	 * This assumes that dq5 == dq6 >> 1
+	 * It's done this way so that we only look for asserted dq5
+	 * on interleaved chips that are still toggling - we can
+	 * ignore chips that have already completed and might have
+	 * dq5 asserted.  It is important to use oldstatus because
+	 * a completed state might look like it has toggled from
+	 * the previous read of status bits and it might trigger
+	 * an erroneous error.
+	 *
+	 * FIXME - this is rather complicated - trying to deal with
+	 * asynchronous transitions and cases where the toggle bit
+	 * appears to toggle when it has really just settled to true
+	 * data and having to read the locations an additional two
+	 * times to verify bits.  It might be that we just need
+	 * to watch dq6 and our timeout and ignore dq5.  If the
+	 * timeout is exceeded and we still have dq6 toggling we then
+	 * look for asserted dq5 for each toggling dq6.  That would
+	 * be safer, easier to code and get correct, and marginally
+	 * less efficient.  Who cares if the error case, which should
+	 * be very infrequent, is optimized.
+	 */
 	while( ( ( status ^ oldstatus ) & dq6 )
-	       && ! ( status & dq5 )
+	       && ! ( oldstatus & ( ( status ^ oldstatus ) & dq6 ) >> 1 )
 	       && ! time_after(jiffies, timeo) ) {
 
 		if (need_resched()) {
@@ -565,11 +588,12 @@
 
 		oldstatus = cfi_read( map, adr );
 		status = cfi_read( map, adr );
-		DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.2x 0x%.2x\n", __func__, oldstatus, status );
+		DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+		       __func__, oldstatus, status );
 	}
 
 	if ( ( ( status ^ oldstatus ) & dq6 )
-	       && ! ( status & dq5 ) ) {
+	     && ! ( oldstatus & ( ( status ^ oldstatus ) & dq6 ) >> 1 ) ) {
 		/* dq6 still toggling and dq5 still low - must be timeout */
 		printk( KERN_WARNING
 			"MTD %s(): Timed out while waiting for write to complete.\n",
@@ -592,15 +616,17 @@
 	 * dq6 is not still toggling  and that the status
 	 * bits erroneously match the datum that was written.
 	 */
-	prev_oldstatus = oldstatus;
-	prev_status = status;
 
 	/*
 	 * Be optimistic and check success first - then decode any
 	 * failures.
 	 */
+	prev_oldstatus = oldstatus;
+	prev_status = status;
 	oldstatus = cfi_read(map, adr);
 	status = cfi_read(map, adr);
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+	       __func__, oldstatus, status );
 
 	if ( oldstatus == datum && status == datum ) {
 		/* success - do nothing */
@@ -609,40 +635,28 @@
 
 	/* something has failed */
 	if ( ( ( status ^ oldstatus ) & dq6 )
-	     && ( status & dq5 ) ) {
+	     && ( status & ( ( status ^ oldstatus ) & dq6 >> 1 ) ) ) {
 		printk(KERN_WARNING
 		       "MTD %s(): Internal flash device timeout occured.\n",
 		       __func__ );
-		goto write_failed;
+	} else {
+		/*
+		 * If we get to here then it means that something failed
+		 * and that the usual error state of toggling dq6
+		 * while dq5 goes high did not happen.  Something
+		 * is seriously wacky!  Dump some debug info.
+		 */
+		printk(KERN_WARNING
+		       "MTD %s(): Wacky!  Unable to decode failure status\n",
+		       __func__ );
 	}
 
-	/*
-	 * If we get to here then it means that something failed
-	 * and that the usual error state of toggling dq6
-	 * while dq5 goes high did not happen.  Something
-	 * is seriously wacky!  Dump some debug info.
-	 */
-	printk(KERN_WARNING "MTD %s(): Wacky!  Unable to decode failure status\n", __func__ );
 	printk(KERN_WARNING
-	       "MTD %s(): 0x%.2x: 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n",
-	       __func__, datum,
+	       "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
+	       __func__, adr, datum,
 	       prev_oldstatus, prev_status,
 	       oldstatus, status);
 
-	printk(KERN_WARNING "MTD %s(): dq6: %d %d %d %d\n",
-	       __func__,
-	       prev_oldstatus & dq6 ? 1 : 0,
-	       prev_status & dq6 ? 1 : 0,
-	       oldstatus & dq6 ? 1 : 0,
-	       status & dq6 ? 1 : 0);
-
-	printk(KERN_WARNING "MTD %s(): dq5: %d %d %d %d\n",
-	       __func__,
-	       prev_oldstatus & dq5 ? 1 : 0,
-	       prev_status & dq5 ? 1 : 0,
-	       oldstatus & dq5 ? 1 : 0,
-	       status & dq5 ? 1 : 0);
-
  write_failed:
 	ret = -EIO;
 	/* reset on all failures. */
@@ -804,13 +818,14 @@
 
 static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
 {
-	unsigned int oldstatus, status;
-	unsigned int dq6, dq5;
+	unsigned int oldstatus, status, prev_oldstatus, prev_status;
+	unsigned int dq6;
 	unsigned long timeo = jiffies + HZ;
-	unsigned int adr;
+	unsigned long int adr;
 	struct cfi_private *cfi = map->fldrv_priv;
-	int ret = 0;
 	DECLARE_WAITQUEUE(wait, current);
+	int ret = 0;
+	__u32 ones = 0;
 
  retry:
 	cfi_spin_lock(chip->mutex);
@@ -833,6 +848,8 @@
 	}	
 
 	chip->state = FL_ERASING;
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
+	       __func__, chip->start );
 	
 	/* Handle devices with one erase region, that only implement
 	 * the chip erase command.
@@ -858,12 +875,14 @@
 	 */
 	/* see comments in do_write_oneword */
 	dq6 = CMD(1<<6);
-	dq5 = CMD(1<<5);
 
 	oldstatus = cfi_read(map, adr);
 	status = cfi_read(map, adr);
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+	       __func__, oldstatus, status );
+
 	while( ( ( status ^ oldstatus ) & dq6 )
-	       && ! ( status & dq5 )
+	       && ! ( oldstatus & ( ( status ^ oldstatus ) & dq6 >> 1 ) )
 	       && ! time_after(jiffies, timeo) ) {
 		int wait_reps;
 
@@ -895,7 +914,7 @@
 		for(wait_reps = 0;
 		    (wait_reps < 100)
 			    && ( ( status ^ oldstatus ) & dq6 )
-			    && ! ( status & dq5 );
+			    && ! ( oldstatus & ( ( status ^ oldstatus ) & dq6 >> 1 ) );
 		    wait_reps++) {
 			
 			/* Latency issues. Drop the lock, wait a while and retry */
@@ -906,63 +925,82 @@
 			cfi_spin_lock(chip->mutex);
 			oldstatus = cfi_read(map, adr);
 			status = cfi_read(map, adr);
+			DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+			       __func__, oldstatus, status );
 		}
 		oldstatus = cfi_read(map, adr);
 		status = cfi_read(map, adr);
+		DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+		       __func__, oldstatus, status );
 	}
 
 	if ( ( ( status ^ oldstatus ) & dq6 )
-	       && ! ( status & dq5 ) ) {
+	     && ! ( oldstatus & ( ( status ^ oldstatus ) & dq6 >> 1 ) ) ) {
 		/* dq6 still toggling and dq5 still low - must be timeout */
 		printk( KERN_WARNING
 			"MTD %s(): Timed out while waiting for erase to complete.\n",
-			__func__ );        
-		/* reset */
-		cfi_write( map, CMD(0xF0), chip->start );
-		ret = -EIO;
-	} else {
-		__u32 ones = 0;
-/*
-		 * Be optimistic and check success first - then decode any
-		 * failures.
-		 */
-		oldstatus = cfi_read(map, adr);
-		status = cfi_read(map, adr);
+			__func__ );
+		goto erase_failed;
+	}
+
+	/*
+	 * Be optimistic and check success first - then decode any
+	 * failures.
+	 */
+	prev_oldstatus = oldstatus;
+	prev_status = status;
+	oldstatus = cfi_read(map, adr);
+	status = cfi_read(map, adr);
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+	       __func__, oldstatus, status );
 		
-		switch( CFIDEV_BUSWIDTH ) {
-		case 1:
-			ones = (__u8)~0;
-			break;
-		case 2:
-			ones = (__u16)~0;
-			break;
-		case 4:
-			ones = (__u32)~0;
-			break;
-		}
+	switch( CFIDEV_BUSWIDTH ) {
+	case 1:
+		ones = (__u8)~0;
+		break;
+	case 2:
+		ones = (__u16)~0;
+		break;
+	case 4:
+		ones = (__u32)~0;
+		break;
+	}
 	
-		if ( oldstatus == ones && status == ones ) {
-			/* success - do nothing */
-		} else {
-			/* something has failed */
-			if ( ( ( status ^ oldstatus ) & dq6 )
-			     && ( status & dq5 ) ) {
-				printk(KERN_WARNING "MTD %s(): Internal flash device timeout occured.\n", __func__ );
-			} else {
-				/*
-				 * This state means that the erase did not succeed
-				 * and that the usual error state of toggling dq6
-				 * while dq5 goes high did not happen.  Something
-				 * is seriously wacky!
-				 */
-				printk(KERN_WARNING "MTD %s(): Wacky!  Unable to decode failure status\n", __func__ );
-			}
-			ret = -EIO;
-			/* reset on all failures. */
-			cfi_write( map, CMD(0xF0), chip->start );
-		}
+	if ( oldstatus == ones && status == ones ) {
+		/* success - do nothing */
+		goto erase_done;
+	}
+
+	/* something has failed */
+	if ( ( ( status ^ oldstatus ) & dq6 )
+	     && ( oldstatus & ( ( status ^ oldstatus ) & dq6 >> 1 ) ) ) {
+		printk(KERN_WARNING
+		       "MTD %s(): Internal flash device timeout occured.\n",
+		       __func__ );
+	} else {
+		/*
+		 * This state means that the erase did not succeed
+		 * and that the usual error state of toggling dq6
+		 * while dq5 goes high did not happen.  Something
+		 * is seriously wacky!
+		 */
+		printk(KERN_WARNING
+		       "MTD %s(): Wacky!  Unable to decode failure status\n",
+		       __func__ );
 	}
 
+	printk(KERN_WARNING
+	       "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
+	       __func__, adr, ones,
+	       prev_oldstatus, prev_status,
+	       oldstatus, status);
+
+ erase_failed:
+	ret = -EIO;
+	/* reset on all failures. */
+	cfi_write( map, CMD(0xF0), chip->start );
+
+ erase_done:
 	DISABLE_VPP(map);
 	chip->state = FL_READY;
 	wake_up(&chip->wq);
@@ -970,14 +1008,16 @@
 	return ret;
 }
 
+
 static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
 {
-	unsigned int oldstatus, status;
-	unsigned int dq6, dq5;
+	unsigned int oldstatus, status, prev_oldstatus, prev_status;
+	unsigned int dq6;
 	unsigned long timeo = jiffies + HZ;
 	struct cfi_private *cfi = map->fldrv_priv;
-	int ret = 0;
 	DECLARE_WAITQUEUE(wait, current);
+	int ret = 0;
+	__u32 ones = 0;
 
  retry:
 	cfi_spin_lock(chip->mutex);
@@ -1002,12 +1042,16 @@
 	chip->state = FL_ERASING;
 
 	adr += chip->start;
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
+	       __func__, adr );
+
 	ENABLE_VPP(map);
 	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
 	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
 	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
 	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
 	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+
 	cfi_write(map, CMD(0x30), adr);
 	
 	timeo = jiffies + (HZ*20);
@@ -1023,12 +1067,14 @@
 	 */
 	/* see comments in do_write_oneword */
 	dq6 = CMD(1<<6);
-	dq5 = CMD(1<<5);
 
 	oldstatus = cfi_read(map, adr);
 	status = cfi_read(map, adr);
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+	       __func__, oldstatus, status );
+
 	while( ( ( status ^ oldstatus ) & dq6 )
-	       && ! ( status & dq5 )
+	       && ! ( oldstatus & ( ( status ^ oldstatus ) & dq6 >> 1 ) )
 	       && ! time_after(jiffies, timeo) ) {
 		int wait_reps;
 
@@ -1060,7 +1106,7 @@
 		for(wait_reps = 0;
 		    (wait_reps < 100)
 			    && ( ( status ^ oldstatus ) & dq6 )
-			    && ! ( status & dq5 );
+			    && ! ( oldstatus & ( ( status ^ oldstatus ) & dq6 >> 1 ) );
 		    wait_reps++) {
 			
 			/* Latency issues. Drop the lock, wait a while and retry */
@@ -1071,64 +1117,82 @@
 			cfi_spin_lock(chip->mutex);
 			oldstatus = cfi_read(map, adr);
 			status = cfi_read(map, adr);
+			DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+			       __func__, oldstatus, status );
 		}
 		oldstatus = cfi_read(map, adr);
 		status = cfi_read(map, adr);
+		DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+		       __func__, oldstatus, status );
 	}
 
 	if ( ( ( status ^ oldstatus ) & dq6 )
-	       && ! ( status & dq5 ) ) {
+	     && ! ( oldstatus & ( ( status ^ oldstatus ) & dq6 >> 1 ) ) ) {
 		/* dq6 still toggling and dq5 still low - must be timeout */
 		printk( KERN_WARNING
 			"MTD %s(): Timed out while waiting for erase to complete.\n",
-			__func__ );        
-		/* reset */
-		cfi_write( map, CMD(0xF0), chip->start );
-		ret = -EIO;
-	} else {
-		__u32 ones = 0;
+			__func__ );
+		goto erase_failed;
+	}
 
-		/*
-		 * Be optimistic and check success first - then decode any
-		 * failures.
-		 */
-		oldstatus = cfi_read(map, adr);
-		status = cfi_read(map, adr);
+	/*
+	 * Be optimistic and check success first - then decode any
+	 * failures.
+	 */
+	prev_oldstatus = oldstatus;
+	prev_status = status;
+	oldstatus = cfi_read(map, adr);
+	status = cfi_read(map, adr);
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n",
+	       __func__, oldstatus, status );
 
-		switch( CFIDEV_BUSWIDTH ) {
-		case 1:
-			ones = (__u8)~0;
-			break;
-		case 2:
-			ones = (__u16)~0;
-			break;
-		case 4:
-			ones = (__u32)~0;
-			break;
-		}
+	switch( CFIDEV_BUSWIDTH ) {
+	case 1:
+		ones = (__u8)~0;
+		break;
+	case 2:
+		ones = (__u16)~0;
+		break;
+	case 4:
+		ones = (__u32)~0;
+		break;
+	}
 	
-		if ( oldstatus == ones && status == ones ) {
-			/* success - do nothing */
-		} else {
-			/* something has failed */
-			if ( ( ( status ^ oldstatus ) & dq6 )
-			     && ( status & dq5 ) ) {
-				printk(KERN_WARNING "MTD %s(): Internal flash device timeout occured.\n", __func__ );
-			} else {
-				/*
-				 * This state means that the erase did not succeed
-				 * and that the usual error state of toggling dq6
-				 * while dq5 goes high did not happen.  Something
-				 * is seriously wacky!
-				 */
-				printk(KERN_WARNING "MTD %s(): Wacky!  Unable to decode failure status\n", __func__ );
-			}
-			ret = -EIO;
-			/* reset on all failures. */
-			cfi_write( map, CMD(0xF0), chip->start );
-		}
+	if ( oldstatus == ones && status == ones ) {
+		/* success - do nothing */
+		goto erase_done;
 	}
 
+	/* something has failed */
+	if ( ( ( status ^ oldstatus ) & dq6 )
+	     && ( oldstatus & ( ( status ^ oldstatus ) & dq6 >> 1 ) ) ) {
+		printk(KERN_WARNING
+		       "MTD %s(): Internal flash device timeout occured.\n",
+		       __func__ );
+	} else {
+		/*
+		 * This state means that the erase did not succeed
+		 * and that the usual error state of toggling dq6
+		 * while dq5 goes high did not happen.  Something
+		 * is seriously wacky!
+		 */
+		printk(KERN_WARNING
+		       "MTD %s(): Wacky!  Unable to decode failure status\n",
+		       __func__ );
+	}
+
+	printk(KERN_WARNING
+	       "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
+	       __func__, adr, ones,
+	       prev_oldstatus, prev_status,
+	       oldstatus, status);
+
+ erase_failed:
+	ret = -EIO;
+	/* reset on all failures. */
+	cfi_write( map, CMD(0xF0), chip->start );
+
+ erase_done:
 	DISABLE_VPP(map);
 	chip->state = FL_READY;
 	wake_up(&chip->wq);




More information about the linux-mtd-cvs mailing list