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