mtd/drivers/mtd/chips cfi_cmdset_0001.c,1.107,1.108
Joakim Tjernlund
jocke at infradead.org
Fri Jan 3 19:57:07 EST 2003
Update of /home/cvs/mtd/drivers/mtd/chips
In directory phoenix.infradead.org:/tmp/cvs-serv18134
Modified Files:
cfi_cmdset_0001.c
Log Message:
Added support for Suspend Erase to Write.
#define DISABLE_ERASE_SUSPEND_ON_WRITE to disable it.
Index: cfi_cmdset_0001.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/chips/cfi_cmdset_0001.c,v
retrieving revision 1.107
retrieving revision 1.108
diff -u -r1.107 -r1.108
--- cfi_cmdset_0001.c 17 Nov 2002 13:05:01 -0000 1.107
+++ cfi_cmdset_0001.c 4 Jan 2003 00:57:04 -0000 1.108
@@ -170,6 +170,13 @@
cfi_tell_features(extp);
#endif
+#ifdef DISABLE_ERASE_SUSPEND_ON_WRITE
+/* Enable this if you suspect buggy suspend support */
+ if(extp->SuspendCmdSupport & 1)
+ printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
+ "erase on write disabled.\n");
+ extp->SuspendCmdSupport &= ~1;
+#endif
/* Install our own private info structure */
cfi->cmdset_priv = extp;
}
@@ -738,10 +745,11 @@
static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
{
struct cfi_private *cfi = map->fldrv_priv;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
cfi_word status, status_OK;
unsigned long timeo;
DECLARE_WAITQUEUE(wait, current);
- int z;
+ int z, suspended=0;
adr += chip->start;
@@ -783,7 +791,48 @@
cfi_udelay(1);
goto retry;
+ case FL_ERASING:
+ if (!extp ||
+ !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1)))
+ goto sleep; /* We don't support erase suspend */
+
+ cfi_write (map, CMD(0xb0), adr);
+
+ /* If the flash has finished erasing, then 'erase suspend'
+ * appears to make some (28F320) flash devices switch to
+ * 'read' mode. Make sure that we switch to 'read status'
+ * mode so we get the right data. --rmk
+ */
+ cfi_write(map, CMD(0x70), adr);
+ chip->oldstate = FL_ERASING;
+ chip->state = FL_ERASE_SUSPENDING;
+ for (;;) {
+ status = cfi_read(map, adr);
+ if ((status & status_OK) == status_OK)
+ break;
+
+ if (time_after(jiffies, timeo)) {
+ /* Urgh */
+ cfi_write(map, CMD(0xd0), adr);
+ /* make sure we're in 'read status' mode */
+ cfi_write(map, CMD(0x70), adr);
+ chip->state = FL_ERASING;
+ spin_unlock_bh(chip->mutex);
+ printk(KERN_ERR "Chip not ready after erase "
+ "suspended: status = 0x%x\n", status);
+ return -EIO;
+ }
+
+ spin_unlock_bh(chip->mutex);
+ cfi_udelay(1);
+ spin_lock_bh(chip->mutex);
+ }
+ suspended = 1;
+ chip->state = FL_STATUS;
+ break;
+
default:
+ sleep:
/* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -859,6 +908,20 @@
spin_unlock_bh(chip->mutex);
return -EROFS;
}
+ if (suspended) {
+ chip->state = chip->oldstate;
+ /* What if one interleaved chip has finished and the
+ other hasn't? The old code would leave the finished
+ one in READY mode. That's bad, and caused -EROFS
+ errors to be returned from do_erase_oneblock because
+ that's the only bit it checked for at the time.
+ As the state machine appears to explicitly allow
+ sending the 0x70 (Read Status) command to an erasing
+ chip and expecting it to be ignored, that's what we
+ do. */
+ cfi_write(map, CMD(0xd0), adr);
+ cfi_write(map, CMD(0x70), adr);
+ }
wake_up(&chip->wq);
spin_unlock_bh(chip->mutex);
return 0;
@@ -991,10 +1054,11 @@
unsigned long adr, const u_char *buf, int len)
{
struct cfi_private *cfi = map->fldrv_priv;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
cfi_word status, status_OK;
unsigned long cmd_adr, timeo;
DECLARE_WAITQUEUE(wait, current);
- int wbufsize, z;
+ int wbufsize, z, suspended=0;
wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
adr += chip->start;
@@ -1035,7 +1099,48 @@
cfi_udelay(1);
goto retry;
+ case FL_ERASING:
+ if (!extp ||
+ !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1)))
+ goto sleep; /* We don't support erase suspend */
+
+ cfi_write (map, CMD(0xb0), adr);
+
+ /* If the flash has finished erasing, then 'erase suspend'
+ * appears to make some (28F320) flash devices switch to
+ * 'read' mode. Make sure that we switch to 'read status'
+ * mode so we get the right data. --rmk
+ */
+ cfi_write(map, CMD(0x70), adr);
+ chip->oldstate = FL_ERASING;
+ chip->state = FL_ERASE_SUSPENDING;
+ for (;;) {
+ status = cfi_read(map, adr);
+ if ((status & status_OK) == status_OK)
+ break;
+
+ if (time_after(jiffies, timeo)) {
+ /* Urgh */
+ cfi_write(map, CMD(0xd0), adr);
+ /* make sure we're in 'read status' mode */
+ cfi_write(map, CMD(0x70), adr);
+ chip->state = FL_ERASING;
+ spin_unlock_bh(chip->mutex);
+ printk(KERN_ERR "Chip not ready after erase "
+ "suspended: status = 0x%x\n", status);
+ return -EIO;
+ }
+
+ spin_unlock_bh(chip->mutex);
+ cfi_udelay(1);
+ spin_lock_bh(chip->mutex);
+ }
+ suspended = 1;
+ chip->state = FL_STATUS;
+ break;
+
default:
+ sleep:
/* Stick ourselves on a wait queue to be woken when
someone changes the status */
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1165,6 +1270,20 @@
wake_up(&chip->wq);
spin_unlock_bh(chip->mutex);
return -EROFS;
+ }
+ if (suspended) {
+ chip->state = chip->oldstate;
+ /* What if one interleaved chip has finished and the
+ other hasn't? The old code would leave the finished
+ one in READY mode. That's bad, and caused -EROFS
+ errors to be returned from do_erase_oneblock because
+ that's the only bit it checked for at the time.
+ As the state machine appears to explicitly allow
+ sending the 0x70 (Read Status) command to an erasing
+ chip and expecting it to be ignored, that's what we
+ do. */
+ cfi_write(map, CMD(0xd0), adr);
+ cfi_write(map, CMD(0x70), adr);
}
wake_up(&chip->wq);
spin_unlock_bh(chip->mutex);
More information about the linux-mtd-cvs
mailing list