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