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