fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / mtd / chips / cfi_cmdset_0002.c
index fca8ff6..e3acd39 100644 (file)
@@ -4,20 +4,23 @@
  *
  * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
  * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com>
+ * Copyright (C) 2005 MontaVista Software Inc. <source@mvista.com>
  *
  * 2_by_8 routines added by Simon Munton
  *
  * 4_by_16 work by Carolyn J. Smith
  *
+ * XIP support hooks by Vitaly Wool (based on code for Intel flash
+ * by Nicolas Pitre)
+ *
  * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
  *
  * This code is GPL
  *
- * $Id: cfi_cmdset_0002.c,v 1.114 2004/12/11 15:43:53 dedekind Exp $
+ * $Id: cfi_cmdset_0002.c,v 1.122 2005/11/07 11:14:22 gleixner Exp $
  *
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -34,6 +37,7 @@
 #include <linux/mtd/map.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/cfi.h>
+#include <linux/mtd/xip.h>
 
 #define AMD_BOOTLOC_BUG
 #define FORCE_WORD_WRITE 0
 #define MAX_WORD_RETRIES 3
 
 #define MANUFACTURER_AMD       0x0001
+#define MANUFACTURER_ATMEL     0x001F
 #define MANUFACTURER_SST       0x00BF
 #define SST49LF004B            0x0060
+#define SST49LF040B            0x0050
+#define SST49LF008A            0x005a
+#define AT49BV6416             0x00d6
 
 static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
@@ -63,6 +71,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
 static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
 #include "fwh_lock.h"
 
+static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len);
+static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
+
 static struct mtd_chip_driver cfi_amdstd_chipdrv = {
        .probe          = NULL, /* Not usable directly */
        .destroy        = cfi_amdstd_destroy,
@@ -87,7 +98,7 @@ static void cfi_tell_features(struct cfi_pri_amdstd *extp)
        };
 
        printk("  Silicon revision: %d\n", extp->SiliconRevision >> 1);
-       printk("  Address sensitive unlock: %s\n", 
+       printk("  Address sensitive unlock: %s\n",
               (extp->SiliconRevision & 1) ? "Not required" : "Required");
 
        if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend))
@@ -112,9 +123,9 @@ static void cfi_tell_features(struct cfi_pri_amdstd *extp)
        else
                printk("  Page mode: %d word page\n", extp->PageMode << 2);
 
-       printk("  Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n", 
+       printk("  Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n",
               extp->VppMin >> 4, extp->VppMin & 0xf);
-       printk("  Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n", 
+       printk("  Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n",
               extp->VppMax >> 4, extp->VppMax & 0xf);
 
        if (extp->TopBottom < ARRAY_SIZE(top_bottom))
@@ -156,6 +167,26 @@ static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
        }
 }
 
+/* Atmel chips don't use the same PRI format as AMD chips */
+static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param)
+{
+       struct map_info *map = mtd->priv;
+       struct cfi_private *cfi = map->fldrv_priv;
+       struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
+       struct cfi_pri_atmel atmel_pri;
+
+       memcpy(&atmel_pri, extp, sizeof(atmel_pri));
+       memset((char *)extp + 5, 0, sizeof(*extp) - 5);
+
+       if (atmel_pri.Features & 0x02)
+               extp->EraseSuspend = 2;
+
+       if (atmel_pri.BottomBoot)
+               extp->TopBottom = 2;
+       else
+               extp->TopBottom = 3;
+}
+
 static void fixup_use_secsi(struct mtd_info *mtd, void *param)
 {
        /* Setup for chips with a secsi area */
@@ -171,7 +202,18 @@ static void fixup_use_erase_chip(struct mtd_info *mtd, void *param)
                ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) {
                mtd->erase = cfi_amdstd_erase_chip;
        }
