b43: Disable PMQ mechanism
[safe/jmp/linux-2.6] / drivers / mtd / chips / cfi_cmdset_0002.c
index ddc5bd7..61ea833 100644 (file)
  * XIP support hooks by Vitaly Wool (based on code for Intel flash
  * by Nicolas Pitre)
  *
+ * 25/09/2008 Christopher Moore: TopBottom fixup for many Macronix with CFI V1.0
+ *
  * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
  *
  * This code is GPL
- *
- * $Id: cfi_cmdset_0002.c,v 1.122 2005/11/07 11:14:22 gleixner Exp $
- *
  */
 
 #include <linux/module.h>
 
 #define MANUFACTURER_AMD       0x0001
 #define MANUFACTURER_ATMEL     0x001F
+#define MANUFACTURER_MACRONIX  0x00C2
 #define MANUFACTURER_SST       0x00BF
 #define SST49LF004B            0x0060
+#define SST49LF040B            0x0050
 #define SST49LF008A            0x005a
 #define AT49BV6416             0x00d6
 
@@ -70,8 +71,8 @@ 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 int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 
 static struct mtd_chip_driver cfi_amdstd_chipdrv = {
        .probe          = NULL, /* Not usable directly */
@@ -146,12 +147,44 @@ static void fixup_amd_bootblock(struct mtd_info *mtd, void* param)
 
        if (((major << 8) | minor) < 0x3131) {
                /* CFI version 1.0 => don't trust bootloc */
+
+               DEBUG(MTD_DEBUG_LEVEL1,
+                       "%s: JEDEC Vendor ID is 0x%02X Device ID is 0x%02X\n",
+                       map->name, cfi->mfr, cfi->id);
+
+               /* AFAICS all 29LV400 with a bottom boot block have a device ID
+                * of 0x22BA in 16-bit mode and 0xBA in 8-bit mode.
+                * These were badly detected as they have the 0x80 bit set
+                * so treat them as a special case.
+                */
+               if (((cfi->id == 0xBA) || (cfi->id == 0x22BA)) &&
+
+                       /* Macronix added CFI to their 2nd generation
+                        * MX29LV400C B/T but AFAICS no other 29LV400 (AMD,
+                        * Fujitsu, Spansion, EON, ESI and older Macronix)
+                        * has CFI.
+                        *
+                        * Therefore also check the manufacturer.
+                        * This reduces the risk of false detection due to
+                        * the 8-bit device ID.
+                        */
+                       (cfi->mfr == MANUFACTURER_MACRONIX)) {
+                       DEBUG(MTD_DEBUG_LEVEL1,
+                               "%s: Macronix MX29LV400C with bottom boot block"
+                               " detected\n", map->name);
+                       extp->TopBottom = 2;    /* bottom boot */
+               } else
                if (cfi->id & 0x80) {
                        printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id);
                        extp->TopBottom = 3;    /* top boot */
                } else {
                        extp->TopBottom = 2;    /* bottom boot */
                }
+
+               DEBUG(MTD_DEBUG_LEVEL1,
+                       "%s: AMD CFI PRI V%c.%c has no boot block field;"
+                       " deduced %s from Device ID\n", map->name, major, minor,
+                       extp->TopBottom == 2 ? "bottom" : "top");
        }
 }
 #endif
@@ -175,15 +208,27 @@ static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param)
        struct cfi_pri_atmel atmel_pri;
 
        memcpy(&atmel_pri, extp, sizeof(atmel_pri));
-       memset(extp + 5, 0, sizeof(*extp) - 5);
+       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;
+       /* Some chips got it backwards... */
+       if (cfi->id == AT49BV6416) {
+               if (atmel_pri.BottomBoot)
+                       extp->TopBottom = 3;
+               else
+                       extp->TopBottom = 2;
+       } else {
+               if (atmel_pri.BottomBoot)
+                       extp->TopBottom = 2;
+               else
+                       extp->TopBottom = 3;
+       }
+
+       /* burst write mode not supported */
+       cfi->cfiq->BufWriteTimeoutTyp = 0;
+       cfi->cfiq->BufWriteTimeoutMax = 0;
 }
 
 static void fixup_use_secsi(struct mtd_info *mtd, void *param)
