cfi_cmdset_0020.c revision 5fa433942ba4e399f7e28764c9db4ade89e91d40
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Common Flash Interface support: 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ST Advanced Architecture Command Set (ID 0x0020) 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (C) 2000 Red Hat. GPL'd 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 71f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner * $Id: cfi_cmdset_0020.c,v 1.22 2005/11/07 11:14:22 gleixner Exp $ 8c2965f1129ee54afcc4ef293ff0f25fa3a7e7392Thomas Gleixner * 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 10/10/2000 Nicolas Pitre <nico@cam.org> 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * - completely revamped method functions so they are aware and 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * independent of the flash geometry (buswidth, interleave, etc.) 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * - scalability vs code size is completely set at compile-time 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (see include/linux/mtd/cfi.h for selection) 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * - optimized write buffer method 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 06/21/2002 Joern Engel <joern@wh.fh-wedel.de> and others 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * - modified Intel Command Set 0x0001 to support ST Advanced Architecture 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (command set 0x0020) 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * - added a writev function 196a8b4d319c52f8a3fdca46b185d001fbf0939911Joern Engel * 07/13/2005 Joern Engel <joern@wh.fh-wedel.de> 206a8b4d319c52f8a3fdca46b185d001fbf0939911Joern Engel * - Plugged memory leak in cfi_staa_writev(). 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h> 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h> 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h> 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h> 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/map.h> 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/cfi.h> 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h> 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/compatmac.h> 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long count, loff_t to, size_t *retlen); 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *); 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cfi_staa_sync (struct mtd_info *); 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len); 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_suspend (struct mtd_info *); 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cfi_staa_resume (struct mtd_info *); 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cfi_staa_destroy(struct mtd_info *); 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct mtd_info *cfi_cmdset_0020(struct map_info *, int); 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_info *cfi_staa_setup (struct map_info *); 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_chip_driver cfi_staa_chipdrv = { 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = NULL, /* Not usable directly */ 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .destroy = cfi_staa_destroy, 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = "cfi_cmdset_0020", 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .module = THIS_MODULE 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* #define DEBUG_LOCK_BITS */ 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//#define DEBUG_CFI_FEATURES 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_CFI_FEATURES 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cfi_tell_features(struct cfi_pri_intelext *extp) 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=9; i<32; i++) { 831f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner if (extp->FeatureSupport & (1<<i)) 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Unknown Bit %X: supported\n", i); 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 861f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport); 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=1; i<8; i++) { 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (extp->SuspendCmdSupport & (1<<i)) 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Unknown Bit %X: supported\n", i); 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 931f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask); 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=2; i<16; i++) { 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (extp->BlkStatusRegMask & (1<<i)) 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" - Unknown Bit %X Active: yes\n",i); 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1011f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 1021f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds extp->VccOptimal >> 8, extp->VccOptimal & 0xf); 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (extp->VppOptimal) 1051f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds extp->VppOptimal >> 8, extp->VppOptimal & 0xf); 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This routine is made available to other mtd code via 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * inter_module_register. It must only be accessed through 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * inter_module_get which will bump the use count of this module. The 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * addresses passed back in cfi are valid as long as the use count of 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * this module is non-zero, i.e. between inter_module_get and 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary) 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (cfi->cfi_mode) { 1231f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner /* 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * It's a real CFI chip, not one for which the probe 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * routine faked a CFI structure. So we read the feature 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * table from it. 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_pri_intelext *extp; 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics"); 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!extp) 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return NULL; 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 135d88f977b85d251f548add3d0a76fc186f99b1b21Todd Poynor if (extp->MajorVersion != '1' || 136d88f977b85d251f548add3d0a76fc186f99b1b21Todd Poynor (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { 137d88f977b85d251f548add3d0a76fc186f99b1b21Todd Poynor printk(KERN_ERR " Unknown ST Microelectronics" 138d88f977b85d251f548add3d0a76fc186f99b1b21Todd Poynor " Extended Query version %c.%c.\n", 139d88f977b85d251f548add3d0a76fc186f99b1b21Todd Poynor extp->MajorVersion, extp->MinorVersion); 140d88f977b85d251f548add3d0a76fc186f99b1b21Todd Poynor kfree(extp); 141d88f977b85d251f548add3d0a76fc186f99b1b21Todd Poynor return NULL; 142d88f977b85d251f548add3d0a76fc186f99b1b21Todd Poynor } 143d88f977b85d251f548add3d0a76fc186f99b1b21Todd Poynor 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Do some byteswapping if necessary */ 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask); 1471f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_CFI_FEATURES 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Tell the user about it in lots of lovely detail */ 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_tell_features(extp); 1511f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner#endif 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Install our own private info structure */ 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi->cmdset_priv = extp; 1551f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner } 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=0; i< cfi->numchips; i++) { 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi->chips[i].word_write_time = 128; 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi->chips[i].buffer_write_time = 128; 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi->chips[i].erase_time = 1024; 1611f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner } 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return cfi_staa_setup(map); 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 16583ea4ef213628683e5a63f2987a91044ab868051David WoodhouseEXPORT_SYMBOL_GPL(cfi_cmdset_0020); 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_info *cfi_staa_setup(struct map_info *map) 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_info *mtd; 1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long offset = 0; 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i,j; 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!mtd) { 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "Failed to allocate memory for MTD device\n"); 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(cfi->cmdset_priv); 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return NULL; 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(mtd, 0, sizeof(*mtd)); 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->priv = map; 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->type = MTD_NORFLASH; 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->size = devsize * cfi->numchips; 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; 1901f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mtd->numeraseregions, GFP_KERNEL); 1921f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner if (!mtd->eraseregions) { 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(cfi->cmdset_priv); 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtd); 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return NULL; 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1981f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long ernum, ersize; 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mtd->erasesize < ersize) { 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->erasesize = ersize; 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (j=0; j<cfi->numchips; j++) { 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset += (ersize * ernum); 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (offset != devsize) { 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Argh */ 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtd->eraseregions); 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(cfi->cmdset_priv); 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtd); 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return NULL; 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=0; i<mtd->numeraseregions;i++){ 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_DEBUG "%d: offset=0x%x,size=0x%x,blocks=%d\n", 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i,mtd->eraseregions[i].offset, 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->eraseregions[i].erasesize, 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->eraseregions[i].numblocks); 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2311f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner /* Also select the correct geometry setup too */ 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->erase = cfi_staa_erase_varsize; 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->read = cfi_staa_read; 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->write = cfi_staa_write_buffers; 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->writev = cfi_staa_writev; 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->sync = cfi_staa_sync; 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->lock = cfi_staa_lock; 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->unlock = cfi_staa_unlock; 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->suspend = cfi_staa_suspend; 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->resume = cfi_staa_resume; 2415fa433942ba4e399f7e28764c9db4ade89e91d40Joern Engel mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE; 242c8b229de2b05c2b3e8d282ce260935a88ac030caJoern Engel mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */ 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->fldrv = &cfi_staa_chipdrv; 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __module_get(THIS_MODULE); 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->name = map->name; 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return mtd; 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_word status, status_OK; 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long timeo; 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DECLARE_WAITQUEUE(wait, current); 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int suspended = 0; 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long cmd_addr; 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr += chip->start; 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2611f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner /* Ensure cmd read/writes are aligned. */ 2621f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner cmd_addr = adr & ~(map_bankwidth(map)-1); 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Let's determine this according to the interleave only once */ 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status_OK = CMD(0x80); 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retry: 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check that the chip's ready to talk to us. 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If it's in FL_ERASING state, suspend it and make it talk now. 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (chip->state) { 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_ERASING: 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto sleep; /* We don't support erase suspend */ 2781f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write (map, CMD(0xb0), cmd_addr); 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* If the flash has finished erasing, then 'erase suspend' 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * appears to make some (28F320) flash devices switch to 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 'read' mode. Make sure that we switch to 'read status' 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mode so we get the right data. --rmk 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), cmd_addr); 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->oldstate = FL_ERASING; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_ERASE_SUSPENDING; 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds // printk("Erase suspending at 0x%lx\n", cmd_addr); 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) { 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, cmd_addr); 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2931f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Urgh */ 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0xd0), cmd_addr); 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* make sure we're in 'read status' mode */ 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), cmd_addr); 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_ERASING; 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "Chip not ready after erase " 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "suspended: status = 0x%lx\n", status.x[0]); 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3051f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3101f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds suspended = 1; 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0xff), cmd_addr); 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_READY; 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3151f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_WRITING: 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Not quite yet */ 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_READY: 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_CFI_QUERY: 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_JEDEC_QUERY: 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), cmd_addr); 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_STATUS: 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, cmd_addr); 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) { 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0xff), cmd_addr); 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_READY; 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3361f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Urgh. Chip not yet ready to talk to us. */ 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Latency issues. Drop the lock, wait a while and retry */ 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sleep: 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Stick ourselves on a wait queue to be woken when 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds someone changes the status */ 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&chip->wq, &wait); 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&chip->wq, &wait); 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_copy_from(map, buf, adr, len); 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (suspended) { 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = chip->oldstate; 3661f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner /* What if one interleaved chip has finished and the 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds other hasn't? The old code would leave the finished 3681f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner one in READY mode. That's bad, and caused -EROFS 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds errors to be returned from do_erase_oneblock because 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds that's the only bit it checked for at the time. 3711f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner As the state machine appears to explicitly allow 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sending the 0x70 (Read Status) command to an erasing 3731f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner chip and expecting it to be ignored, that's what we 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds do. */ 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0xd0), cmd_addr); 3761f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner map_write(map, CMD(0x70), cmd_addr); 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&chip->wq); 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct map_info *map = mtd->priv; 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long ofs; 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int chipnum; 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* ofs: offset within the first chip that the first read should start */ 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chipnum = (from >> cfi->chipshift); 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = from - (chipnum << cfi->chipshift); 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = 0; 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (len) { 3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long thislen; 4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (chipnum >= cfi->numchips) 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((len + ofs -1) >> cfi->chipshift) 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds thislen = (1<<cfi->chipshift) - ofs; 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds thislen = len; 4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); 4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen += thislen; 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= thislen; 4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf += thislen; 4161f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = 0; 4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chipnum++; 4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4231f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixnerstatic inline int do_write_buffer(struct map_info *map, struct flchip *chip, 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long adr, const u_char *buf, int len) 4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_word status, status_OK; 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long cmd_adr, timeo; 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DECLARE_WAITQUEUE(wait, current); 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int wbufsize, z; 4311f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* M58LW064A requires bus alignment for buffer wriets -- saw */ 4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (adr & (map_bankwidth(map)-1)) 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr += chip->start; 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd_adr = adr & ~(wbufsize-1); 4391f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Let's determine this according to the interleave only once */ 4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status_OK = CMD(0x80); 4421f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retry: 4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_CFI_FEATURES 4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: chip->state[%d]\n", __FUNCTION__, chip->state); 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 4501f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check that the chip's ready to talk to us. 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Later, we can actually think about interrupting it 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * if it's in FL_ERASING state. 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Not just yet, though. 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (chip->state) { 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_READY: 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4591f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_CFI_QUERY: 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_JEDEC_QUERY: 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), cmd_adr); 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_CFI_FEATURES 4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: 1 status[%x]\n", __FUNCTION__, map_read(map, cmd_adr)); 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_STATUS: 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, cmd_adr); 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Urgh. Chip not yet ready to talk to us. */ 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n", 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status.x[0], map_read(map, cmd_adr).x[0]); 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Latency issues. Drop the lock, wait a while and retry */ 4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Stick ourselves on a wait queue to be woken when 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds someone changes the status */ 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&chip->wq, &wait); 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&chip->wq, &wait); 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ENABLE_VPP(map); 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0xe8), cmd_adr); 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_WRITING_TO_BUFFER; 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds z = 0; 5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) { 5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, cmd_adr); 5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) 5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (++z > 100) { 5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Argh. Not ready for write to buffer */ 5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DISABLE_VPP(map); 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), cmd_adr); 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); 5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Write length of data to come */ 5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(len/map_bankwidth(map)-1), cmd_adr ); 5241f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Write data */ 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (z = 0; z < len; 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds z += map_bankwidth(map), buf += map_bankwidth(map)) { 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_word d; 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds d = map_word_load(map, buf); 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, d, adr+z); 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* GO GO GO */ 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0xd0), cmd_adr); 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_WRITING; 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(chip->buffer_write_time); 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + (HZ/2); 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds z = 0; 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) { 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (chip->state != FL_WRITING) { 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Someone's suspended the write. Sleep */ 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&chip->wq, &wait); 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&chip->wq, &wait); 5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + (HZ / 2); /* FIXME */ 5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, cmd_adr); 5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) 5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* OK Still waiting */ 5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* clear status */ 5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x50), cmd_adr); 5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* put back into read status register mode */ 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DISABLE_VPP(map); 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); 5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5711f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Latency issues. Drop the lock, wait a while and retry */ 5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds z++; 5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!z) { 5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->buffer_write_time--; 5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!chip->buffer_write_time) 5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->buffer_write_time++; 5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5831f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner if (z > 1) 5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->buffer_write_time++; 5851f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Done and happy. */ 5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DISABLE_VPP(map); 5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ 5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_bitsset(map, status, CMD(0x3a))) { 5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_CFI_FEATURES 5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: 2 status[%lx]\n", __FUNCTION__, status.x[0]); 5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* clear status */ 5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x50), cmd_adr); 5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* put back into read status register mode */ 5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&chip->wq); 6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO; 6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&chip->wq); 6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6091f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixnerstatic int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, 6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t len, size_t *retlen, const u_char *buf) 6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct map_info *map = mtd->priv; 6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int chipnum; 6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long ofs; 6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = 0; 6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!len) 6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chipnum = to >> cfi->chipshift; 6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = to - (chipnum << cfi->chipshift); 6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_CFI_FEATURES 6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: map_bankwidth(map)[%x]\n", __FUNCTION__, map_bankwidth(map)); 6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: chipnum[%x] wbufsize[%x]\n", __FUNCTION__, chipnum, wbufsize); 6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s: ofs[%x] len[%x]\n", __FUNCTION__, ofs, len); 6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 6311f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Write buffer is worth it only if more than one word to write... */ 6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (len > 0) { 6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We must not cross write block boundaries */ 6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int size = wbufsize - (ofs & (wbufsize-1)); 6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (size > len) 6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = len; 6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6401f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner ret = do_write_buffer(map, &cfi->chips[chipnum], 6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs, buf, size); 6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs += size; 6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf += size; 6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (*retlen) += size; 6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= size; 6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ofs >> cfi->chipshift) { 6511f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner chipnum ++; 6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = 0; 6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (chipnum == cfi->numchips) 6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6571f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Writev for ECC-Flashes is a little more complicated. We need to maintain 6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a small buffer for this. 6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * XXX: If the buffer size is not a multiple of 2, this will break 6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ECCBUF_SIZE (mtd->eccsize) 6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) 6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) 6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int 6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldscfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, 6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long count, loff_t to, size_t *retlen) 6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long i; 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t totlen = 0, thislen; 6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t buflen = 0; 6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds static char *buffer; 6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ECCBUF_SIZE) { 6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We should fall back to a general writev implementation. 6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Until that is written, just break. 6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buffer = kmalloc(ECCBUF_SIZE, GFP_KERNEL); 6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!buffer) 6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOMEM; 6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=0; i<count; i++) { 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t elem_len = vecs[i].iov_len; 6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void *elem_base = vecs[i].iov_base; 6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!elem_len) /* FIXME: Might be unnecessary. Check that */ 6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (buflen) { /* cut off head */ 6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (buflen + elem_len < ECCBUF_SIZE) { /* just accumulate */ 6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(buffer+buflen, elem_base, elem_len); 6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen += elem_len; 6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(buffer+buflen, elem_base, ECCBUF_SIZE-buflen); 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = mtd->write(mtd, to, ECCBUF_SIZE, &thislen, buffer); 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds totlen += thislen; 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret || thislen != ECCBUF_SIZE) 7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto write_error; 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds elem_len -= thislen-buflen; 7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds elem_base += thislen-buflen; 7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds to += ECCBUF_SIZE; 7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ECCBUF_DIV(elem_len)) { /* write clean aligned data */ 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = mtd->write(mtd, to, ECCBUF_DIV(elem_len), &thislen, elem_base); 7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds totlen += thislen; 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret || thislen != ECCBUF_DIV(elem_len)) 7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto write_error; 7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds to += thislen; 7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buflen = ECCBUF_MOD(elem_len); /* cut off tail */ 7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (buflen) { 7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(buffer, 0xff, ECCBUF_SIZE); 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy(buffer, elem_base + thislen, buflen); 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (buflen) { /* flush last page, even if not full */ 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* This is sometimes intended behaviour, really */ 7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = mtd->write(mtd, to, buflen, &thislen, buffer); 7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds totlen += thislen; 7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret || thislen != ECCBUF_SIZE) 7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto write_error; 7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldswrite_error: 7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retlen) 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = totlen; 7326a8b4d319c52f8a3fdca46b185d001fbf0939911Joern Engel kfree(buffer); 7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) 7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_word status, status_OK; 7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long timeo; 7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int retries = 3; 7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DECLARE_WAITQUEUE(wait, current); 7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr += chip->start; 7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Let's determine this according to the interleave only once */ 7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status_OK = CMD(0x80); 7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsretry: 7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check that the chip's ready to talk to us. */ 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (chip->state) { 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_CFI_QUERY: 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_JEDEC_QUERY: 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_READY: 7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_STATUS: 7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, adr); 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 7671f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Urgh. Chip not yet ready to talk to us. */ 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); 7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Latency issues. Drop the lock, wait a while and retry */ 7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Stick ourselves on a wait queue to be woken when 7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds someone changes the status */ 7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&chip->wq, &wait); 7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); 7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&chip->wq, &wait); 7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ENABLE_VPP(map); 7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Clear the status register first */ 7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x50), adr); 7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Now erase */ 7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x20), adr); 7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0xD0), adr); 7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_ERASING; 8001f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds msleep(1000); 8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME. Use a timer to check this, and return immediately. */ 8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Once the state machine's known to be working I'll do that */ 8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + (HZ*20); 8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) { 8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (chip->state != FL_ERASING) { 8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Someone's suspended the erase. Sleep */ 8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&chip->wq, &wait); 8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); 8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&chip->wq, &wait); 8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + (HZ*20); /* FIXME */ 8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, adr); 8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) 8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 8251f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* OK Still waiting */ 8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); 8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DISABLE_VPP(map); 8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8351f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Latency issues. Drop the lock, wait a while and retry */ 8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8411f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DISABLE_VPP(map); 8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = 0; 8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We've broken this before. It doesn't hurt to be safe */ 8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, adr); 8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* check for lock bit */ 8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_bitsset(map, status, CMD(0x3a))) { 8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char chipstatus = status.x[0]; 8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!map_word_equal(map, status, CMD(chipstatus))) { 8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i, w; 8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (w=0; w<map_words(map); w++) { 8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i<cfi_interleave(cfi); i++) { 8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chipstatus |= status.x[w] >> (cfi->device_type * 8); 8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", 8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status.x[0], chipstatus); 8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the error bits */ 8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x50), adr); 8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 8661f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((chipstatus & 0x30) == 0x30) { 8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); 8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EIO; 8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (chipstatus & 0x02) { 8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Protection bit set */ 8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EROFS; 8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (chipstatus & 0x8) { 8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Voltage */ 8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); 8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EIO; 8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (chipstatus & 0x20) { 8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (retries--) { 8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); 8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); 8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EIO; 8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&chip->wq); 8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint cfi_staa_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) 8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ struct map_info *map = mtd->priv; 8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long adr, len; 8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int chipnum, ret = 0; 9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i, first; 9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_erase_region_info *regions = mtd->eraseregions; 9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (instr->addr > mtd->size) 9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((instr->len + instr->addr) > mtd->size) 9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check that both start and end of the requested erase are 9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * aligned with the erasesize at the appropriate addresses. 9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i = 0; 9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9151f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner /* Skip all erase regions which are ended before the start of 9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds the requested erase. Actually, to save on the calculations, 9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds we skip to the first erase region which starts after the 9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds start of the requested erase, and then go back one. 9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 9201f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) 9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i++; 9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i--; 9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9251f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner /* OK, now i is pointing at the erase region in which this 9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase request starts. Check the start of the requested 9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds erase range is aligned with the erase size which is in 9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds effect here. 9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (instr->addr & (regions[i].erasesize-1)) 9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Remember the erase region we start on */ 9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds first = i; 9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Next, check that the end of the requested erase is aligned 9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * with the erase region at that address. 9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset) 9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i++; 9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* As before, drop back one to point at the region in which 9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds the address actually falls 9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i--; 9481f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((instr->addr + instr->len) & (regions[i].erasesize-1)) 9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chipnum = instr->addr >> cfi->chipshift; 9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr = instr->addr - (chipnum << cfi->chipshift); 9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len = instr->len; 9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i=first; 9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while(len) { 9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); 9601f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr += regions[i].erasesize; 9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= regions[i].erasesize; 9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) 9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds i++; 9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (adr >> cfi->chipshift) { 9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr = 0; 9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chipnum++; 9731f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (chipnum >= cfi->numchips) 9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9781f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds instr->state = MTD_ERASE_DONE; 9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd_erase_callback(instr); 9811f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cfi_staa_sync (struct mtd_info *mtd) 9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct map_info *map = mtd->priv; 9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct flchip *chip; 9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DECLARE_WAITQUEUE(wait, current); 9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=0; !ret && i<cfi->numchips; i++) { 9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip = &cfi->chips[i]; 9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retry: 9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch(chip->state) { 10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_READY: 10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_STATUS: 10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_CFI_QUERY: 10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_JEDEC_QUERY: 10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->oldstate = chip->state; 10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_SYNCING; 10071f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner /* No need to wake_up() on this state change - 10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as the whole point is that nobody can do anything 10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * with the chip now anyway. 10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_SYNCING: 10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Not an idle state */ 10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&chip->wq, &wait); 10181f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); 10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&chip->wq, &wait); 10221f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Unlock the chips again */ 10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i--; i >=0; i--) { 10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip = &cfi->chips[i]; 10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 10331f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (chip->state == FL_SYNCING) { 10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = chip->oldstate; 10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&chip->wq); 10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) 10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_word status, status_OK; 10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long timeo = jiffies + HZ; 10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DECLARE_WAITQUEUE(wait, current); 10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr += chip->start; 10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Let's determine this according to the interleave only once */ 10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status_OK = CMD(0x80); 10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsretry: 10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check that the chip's ready to talk to us. */ 10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (chip->state) { 10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_CFI_QUERY: 10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_JEDEC_QUERY: 10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_READY: 10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_STATUS: 10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, adr); 10681f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner if (map_word_andequal(map, status, status_OK, status_OK)) 10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 10701f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Urgh. Chip not yet ready to talk to us. */ 10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "waiting for chip to be ready timed out in lock\n"); 10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Latency issues. Drop the lock, wait a while and retry */ 10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Stick ourselves on a wait queue to be woken when 10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds someone changes the status */ 10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&chip->wq, &wait); 10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); 10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&chip->wq, &wait); 10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ENABLE_VPP(map); 10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x60), adr); 10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x01), adr); 10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_LOCKING; 10991f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds msleep(1000); 11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME. Use a timer to check this, and return immediately. */ 11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Once the state machine's known to be working I'll do that */ 11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + (HZ*2); 11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) { 11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, adr); 11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) 11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 11131f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* OK Still waiting */ 11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); 11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DISABLE_VPP(map); 11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11231f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Latency issues. Drop the lock, wait a while and retry */ 11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11291f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Done and happy. */ 11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DISABLE_VPP(map); 11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&chip->wq); 11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len) 11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct map_info *map = mtd->priv; 11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long adr; 11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int chipnum, ret = 0; 11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_LOCK_BITS 11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ofs_factor = cfi->interleave * cfi->device_type; 11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ofs & (mtd->erasesize - 1)) 11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (len & (mtd->erasesize -1)) 11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((len + ofs) > mtd->size) 11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chipnum = ofs >> cfi->chipshift; 11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr = ofs - (chipnum << cfi->chipshift); 11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while(len) { 11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_LOCK_BITS 11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("before lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); 11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = do_lock_oneblock(map, &cfi->chips[chipnum], adr); 11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_LOCK_BITS 11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("after lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); 11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 11731f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner#endif 11741f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) 11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr += mtd->erasesize; 11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= mtd->erasesize; 11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (adr >> cfi->chipshift) { 11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr = 0; 11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chipnum++; 11841f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (chipnum >= cfi->numchips) 11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) 11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_word status, status_OK; 11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long timeo = jiffies + HZ; 11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DECLARE_WAITQUEUE(wait, current); 11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr += chip->start; 11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Let's determine this according to the interleave only once */ 12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status_OK = CMD(0x80); 12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsretry: 12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check that the chip's ready to talk to us. */ 12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (chip->state) { 12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_CFI_QUERY: 12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_JEDEC_QUERY: 12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_READY: 12121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 12141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_STATUS: 12161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, adr); 12171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) 12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 12191f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Urgh. Chip not yet ready to talk to us. */ 12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 12231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "waiting for chip to be ready timed out in unlock\n"); 12241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 12251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Latency issues. Drop the lock, wait a while and retry */ 12281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 12311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 12331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Stick ourselves on a wait queue to be woken when 12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds someone changes the status */ 12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds set_current_state(TASK_UNINTERRUPTIBLE); 12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_wait_queue(&chip->wq, &wait); 12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds schedule(); 12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds remove_wait_queue(&chip->wq, &wait); 12401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + HZ; 12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto retry; 12421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ENABLE_VPP(map); 12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x60), adr); 12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0xD0), adr); 12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_UNLOCKING; 12481f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds msleep(1000); 12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME. Use a timer to check this, and return immediately. */ 12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Once the state machine's known to be working I'll do that */ 12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timeo = jiffies + (HZ*2); 12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) { 12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds status = map_read(map, adr); 12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_word_andequal(map, status, status_OK, status_OK)) 12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 12621f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* OK Still waiting */ 12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (time_after(jiffies, timeo)) { 12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0x70), adr); 12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); 12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DISABLE_VPP(map); 12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EIO; 12711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12721f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Latency issues. Drop the unlock, wait a while and retry */ 12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_udelay(1); 12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 12781f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Done and happy. */ 12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_STATUS; 12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DISABLE_VPP(map); 12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&chip->wq); 12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) 12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct map_info *map = mtd->priv; 12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long adr; 12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int chipnum, ret = 0; 12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_LOCK_BITS 12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ofs_factor = cfi->interleave * cfi->device_type; 12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chipnum = ofs >> cfi->chipshift; 12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds adr = ofs - (chipnum << cfi->chipshift); 12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_LOCK_BITS 13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { 13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long temp_adr = adr; 13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long temp_len = len; 13031f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (temp_len) { 13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("before unlock %x: block status register is %x\n",temp_adr,cfi_read_query(map, temp_adr+(2*ofs_factor))); 13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds temp_adr += mtd->erasesize; 13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds temp_len -= mtd->erasesize; 13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = do_unlock_oneblock(map, &cfi->chips[chipnum], adr); 13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_LOCK_BITS 13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("after unlock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); 13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 13211f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int cfi_staa_suspend(struct mtd_info *mtd) 13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct map_info *map = mtd->priv; 13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct flchip *chip; 13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=0; !ret && i<cfi->numchips; i++) { 13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip = &cfi->chips[i]; 13351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch(chip->state) { 13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_READY: 13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_STATUS: 13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_CFI_QUERY: 13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_JEDEC_QUERY: 13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->oldstate = chip->state; 13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_PM_SUSPENDED; 13451f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner /* No need to wake_up() on this state change - 13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as the whole point is that nobody can do anything 13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * with the chip now anyway. 13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case FL_PM_SUSPENDED: 13501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 13511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 13531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EAGAIN; 13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 13551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 13571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Unlock the chips again */ 13601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) { 13621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i--; i >=0; i--) { 13631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip = &cfi->chips[i]; 13641f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 13661f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (chip->state == FL_PM_SUSPENDED) { 13681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* No need to force it into a known state here, 13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds because we're returning failure, and it didn't 13701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds get power cycled */ 13711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = chip->oldstate; 13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&chip->wq); 13731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 13761f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner } 13771f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 13781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 13791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cfi_staa_resume(struct mtd_info *mtd) 13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 13831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct map_info *map = mtd->priv; 13841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 13851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct flchip *chip; 13871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i=0; i<cfi->numchips; i++) { 13891f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip = &cfi->chips[i]; 13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_lock_bh(chip->mutex); 13931f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner 13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Go to known state. Chip may have been power cycled */ 13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (chip->state == FL_PM_SUSPENDED) { 13961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_write(map, CMD(0xFF), 0); 13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds chip->state = FL_READY; 13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wake_up(&chip->wq); 13991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds spin_unlock_bh(chip->mutex); 14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 14041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void cfi_staa_destroy(struct mtd_info *mtd) 14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct map_info *map = mtd->priv; 14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 14091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(cfi->cmdset_priv); 14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(cfi); 14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 1414