-       
+
+}
+
+/*
+ * Some Atmel chips (e.g. the AT49BV6416) power-up with all sectors
+ * locked by default.
+ */
+static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param)
+{
+       mtd->lock = cfi_atmel_lock;
+       mtd->unlock = cfi_atmel_unlock;
+       mtd->flags |= MTD_STUPID_LOCK;
 }
 
 static struct cfi_fixup cfi_fixup_table[] = {
@@ -187,10 +229,13 @@ static struct cfi_fixup cfi_fixup_table[] = {
 #if !FORCE_WORD_WRITE
        { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
 #endif
+       { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
        { 0, 0, NULL, NULL }
 };
 static struct cfi_fixup jedec_fixup_table[] = {
        { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
+       { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, },
+       { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
        { 0, 0, NULL, NULL }
 };
 
@@ -201,6 +246,7 @@ static struct cfi_fixup fixup_table[] = {
         * we know that is the case.
         */
        { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip, NULL },
+       { CFI_MFR_ATMEL, AT49BV6416, fixup_use_atmel_lock, NULL },
        { 0, 0, NULL, NULL }
 };
 
@@ -211,12 +257,11 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
        struct mtd_info *mtd;
        int i;
 
-       mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+       mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
        if (!mtd) {
                printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
                return NULL;
        }
-       memset(mtd, 0, sizeof(*mtd));
        mtd->priv = map;
        mtd->type = MTD_NORFLASH;
 
@@ -229,10 +274,11 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
        mtd->resume  = cfi_amdstd_resume;
        mtd->flags   = MTD_CAP_NORFLASH;
        mtd->name    = map->name;
+       mtd->writesize = 1;
 
        if (cfi->cfi_mode==CFI_MODE_CFI){
                unsigned char bootloc;
-               /* 
+               /*
                 * It's a real CFI chip, not one for which the probe
                 * routine faked a CFI structure. So we read the feature
                 * table from it.
@@ -246,8 +292,18 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
                        return NULL;
                }
 
+               if (extp->MajorVersion != '1' ||
+                   (extp->MinorVersion < '0' || extp->MinorVersion > '4')) {
+                       printk(KERN_ERR "  Unknown Amd/Fujitsu Extended Query "
+                              "version %c.%c.\n",  extp->MajorVersion,
+                              extp->MinorVersion);
+                       kfree(extp);
+                       kfree(mtd);
+                       return NULL;
+               }
+
                /* Install our own private info structure */
-               cfi->cmdset_priv = extp;        
+               cfi->cmdset_priv = extp;
 
                /* Apply cfi device specific fixups */
                cfi_fixup(mtd, cfi_fixup_table);
@@ -255,7 +311,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
 #ifdef DEBUG_CFI_FEATURES
                /* Tell the user about it in lots of lovely detail */
                cfi_tell_features(extp);
-#endif 
+#endif
 
                bootloc = extp->TopBottom;
                if ((bootloc != 2) && (bootloc != 3)) {
@@ -266,11 +322,11 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
 
                if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {
                        printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name);
-                       
+
                        for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) {
                                int j = (cfi->cfiq->NumEraseRegions-1)-i;
                                __u32 swap;
-                               
+
                                swap = cfi->cfiq->EraseRegionInfo[i];
                                cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j];
                                cfi->cfiq->EraseRegionInfo[j] = swap;
@@ -281,11 +337,11 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
                cfi->addr_unlock2 = 0x2aa;
                /* Modify the unlock address if we are in compatibility mode */
                if (    /* x16 in x8 mode */
-                       ((cfi->device_type == CFI_DEVICETYPE_X8) && 
+                       ((cfi->device_type == CFI_DEVICETYPE_X8) &&
                                (cfi->cfiq->InterfaceDesc == 2)) ||
                        /* x32 in x16 mode */
                        ((cfi->device_type == CFI_DEVICETYPE_X16) &&
-                               (cfi->cfiq->InterfaceDesc == 4))) 
+                               (cfi->cfiq->InterfaceDesc == 4)))
                {
                        cfi->addr_unlock1 = 0xaaa;
                        cfi->addr_unlock2 = 0x555;
@@ -303,13 +359,13 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
                cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
                cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
                cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp;
-       }               
-       
+       }
+
        map->fldrv = &cfi_amdstd_chipdrv;
-       
+
        return cfi_amdstd_setup(mtd);
 }
-
+EXPORT_SYMBOL_GPL(cfi_cmdset_0002);
 
 static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
 {
@@ -319,24 +375,24 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
        unsigned long offset = 0;
        int i,j;
 
-       printk(KERN_NOTICE "number of %s chips: %d\n", 
+       printk(KERN_NOTICE "number of %s chips: %d\n",
               (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips);
-       /* Select the correct geometry setup */ 
+       /* Select the correct geometry setup */
        mtd->size = devsize * cfi->numchips;
 
        mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
        mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
                                    * mtd->numeraseregions, GFP_KERNEL);
-       if (!mtd->eraseregions) { 
+       if (!mtd->eraseregions) {
                printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
                goto setup_err;
        }
-                       
+
        for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
                unsigned long ernum, ersize;
                ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
                ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
-                       
+
                if (mtd->erasesize < ersize) {
                        mtd->erasesize = ersize;
                }
@@ -371,8 +427,7 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
 
  setup_err:
        if(mtd) {
-               if(mtd->eraseregions)
-                       kfree(mtd->eraseregions);
+               kfree(mtd->eraseregions);
                kfree(mtd);
        }
        kfree(cfi->cmdset_priv);
@@ -391,7 +446,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).
  */
-static int chip_ready(struct map_info *map, unsigned long addr)
+static int __xipram chip_ready(struct map_info *map, unsigned long addr)
 {
        map_word d, t;
 
@@ -401,6 +456,32 @@ static int chip_ready(struct map_info *map, unsigned long addr)
        return map_word_equal(map, d, t);
 }
 
+/*
+ * Return true if the chip is ready and has the correct value.
+ *
+ * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
+ * non-suspended sector) and it is indicated by no bits toggling.
+ *
+ * Error are indicated by toggling bits or bits held with the wrong value,
+ * or with bits toggling.
+ *
+ * Note that anything more complicated than checking if no bits are toggling
+ * (including checking DQ5 for an error status) is tricky to get working
+ * correctly and is therefore not done (particulary with interleaved chips
+ * as each chip must be checked independantly of the others).
+ *
+ */
+static int __xipram chip_good(struct map_info *map, unsigned long addr, map_word expected)
+{
+       map_word oldd, curd;
+
+       oldd = map_read(map, addr);
+       curd = map_read(map, addr);
+
+       return  map_word_equal(map, oldd, curd) &&
+               map_word_equal(map, curd, expected);
+}
+
 static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
 {
        DECLARE_WAITQUEUE(wait, current);
@@ -420,16 +501,16 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
 
                        if (time_after(jiffies, timeo)) {
                                printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
-                               cfi_spin_unlock(chip->mutex);
+                               spin_unlock(chip->mutex);
                                return -EIO;
                        }
-                       cfi_spin_unlock(chip->mutex);
+                       spin_unlock(chip->mutex);
                        cfi_udelay(1);
-                       cfi_spin_lock(chip->mutex);
+                       spin_lock(chip->mutex);
                        /* Someone else might have been playing with it. */
                        goto retry;
                }
-                               
+
        case FL_READY:
        case FL_CFI_QUERY:
        case FL_JEDEC_QUERY:
@@ -439,10 +520,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
                if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */
                        goto sleep;
 
-               if (!(mode == FL_READY || mode == FL_POINT
+               if (!(   mode == FL_READY
+                     || mode == FL_POINT
                      || !cfip
                      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
-                     || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1))))
+                     || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)
+                   )))
                        goto sleep;
 
                /* We could check to see if we're trying to access the sector
@@ -472,16 +555,24 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
                                printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);
                                return -EIO;
                        }
-                       
-                       cfi_spin_unlock(chip->mutex);
+
+                       spin_unlock(chip->mutex);
                        cfi_udelay(1);
-                       cfi_spin_lock(chip->mutex);
+                       spin_lock(chip->mutex);
                        /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
                           So we can just loop here. */
                }
                chip->state = FL_READY;
                return 0;
 