@@ -212,11 +257,46 @@ 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_POWERUP_LOCK;
+}
+
+static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param)
+{
+       struct map_info *map = mtd->priv;
+       struct cfi_private *cfi = map->fldrv_priv;
+
+       if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) {
+               cfi->cfiq->EraseRegionInfo[0] |= 0x0040;
+               pr_warning("%s: Bad S29GL064N CFI data, adjust from 64 to 128 sectors\n", mtd->name);
+       }
+}
+
+static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
+{
+       struct map_info *map = mtd->priv;
+       struct cfi_private *cfi = map->fldrv_priv;
+
+       if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) {
+               cfi->cfiq->EraseRegionInfo[1] &= ~0x0040;
+               pr_warning("%s: Bad S29GL032N CFI data, adjust from 127 to 63 sectors\n", mtd->name);
+       }
+}
+
+static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param)
+{
+       struct map_info *map = mtd->priv;
+       struct cfi_private *cfi = map->fldrv_priv;
+       if (cfi->cfiq->BufWriteTimeoutTyp) {
+               pr_warning("Don't use write buffer on ST flash M29W128G\n");
+               cfi->cfiq->BufWriteTimeoutTyp = 0;
+       }
 }
 
 static struct cfi_fixup cfi_fixup_table[] = {
+       { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
 #ifdef AMD_BOOTLOC_BUG
        { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL },
+       { MANUFACTURER_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL },
 #endif
        { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, },
        { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, },
@@ -224,14 +304,19 @@ static struct cfi_fixup cfi_fixup_table[] = {
        { CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, },
        { CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, },
        { CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, },
+       { CFI_MFR_AMD, 0x0c01, fixup_s29gl064n_sectors, NULL, },
+       { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, },
+       { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, },
+       { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },
+       { CFI_MFR_ST,  0x227E, fixup_M29W128G_write_buffer, NULL, },
 #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 }
 };
@@ -248,18 +333,25 @@ static struct cfi_fixup fixup_table[] = {
 };
 
 
+static void cfi_fixup_major_minor(struct cfi_private *cfi,
+                                 struct cfi_pri_amdstd *extp)
+{
+       if (cfi->mfr == CFI_MFR_SAMSUNG && cfi->id == 0x257e &&
+           extp->MajorVersion == '0')
+               extp->MajorVersion = '1';
+}
+
 struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
 {
        struct cfi_private *cfi = map->fldrv_priv;
        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;
 
@@ -290,6 +382,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
                        return NULL;
                }
 
+               cfi_fixup_major_minor(cfi, extp);
+
                if (extp->MajorVersion != '1' ||
                    (extp->MinorVersion < '0' || extp->MinorVersion > '4')) {
                        printk(KERN_ERR "  Unknown Amd/Fujitsu Extended Query "
@@ -333,17 +427,6 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
                /* Set the default CFI lock/unlock addresses */
                cfi->addr_unlock1 = 0x555;
                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->cfiq->InterfaceDesc == 2)) ||
-                       /* x32 in x16 mode */
-                       ((cfi->device_type == CFI_DEVICETYPE_X16) &&
-                               (cfi->cfiq->InterfaceDesc == 4)))
-               {
-                       cfi->addr_unlock1 = 0xaaa;
-                       cfi->addr_unlock2 = 0x555;
-               }
 
        } /* CFI mode */
        else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
@@ -357,6 +440,8 @@ 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;
+               cfi->chips[i].ref_point_counter = 0;
+               init_waitqueue_head(&(cfi->chips[i].wq));
        }
 
        map->fldrv = &cfi_amdstd_chipdrv;
@@ -518,10 +603,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
@@ -711,10 +798,10 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
                        chip->erase_suspended = 1;
                        map_write(map, CMD(0xf0), adr);
                        (void) map_read(map, adr);
-                       asm volatile (".rep 8; nop; .endr");
+                       xip_iprefetch();
                        local_irq_enable();
                        spin_unlock(chip->mutex);
-                       asm volatile (".rep 8; nop; .endr");
+                       xip_iprefetch();
                        cond_resched();
 
                        /*
@@ -1603,7 +1690,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
 }
 
 
-int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
 {
        unsigned long ofs, len;
        int ret;
@@ -1708,12 +1795,12 @@ out_unlock:
        return ret;
 }
 
-static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+static int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_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)
+static int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 {
        return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL);
 }
@@ -1751,6 +1838,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
 
                default:
                        /* Not an idle state */
+                       set_current_state(TASK_UNINTERRUPTIBLE);
                        add_wait_queue(&chip->wq, &wait);
 
                        spin_unlock(chip->mutex);