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