+       case FL_XIP_WHILE_ERASING:
+               if (mode != FL_READY && mode != FL_POINT &&
+                   (!cfip || !(cfip->EraseSuspend&2)))
+                       goto sleep;
+               chip->oldstate = chip->state;
+               chip->state = FL_READY;
+               return 0;
+
        case FL_POINT:
                /* Only if there's no operation suspended... */
                if (mode == FL_READY && chip->oldstate == FL_READY)
@@ -491,10 +582,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
        sleep:
                set_current_state(TASK_UNINTERRUPTIBLE);
                add_wait_queue(&chip->wq, &wait);
-               cfi_spin_unlock(chip->mutex);
+               spin_unlock(chip->mutex);
                schedule();
                remove_wait_queue(&chip->wq, &wait);
-               cfi_spin_lock(chip->mutex);
+               spin_lock(chip->mutex);
                goto resettime;
        }
 }
@@ -512,6 +603,11 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
                chip->state = FL_ERASING;
                break;
 
+       case FL_XIP_WHILE_ERASING:
+               chip->state = chip->oldstate;
+               chip->oldstate = FL_READY;
+               break;
+
        case FL_READY:
        case FL_STATUS:
                /* We should really make set_vpp() count, rather than doing this */
@@ -523,6 +619,198 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
        wake_up(&chip->wq);
 }
 
+#ifdef CONFIG_MTD_XIP
+
+/*
+ * No interrupt what so ever can be serviced while the flash isn't in array
+ * mode.  This is ensured by the xip_disable() and xip_enable() functions
+ * enclosing any code path where the flash is known not to be in array mode.
+ * And within a XIP disabled code path, only functions marked with __xipram
+ * may be called and nothing else (it's a good thing to inspect generated
+ * assembly to make sure inline functions were actually inlined and that gcc
+ * didn't emit calls to its own support functions). Also configuring MTD CFI
+ * support to a single buswidth and a single interleave is also recommended.
+ */
+
+static void xip_disable(struct map_info *map, struct flchip *chip,
+                       unsigned long adr)
+{
+       /* TODO: chips with no XIP use should ignore and return */
+       (void) map_read(map, adr); /* ensure mmu mapping is up to date */
+       local_irq_disable();
+}
+
+static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
+                               unsigned long adr)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+
+       if (chip->state != FL_POINT && chip->state != FL_READY) {
+               map_write(map, CMD(0xf0), adr);
+               chip->state = FL_READY;
+       }
+       (void) map_read(map, adr);
+       xip_iprefetch();
+       local_irq_enable();
+}
+
+/*
+ * When a delay is required for the flash operation to complete, the
+ * xip_udelay() function is polling for both the given timeout and pending
+ * (but still masked) hardware interrupts.  Whenever there is an interrupt
+ * pending then the flash erase operation is suspended, array mode restored
+ * and interrupts unmasked.  Task scheduling might also happen at that
+ * point.  The CPU eventually returns from the interrupt or the call to
+ * schedule() and the suspended flash operation is resumed for the remaining
+ * of the delay period.
+ *
+ * Warning: this function _will_ fool interrupt latency tracing tools.
+ */
+
+static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
+                               unsigned long adr, int usec)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+       struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
+       map_word status, OK = CMD(0x80);
+       unsigned long suspended, start = xip_currtime();
+       flstate_t oldstate;
+
+       do {
+               cpu_relax();
+               if (xip_irqpending() && extp &&
+                   ((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) &&
+                   (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
+                       /*
+                        * Let's suspend the erase operation when supported.
+                        * Note that we currently don't try to suspend
+                        * interleaved chips if there is already another
+                        * operation suspended (imagine what happens
+                        * when one chip was already done with the current
+                        * operation while another chip suspended it, then
+                        * we resume the whole thing at once).  Yes, it
+                        * can happen!
+                        */
+                       map_write(map, CMD(0xb0), adr);
+                       usec -= xip_elapsed_since(start);
+                       suspended = xip_currtime();
+                       do {
+                               if (xip_elapsed_since(suspended) > 100000) {
+                                       /*
+                                        * The chip doesn't want to suspend
+                                        * after waiting for 100 msecs.
+                                        * This is a critical error but there
+                                        * is not much we can do here.
+                                        */
+                                       return;
+                               }
+                               status = map_read(map, adr);
+                       } while (!map_word_andequal(map, status, OK, OK));
+
+                       /* Suspend succeeded */
+                       oldstate = chip->state;
+                       if (!map_word_bitsset(map, status, CMD(0x40)))
+                               break;
+                       chip->state = FL_XIP_WHILE_ERASING;
+                       chip->erase_suspended = 1;
+                       map_write(map, CMD(0xf0), adr);
+                       (void) map_read(map, adr);
+                       asm volatile (".rep 8; nop; .endr");
+                       local_irq_enable();
+                       spin_unlock(chip->mutex);
+                       asm volatile (".rep 8; nop; .endr");
+                       cond_resched();
+
+                       /*
+                        * We're back.  However someone else might have
+                        * decided to go write to the chip if we are in
+                        * a suspended erase state.  If so let's wait
+                        * until it's done.
+                        */
+                       spin_lock(chip->mutex);
+                       while (chip->state != FL_XIP_WHILE_ERASING) {
+                               DECLARE_WAITQUEUE(wait, current);
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               add_wait_queue(&chip->wq, &wait);
+                               spin_unlock(chip->mutex);
+                               schedule();
+                               remove_wait_queue(&chip->wq, &wait);
+                               spin_lock(chip->mutex);
+                       }
+                       /* Disallow XIP again */
+                       local_irq_disable();
+
+                       /* Resume the write or erase operation */
+                       map_write(map, CMD(0x30), adr);
+                       chip->state = oldstate;
+                       start = xip_currtime();
+               } else if (usec >= 1000000/HZ) {
+                       /*
+                        * Try to save on CPU power when waiting delay
+                        * is at least a system timer tick period.
+                        * No need to be extremely accurate here.
+                        */
+                       xip_cpu_idle();
+               }
+               status = map_read(map, adr);
+       } while (!map_word_andequal(map, status, OK, OK)
+                && xip_elapsed_since(start) < usec);
+}
+
+#define UDELAY(map, chip, adr, usec)  xip_udelay(map, chip, adr, usec)
+
+/*
+ * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
+ * the flash is actively programming or erasing since we have to poll for
+ * the operation to complete anyway.  We can't do that in a generic way with
+ * a XIP setup so do it before the actual flash operation in this case
+ * and stub it out from INVALIDATE_CACHE_UDELAY.
+ */
+#define XIP_INVAL_CACHED_RANGE(map, from, size)  \
+       INVALIDATE_CACHED_RANGE(map, from, size)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
+       UDELAY(map, chip, adr, usec)
+
+/*
+ * Extra notes:
+ *
+ * Activating this XIP support changes the way the code works a bit.  For
+ * example the code to suspend the current process when concurrent access
+ * happens is never executed because xip_udelay() will always return with the
+ * same chip state as it was entered with.  This is why there is no care for
+ * the presence of add_wait_queue() or schedule() calls from within a couple
+ * xip_disable()'d  areas of code, like in do_erase_oneblock for example.
+ * The queueing and scheduling are always happening within xip_udelay().
+ *
+ * Similarly, get_chip() and put_chip() just happen to always be executed
+ * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state
+ * is in array mode, therefore never executing many cases therein and not
+ * causing any problem with XIP.
+ */
+
+#else
+
+#define xip_disable(map, chip, adr)
+#define xip_enable(map, chip, adr)
+#define XIP_INVAL_CACHED_RANGE(x...)
+
+#define UDELAY(map, chip, adr, usec)  \
+do {  \
+       spin_unlock(chip->mutex);  \
+       cfi_udelay(usec);  \
+       spin_lock(chip->mutex);  \
+} while (0)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
+do {  \
+       spin_unlock(chip->mutex);  \
+       INVALIDATE_CACHED_RANGE(map, adr, len);  \
+       cfi_udelay(usec);  \
+       spin_lock(chip->mutex);  \
+} while (0)
+
+#endif
 
 static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
 {
@@ -532,13 +820,13 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof
 
        adr += chip->start;
 
-       /* Ensure cmd read/writes are aligned. */ 
-       cmd_addr = adr & ~(map_bankwidth(map)-1); 
+       /* Ensure cmd read/writes are aligned. */
+       cmd_addr = adr & ~(map_bankwidth(map)-1);
 
-       cfi_spin_lock(chip->mutex);
+       spin_lock(chip->mutex);
        ret = get_chip(map, chip, cmd_addr, FL_READY);
        if (ret) {
-               cfi_spin_unlock(chip->mutex);
+               spin_unlock(chip->mutex);
                return ret;
        }
 
@@ -551,7 +839,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof
 
        put_chip(map, chip, cmd_addr);
 
-       cfi_spin_unlock(chip->mutex);
+       spin_unlock(chip->mutex);
        return 0;
 }
 
@@ -605,7 +893,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
        struct cfi_private *cfi = map->fldrv_priv;
 
  retry:
-       cfi_spin_lock(chip->mutex);
+       spin_lock(chip->mutex);
 
        if (chip->state != FL_READY){
 #if 0
@@ -613,8 +901,8 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
 #endif
                set_current_state(TASK_UNINTERRUPTIBLE);
                add_wait_queue(&chip->wq, &wait);
-               
-               cfi_spin_unlock(chip->mutex);
+
+               spin_unlock(chip->mutex);
 
                schedule();
                remove_wait_queue(&chip->wq, &wait);
@@ -625,7 +913,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
                timeo = jiffies + HZ;
 
                goto retry;
-       }       
+       }
 
        adr += chip->start;
 
@@ -634,16 +922,16 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
        cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
-       
+
        map_copy_from(map, buf, adr, len);
 
        cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
-       
+
        wake_up(&chip->wq);
-       cfi_spin_unlock(chip->mutex);
+       spin_unlock(chip->mutex);
 
        return 0;
 }
@@ -692,7 +980,7 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len,
 }
 
 
-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
+static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
 {
        struct cfi_private *cfi = map->fldrv_priv;
        unsigned long timeo = jiffies + HZ;
@@ -712,10 +1000,10 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
 
        adr += chip->start;
 
-       cfi_spin_lock(chip->mutex);
+       spin_lock(chip->mutex);
        ret = get_chip(map, chip, adr, FL_WRITING);
        if (ret) {
-               cfi_spin_unlock(chip->mutex);
+               spin_unlock(chip->mutex);
                return ret;
        }
 
@@ -735,7 +1023,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
                goto op_done;
        }
 
+       XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
        ENABLE_VPP(map);
+       xip_disable(map, chip, adr);
  retry:
        cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
@@ -743,12 +1033,12 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
        map_write(map, datum, adr);
        chip->state = FL_WRITING;
 
-       cfi_spin_unlock(chip->mutex);
-       cfi_udelay(chip->word_write_time);
-       cfi_spin_lock(chip->mutex);
+       INVALIDATE_CACHE_UDELAY(map, chip,
+                               adr, map_bankwidth(map),
+                               chip->word_write_time);
 
        /* See comment above for timeout value. */
-       timeo = jiffies + uWriteTimeout; 
+       timeo = jiffies + uWriteTimeout;
        for (;;) {
                if (chip->state != FL_WRITING) {
                        /* Someone's suspended the write. Sleep */
@@ -756,39 +1046,43 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
 
                        set_current_state(TASK_UNINTERRUPTIBLE);
                        add_wait_queue(&chip->wq, &wait);
-                       cfi_spin_unlock(chip->mutex);
+                       spin_unlock(chip->mutex);
                        schedule();
                        remove_wait_queue(&chip->wq, &wait);
                        timeo = jiffies + (HZ / 2); /* FIXME */
-                       cfi_spin_lock(chip->mutex);
+                       spin_lock(chip->mutex);
                        continue;
                }
 
-               if (chip_ready(map, adr))
-                       goto op_done;
+               if (time_after(jiffies, timeo) && !chip_ready(map, adr)){
+                       xip_enable(map, chip, adr);
+                       printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
+                       xip_disable(map, chip, adr);
+                       break;
+               }
 
-               if (time_after(jiffies, timeo))
-                        break;
+               if (chip_ready(map, adr))
+                       break;
 
                /* Latency issues. Drop the lock, wait a while and retry */
-               cfi_spin_unlock(chip->mutex);
-               cfi_udelay(1);
-               cfi_spin_lock(chip->mutex);
+               UDELAY(map, chip, adr, 1);
        }
+       /* Did we succeed? */
+       if (!chip_good(map, adr, datum)) {
+               /* reset on all failures. */
+               map_write( map, CMD(0xF0), chip->start );
+               /* FIXME - should have reset delay before continuing */
 
-       printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
-
-       /* reset on all failures. */
-       map_write( map, CMD(0xF0), chip->start );
-       /* FIXME - should have reset delay before continuing */
-       if (++retry_cnt <= MAX_WORD_RETRIES) 
-               goto retry;
+               if (++retry_cnt <= MAX_WORD_RETRIES)
+                       goto retry;
 
-       ret = -EIO;
+               ret = -EIO;
+       }
+       xip_enable(map, chip, adr);
  op_done:
        chip->state = FL_READY;
        put_chip(map, chip, adr);
-       cfi_spin_unlock(chip->mutex);
+       spin_unlock(chip->mutex);
 
        return ret;
 }
@@ -820,7 +1114,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                map_word tmp_buf;
 
  retry:
-               cfi_spin_lock(cfi->chips[chipnum].mutex);
+               spin_lock(cfi->chips[chipnum].mutex);
 
                if (cfi->chips[chipnum].state != FL_READY) {
 #if 0
@@ -829,7 +1123,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                        set_current_state(TASK_UNINTERRUPTIBLE);
                        add_wait_queue(&cfi->chips[chipnum].wq, &wait);
 
-                       cfi_spin_unlock(cfi->chips[chipnum].mutex);
+                       spin_unlock(cfi->chips[chipnum].mutex);
 
                        schedule();
                        remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
@@ -843,31 +1137,31 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                /* Load 'tmp_buf' with old contents of flash */
                tmp_buf = map_read(map, bus_ofs+chipstart);
 
-               cfi_spin_unlock(cfi->chips[chipnum].mutex);
+               spin_unlock(cfi->chips[chipnum].mutex);
 
                /* Number of bytes to copy from buffer */
                n = min_t(int, len, map_bankwidth(map)-i);
-               
+
                tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
 
-               ret = do_write_oneword(map, &cfi->chips[chipnum], 
+               ret = do_write_oneword(map, &cfi->chips[chipnum],
                                       bus_ofs, tmp_buf);
-               if (ret) 
+               if (ret)
                        return ret;
-               
+
                ofs += n;
                buf += n;
                (*retlen) += n;
                len -= n;
 
                if (ofs >> cfi->chipshift) {
-                       chipnum ++; 
+                       chipnum ++;
                        ofs = 0;
                        if (chipnum == cfi->numchips)
                                return 0;
                }
        }
-       
+
        /* We are now aligned, write as much as possible */
        while(len >= map_bankwidth(map)) {
                map_word datum;
@@ -885,7 +1179,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                len -= map_bankwidth(map);
 
                if (ofs >> cfi->chipshift) {
-                       chipnum ++; 
+                       chipnum ++;
                        ofs = 0;
                        if (chipnum == cfi->numchips)
                                return 0;
@@ -898,7 +1192,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                map_word tmp_buf;
 
  retry1:
-               cfi_spin_lock(cfi->chips[chipnum].mutex);
+               spin_lock(cfi->chips[chipnum].mutex);
 
                if (cfi->chips[chipnum].state != FL_READY) {
 #if 0
@@ -907,7 +1201,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
                        set_current_state(TASK_UNINTERRUPTIBLE);
                        add_wait_queue(&cfi->chips[chipnum].wq, &wait);
 
-                       cfi_spin_unlock(cfi->chips[chipnum].mutex);
+                       spin_unlock(cfi->chips[chipnum].mutex);
 
                        schedule();
                        remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
@@ -920,15 +1214,15 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
 
                tmp_buf = map_read(map, ofs + chipstart);
 
-               cfi_spin_unlock(cfi->chips[chipnum].mutex);
+               spin_unlock(cfi->chips[chipnum].mutex);
 
                tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
-       
-               ret = do_write_oneword(map, &cfi->chips[chipnum], 
+
+               ret = do_write_oneword(map, &cfi->chips[chipnum],
                                ofs, tmp_buf);
-               if (ret) 
+               if (ret)
                        return ret;
-               
+
                (*retlen) += len;
        }
 
@@ -939,8 +1233,9 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
 /*
  * FIXME: interleaved mode not tested, and probably not supported!
  */
-static inline int do_write_buffer(struct map_info *map, struct flchip *chip, 
-                                 unsigned long adr, const u_char *buf, int len)
+static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
+                                   unsigned long adr, const u_char *buf,
+                                   int len)
 {
        struct cfi_private *cfi = map->fldrv_priv;
        unsigned long timeo = jiffies + HZ;
@@ -954,10 +1249,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
        adr += chip->start;
        cmd_adr = adr;
 
-       cfi_spin_lock(chip->mutex);
+       spin_lock(chip->mutex);
        ret = get_chip(map, chip, adr, FL_WRITING);
        if (ret) {
-               cfi_spin_unlock(chip->mutex);
+               spin_unlock(chip->mutex);
                return ret;
        }
 
@@ -966,7 +1261,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
        DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
               __func__, adr, datum.x[0] );
 
+       XIP_INVAL_CACHED_RANGE(map, adr, len);
        ENABLE_VPP(map);
+       xip_disable(map, chip, cmd_adr);
+
        cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
        //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
@@ -996,12 +1294,12 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
        map_write(map, CMD(0x29), cmd_adr);
        chip->state = FL_WRITING;
 
-       cfi_spin_unlock(chip->mutex);
-       cfi_udelay(chip->buffer_write_time);
-       cfi_spin_lock(chip->mutex);
+       INVALIDATE_CACHE_UDELAY(map, chip,
+                               adr, map_bankwidth(map),
+                               chip->word_write_time);
+
+       timeo = jiffies + uWriteTimeout;
 
-       timeo = jiffies + uWriteTimeout; 
-               
        for (;;) {
                if (chip->state != FL_WRITING) {
                        /* Someone's suspended the write. Sleep */
@@ -1009,38 +1307,39 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
 
                        set_current_state(TASK_UNINTERRUPTIBLE);
                        add_wait_queue(&chip->wq, &wait);
-                       cfi_spin_unlock(chip->mutex);
+                       spin_unlock(chip->mutex);
                        schedule();
                        remove_wait_queue(&chip->wq, &wait);
                        timeo = jiffies + (HZ / 2); /* FIXME */
-                       cfi_spin_lock(chip->mutex);
+                       spin_lock(chip->mutex);
                        continue;
                }
 
-               if (chip_ready(map, adr))
-                       goto op_done;
-                   
-               if( time_after(jiffies, timeo))
+               if (time_after(jiffies, timeo) && !chip_ready(map, adr))
                        break;
 
+               if (chip_ready(map, adr)) {
+                       xip_enable(map, chip, adr);
+                       goto op_done;
+               }
+
                /* Latency issues. Drop the lock, wait a while and retry */
-               cfi_spin_unlock(chip->mutex);
-               cfi_udelay(1);
-               cfi_spin_lock(chip->mutex);
+               UDELAY(map, chip, adr, 1);
        }
 
-       printk(KERN_WARNING "MTD %s(): software timeout\n",
-              __func__ );
-
        /* reset on all failures. */
        map_write( map, CMD(0xF0), chip->start );
+       xip_enable(map, chip, adr);
        /* FIXME - should have reset delay before continuing */
 
+       printk(KERN_WARNING "MTD %s(): software timeout\n",
+              __func__ );
+
        ret = -EIO;
  op_done:
        chip->state = FL_READY;
        put_chip(map, chip, adr);
-       cfi_spin_unlock(chip->mutex);
+       spin_unlock(chip->mutex);
 
        return ret;
 }
@@ -1094,7 +1393,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
                if (size % map_bankwidth(map))
                        size -= size % map_bankwidth(map);
 
-               ret = do_write_buffer(map, &cfi->chips[chipnum], 
+               ret = do_write_buffer(map, &cfi->chips[chipnum],
                                      ofs, buf, size);
                if (ret)
                        return ret;
@@ -1105,7 +1404,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
                len -= size;
 
                if (ofs >> cfi->chipshift) {
-                       chipnum ++; 
+                       chipnum ++;
                        ofs = 0;
                        if (chipnum == cfi->numchips)
                                return 0;
@@ -1130,7 +1429,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
  * Handle devices with one erase region, that only implement
  * the chip erase command.
  */
-static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
+static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
 {
        struct cfi_private *cfi = map->fldrv_priv;
        unsigned long timeo = jiffies + HZ;
@@ -1140,17 +1439,20 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
 
        adr = cfi->addr_unlock1;
 
-       cfi_spin_lock(chip->mutex);
+       spin_lock(chip->mutex);
        ret = get_chip(map, chip, adr, FL_WRITING);
        if (ret) {
-               cfi_spin_unlock(chip->mutex);
+               spin_unlock(chip->mutex);
                return ret;
        }
 
        DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
               __func__, chip->start );
 
+       XIP_INVAL_CACHED_RANGE(map, adr, map->size);
        ENABLE_VPP(map);
+       xip_disable(map, chip, adr);
+
        cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
@@ -1162,9 +1464,9 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
        chip->erase_suspended = 0;
        chip->in_progress_block_addr = adr;
 
-       cfi_spin_unlock(chip->mutex);
-       msleep(chip->erase_time/2);
-       cfi_spin_lock(chip->mutex);
+       INVALIDATE_CACHE_UDELAY(map, chip,
+                               adr, map->size,
+                               chip->erase_time*500);
 
        timeo = jiffies + (HZ*20);
 
@@ -1173,10 +1475,10 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
                        /* Someone's suspended the erase. Sleep */
                        set_current_state(TASK_UNINTERRUPTIBLE);
                        add_wait_queue(&chip->wq, &wait);
-                       cfi_spin_unlock(chip->mutex);
+                       spin_unlock(chip->mutex);
                        schedule();
                        remove_wait_queue(&chip->wq, &wait);
-                       cfi_spin_lock(chip->mutex);
+                       spin_lock(chip->mutex);
                        continue;
                }
                if (chip->erase_suspended) {
@@ -1187,36 +1489,36 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
                }
 
                if (chip_ready(map, adr))
-                       goto op_done;
+                       break;
 
-               if (time_after(jiffies, timeo))
+               if (time_after(jiffies, timeo)) {
+                       printk(KERN_WARNING "MTD %s(): software timeout\n",
+                               __func__ );
                        break;
+               }
 
                /* Latency issues. Drop the lock, wait a while and retry */
-               cfi_spin_unlock(chip->mutex);
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               schedule_timeout(1);
-               cfi_spin_lock(chip->mutex);
+               UDELAY(map, chip, adr, 1000000/HZ);
        }
+       /* Did we succeed? */
+       if (!chip_good(map, adr, map_word_ff(map))) {
+               /* reset on all failures. */
+               map_write( map, CMD(0xF0), chip->start );
+               /* FIXME - should have reset delay before continuing */
 
-       printk(KERN_WARNING "MTD %s(): software timeout\n",
-              __func__ );
-
-       /* reset on all failures. */
-       map_write( map, CMD(0xF0), chip->start );
-       /* FIXME - should have reset delay before continuing */
+               ret = -EIO;
+       }
 
-       ret = -EIO;
- op_done:
        chip->state = FL_READY;
+       xip_enable(map, chip, adr);
        put_chip(map, chip, adr);
-       cfi_spin_unlock(chip->mutex);
+       spin_unlock(chip->mutex);
 
        return ret;
 }
 
 
-static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
+static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
 {
        struct cfi_private *cfi = map->fldrv_priv;
        unsigned long timeo = jiffies + HZ;
@@ -1225,17 +1527,20 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
 
        adr += chip->start;
 
-       cfi_spin_lock(chip->mutex);
+       spin_lock(chip->mutex);
        ret = get_chip(map, chip, adr, FL_ERASING);
        if (ret) {
-               cfi_spin_unlock(chip->mutex);
+               spin_unlock(chip->mutex);
                return ret;
        }
 
        DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
               __func__, adr );
 
+       XIP_INVAL_CACHED_RANGE(map, adr, len);
        ENABLE_VPP(map);
+       xip_disable(map, chip, adr);
+
        cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
@@ -1246,10 +1551,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
        chip->state = FL_ERASING;
        chip->erase_suspended = 0;
        chip->in_progress_block_addr = adr;
-       
-       cfi_spin_unlock(chip->mutex);
-       msleep(chip->erase_time/2);
-       cfi_spin_lock(chip->mutex);
+
+       INVALIDATE_CACHE_UDELAY(map, chip,
+                               adr, len,
+                               chip->erase_time*500);
 
        timeo = jiffies + (HZ*20);
 
@@ -1258,10 +1563,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
                        /* Someone's suspended the erase. Sleep */
                        set_current_state(TASK_UNINTERRUPTIBLE);
                        add_wait_queue(&chip->wq, &wait);
-                       cfi_spin_unlock(chip->mutex);
+                       spin_unlock(chip->mutex);
                        schedule();
                        remove_wait_queue(&chip->wq, &wait);
-                       cfi_spin_lock(chip->mutex);
+                       spin_lock(chip->mutex);
                        continue;
                }
                if (chip->erase_suspended) {
@@ -1271,31 +1576,33 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
                        chip->erase_suspended = 0;
                }
 
-               if (chip_ready(map, adr))
-                       goto op_done;
+               if (chip_ready(map, adr)) {
+                       xip_enable(map, chip, adr);
+                       break;
+               }
 
-               if (time_after(jiffies, timeo))
+               if (time_after(jiffies, timeo)) {
+                       xip_enable(map, chip, adr);
+                       printk(KERN_WARNING "MTD %s(): software timeout\n",
+                               __func__ );
                        break;
+               }
 
                /* Latency issues. Drop the lock, wait a while and retry */
-               cfi_spin_unlock(chip->mutex);
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               schedule_timeout(1);
-               cfi_spin_lock(chip->mutex);
+               UDELAY(map, chip, adr, 1000000/HZ);
+       }
+       /* Did we succeed? */
+       if (!chip_good(map, adr, map_word_ff(map))) {
+               /* reset on all failures. */
+               map_write( map, CMD(0xF0), chip->start );
+               /* FIXME - should have reset delay before continuing */
+
+               ret = -EIO;
        }
-       
-       printk(KERN_WARNING "MTD %s(): software timeout\n",
-              __func__ );
-       
-       /* reset on all failures. */
-       map_write( map, CMD(0xF0), chip->start );
-       /* FIXME - should have reset delay before continuing */
 
-       ret = -EIO;
- op_done:
        chip->state = FL_READY;
        put_chip(map, chip, adr);
-       cfi_spin_unlock(chip->mutex);
+       spin_unlock(chip->mutex);
        return ret;
 }
 
@@ -1314,7 +1621,7 @@ int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
 
        instr->state = MTD_ERASE_DONE;
        mtd_erase_callback(instr);
-       
+
        return 0;
 }
 
@@ -1337,10 +1644,84 @@ static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
 
        instr->state = MTD_ERASE_DONE;
        mtd_erase_callback(instr);
-       
+
        return 0;
 }
 
+static int do_atmel_lock(struct map_info *map, struct flchip *chip,
+                        unsigned long adr, int len, void *thunk)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+       int ret;
+
+       spin_lock(chip->mutex);
+       ret = get_chip(map, chip, adr + chip->start, FL_LOCKING);
+       if (ret)
+               goto out_unlock;
+       chip->state = FL_LOCKING;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "MTD %s(): LOCK 0x%08lx len %d\n",
+             __func__, adr, len);
+
+       cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
+                        cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
+                        cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi,
+                        cfi->device_type, NULL);
+       cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
+                        cfi->device_type, NULL);
+       cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
+                        cfi->device_type, NULL);
+       map_write(map, CMD(0x40), chip->start + adr);
+
+       chip->state = FL_READY;
+       put_chip(map, chip, adr + chip->start);
+       ret = 0;
+
+out_unlock:
+       spin_unlock(chip->mutex);
+       return ret;
+}
+
+static int do_atmel_unlock(struct map_info *map, struct flchip *chip,
+                          unsigned long adr, int len, void *thunk)
+{
+       struct cfi_private *cfi = map->fldrv_priv;
+       int ret;
+
+       spin_lock(chip->mutex);
+       ret = get_chip(map, chip, adr + chip->start, FL_UNLOCKING);
+       if (ret)
+               goto out_unlock;
+       chip->state = FL_UNLOCKING;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "MTD %s(): LOCK 0x%08lx len %d\n",
+             __func__, adr, len);
+
+       cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
+                        cfi->device_type, NULL);
+       map_write(map, CMD(0x70), adr);
+
+       chip->state = FL_READY;
+       put_chip(map, chip, adr + chip->start);
+       ret = 0;
+
+out_unlock:
+       spin_unlock(chip->mutex);
+       return ret;
+}
+
+static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+       return cfi_varsize_frob(mtd, do_atmel_lock, ofs, len, NULL);
+}
+
+static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+       return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL);
+}
+
 
 static void cfi_amdstd_sync (struct mtd_info *mtd)
 {
@@ -1355,7 +1736,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
                chip = &cfi->chips[i];
 
        retry:
-               cfi_spin_lock(chip->mutex);
+               spin_lock(chip->mutex);
 
                switch(chip->state) {
                case FL_READY:
@@ -1364,24 +1745,24 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
                case FL_JEDEC_QUERY:
                        chip->oldstate = chip->state;
                        chip->state = FL_SYNCING;
-                       /* No need to wake_up() on this state change - 
+                       /* No need to wake_up() on this state change -
                         * as the whole point is that nobody can do anything
                         * with the chip now anyway.
                         */
                case FL_SYNCING:
-                       cfi_spin_unlock(chip->mutex);
+                       spin_unlock(chip->mutex);
                        break;
 
                default:
                        /* Not an idle state */
                        add_wait_queue(&chip->wq, &wait);
-                       
-                       cfi_spin_unlock(chip->mutex);
+
+                       spin_unlock(chip->mutex);
 
                        schedule();
 
                        remove_wait_queue(&chip->wq, &wait);
-                       
+
                        goto retry;
                }
        }
@@ -1391,13 +1772,13 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
        for (i--; i >=0; i--) {
                chip = &cfi->chips[i];
 
-               cfi_spin_lock(chip->mutex);
-               
+               spin_lock(chip->mutex);
+
                if (chip->state == FL_SYNCING) {
                        chip->state = chip->oldstate;
                        wake_up(&chip->wq);
                }
-               cfi_spin_unlock(chip->mutex);
+               spin_unlock(chip->mutex);
        }
 }
 
@@ -1413,7 +1794,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
        for (i=0; !ret && i<cfi->numchips; i++) {
                chip = &cfi->chips[i];
 
-               cfi_spin_lock(chip->mutex);
+               spin_lock(chip->mutex);
 
                switch(chip->state) {
                case FL_READY:
@@ -1422,7 +1803,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
                case FL_JEDEC_QUERY:
                        chip->oldstate = chip->state;
                        chip->state = FL_PM_SUSPENDED;
-                       /* No need to wake_up() on this state change - 
+                       /* No need to wake_up() on this state change -
                         * as the whole point is that nobody can do anything
                         * with the chip now anyway.
                         */
@@ -1433,7 +1814,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
                        ret = -EAGAIN;
                        break;
                }
-               cfi_spin_unlock(chip->mutex);
+               spin_unlock(chip->mutex);
        }
 
        /* Unlock the chips again */
@@ -1442,16 +1823,16 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
                for (i--; i >=0; i--) {
                        chip = &cfi->chips[i];
 
-                       cfi_spin_lock(chip->mutex);
-               
+                       spin_lock(chip->mutex);
+
                        if (chip->state == FL_PM_SUSPENDED) {
                                chip->state = chip->oldstate;
                                wake_up(&chip->wq);
                        }
-                       cfi_spin_unlock(chip->mutex);
+                       spin_unlock(chip->mutex);
                }
        }
-       
+
        return ret;
 }
 
@@ -1464,11 +1845,11 @@ static void cfi_amdstd_resume(struct mtd_info *mtd)
        struct flchip *chip;
 
        for (i=0; i<cfi->numchips; i++) {
-       
+
                chip = &cfi->chips[i];
 
-               cfi_spin_lock(chip->mutex);
-               
+               spin_lock(chip->mutex);
+
                if (chip->state == FL_PM_SUSPENDED) {
                        chip->state = FL_READY;
                        map_write(map, CMD(0xF0), chip->start);
@@ -1477,7 +1858,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd)
                else
                        printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n");
 
-               cfi_spin_unlock(chip->mutex);
+               spin_unlock(chip->mutex);
        }
 }
 
@@ -1485,31 +1866,13 @@ static void cfi_amdstd_destroy(struct mtd_info *mtd)
 {
        struct map_info *map = mtd->priv;
        struct cfi_private *cfi = map->fldrv_priv;
+
        kfree(cfi->cmdset_priv);
        kfree(cfi->cfiq);
        kfree(cfi);
        kfree(mtd->eraseregions);
 }
 
-static char im_name[]="cfi_cmdset_0002";
-
-
-static int __init cfi_amdstd_init(void)
-{
-       inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002);
-       return 0;
-}
-
-
-static void __exit cfi_amdstd_exit(void)
-{
-       inter_module_unregister(im_name);
-}
-
-
-module_init(cfi_amdstd_init);
-module_exit(cfi_amdstd_exit);
-
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al.");
 MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");