162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Common Flash Interface support:
462306a36Sopenharmony_ci *   Intel Extended Vendor Command Set (ID 0x0001)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * (C) 2000 Red Hat.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * 10/10/2000	Nicolas Pitre <nico@fluxnic.net>
1062306a36Sopenharmony_ci * 	- completely revamped method functions so they are aware and
1162306a36Sopenharmony_ci * 	  independent of the flash geometry (buswidth, interleave, etc.)
1262306a36Sopenharmony_ci * 	- scalability vs code size is completely set at compile-time
1362306a36Sopenharmony_ci * 	  (see include/linux/mtd/cfi.h for selection)
1462306a36Sopenharmony_ci *	- optimized write buffer method
1562306a36Sopenharmony_ci * 02/05/2002	Christopher Hoover <ch@hpl.hp.com>/<ch@murgatroid.com>
1662306a36Sopenharmony_ci *	- reworked lock/unlock/erase support for var size flash
1762306a36Sopenharmony_ci * 21/03/2007   Rodolfo Giometti <giometti@linux.it>
1862306a36Sopenharmony_ci * 	- auto unlock sectors on resume for auto locking flash on power up
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <linux/types.h>
2362306a36Sopenharmony_ci#include <linux/kernel.h>
2462306a36Sopenharmony_ci#include <linux/sched.h>
2562306a36Sopenharmony_ci#include <asm/io.h>
2662306a36Sopenharmony_ci#include <asm/byteorder.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/errno.h>
2962306a36Sopenharmony_ci#include <linux/slab.h>
3062306a36Sopenharmony_ci#include <linux/delay.h>
3162306a36Sopenharmony_ci#include <linux/interrupt.h>
3262306a36Sopenharmony_ci#include <linux/reboot.h>
3362306a36Sopenharmony_ci#include <linux/bitmap.h>
3462306a36Sopenharmony_ci#include <linux/mtd/xip.h>
3562306a36Sopenharmony_ci#include <linux/mtd/map.h>
3662306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
3762306a36Sopenharmony_ci#include <linux/mtd/cfi.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */
4062306a36Sopenharmony_ci/* #define CMDSET0001_DISABLE_WRITE_SUSPEND */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci// debugging, turns off buffer write mode if set to 1
4362306a36Sopenharmony_ci#define FORCE_WORD_WRITE 0
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Intel chips */
4662306a36Sopenharmony_ci#define I82802AB	0x00ad
4762306a36Sopenharmony_ci#define I82802AC	0x00ac
4862306a36Sopenharmony_ci#define PF38F4476	0x881c
4962306a36Sopenharmony_ci#define M28F00AP30	0x8963
5062306a36Sopenharmony_ci/* STMicroelectronics chips */
5162306a36Sopenharmony_ci#define M50LPW080       0x002F
5262306a36Sopenharmony_ci#define M50FLW080A	0x0080
5362306a36Sopenharmony_ci#define M50FLW080B	0x0081
5462306a36Sopenharmony_ci/* Atmel chips */
5562306a36Sopenharmony_ci#define AT49BV640D	0x02de
5662306a36Sopenharmony_ci#define AT49BV640DT	0x02db
5762306a36Sopenharmony_ci/* Sharp chips */
5862306a36Sopenharmony_ci#define LH28F640BFHE_PTTL90	0x00b0
5962306a36Sopenharmony_ci#define LH28F640BFHE_PBTL90	0x00b1
6062306a36Sopenharmony_ci#define LH28F640BFHE_PTTL70A	0x00b2
6162306a36Sopenharmony_ci#define LH28F640BFHE_PBTL70A	0x00b3
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
6462306a36Sopenharmony_cistatic int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
6562306a36Sopenharmony_cistatic int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
6662306a36Sopenharmony_cistatic int cfi_intelext_writev(struct mtd_info *, const struct kvec *, unsigned long, loff_t, size_t *);
6762306a36Sopenharmony_cistatic int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);
6862306a36Sopenharmony_cistatic void cfi_intelext_sync (struct mtd_info *);
6962306a36Sopenharmony_cistatic int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
7062306a36Sopenharmony_cistatic int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
7162306a36Sopenharmony_cistatic int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs,
7262306a36Sopenharmony_ci				  uint64_t len);
7362306a36Sopenharmony_ci#ifdef CONFIG_MTD_OTP
7462306a36Sopenharmony_cistatic int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
7562306a36Sopenharmony_cistatic int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
7662306a36Sopenharmony_cistatic int cfi_intelext_write_user_prot_reg(struct mtd_info *, loff_t, size_t,
7762306a36Sopenharmony_ci					    size_t *, const u_char *);
7862306a36Sopenharmony_cistatic int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);
7962306a36Sopenharmony_cistatic int cfi_intelext_get_fact_prot_info(struct mtd_info *, size_t,
8062306a36Sopenharmony_ci					   size_t *, struct otp_info *);
8162306a36Sopenharmony_cistatic int cfi_intelext_get_user_prot_info(struct mtd_info *, size_t,
8262306a36Sopenharmony_ci					   size_t *, struct otp_info *);
8362306a36Sopenharmony_ci#endif
8462306a36Sopenharmony_cistatic int cfi_intelext_suspend (struct mtd_info *);
8562306a36Sopenharmony_cistatic void cfi_intelext_resume (struct mtd_info *);
8662306a36Sopenharmony_cistatic int cfi_intelext_reboot (struct notifier_block *, unsigned long, void *);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void cfi_intelext_destroy(struct mtd_info *);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0001(struct map_info *, int);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic struct mtd_info *cfi_intelext_setup (struct mtd_info *);
9362306a36Sopenharmony_cistatic int cfi_intelext_partition_fixup(struct mtd_info *, struct cfi_private **);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
9662306a36Sopenharmony_ci		     size_t *retlen, void **virt, resource_size_t *phys);
9762306a36Sopenharmony_cistatic int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
10062306a36Sopenharmony_cistatic int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
10162306a36Sopenharmony_cistatic void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
10262306a36Sopenharmony_ci#include "fwh_lock.h"
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/*
10762306a36Sopenharmony_ci *  *********** SETUP AND PROBE BITS  ***********
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic struct mtd_chip_driver cfi_intelext_chipdrv = {
11162306a36Sopenharmony_ci	.probe		= NULL, /* Not usable directly */
11262306a36Sopenharmony_ci	.destroy	= cfi_intelext_destroy,
11362306a36Sopenharmony_ci	.name		= "cfi_cmdset_0001",
11462306a36Sopenharmony_ci	.module		= THIS_MODULE
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/* #define DEBUG_LOCK_BITS */
11862306a36Sopenharmony_ci/* #define DEBUG_CFI_FEATURES */
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES
12162306a36Sopenharmony_cistatic void cfi_tell_features(struct cfi_pri_intelext *extp)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	int i;
12462306a36Sopenharmony_ci	printk("  Extended Query version %c.%c\n", extp->MajorVersion, extp->MinorVersion);
12562306a36Sopenharmony_ci	printk("  Feature/Command Support:      %4.4X\n", extp->FeatureSupport);
12662306a36Sopenharmony_ci	printk("     - Chip Erase:              %s\n", extp->FeatureSupport&1?"supported":"unsupported");
12762306a36Sopenharmony_ci	printk("     - Suspend Erase:           %s\n", extp->FeatureSupport&2?"supported":"unsupported");
12862306a36Sopenharmony_ci	printk("     - Suspend Program:         %s\n", extp->FeatureSupport&4?"supported":"unsupported");
12962306a36Sopenharmony_ci	printk("     - Legacy Lock/Unlock:      %s\n", extp->FeatureSupport&8?"supported":"unsupported");
13062306a36Sopenharmony_ci	printk("     - Queued Erase:            %s\n", extp->FeatureSupport&16?"supported":"unsupported");
13162306a36Sopenharmony_ci	printk("     - Instant block lock:      %s\n", extp->FeatureSupport&32?"supported":"unsupported");
13262306a36Sopenharmony_ci	printk("     - Protection Bits:         %s\n", extp->FeatureSupport&64?"supported":"unsupported");
13362306a36Sopenharmony_ci	printk("     - Page-mode read:          %s\n", extp->FeatureSupport&128?"supported":"unsupported");
13462306a36Sopenharmony_ci	printk("     - Synchronous read:        %s\n", extp->FeatureSupport&256?"supported":"unsupported");
13562306a36Sopenharmony_ci	printk("     - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported");
13662306a36Sopenharmony_ci	printk("     - Extended Flash Array:    %s\n", extp->FeatureSupport&1024?"supported":"unsupported");
13762306a36Sopenharmony_ci	for (i=11; i<32; i++) {
13862306a36Sopenharmony_ci		if (extp->FeatureSupport & (1<<i))
13962306a36Sopenharmony_ci			printk("     - Unknown Bit %X:      supported\n", i);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	printk("  Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport);
14362306a36Sopenharmony_ci	printk("     - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported");
14462306a36Sopenharmony_ci	for (i=1; i<8; i++) {
14562306a36Sopenharmony_ci		if (extp->SuspendCmdSupport & (1<<i))
14662306a36Sopenharmony_ci			printk("     - Unknown Bit %X:               supported\n", i);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	printk("  Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask);
15062306a36Sopenharmony_ci	printk("     - Lock Bit Active:      %s\n", extp->BlkStatusRegMask&1?"yes":"no");
15162306a36Sopenharmony_ci	printk("     - Lock-Down Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no");
15262306a36Sopenharmony_ci	for (i=2; i<3; i++) {
15362306a36Sopenharmony_ci		if (extp->BlkStatusRegMask & (1<<i))
15462306a36Sopenharmony_ci			printk("     - Unknown Bit %X Active: yes\n",i);
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	printk("     - EFA Lock Bit:         %s\n", extp->BlkStatusRegMask&16?"yes":"no");
15762306a36Sopenharmony_ci	printk("     - EFA Lock-Down Bit:    %s\n", extp->BlkStatusRegMask&32?"yes":"no");
15862306a36Sopenharmony_ci	for (i=6; i<16; i++) {
15962306a36Sopenharmony_ci		if (extp->BlkStatusRegMask & (1<<i))
16062306a36Sopenharmony_ci			printk("     - Unknown Bit %X Active: yes\n",i);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	printk("  Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n",
16462306a36Sopenharmony_ci	       extp->VccOptimal >> 4, extp->VccOptimal & 0xf);
16562306a36Sopenharmony_ci	if (extp->VppOptimal)
16662306a36Sopenharmony_ci		printk("  Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n",
16762306a36Sopenharmony_ci		       extp->VppOptimal >> 4, extp->VppOptimal & 0xf);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci#endif
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/* Atmel chips don't use the same PRI format as Intel chips */
17262306a36Sopenharmony_cistatic void fixup_convert_atmel_pri(struct mtd_info *mtd)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
17562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
17662306a36Sopenharmony_ci	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
17762306a36Sopenharmony_ci	struct cfi_pri_atmel atmel_pri;
17862306a36Sopenharmony_ci	uint32_t features = 0;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Reverse byteswapping */
18162306a36Sopenharmony_ci	extp->FeatureSupport = cpu_to_le32(extp->FeatureSupport);
18262306a36Sopenharmony_ci	extp->BlkStatusRegMask = cpu_to_le16(extp->BlkStatusRegMask);
18362306a36Sopenharmony_ci	extp->ProtRegAddr = cpu_to_le16(extp->ProtRegAddr);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	memcpy(&atmel_pri, extp, sizeof(atmel_pri));
18662306a36Sopenharmony_ci	memset((char *)extp + 5, 0, sizeof(*extp) - 5);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	printk(KERN_ERR "atmel Features: %02x\n", atmel_pri.Features);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (atmel_pri.Features & 0x01) /* chip erase supported */
19162306a36Sopenharmony_ci		features |= (1<<0);
19262306a36Sopenharmony_ci	if (atmel_pri.Features & 0x02) /* erase suspend supported */
19362306a36Sopenharmony_ci		features |= (1<<1);
19462306a36Sopenharmony_ci	if (atmel_pri.Features & 0x04) /* program suspend supported */
19562306a36Sopenharmony_ci		features |= (1<<2);
19662306a36Sopenharmony_ci	if (atmel_pri.Features & 0x08) /* simultaneous operations supported */
19762306a36Sopenharmony_ci		features |= (1<<9);
19862306a36Sopenharmony_ci	if (atmel_pri.Features & 0x20) /* page mode read supported */
19962306a36Sopenharmony_ci		features |= (1<<7);
20062306a36Sopenharmony_ci	if (atmel_pri.Features & 0x40) /* queued erase supported */
20162306a36Sopenharmony_ci		features |= (1<<4);
20262306a36Sopenharmony_ci	if (atmel_pri.Features & 0x80) /* Protection bits supported */
20362306a36Sopenharmony_ci		features |= (1<<6);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	extp->FeatureSupport = features;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* burst write mode not supported */
20862306a36Sopenharmony_ci	cfi->cfiq->BufWriteTimeoutTyp = 0;
20962306a36Sopenharmony_ci	cfi->cfiq->BufWriteTimeoutMax = 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void fixup_at49bv640dx_lock(struct mtd_info *mtd)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
21562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
21662306a36Sopenharmony_ci	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	cfip->FeatureSupport |= (1 << 5);
21962306a36Sopenharmony_ci	mtd->flags |= MTD_POWERUP_LOCK;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
22362306a36Sopenharmony_ci/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */
22462306a36Sopenharmony_cistatic void fixup_intel_strataflash(struct mtd_info *mtd)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
22762306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
22862306a36Sopenharmony_ci	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
23162306a36Sopenharmony_ci	                    "erase on write disabled.\n");
23262306a36Sopenharmony_ci	extp->SuspendCmdSupport &= ~1;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci#endif
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND
23762306a36Sopenharmony_cistatic void fixup_no_write_suspend(struct mtd_info *mtd)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
24062306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
24162306a36Sopenharmony_ci	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (cfip && (cfip->FeatureSupport&4)) {
24462306a36Sopenharmony_ci		cfip->FeatureSupport &= ~4;
24562306a36Sopenharmony_ci		printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n");
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci#endif
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic void fixup_st_m28w320ct(struct mtd_info *mtd)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
25362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	cfi->cfiq->BufWriteTimeoutTyp = 0;	/* Not supported */
25662306a36Sopenharmony_ci	cfi->cfiq->BufWriteTimeoutMax = 0;	/* Not supported */
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void fixup_st_m28w320cb(struct mtd_info *mtd)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
26262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* Note this is done after the region info is endian swapped */
26562306a36Sopenharmony_ci	cfi->cfiq->EraseRegionInfo[1] =
26662306a36Sopenharmony_ci		(cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e;
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int is_LH28F640BF(struct cfi_private *cfi)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	/* Sharp LH28F640BF Family */
27262306a36Sopenharmony_ci	if (cfi->mfr == CFI_MFR_SHARP && (
27362306a36Sopenharmony_ci	    cfi->id == LH28F640BFHE_PTTL90 || cfi->id == LH28F640BFHE_PBTL90 ||
27462306a36Sopenharmony_ci	    cfi->id == LH28F640BFHE_PTTL70A || cfi->id == LH28F640BFHE_PBTL70A))
27562306a36Sopenharmony_ci		return 1;
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic void fixup_LH28F640BF(struct mtd_info *mtd)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
28262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
28362306a36Sopenharmony_ci	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* Reset the Partition Configuration Register on LH28F640BF
28662306a36Sopenharmony_ci	 * to a single partition (PCR = 0x000): PCR is embedded into A0-A15. */
28762306a36Sopenharmony_ci	if (is_LH28F640BF(cfi)) {
28862306a36Sopenharmony_ci		printk(KERN_INFO "Reset Partition Config. Register: 1 Partition of 4 planes\n");
28962306a36Sopenharmony_ci		map_write(map, CMD(0x60), 0);
29062306a36Sopenharmony_ci		map_write(map, CMD(0x04), 0);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		/* We have set one single partition thus
29362306a36Sopenharmony_ci		 * Simultaneous Operations are not allowed */
29462306a36Sopenharmony_ci		printk(KERN_INFO "cfi_cmdset_0001: Simultaneous Operations disabled\n");
29562306a36Sopenharmony_ci		extp->FeatureSupport &= ~512;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void fixup_use_point(struct mtd_info *mtd)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
30262306a36Sopenharmony_ci	if (!mtd->_point && map_is_linear(map)) {
30362306a36Sopenharmony_ci		mtd->_point   = cfi_intelext_point;
30462306a36Sopenharmony_ci		mtd->_unpoint = cfi_intelext_unpoint;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void fixup_use_write_buffers(struct mtd_info *mtd)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
31162306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
31262306a36Sopenharmony_ci	if (cfi->cfiq->BufWriteTimeoutTyp) {
31362306a36Sopenharmony_ci		printk(KERN_INFO "Using buffer write method\n" );
31462306a36Sopenharmony_ci		mtd->_write = cfi_intelext_write_buffers;
31562306a36Sopenharmony_ci		mtd->_writev = cfi_intelext_writev;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci/*
32062306a36Sopenharmony_ci * Some chips power-up with all sectors locked by default.
32162306a36Sopenharmony_ci */
32262306a36Sopenharmony_cistatic void fixup_unlock_powerup_lock(struct mtd_info *mtd)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
32562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
32662306a36Sopenharmony_ci	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (cfip->FeatureSupport&32) {
32962306a36Sopenharmony_ci		printk(KERN_INFO "Using auto-unlock on power-up/resume\n" );
33062306a36Sopenharmony_ci		mtd->flags |= MTD_POWERUP_LOCK;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic struct cfi_fixup cfi_fixup_table[] = {
33562306a36Sopenharmony_ci	{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri },
33662306a36Sopenharmony_ci	{ CFI_MFR_ATMEL, AT49BV640D, fixup_at49bv640dx_lock },
33762306a36Sopenharmony_ci	{ CFI_MFR_ATMEL, AT49BV640DT, fixup_at49bv640dx_lock },
33862306a36Sopenharmony_ci#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
33962306a36Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash },
34062306a36Sopenharmony_ci#endif
34162306a36Sopenharmony_ci#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND
34262306a36Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend },
34362306a36Sopenharmony_ci#endif
34462306a36Sopenharmony_ci#if !FORCE_WORD_WRITE
34562306a36Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers },
34662306a36Sopenharmony_ci#endif
34762306a36Sopenharmony_ci	{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct },
34862306a36Sopenharmony_ci	{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb },
34962306a36Sopenharmony_ci	{ CFI_MFR_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock },
35062306a36Sopenharmony_ci	{ CFI_MFR_SHARP, CFI_ID_ANY, fixup_unlock_powerup_lock },
35162306a36Sopenharmony_ci	{ CFI_MFR_SHARP, CFI_ID_ANY, fixup_LH28F640BF },
35262306a36Sopenharmony_ci	{ 0, 0, NULL }
35362306a36Sopenharmony_ci};
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic struct cfi_fixup jedec_fixup_table[] = {
35662306a36Sopenharmony_ci	{ CFI_MFR_INTEL, I82802AB,   fixup_use_fwh_lock },
35762306a36Sopenharmony_ci	{ CFI_MFR_INTEL, I82802AC,   fixup_use_fwh_lock },
35862306a36Sopenharmony_ci	{ CFI_MFR_ST,    M50LPW080,  fixup_use_fwh_lock },
35962306a36Sopenharmony_ci	{ CFI_MFR_ST,    M50FLW080A, fixup_use_fwh_lock },
36062306a36Sopenharmony_ci	{ CFI_MFR_ST,    M50FLW080B, fixup_use_fwh_lock },
36162306a36Sopenharmony_ci	{ 0, 0, NULL }
36262306a36Sopenharmony_ci};
36362306a36Sopenharmony_cistatic struct cfi_fixup fixup_table[] = {
36462306a36Sopenharmony_ci	/* The CFI vendor ids and the JEDEC vendor IDs appear
36562306a36Sopenharmony_ci	 * to be common.  It is like the devices id's are as
36662306a36Sopenharmony_ci	 * well.  This table is to pick all cases where
36762306a36Sopenharmony_ci	 * we know that is the case.
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point },
37062306a36Sopenharmony_ci	{ 0, 0, NULL }
37162306a36Sopenharmony_ci};
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic void cfi_fixup_major_minor(struct cfi_private *cfi,
37462306a36Sopenharmony_ci						struct cfi_pri_intelext *extp)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	if (cfi->mfr == CFI_MFR_INTEL &&
37762306a36Sopenharmony_ci			cfi->id == PF38F4476 && extp->MinorVersion == '3')
37862306a36Sopenharmony_ci		extp->MinorVersion = '1';
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic int cfi_is_micron_28F00AP30(struct cfi_private *cfi, struct flchip *chip)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	/*
38462306a36Sopenharmony_ci	 * Micron(was Numonyx) 1Gbit bottom boot are buggy w.r.t
38562306a36Sopenharmony_ci	 * Erase Supend for their small Erase Blocks(0x8000)
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	if (cfi->mfr == CFI_MFR_INTEL && cfi->id == M28F00AP30)
38862306a36Sopenharmony_ci		return 1;
38962306a36Sopenharmony_ci	return 0;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic inline struct cfi_pri_intelext *
39362306a36Sopenharmony_ciread_pri_intelext(struct map_info *map, __u16 adr)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
39662306a36Sopenharmony_ci	struct cfi_pri_intelext *extp;
39762306a36Sopenharmony_ci	unsigned int extra_size = 0;
39862306a36Sopenharmony_ci	unsigned int extp_size = sizeof(*extp);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci again:
40162306a36Sopenharmony_ci	extp = (struct cfi_pri_intelext *)cfi_read_pri(map, adr, extp_size, "Intel/Sharp");
40262306a36Sopenharmony_ci	if (!extp)
40362306a36Sopenharmony_ci		return NULL;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	cfi_fixup_major_minor(cfi, extp);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (extp->MajorVersion != '1' ||
40862306a36Sopenharmony_ci	    (extp->MinorVersion < '0' || extp->MinorVersion > '5')) {
40962306a36Sopenharmony_ci		printk(KERN_ERR "  Unknown Intel/Sharp Extended Query "
41062306a36Sopenharmony_ci		       "version %c.%c.\n",  extp->MajorVersion,
41162306a36Sopenharmony_ci		       extp->MinorVersion);
41262306a36Sopenharmony_ci		kfree(extp);
41362306a36Sopenharmony_ci		return NULL;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* Do some byteswapping if necessary */
41762306a36Sopenharmony_ci	extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport);
41862306a36Sopenharmony_ci	extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask);
41962306a36Sopenharmony_ci	extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if (extp->MinorVersion >= '0') {
42262306a36Sopenharmony_ci		extra_size = 0;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		/* Protection Register info */
42562306a36Sopenharmony_ci		if (extp->NumProtectionFields) {
42662306a36Sopenharmony_ci			struct cfi_intelext_otpinfo *otp =
42762306a36Sopenharmony_ci				(struct cfi_intelext_otpinfo *)&extp->extra[0];
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci			extra_size += (extp->NumProtectionFields - 1) *
43062306a36Sopenharmony_ci				sizeof(struct cfi_intelext_otpinfo);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci			if (extp_size >= sizeof(*extp) + extra_size) {
43362306a36Sopenharmony_ci				int i;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci				/* Do some byteswapping if necessary */
43662306a36Sopenharmony_ci				for (i = 0; i < extp->NumProtectionFields - 1; i++) {
43762306a36Sopenharmony_ci					otp->ProtRegAddr = le32_to_cpu(otp->ProtRegAddr);
43862306a36Sopenharmony_ci					otp->FactGroups = le16_to_cpu(otp->FactGroups);
43962306a36Sopenharmony_ci					otp->UserGroups = le16_to_cpu(otp->UserGroups);
44062306a36Sopenharmony_ci					otp++;
44162306a36Sopenharmony_ci				}
44262306a36Sopenharmony_ci			}
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (extp->MinorVersion >= '1') {
44762306a36Sopenharmony_ci		/* Burst Read info */
44862306a36Sopenharmony_ci		extra_size += 2;
44962306a36Sopenharmony_ci		if (extp_size < sizeof(*extp) + extra_size)
45062306a36Sopenharmony_ci			goto need_more;
45162306a36Sopenharmony_ci		extra_size += extp->extra[extra_size - 1];
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (extp->MinorVersion >= '3') {
45562306a36Sopenharmony_ci		int nb_parts, i;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		/* Number of hardware-partitions */
45862306a36Sopenharmony_ci		extra_size += 1;
45962306a36Sopenharmony_ci		if (extp_size < sizeof(*extp) + extra_size)
46062306a36Sopenharmony_ci			goto need_more;
46162306a36Sopenharmony_ci		nb_parts = extp->extra[extra_size - 1];
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		/* skip the sizeof(partregion) field in CFI 1.4 */
46462306a36Sopenharmony_ci		if (extp->MinorVersion >= '4')
46562306a36Sopenharmony_ci			extra_size += 2;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		for (i = 0; i < nb_parts; i++) {
46862306a36Sopenharmony_ci			struct cfi_intelext_regioninfo *rinfo;
46962306a36Sopenharmony_ci			rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[extra_size];
47062306a36Sopenharmony_ci			extra_size += sizeof(*rinfo);
47162306a36Sopenharmony_ci			if (extp_size < sizeof(*extp) + extra_size)
47262306a36Sopenharmony_ci				goto need_more;
47362306a36Sopenharmony_ci			rinfo->NumIdentPartitions=le16_to_cpu(rinfo->NumIdentPartitions);
47462306a36Sopenharmony_ci			extra_size += (rinfo->NumBlockTypes - 1)
47562306a36Sopenharmony_ci				      * sizeof(struct cfi_intelext_blockinfo);
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		if (extp->MinorVersion >= '4')
47962306a36Sopenharmony_ci			extra_size += sizeof(struct cfi_intelext_programming_regioninfo);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		if (extp_size < sizeof(*extp) + extra_size) {
48262306a36Sopenharmony_ci			need_more:
48362306a36Sopenharmony_ci			extp_size = sizeof(*extp) + extra_size;
48462306a36Sopenharmony_ci			kfree(extp);
48562306a36Sopenharmony_ci			if (extp_size > 4096) {
48662306a36Sopenharmony_ci				printk(KERN_ERR
48762306a36Sopenharmony_ci					"%s: cfi_pri_intelext is too fat\n",
48862306a36Sopenharmony_ci					__func__);
48962306a36Sopenharmony_ci				return NULL;
49062306a36Sopenharmony_ci			}
49162306a36Sopenharmony_ci			goto again;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	return extp;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
50162306a36Sopenharmony_ci	struct mtd_info *mtd;
50262306a36Sopenharmony_ci	int i;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
50562306a36Sopenharmony_ci	if (!mtd)
50662306a36Sopenharmony_ci		return NULL;
50762306a36Sopenharmony_ci	mtd->priv = map;
50862306a36Sopenharmony_ci	mtd->type = MTD_NORFLASH;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/* Fill in the default mtd operations */
51162306a36Sopenharmony_ci	mtd->_erase   = cfi_intelext_erase_varsize;
51262306a36Sopenharmony_ci	mtd->_read    = cfi_intelext_read;
51362306a36Sopenharmony_ci	mtd->_write   = cfi_intelext_write_words;
51462306a36Sopenharmony_ci	mtd->_sync    = cfi_intelext_sync;
51562306a36Sopenharmony_ci	mtd->_lock    = cfi_intelext_lock;
51662306a36Sopenharmony_ci	mtd->_unlock  = cfi_intelext_unlock;
51762306a36Sopenharmony_ci	mtd->_is_locked = cfi_intelext_is_locked;
51862306a36Sopenharmony_ci	mtd->_suspend = cfi_intelext_suspend;
51962306a36Sopenharmony_ci	mtd->_resume  = cfi_intelext_resume;
52062306a36Sopenharmony_ci	mtd->flags   = MTD_CAP_NORFLASH;
52162306a36Sopenharmony_ci	mtd->name    = map->name;
52262306a36Sopenharmony_ci	mtd->writesize = 1;
52362306a36Sopenharmony_ci	mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	mtd->reboot_notifier.notifier_call = cfi_intelext_reboot;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (cfi->cfi_mode == CFI_MODE_CFI) {
52862306a36Sopenharmony_ci		/*
52962306a36Sopenharmony_ci		 * It's a real CFI chip, not one for which the probe
53062306a36Sopenharmony_ci		 * routine faked a CFI structure. So we read the feature
53162306a36Sopenharmony_ci		 * table from it.
53262306a36Sopenharmony_ci		 */
53362306a36Sopenharmony_ci		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
53462306a36Sopenharmony_ci		struct cfi_pri_intelext *extp;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		extp = read_pri_intelext(map, adr);
53762306a36Sopenharmony_ci		if (!extp) {
53862306a36Sopenharmony_ci			kfree(mtd);
53962306a36Sopenharmony_ci			return NULL;
54062306a36Sopenharmony_ci		}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		/* Install our own private info structure */
54362306a36Sopenharmony_ci		cfi->cmdset_priv = extp;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		cfi_fixup(mtd, cfi_fixup_table);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES
54862306a36Sopenharmony_ci		/* Tell the user about it in lots of lovely detail */
54962306a36Sopenharmony_ci		cfi_tell_features(extp);
55062306a36Sopenharmony_ci#endif
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		if(extp->SuspendCmdSupport & 1) {
55362306a36Sopenharmony_ci			printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n");
55462306a36Sopenharmony_ci		}
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci	else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
55762306a36Sopenharmony_ci		/* Apply jedec specific fixups */
55862306a36Sopenharmony_ci		cfi_fixup(mtd, jedec_fixup_table);
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci	/* Apply generic fixups */
56162306a36Sopenharmony_ci	cfi_fixup(mtd, fixup_table);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	for (i=0; i< cfi->numchips; i++) {
56462306a36Sopenharmony_ci		if (cfi->cfiq->WordWriteTimeoutTyp)
56562306a36Sopenharmony_ci			cfi->chips[i].word_write_time =
56662306a36Sopenharmony_ci				1<<cfi->cfiq->WordWriteTimeoutTyp;
56762306a36Sopenharmony_ci		else
56862306a36Sopenharmony_ci			cfi->chips[i].word_write_time = 50000;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		if (cfi->cfiq->BufWriteTimeoutTyp)
57162306a36Sopenharmony_ci			cfi->chips[i].buffer_write_time =
57262306a36Sopenharmony_ci				1<<cfi->cfiq->BufWriteTimeoutTyp;
57362306a36Sopenharmony_ci		/* No default; if it isn't specified, we won't use it */
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		if (cfi->cfiq->BlockEraseTimeoutTyp)
57662306a36Sopenharmony_ci			cfi->chips[i].erase_time =
57762306a36Sopenharmony_ci				1000<<cfi->cfiq->BlockEraseTimeoutTyp;
57862306a36Sopenharmony_ci		else
57962306a36Sopenharmony_ci			cfi->chips[i].erase_time = 2000000;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		if (cfi->cfiq->WordWriteTimeoutTyp &&
58262306a36Sopenharmony_ci		    cfi->cfiq->WordWriteTimeoutMax)
58362306a36Sopenharmony_ci			cfi->chips[i].word_write_time_max =
58462306a36Sopenharmony_ci				1<<(cfi->cfiq->WordWriteTimeoutTyp +
58562306a36Sopenharmony_ci				    cfi->cfiq->WordWriteTimeoutMax);
58662306a36Sopenharmony_ci		else
58762306a36Sopenharmony_ci			cfi->chips[i].word_write_time_max = 50000 * 8;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		if (cfi->cfiq->BufWriteTimeoutTyp &&
59062306a36Sopenharmony_ci		    cfi->cfiq->BufWriteTimeoutMax)
59162306a36Sopenharmony_ci			cfi->chips[i].buffer_write_time_max =
59262306a36Sopenharmony_ci				1<<(cfi->cfiq->BufWriteTimeoutTyp +
59362306a36Sopenharmony_ci				    cfi->cfiq->BufWriteTimeoutMax);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		if (cfi->cfiq->BlockEraseTimeoutTyp &&
59662306a36Sopenharmony_ci		    cfi->cfiq->BlockEraseTimeoutMax)
59762306a36Sopenharmony_ci			cfi->chips[i].erase_time_max =
59862306a36Sopenharmony_ci				1000<<(cfi->cfiq->BlockEraseTimeoutTyp +
59962306a36Sopenharmony_ci				       cfi->cfiq->BlockEraseTimeoutMax);
60062306a36Sopenharmony_ci		else
60162306a36Sopenharmony_ci			cfi->chips[i].erase_time_max = 2000000 * 8;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci		cfi->chips[i].ref_point_counter = 0;
60462306a36Sopenharmony_ci		init_waitqueue_head(&(cfi->chips[i].wq));
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	map->fldrv = &cfi_intelext_chipdrv;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return cfi_intelext_setup(mtd);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0003(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001")));
61262306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0200(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001")));
61362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0001);
61462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0003);
61562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0200);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
62062306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
62162306a36Sopenharmony_ci	unsigned long offset = 0;
62262306a36Sopenharmony_ci	int i,j;
62362306a36Sopenharmony_ci	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	mtd->size = devsize * cfi->numchips;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
63062306a36Sopenharmony_ci	mtd->eraseregions = kcalloc(mtd->numeraseregions,
63162306a36Sopenharmony_ci				    sizeof(struct mtd_erase_region_info),
63262306a36Sopenharmony_ci				    GFP_KERNEL);
63362306a36Sopenharmony_ci	if (!mtd->eraseregions)
63462306a36Sopenharmony_ci		goto setup_err;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
63762306a36Sopenharmony_ci		unsigned long ernum, ersize;
63862306a36Sopenharmony_ci		ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
63962306a36Sopenharmony_ci		ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		if (mtd->erasesize < ersize) {
64262306a36Sopenharmony_ci			mtd->erasesize = ersize;
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci		for (j=0; j<cfi->numchips; j++) {
64562306a36Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
64662306a36Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
64762306a36Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
64862306a36Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap = kmalloc(ernum / 8 + 1, GFP_KERNEL);
64962306a36Sopenharmony_ci			if (!mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap)
65062306a36Sopenharmony_ci				goto setup_err;
65162306a36Sopenharmony_ci		}
65262306a36Sopenharmony_ci		offset += (ersize * ernum);
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (offset != devsize) {
65662306a36Sopenharmony_ci		/* Argh */
65762306a36Sopenharmony_ci		printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
65862306a36Sopenharmony_ci		goto setup_err;
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	for (i=0; i<mtd->numeraseregions;i++){
66262306a36Sopenharmony_ci		printk(KERN_DEBUG "erase region %d: offset=0x%llx,size=0x%x,blocks=%d\n",
66362306a36Sopenharmony_ci		       i,(unsigned long long)mtd->eraseregions[i].offset,
66462306a36Sopenharmony_ci		       mtd->eraseregions[i].erasesize,
66562306a36Sopenharmony_ci		       mtd->eraseregions[i].numblocks);
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci#ifdef CONFIG_MTD_OTP
66962306a36Sopenharmony_ci	mtd->_read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
67062306a36Sopenharmony_ci	mtd->_read_user_prot_reg = cfi_intelext_read_user_prot_reg;
67162306a36Sopenharmony_ci	mtd->_write_user_prot_reg = cfi_intelext_write_user_prot_reg;
67262306a36Sopenharmony_ci	mtd->_lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
67362306a36Sopenharmony_ci	mtd->_get_fact_prot_info = cfi_intelext_get_fact_prot_info;
67462306a36Sopenharmony_ci	mtd->_get_user_prot_info = cfi_intelext_get_user_prot_info;
67562306a36Sopenharmony_ci#endif
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* This function has the potential to distort the reality
67862306a36Sopenharmony_ci	   a bit and therefore should be called last. */
67962306a36Sopenharmony_ci	if (cfi_intelext_partition_fixup(mtd, &cfi) != 0)
68062306a36Sopenharmony_ci		goto setup_err;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	__module_get(THIS_MODULE);
68362306a36Sopenharmony_ci	register_reboot_notifier(&mtd->reboot_notifier);
68462306a36Sopenharmony_ci	return mtd;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci setup_err:
68762306a36Sopenharmony_ci	if (mtd->eraseregions)
68862306a36Sopenharmony_ci		for (i=0; i<cfi->cfiq->NumEraseRegions; i++)
68962306a36Sopenharmony_ci			for (j=0; j<cfi->numchips; j++)
69062306a36Sopenharmony_ci				kfree(mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap);
69162306a36Sopenharmony_ci	kfree(mtd->eraseregions);
69262306a36Sopenharmony_ci	kfree(mtd);
69362306a36Sopenharmony_ci	kfree(cfi->cmdset_priv);
69462306a36Sopenharmony_ci	return NULL;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic int cfi_intelext_partition_fixup(struct mtd_info *mtd,
69862306a36Sopenharmony_ci					struct cfi_private **pcfi)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
70162306a36Sopenharmony_ci	struct cfi_private *cfi = *pcfi;
70262306a36Sopenharmony_ci	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/*
70562306a36Sopenharmony_ci	 * Probing of multi-partition flash chips.
70662306a36Sopenharmony_ci	 *
70762306a36Sopenharmony_ci	 * To support multiple partitions when available, we simply arrange
70862306a36Sopenharmony_ci	 * for each of them to have their own flchip structure even if they
70962306a36Sopenharmony_ci	 * are on the same physical chip.  This means completely recreating
71062306a36Sopenharmony_ci	 * a new cfi_private structure right here which is a blatent code
71162306a36Sopenharmony_ci	 * layering violation, but this is still the least intrusive
71262306a36Sopenharmony_ci	 * arrangement at this point. This can be rearranged in the future
71362306a36Sopenharmony_ci	 * if someone feels motivated enough.  --nico
71462306a36Sopenharmony_ci	 */
71562306a36Sopenharmony_ci	if (extp && extp->MajorVersion == '1' && extp->MinorVersion >= '3'
71662306a36Sopenharmony_ci	    && extp->FeatureSupport & (1 << 9)) {
71762306a36Sopenharmony_ci		int offs = 0;
71862306a36Sopenharmony_ci		struct cfi_private *newcfi;
71962306a36Sopenharmony_ci		struct flchip *chip;
72062306a36Sopenharmony_ci		struct flchip_shared *shared;
72162306a36Sopenharmony_ci		int numregions, numparts, partshift, numvirtchips, i, j;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		/* Protection Register info */
72462306a36Sopenharmony_ci		if (extp->NumProtectionFields)
72562306a36Sopenharmony_ci			offs = (extp->NumProtectionFields - 1) *
72662306a36Sopenharmony_ci			       sizeof(struct cfi_intelext_otpinfo);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		/* Burst Read info */
72962306a36Sopenharmony_ci		offs += extp->extra[offs+1]+2;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		/* Number of partition regions */
73262306a36Sopenharmony_ci		numregions = extp->extra[offs];
73362306a36Sopenharmony_ci		offs += 1;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		/* skip the sizeof(partregion) field in CFI 1.4 */
73662306a36Sopenharmony_ci		if (extp->MinorVersion >= '4')
73762306a36Sopenharmony_ci			offs += 2;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		/* Number of hardware partitions */
74062306a36Sopenharmony_ci		numparts = 0;
74162306a36Sopenharmony_ci		for (i = 0; i < numregions; i++) {
74262306a36Sopenharmony_ci			struct cfi_intelext_regioninfo *rinfo;
74362306a36Sopenharmony_ci			rinfo = (struct cfi_intelext_regioninfo *)&extp->extra[offs];
74462306a36Sopenharmony_ci			numparts += rinfo->NumIdentPartitions;
74562306a36Sopenharmony_ci			offs += sizeof(*rinfo)
74662306a36Sopenharmony_ci				+ (rinfo->NumBlockTypes - 1) *
74762306a36Sopenharmony_ci				  sizeof(struct cfi_intelext_blockinfo);
74862306a36Sopenharmony_ci		}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		if (!numparts)
75162306a36Sopenharmony_ci			numparts = 1;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci		/* Programming Region info */
75462306a36Sopenharmony_ci		if (extp->MinorVersion >= '4') {
75562306a36Sopenharmony_ci			struct cfi_intelext_programming_regioninfo *prinfo;
75662306a36Sopenharmony_ci			prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs];
75762306a36Sopenharmony_ci			mtd->writesize = cfi->interleave << prinfo->ProgRegShift;
75862306a36Sopenharmony_ci			mtd->flags &= ~MTD_BIT_WRITEABLE;
75962306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n",
76062306a36Sopenharmony_ci			       map->name, mtd->writesize,
76162306a36Sopenharmony_ci			       cfi->interleave * prinfo->ControlValid,
76262306a36Sopenharmony_ci			       cfi->interleave * prinfo->ControlInvalid);
76362306a36Sopenharmony_ci		}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci		/*
76662306a36Sopenharmony_ci		 * All functions below currently rely on all chips having
76762306a36Sopenharmony_ci		 * the same geometry so we'll just assume that all hardware
76862306a36Sopenharmony_ci		 * partitions are of the same size too.
76962306a36Sopenharmony_ci		 */
77062306a36Sopenharmony_ci		partshift = cfi->chipshift - __ffs(numparts);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		if ((1 << partshift) < mtd->erasesize) {
77362306a36Sopenharmony_ci			printk( KERN_ERR
77462306a36Sopenharmony_ci				"%s: bad number of hw partitions (%d)\n",
77562306a36Sopenharmony_ci				__func__, numparts);
77662306a36Sopenharmony_ci			return -EINVAL;
77762306a36Sopenharmony_ci		}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		numvirtchips = cfi->numchips * numparts;
78062306a36Sopenharmony_ci		newcfi = kmalloc(struct_size(newcfi, chips, numvirtchips),
78162306a36Sopenharmony_ci				 GFP_KERNEL);
78262306a36Sopenharmony_ci		if (!newcfi)
78362306a36Sopenharmony_ci			return -ENOMEM;
78462306a36Sopenharmony_ci		shared = kmalloc_array(cfi->numchips,
78562306a36Sopenharmony_ci				       sizeof(struct flchip_shared),
78662306a36Sopenharmony_ci				       GFP_KERNEL);
78762306a36Sopenharmony_ci		if (!shared) {
78862306a36Sopenharmony_ci			kfree(newcfi);
78962306a36Sopenharmony_ci			return -ENOMEM;
79062306a36Sopenharmony_ci		}
79162306a36Sopenharmony_ci		memcpy(newcfi, cfi, sizeof(struct cfi_private));
79262306a36Sopenharmony_ci		newcfi->numchips = numvirtchips;
79362306a36Sopenharmony_ci		newcfi->chipshift = partshift;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		chip = &newcfi->chips[0];
79662306a36Sopenharmony_ci		for (i = 0; i < cfi->numchips; i++) {
79762306a36Sopenharmony_ci			shared[i].writing = shared[i].erasing = NULL;
79862306a36Sopenharmony_ci			mutex_init(&shared[i].lock);
79962306a36Sopenharmony_ci			for (j = 0; j < numparts; j++) {
80062306a36Sopenharmony_ci				*chip = cfi->chips[i];
80162306a36Sopenharmony_ci				chip->start += j << partshift;
80262306a36Sopenharmony_ci				chip->priv = &shared[i];
80362306a36Sopenharmony_ci				/* those should be reset too since
80462306a36Sopenharmony_ci				   they create memory references. */
80562306a36Sopenharmony_ci				init_waitqueue_head(&chip->wq);
80662306a36Sopenharmony_ci				mutex_init(&chip->mutex);
80762306a36Sopenharmony_ci				chip++;
80862306a36Sopenharmony_ci			}
80962306a36Sopenharmony_ci		}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: %d set(s) of %d interleaved chips "
81262306a36Sopenharmony_ci				  "--> %d partitions of %d KiB\n",
81362306a36Sopenharmony_ci				  map->name, cfi->numchips, cfi->interleave,
81462306a36Sopenharmony_ci				  newcfi->numchips, 1<<(newcfi->chipshift-10));
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		map->fldrv_priv = newcfi;
81762306a36Sopenharmony_ci		*pcfi = newcfi;
81862306a36Sopenharmony_ci		kfree(cfi);
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	return 0;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci/*
82562306a36Sopenharmony_ci *  *********** CHIP ACCESS FUNCTIONS ***********
82662306a36Sopenharmony_ci */
82762306a36Sopenharmony_cistatic int chip_ready (struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
83062306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
83162306a36Sopenharmony_ci	map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01);
83262306a36Sopenharmony_ci	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
83362306a36Sopenharmony_ci	unsigned long timeo = jiffies + HZ;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* Prevent setting state FL_SYNCING for chip in suspended state. */
83662306a36Sopenharmony_ci	if (mode == FL_SYNCING && chip->oldstate != FL_READY)
83762306a36Sopenharmony_ci		goto sleep;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	switch (chip->state) {
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	case FL_STATUS:
84262306a36Sopenharmony_ci		for (;;) {
84362306a36Sopenharmony_ci			status = map_read(map, adr);
84462306a36Sopenharmony_ci			if (map_word_andequal(map, status, status_OK, status_OK))
84562306a36Sopenharmony_ci				break;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci			/* At this point we're fine with write operations
84862306a36Sopenharmony_ci			   in other partitions as they don't conflict. */
84962306a36Sopenharmony_ci			if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS))
85062306a36Sopenharmony_ci				break;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
85362306a36Sopenharmony_ci			cfi_udelay(1);
85462306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
85562306a36Sopenharmony_ci			/* Someone else might have been playing with it. */
85662306a36Sopenharmony_ci			return -EAGAIN;
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci		fallthrough;
85962306a36Sopenharmony_ci	case FL_READY:
86062306a36Sopenharmony_ci	case FL_CFI_QUERY:
86162306a36Sopenharmony_ci	case FL_JEDEC_QUERY:
86262306a36Sopenharmony_ci		return 0;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	case FL_ERASING:
86562306a36Sopenharmony_ci		if (!cfip ||
86662306a36Sopenharmony_ci		    !(cfip->FeatureSupport & 2) ||
86762306a36Sopenharmony_ci		    !(mode == FL_READY || mode == FL_POINT ||
86862306a36Sopenharmony_ci		     (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
86962306a36Sopenharmony_ci			goto sleep;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		/* Do not allow suspend iff read/write to EB address */
87262306a36Sopenharmony_ci		if ((adr & chip->in_progress_block_mask) ==
87362306a36Sopenharmony_ci		    chip->in_progress_block_addr)
87462306a36Sopenharmony_ci			goto sleep;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		/* do not suspend small EBs, buggy Micron Chips */
87762306a36Sopenharmony_ci		if (cfi_is_micron_28F00AP30(cfi, chip) &&
87862306a36Sopenharmony_ci		    (chip->in_progress_block_mask == ~(0x8000-1)))
87962306a36Sopenharmony_ci			goto sleep;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		/* Erase suspend */
88262306a36Sopenharmony_ci		map_write(map, CMD(0xB0), chip->in_progress_block_addr);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci		/* If the flash has finished erasing, then 'erase suspend'
88562306a36Sopenharmony_ci		 * appears to make some (28F320) flash devices switch to
88662306a36Sopenharmony_ci		 * 'read' mode.  Make sure that we switch to 'read status'
88762306a36Sopenharmony_ci		 * mode so we get the right data. --rmk
88862306a36Sopenharmony_ci		 */
88962306a36Sopenharmony_ci		map_write(map, CMD(0x70), chip->in_progress_block_addr);
89062306a36Sopenharmony_ci		chip->oldstate = FL_ERASING;
89162306a36Sopenharmony_ci		chip->state = FL_ERASE_SUSPENDING;
89262306a36Sopenharmony_ci		chip->erase_suspended = 1;
89362306a36Sopenharmony_ci		for (;;) {
89462306a36Sopenharmony_ci			status = map_read(map, chip->in_progress_block_addr);
89562306a36Sopenharmony_ci			if (map_word_andequal(map, status, status_OK, status_OK))
89662306a36Sopenharmony_ci			        break;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci			if (time_after(jiffies, timeo)) {
89962306a36Sopenharmony_ci				/* Urgh. Resume and pretend we weren't here.
90062306a36Sopenharmony_ci				 * Make sure we're in 'read status' mode if it had finished */
90162306a36Sopenharmony_ci				put_chip(map, chip, adr);
90262306a36Sopenharmony_ci				printk(KERN_ERR "%s: Chip not ready after erase "
90362306a36Sopenharmony_ci				       "suspended: status = 0x%lx\n", map->name, status.x[0]);
90462306a36Sopenharmony_ci				return -EIO;
90562306a36Sopenharmony_ci			}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
90862306a36Sopenharmony_ci			cfi_udelay(1);
90962306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
91062306a36Sopenharmony_ci			/* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
91162306a36Sopenharmony_ci			   So we can just loop here. */
91262306a36Sopenharmony_ci		}
91362306a36Sopenharmony_ci		chip->state = FL_STATUS;
91462306a36Sopenharmony_ci		return 0;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	case FL_XIP_WHILE_ERASING:
91762306a36Sopenharmony_ci		if (mode != FL_READY && mode != FL_POINT &&
91862306a36Sopenharmony_ci		    (mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1)))
91962306a36Sopenharmony_ci			goto sleep;
92062306a36Sopenharmony_ci		chip->oldstate = chip->state;
92162306a36Sopenharmony_ci		chip->state = FL_READY;
92262306a36Sopenharmony_ci		return 0;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	case FL_SHUTDOWN:
92562306a36Sopenharmony_ci		/* The machine is rebooting now,so no one can get chip anymore */
92662306a36Sopenharmony_ci		return -EIO;
92762306a36Sopenharmony_ci	case FL_POINT:
92862306a36Sopenharmony_ci		/* Only if there's no operation suspended... */
92962306a36Sopenharmony_ci		if (mode == FL_READY && chip->oldstate == FL_READY)
93062306a36Sopenharmony_ci			return 0;
93162306a36Sopenharmony_ci		fallthrough;
93262306a36Sopenharmony_ci	default:
93362306a36Sopenharmony_ci	sleep:
93462306a36Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
93562306a36Sopenharmony_ci		add_wait_queue(&chip->wq, &wait);
93662306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
93762306a36Sopenharmony_ci		schedule();
93862306a36Sopenharmony_ci		remove_wait_queue(&chip->wq, &wait);
93962306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
94062306a36Sopenharmony_ci		return -EAGAIN;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_cistatic int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	int ret;
94762306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci retry:
95062306a36Sopenharmony_ci	if (chip->priv &&
95162306a36Sopenharmony_ci	    (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE
95262306a36Sopenharmony_ci	    || mode == FL_SHUTDOWN) && chip->state != FL_SYNCING) {
95362306a36Sopenharmony_ci		/*
95462306a36Sopenharmony_ci		 * OK. We have possibility for contention on the write/erase
95562306a36Sopenharmony_ci		 * operations which are global to the real chip and not per
95662306a36Sopenharmony_ci		 * partition.  So let's fight it over in the partition which
95762306a36Sopenharmony_ci		 * currently has authority on the operation.
95862306a36Sopenharmony_ci		 *
95962306a36Sopenharmony_ci		 * The rules are as follows:
96062306a36Sopenharmony_ci		 *
96162306a36Sopenharmony_ci		 * - any write operation must own shared->writing.
96262306a36Sopenharmony_ci		 *
96362306a36Sopenharmony_ci		 * - any erase operation must own _both_ shared->writing and
96462306a36Sopenharmony_ci		 *   shared->erasing.
96562306a36Sopenharmony_ci		 *
96662306a36Sopenharmony_ci		 * - contention arbitration is handled in the owner's context.
96762306a36Sopenharmony_ci		 *
96862306a36Sopenharmony_ci		 * The 'shared' struct can be read and/or written only when
96962306a36Sopenharmony_ci		 * its lock is taken.
97062306a36Sopenharmony_ci		 */
97162306a36Sopenharmony_ci		struct flchip_shared *shared = chip->priv;
97262306a36Sopenharmony_ci		struct flchip *contender;
97362306a36Sopenharmony_ci		mutex_lock(&shared->lock);
97462306a36Sopenharmony_ci		contender = shared->writing;
97562306a36Sopenharmony_ci		if (contender && contender != chip) {
97662306a36Sopenharmony_ci			/*
97762306a36Sopenharmony_ci			 * The engine to perform desired operation on this
97862306a36Sopenharmony_ci			 * partition is already in use by someone else.
97962306a36Sopenharmony_ci			 * Let's fight over it in the context of the chip
98062306a36Sopenharmony_ci			 * currently using it.  If it is possible to suspend,
98162306a36Sopenharmony_ci			 * that other partition will do just that, otherwise
98262306a36Sopenharmony_ci			 * it'll happily send us to sleep.  In any case, when
98362306a36Sopenharmony_ci			 * get_chip returns success we're clear to go ahead.
98462306a36Sopenharmony_ci			 */
98562306a36Sopenharmony_ci			ret = mutex_trylock(&contender->mutex);
98662306a36Sopenharmony_ci			mutex_unlock(&shared->lock);
98762306a36Sopenharmony_ci			if (!ret)
98862306a36Sopenharmony_ci				goto retry;
98962306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
99062306a36Sopenharmony_ci			ret = chip_ready(map, contender, contender->start, mode);
99162306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci			if (ret == -EAGAIN) {
99462306a36Sopenharmony_ci				mutex_unlock(&contender->mutex);
99562306a36Sopenharmony_ci				goto retry;
99662306a36Sopenharmony_ci			}
99762306a36Sopenharmony_ci			if (ret) {
99862306a36Sopenharmony_ci				mutex_unlock(&contender->mutex);
99962306a36Sopenharmony_ci				return ret;
100062306a36Sopenharmony_ci			}
100162306a36Sopenharmony_ci			mutex_lock(&shared->lock);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci			/* We should not own chip if it is already
100462306a36Sopenharmony_ci			 * in FL_SYNCING state. Put contender and retry. */
100562306a36Sopenharmony_ci			if (chip->state == FL_SYNCING) {
100662306a36Sopenharmony_ci				put_chip(map, contender, contender->start);
100762306a36Sopenharmony_ci				mutex_unlock(&contender->mutex);
100862306a36Sopenharmony_ci				goto retry;
100962306a36Sopenharmony_ci			}
101062306a36Sopenharmony_ci			mutex_unlock(&contender->mutex);
101162306a36Sopenharmony_ci		}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci		/* Check if we already have suspended erase
101462306a36Sopenharmony_ci		 * on this chip. Sleep. */
101562306a36Sopenharmony_ci		if (mode == FL_ERASING && shared->erasing
101662306a36Sopenharmony_ci		    && shared->erasing->oldstate == FL_ERASING) {
101762306a36Sopenharmony_ci			mutex_unlock(&shared->lock);
101862306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
101962306a36Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
102062306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
102162306a36Sopenharmony_ci			schedule();
102262306a36Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
102362306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
102462306a36Sopenharmony_ci			goto retry;
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci		/* We now own it */
102862306a36Sopenharmony_ci		shared->writing = chip;
102962306a36Sopenharmony_ci		if (mode == FL_ERASING)
103062306a36Sopenharmony_ci			shared->erasing = chip;
103162306a36Sopenharmony_ci		mutex_unlock(&shared->lock);
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci	ret = chip_ready(map, chip, adr, mode);
103462306a36Sopenharmony_ci	if (ret == -EAGAIN)
103562306a36Sopenharmony_ci		goto retry;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	return ret;
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
104162306a36Sopenharmony_ci{
104262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	if (chip->priv) {
104562306a36Sopenharmony_ci		struct flchip_shared *shared = chip->priv;
104662306a36Sopenharmony_ci		mutex_lock(&shared->lock);
104762306a36Sopenharmony_ci		if (shared->writing == chip && chip->oldstate == FL_READY) {
104862306a36Sopenharmony_ci			/* We own the ability to write, but we're done */
104962306a36Sopenharmony_ci			shared->writing = shared->erasing;
105062306a36Sopenharmony_ci			if (shared->writing && shared->writing != chip) {
105162306a36Sopenharmony_ci				/* give back ownership to who we loaned it from */
105262306a36Sopenharmony_ci				struct flchip *loaner = shared->writing;
105362306a36Sopenharmony_ci				mutex_lock(&loaner->mutex);
105462306a36Sopenharmony_ci				mutex_unlock(&shared->lock);
105562306a36Sopenharmony_ci				mutex_unlock(&chip->mutex);
105662306a36Sopenharmony_ci				put_chip(map, loaner, loaner->start);
105762306a36Sopenharmony_ci				mutex_lock(&chip->mutex);
105862306a36Sopenharmony_ci				mutex_unlock(&loaner->mutex);
105962306a36Sopenharmony_ci				wake_up(&chip->wq);
106062306a36Sopenharmony_ci				return;
106162306a36Sopenharmony_ci			}
106262306a36Sopenharmony_ci			shared->erasing = NULL;
106362306a36Sopenharmony_ci			shared->writing = NULL;
106462306a36Sopenharmony_ci		} else if (shared->erasing == chip && shared->writing != chip) {
106562306a36Sopenharmony_ci			/*
106662306a36Sopenharmony_ci			 * We own the ability to erase without the ability
106762306a36Sopenharmony_ci			 * to write, which means the erase was suspended
106862306a36Sopenharmony_ci			 * and some other partition is currently writing.
106962306a36Sopenharmony_ci			 * Don't let the switch below mess things up since
107062306a36Sopenharmony_ci			 * we don't have ownership to resume anything.
107162306a36Sopenharmony_ci			 */
107262306a36Sopenharmony_ci			mutex_unlock(&shared->lock);
107362306a36Sopenharmony_ci			wake_up(&chip->wq);
107462306a36Sopenharmony_ci			return;
107562306a36Sopenharmony_ci		}
107662306a36Sopenharmony_ci		mutex_unlock(&shared->lock);
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	switch(chip->oldstate) {
108062306a36Sopenharmony_ci	case FL_ERASING:
108162306a36Sopenharmony_ci		/* What if one interleaved chip has finished and the
108262306a36Sopenharmony_ci		   other hasn't? The old code would leave the finished
108362306a36Sopenharmony_ci		   one in READY mode. That's bad, and caused -EROFS
108462306a36Sopenharmony_ci		   errors to be returned from do_erase_oneblock because
108562306a36Sopenharmony_ci		   that's the only bit it checked for at the time.
108662306a36Sopenharmony_ci		   As the state machine appears to explicitly allow
108762306a36Sopenharmony_ci		   sending the 0x70 (Read Status) command to an erasing
108862306a36Sopenharmony_ci		   chip and expecting it to be ignored, that's what we
108962306a36Sopenharmony_ci		   do. */
109062306a36Sopenharmony_ci		map_write(map, CMD(0xd0), chip->in_progress_block_addr);
109162306a36Sopenharmony_ci		map_write(map, CMD(0x70), chip->in_progress_block_addr);
109262306a36Sopenharmony_ci		chip->oldstate = FL_READY;
109362306a36Sopenharmony_ci		chip->state = FL_ERASING;
109462306a36Sopenharmony_ci		break;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	case FL_XIP_WHILE_ERASING:
109762306a36Sopenharmony_ci		chip->state = chip->oldstate;
109862306a36Sopenharmony_ci		chip->oldstate = FL_READY;
109962306a36Sopenharmony_ci		break;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	case FL_READY:
110262306a36Sopenharmony_ci	case FL_STATUS:
110362306a36Sopenharmony_ci	case FL_JEDEC_QUERY:
110462306a36Sopenharmony_ci		break;
110562306a36Sopenharmony_ci	default:
110662306a36Sopenharmony_ci		printk(KERN_ERR "%s: put_chip() called with oldstate %d!!\n", map->name, chip->oldstate);
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci	wake_up(&chip->wq);
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci#ifdef CONFIG_MTD_XIP
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci/*
111462306a36Sopenharmony_ci * No interrupt what so ever can be serviced while the flash isn't in array
111562306a36Sopenharmony_ci * mode.  This is ensured by the xip_disable() and xip_enable() functions
111662306a36Sopenharmony_ci * enclosing any code path where the flash is known not to be in array mode.
111762306a36Sopenharmony_ci * And within a XIP disabled code path, only functions marked with __xipram
111862306a36Sopenharmony_ci * may be called and nothing else (it's a good thing to inspect generated
111962306a36Sopenharmony_ci * assembly to make sure inline functions were actually inlined and that gcc
112062306a36Sopenharmony_ci * didn't emit calls to its own support functions). Also configuring MTD CFI
112162306a36Sopenharmony_ci * support to a single buswidth and a single interleave is also recommended.
112262306a36Sopenharmony_ci */
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cistatic void xip_disable(struct map_info *map, struct flchip *chip,
112562306a36Sopenharmony_ci			unsigned long adr)
112662306a36Sopenharmony_ci{
112762306a36Sopenharmony_ci	/* TODO: chips with no XIP use should ignore and return */
112862306a36Sopenharmony_ci	(void) map_read(map, adr); /* ensure mmu mapping is up to date */
112962306a36Sopenharmony_ci	local_irq_disable();
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic void __xipram xip_enable(struct map_info *map, struct flchip *chip,
113362306a36Sopenharmony_ci				unsigned long adr)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
113662306a36Sopenharmony_ci	if (chip->state != FL_POINT && chip->state != FL_READY) {
113762306a36Sopenharmony_ci		map_write(map, CMD(0xff), adr);
113862306a36Sopenharmony_ci		chip->state = FL_READY;
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci	(void) map_read(map, adr);
114162306a36Sopenharmony_ci	xip_iprefetch();
114262306a36Sopenharmony_ci	local_irq_enable();
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/*
114662306a36Sopenharmony_ci * When a delay is required for the flash operation to complete, the
114762306a36Sopenharmony_ci * xip_wait_for_operation() function is polling for both the given timeout
114862306a36Sopenharmony_ci * and pending (but still masked) hardware interrupts.  Whenever there is an
114962306a36Sopenharmony_ci * interrupt pending then the flash erase or write operation is suspended,
115062306a36Sopenharmony_ci * array mode restored and interrupts unmasked.  Task scheduling might also
115162306a36Sopenharmony_ci * happen at that point.  The CPU eventually returns from the interrupt or
115262306a36Sopenharmony_ci * the call to schedule() and the suspended flash operation is resumed for
115362306a36Sopenharmony_ci * the remaining of the delay period.
115462306a36Sopenharmony_ci *
115562306a36Sopenharmony_ci * Warning: this function _will_ fool interrupt latency tracing tools.
115662306a36Sopenharmony_ci */
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic int __xipram xip_wait_for_operation(
115962306a36Sopenharmony_ci		struct map_info *map, struct flchip *chip,
116062306a36Sopenharmony_ci		unsigned long adr, unsigned int chip_op_time_max)
116162306a36Sopenharmony_ci{
116262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
116362306a36Sopenharmony_ci	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
116462306a36Sopenharmony_ci	map_word status, OK = CMD(0x80);
116562306a36Sopenharmony_ci	unsigned long usec, suspended, start, done;
116662306a36Sopenharmony_ci	flstate_t oldstate, newstate;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci       	start = xip_currtime();
116962306a36Sopenharmony_ci	usec = chip_op_time_max;
117062306a36Sopenharmony_ci	if (usec == 0)
117162306a36Sopenharmony_ci		usec = 500000;
117262306a36Sopenharmony_ci	done = 0;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	do {
117562306a36Sopenharmony_ci		cpu_relax();
117662306a36Sopenharmony_ci		if (xip_irqpending() && cfip &&
117762306a36Sopenharmony_ci		    ((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) ||
117862306a36Sopenharmony_ci		     (chip->state == FL_WRITING && (cfip->FeatureSupport&4))) &&
117962306a36Sopenharmony_ci		    (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
118062306a36Sopenharmony_ci			/*
118162306a36Sopenharmony_ci			 * Let's suspend the erase or write operation when
118262306a36Sopenharmony_ci			 * supported.  Note that we currently don't try to
118362306a36Sopenharmony_ci			 * suspend interleaved chips if there is already
118462306a36Sopenharmony_ci			 * another operation suspended (imagine what happens
118562306a36Sopenharmony_ci			 * when one chip was already done with the current
118662306a36Sopenharmony_ci			 * operation while another chip suspended it, then
118762306a36Sopenharmony_ci			 * we resume the whole thing at once).  Yes, it
118862306a36Sopenharmony_ci			 * can happen!
118962306a36Sopenharmony_ci			 */
119062306a36Sopenharmony_ci			usec -= done;
119162306a36Sopenharmony_ci			map_write(map, CMD(0xb0), adr);
119262306a36Sopenharmony_ci			map_write(map, CMD(0x70), adr);
119362306a36Sopenharmony_ci			suspended = xip_currtime();
119462306a36Sopenharmony_ci			do {
119562306a36Sopenharmony_ci				if (xip_elapsed_since(suspended) > 100000) {
119662306a36Sopenharmony_ci					/*
119762306a36Sopenharmony_ci					 * The chip doesn't want to suspend
119862306a36Sopenharmony_ci					 * after waiting for 100 msecs.
119962306a36Sopenharmony_ci					 * This is a critical error but there
120062306a36Sopenharmony_ci					 * is not much we can do here.
120162306a36Sopenharmony_ci					 */
120262306a36Sopenharmony_ci					return -EIO;
120362306a36Sopenharmony_ci				}
120462306a36Sopenharmony_ci				status = map_read(map, adr);
120562306a36Sopenharmony_ci			} while (!map_word_andequal(map, status, OK, OK));
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci			/* Suspend succeeded */
120862306a36Sopenharmony_ci			oldstate = chip->state;
120962306a36Sopenharmony_ci			if (oldstate == FL_ERASING) {
121062306a36Sopenharmony_ci				if (!map_word_bitsset(map, status, CMD(0x40)))
121162306a36Sopenharmony_ci					break;
121262306a36Sopenharmony_ci				newstate = FL_XIP_WHILE_ERASING;
121362306a36Sopenharmony_ci				chip->erase_suspended = 1;
121462306a36Sopenharmony_ci			} else {
121562306a36Sopenharmony_ci				if (!map_word_bitsset(map, status, CMD(0x04)))
121662306a36Sopenharmony_ci					break;
121762306a36Sopenharmony_ci				newstate = FL_XIP_WHILE_WRITING;
121862306a36Sopenharmony_ci				chip->write_suspended = 1;
121962306a36Sopenharmony_ci			}
122062306a36Sopenharmony_ci			chip->state = newstate;
122162306a36Sopenharmony_ci			map_write(map, CMD(0xff), adr);
122262306a36Sopenharmony_ci			(void) map_read(map, adr);
122362306a36Sopenharmony_ci			xip_iprefetch();
122462306a36Sopenharmony_ci			local_irq_enable();
122562306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
122662306a36Sopenharmony_ci			xip_iprefetch();
122762306a36Sopenharmony_ci			cond_resched();
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci			/*
123062306a36Sopenharmony_ci			 * We're back.  However someone else might have
123162306a36Sopenharmony_ci			 * decided to go write to the chip if we are in
123262306a36Sopenharmony_ci			 * a suspended erase state.  If so let's wait
123362306a36Sopenharmony_ci			 * until it's done.
123462306a36Sopenharmony_ci			 */
123562306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
123662306a36Sopenharmony_ci			while (chip->state != newstate) {
123762306a36Sopenharmony_ci				DECLARE_WAITQUEUE(wait, current);
123862306a36Sopenharmony_ci				set_current_state(TASK_UNINTERRUPTIBLE);
123962306a36Sopenharmony_ci				add_wait_queue(&chip->wq, &wait);
124062306a36Sopenharmony_ci				mutex_unlock(&chip->mutex);
124162306a36Sopenharmony_ci				schedule();
124262306a36Sopenharmony_ci				remove_wait_queue(&chip->wq, &wait);
124362306a36Sopenharmony_ci				mutex_lock(&chip->mutex);
124462306a36Sopenharmony_ci			}
124562306a36Sopenharmony_ci			/* Disallow XIP again */
124662306a36Sopenharmony_ci			local_irq_disable();
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci			/* Resume the write or erase operation */
124962306a36Sopenharmony_ci			map_write(map, CMD(0xd0), adr);
125062306a36Sopenharmony_ci			map_write(map, CMD(0x70), adr);
125162306a36Sopenharmony_ci			chip->state = oldstate;
125262306a36Sopenharmony_ci			start = xip_currtime();
125362306a36Sopenharmony_ci		} else if (usec >= 1000000/HZ) {
125462306a36Sopenharmony_ci			/*
125562306a36Sopenharmony_ci			 * Try to save on CPU power when waiting delay
125662306a36Sopenharmony_ci			 * is at least a system timer tick period.
125762306a36Sopenharmony_ci			 * No need to be extremely accurate here.
125862306a36Sopenharmony_ci			 */
125962306a36Sopenharmony_ci			xip_cpu_idle();
126062306a36Sopenharmony_ci		}
126162306a36Sopenharmony_ci		status = map_read(map, adr);
126262306a36Sopenharmony_ci		done = xip_elapsed_since(start);
126362306a36Sopenharmony_ci	} while (!map_word_andequal(map, status, OK, OK)
126462306a36Sopenharmony_ci		 && done < usec);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	return (done >= usec) ? -ETIME : 0;
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci/*
127062306a36Sopenharmony_ci * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
127162306a36Sopenharmony_ci * the flash is actively programming or erasing since we have to poll for
127262306a36Sopenharmony_ci * the operation to complete anyway.  We can't do that in a generic way with
127362306a36Sopenharmony_ci * a XIP setup so do it before the actual flash operation in this case
127462306a36Sopenharmony_ci * and stub it out from INVAL_CACHE_AND_WAIT.
127562306a36Sopenharmony_ci */
127662306a36Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(map, from, size)  \
127762306a36Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, from, size)
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, usec, usec_max) \
128062306a36Sopenharmony_ci	xip_wait_for_operation(map, chip, cmd_adr, usec_max)
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci#else
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci#define xip_disable(map, chip, adr)
128562306a36Sopenharmony_ci#define xip_enable(map, chip, adr)
128662306a36Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(x...)
128762306a36Sopenharmony_ci#define INVAL_CACHE_AND_WAIT inval_cache_and_wait_for_operation
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_cistatic int inval_cache_and_wait_for_operation(
129062306a36Sopenharmony_ci		struct map_info *map, struct flchip *chip,
129162306a36Sopenharmony_ci		unsigned long cmd_adr, unsigned long inval_adr, int inval_len,
129262306a36Sopenharmony_ci		unsigned int chip_op_time, unsigned int chip_op_time_max)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
129562306a36Sopenharmony_ci	map_word status, status_OK = CMD(0x80);
129662306a36Sopenharmony_ci	int chip_state = chip->state;
129762306a36Sopenharmony_ci	unsigned int timeo, sleep_time, reset_timeo;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
130062306a36Sopenharmony_ci	if (inval_len)
130162306a36Sopenharmony_ci		INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len);
130262306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	timeo = chip_op_time_max;
130562306a36Sopenharmony_ci	if (!timeo)
130662306a36Sopenharmony_ci		timeo = 500000;
130762306a36Sopenharmony_ci	reset_timeo = timeo;
130862306a36Sopenharmony_ci	sleep_time = chip_op_time / 2;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	for (;;) {
131162306a36Sopenharmony_ci		if (chip->state != chip_state) {
131262306a36Sopenharmony_ci			/* Someone's suspended the operation: sleep */
131362306a36Sopenharmony_ci			DECLARE_WAITQUEUE(wait, current);
131462306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
131562306a36Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
131662306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
131762306a36Sopenharmony_ci			schedule();
131862306a36Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
131962306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
132062306a36Sopenharmony_ci			continue;
132162306a36Sopenharmony_ci		}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci		status = map_read(map, cmd_adr);
132462306a36Sopenharmony_ci		if (map_word_andequal(map, status, status_OK, status_OK))
132562306a36Sopenharmony_ci			break;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci		if (chip->erase_suspended && chip_state == FL_ERASING)  {
132862306a36Sopenharmony_ci			/* Erase suspend occurred while sleep: reset timeout */
132962306a36Sopenharmony_ci			timeo = reset_timeo;
133062306a36Sopenharmony_ci			chip->erase_suspended = 0;
133162306a36Sopenharmony_ci		}
133262306a36Sopenharmony_ci		if (chip->write_suspended && chip_state == FL_WRITING)  {
133362306a36Sopenharmony_ci			/* Write suspend occurred while sleep: reset timeout */
133462306a36Sopenharmony_ci			timeo = reset_timeo;
133562306a36Sopenharmony_ci			chip->write_suspended = 0;
133662306a36Sopenharmony_ci		}
133762306a36Sopenharmony_ci		if (!timeo) {
133862306a36Sopenharmony_ci			map_write(map, CMD(0x70), cmd_adr);
133962306a36Sopenharmony_ci			chip->state = FL_STATUS;
134062306a36Sopenharmony_ci			return -ETIME;
134162306a36Sopenharmony_ci		}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci		/* OK Still waiting. Drop the lock, wait a while and retry. */
134462306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
134562306a36Sopenharmony_ci		if (sleep_time >= 1000000/HZ) {
134662306a36Sopenharmony_ci			/*
134762306a36Sopenharmony_ci			 * Half of the normal delay still remaining
134862306a36Sopenharmony_ci			 * can be performed with a sleeping delay instead
134962306a36Sopenharmony_ci			 * of busy waiting.
135062306a36Sopenharmony_ci			 */
135162306a36Sopenharmony_ci			msleep(sleep_time/1000);
135262306a36Sopenharmony_ci			timeo -= sleep_time;
135362306a36Sopenharmony_ci			sleep_time = 1000000/HZ;
135462306a36Sopenharmony_ci		} else {
135562306a36Sopenharmony_ci			udelay(1);
135662306a36Sopenharmony_ci			cond_resched();
135762306a36Sopenharmony_ci			timeo--;
135862306a36Sopenharmony_ci		}
135962306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
136062306a36Sopenharmony_ci	}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	/* Done and happy. */
136362306a36Sopenharmony_ci 	chip->state = FL_STATUS;
136462306a36Sopenharmony_ci	return 0;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci#endif
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci#define WAIT_TIMEOUT(map, chip, adr, udelay, udelay_max) \
137062306a36Sopenharmony_ci	INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, udelay, udelay_max);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_cistatic int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
137462306a36Sopenharmony_ci{
137562306a36Sopenharmony_ci	unsigned long cmd_addr;
137662306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
137762306a36Sopenharmony_ci	int ret;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	adr += chip->start;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	/* Ensure cmd read/writes are aligned. */
138262306a36Sopenharmony_ci	cmd_addr = adr & ~(map_bankwidth(map)-1);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	ret = get_chip(map, chip, cmd_addr, FL_POINT);
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	if (!ret) {
138962306a36Sopenharmony_ci		if (chip->state != FL_POINT && chip->state != FL_READY)
139062306a36Sopenharmony_ci			map_write(map, CMD(0xff), cmd_addr);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci		chip->state = FL_POINT;
139362306a36Sopenharmony_ci		chip->ref_point_counter++;
139462306a36Sopenharmony_ci	}
139562306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	return ret;
139862306a36Sopenharmony_ci}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_cistatic int cfi_intelext_point(struct mtd_info *mtd, loff_t from, size_t len,
140162306a36Sopenharmony_ci		size_t *retlen, void **virt, resource_size_t *phys)
140262306a36Sopenharmony_ci{
140362306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
140462306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
140562306a36Sopenharmony_ci	unsigned long ofs, last_end = 0;
140662306a36Sopenharmony_ci	int chipnum;
140762306a36Sopenharmony_ci	int ret;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	if (!map->virt)
141062306a36Sopenharmony_ci		return -EINVAL;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	/* Now lock the chip(s) to POINT state */
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	/* ofs: offset within the first chip that the first read should start */
141562306a36Sopenharmony_ci	chipnum = (from >> cfi->chipshift);
141662306a36Sopenharmony_ci	ofs = from - (chipnum << cfi->chipshift);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	*virt = map->virt + cfi->chips[chipnum].start + ofs;
141962306a36Sopenharmony_ci	if (phys)
142062306a36Sopenharmony_ci		*phys = map->phys + cfi->chips[chipnum].start + ofs;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	while (len) {
142362306a36Sopenharmony_ci		unsigned long thislen;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci		if (chipnum >= cfi->numchips)
142662306a36Sopenharmony_ci			break;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci		/* We cannot point across chips that are virtually disjoint */
142962306a36Sopenharmony_ci		if (!last_end)
143062306a36Sopenharmony_ci			last_end = cfi->chips[chipnum].start;
143162306a36Sopenharmony_ci		else if (cfi->chips[chipnum].start != last_end)
143262306a36Sopenharmony_ci			break;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		if ((len + ofs -1) >> cfi->chipshift)
143562306a36Sopenharmony_ci			thislen = (1<<cfi->chipshift) - ofs;
143662306a36Sopenharmony_ci		else
143762306a36Sopenharmony_ci			thislen = len;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci		ret = do_point_onechip(map, &cfi->chips[chipnum], ofs, thislen);
144062306a36Sopenharmony_ci		if (ret)
144162306a36Sopenharmony_ci			break;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci		*retlen += thislen;
144462306a36Sopenharmony_ci		len -= thislen;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci		ofs = 0;
144762306a36Sopenharmony_ci		last_end += 1 << cfi->chipshift;
144862306a36Sopenharmony_ci		chipnum++;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci	return 0;
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic int cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
145662306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
145762306a36Sopenharmony_ci	unsigned long ofs;
145862306a36Sopenharmony_ci	int chipnum, err = 0;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	/* Now unlock the chip(s) POINT state */
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	/* ofs: offset within the first chip that the first read should start */
146362306a36Sopenharmony_ci	chipnum = (from >> cfi->chipshift);
146462306a36Sopenharmony_ci	ofs = from - (chipnum <<  cfi->chipshift);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	while (len && !err) {
146762306a36Sopenharmony_ci		unsigned long thislen;
146862306a36Sopenharmony_ci		struct flchip *chip;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci		chip = &cfi->chips[chipnum];
147162306a36Sopenharmony_ci		if (chipnum >= cfi->numchips)
147262306a36Sopenharmony_ci			break;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci		if ((len + ofs -1) >> cfi->chipshift)
147562306a36Sopenharmony_ci			thislen = (1<<cfi->chipshift) - ofs;
147662306a36Sopenharmony_ci		else
147762306a36Sopenharmony_ci			thislen = len;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
148062306a36Sopenharmony_ci		if (chip->state == FL_POINT) {
148162306a36Sopenharmony_ci			chip->ref_point_counter--;
148262306a36Sopenharmony_ci			if(chip->ref_point_counter == 0)
148362306a36Sopenharmony_ci				chip->state = FL_READY;
148462306a36Sopenharmony_ci		} else {
148562306a36Sopenharmony_ci			printk(KERN_ERR "%s: Error: unpoint called on non pointed region\n", map->name);
148662306a36Sopenharmony_ci			err = -EINVAL;
148762306a36Sopenharmony_ci		}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		put_chip(map, chip, chip->start);
149062306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci		len -= thislen;
149362306a36Sopenharmony_ci		ofs = 0;
149462306a36Sopenharmony_ci		chipnum++;
149562306a36Sopenharmony_ci	}
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	return err;
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_cistatic inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
150162306a36Sopenharmony_ci{
150262306a36Sopenharmony_ci	unsigned long cmd_addr;
150362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
150462306a36Sopenharmony_ci	int ret;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	adr += chip->start;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	/* Ensure cmd read/writes are aligned. */
150962306a36Sopenharmony_ci	cmd_addr = adr & ~(map_bankwidth(map)-1);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
151262306a36Sopenharmony_ci	ret = get_chip(map, chip, cmd_addr, FL_READY);
151362306a36Sopenharmony_ci	if (ret) {
151462306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
151562306a36Sopenharmony_ci		return ret;
151662306a36Sopenharmony_ci	}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	if (chip->state != FL_POINT && chip->state != FL_READY) {
151962306a36Sopenharmony_ci		map_write(map, CMD(0xff), cmd_addr);
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		chip->state = FL_READY;
152262306a36Sopenharmony_ci	}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	map_copy_from(map, buf, adr, len);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	put_chip(map, chip, cmd_addr);
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
152962306a36Sopenharmony_ci	return 0;
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
153562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
153662306a36Sopenharmony_ci	unsigned long ofs;
153762306a36Sopenharmony_ci	int chipnum;
153862306a36Sopenharmony_ci	int ret = 0;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	/* ofs: offset within the first chip that the first read should start */
154162306a36Sopenharmony_ci	chipnum = (from >> cfi->chipshift);
154262306a36Sopenharmony_ci	ofs = from - (chipnum <<  cfi->chipshift);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	while (len) {
154562306a36Sopenharmony_ci		unsigned long thislen;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci		if (chipnum >= cfi->numchips)
154862306a36Sopenharmony_ci			break;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci		if ((len + ofs -1) >> cfi->chipshift)
155162306a36Sopenharmony_ci			thislen = (1<<cfi->chipshift) - ofs;
155262306a36Sopenharmony_ci		else
155362306a36Sopenharmony_ci			thislen = len;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci		ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf);
155662306a36Sopenharmony_ci		if (ret)
155762306a36Sopenharmony_ci			break;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci		*retlen += thislen;
156062306a36Sopenharmony_ci		len -= thislen;
156162306a36Sopenharmony_ci		buf += thislen;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci		ofs = 0;
156462306a36Sopenharmony_ci		chipnum++;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci	return ret;
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_cistatic int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
157062306a36Sopenharmony_ci				     unsigned long adr, map_word datum, int mode)
157162306a36Sopenharmony_ci{
157262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
157362306a36Sopenharmony_ci	map_word status, write_cmd;
157462306a36Sopenharmony_ci	int ret;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	adr += chip->start;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	switch (mode) {
157962306a36Sopenharmony_ci	case FL_WRITING:
158062306a36Sopenharmony_ci		write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0x40) : CMD(0x41);
158162306a36Sopenharmony_ci		break;
158262306a36Sopenharmony_ci	case FL_OTP_WRITE:
158362306a36Sopenharmony_ci		write_cmd = CMD(0xc0);
158462306a36Sopenharmony_ci		break;
158562306a36Sopenharmony_ci	default:
158662306a36Sopenharmony_ci		return -EINVAL;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
159062306a36Sopenharmony_ci	ret = get_chip(map, chip, adr, mode);
159162306a36Sopenharmony_ci	if (ret) {
159262306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
159362306a36Sopenharmony_ci		return ret;
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
159762306a36Sopenharmony_ci	ENABLE_VPP(map);
159862306a36Sopenharmony_ci	xip_disable(map, chip, adr);
159962306a36Sopenharmony_ci	map_write(map, write_cmd, adr);
160062306a36Sopenharmony_ci	map_write(map, datum, adr);
160162306a36Sopenharmony_ci	chip->state = mode;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
160462306a36Sopenharmony_ci				   adr, map_bankwidth(map),
160562306a36Sopenharmony_ci				   chip->word_write_time,
160662306a36Sopenharmony_ci				   chip->word_write_time_max);
160762306a36Sopenharmony_ci	if (ret) {
160862306a36Sopenharmony_ci		xip_enable(map, chip, adr);
160962306a36Sopenharmony_ci		printk(KERN_ERR "%s: word write error (status timeout)\n", map->name);
161062306a36Sopenharmony_ci		goto out;
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	/* check for errors */
161462306a36Sopenharmony_ci	status = map_read(map, adr);
161562306a36Sopenharmony_ci	if (map_word_bitsset(map, status, CMD(0x1a))) {
161662306a36Sopenharmony_ci		unsigned long chipstatus = MERGESTATUS(status);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci		/* reset status */
161962306a36Sopenharmony_ci		map_write(map, CMD(0x50), adr);
162062306a36Sopenharmony_ci		map_write(map, CMD(0x70), adr);
162162306a36Sopenharmony_ci		xip_enable(map, chip, adr);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci		if (chipstatus & 0x02) {
162462306a36Sopenharmony_ci			ret = -EROFS;
162562306a36Sopenharmony_ci		} else if (chipstatus & 0x08) {
162662306a36Sopenharmony_ci			printk(KERN_ERR "%s: word write error (bad VPP)\n", map->name);
162762306a36Sopenharmony_ci			ret = -EIO;
162862306a36Sopenharmony_ci		} else {
162962306a36Sopenharmony_ci			printk(KERN_ERR "%s: word write error (status 0x%lx)\n", map->name, chipstatus);
163062306a36Sopenharmony_ci			ret = -EINVAL;
163162306a36Sopenharmony_ci		}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci		goto out;
163462306a36Sopenharmony_ci	}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	xip_enable(map, chip, adr);
163762306a36Sopenharmony_ci out:	DISABLE_VPP(map);
163862306a36Sopenharmony_ci	put_chip(map, chip, adr);
163962306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
164062306a36Sopenharmony_ci	return ret;
164162306a36Sopenharmony_ci}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_cistatic int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf)
164562306a36Sopenharmony_ci{
164662306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
164762306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
164862306a36Sopenharmony_ci	int ret;
164962306a36Sopenharmony_ci	int chipnum;
165062306a36Sopenharmony_ci	unsigned long ofs;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	chipnum = to >> cfi->chipshift;
165362306a36Sopenharmony_ci	ofs = to  - (chipnum << cfi->chipshift);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	/* If it's not bus-aligned, do the first byte write */
165662306a36Sopenharmony_ci	if (ofs & (map_bankwidth(map)-1)) {
165762306a36Sopenharmony_ci		unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1);
165862306a36Sopenharmony_ci		int gap = ofs - bus_ofs;
165962306a36Sopenharmony_ci		int n;
166062306a36Sopenharmony_ci		map_word datum;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci		n = min_t(int, len, map_bankwidth(map)-gap);
166362306a36Sopenharmony_ci		datum = map_word_ff(map);
166462306a36Sopenharmony_ci		datum = map_word_load_partial(map, datum, buf, gap, n);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci		ret = do_write_oneword(map, &cfi->chips[chipnum],
166762306a36Sopenharmony_ci					       bus_ofs, datum, FL_WRITING);
166862306a36Sopenharmony_ci		if (ret)
166962306a36Sopenharmony_ci			return ret;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci		len -= n;
167262306a36Sopenharmony_ci		ofs += n;
167362306a36Sopenharmony_ci		buf += n;
167462306a36Sopenharmony_ci		(*retlen) += n;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci		if (ofs >> cfi->chipshift) {
167762306a36Sopenharmony_ci			chipnum ++;
167862306a36Sopenharmony_ci			ofs = 0;
167962306a36Sopenharmony_ci			if (chipnum == cfi->numchips)
168062306a36Sopenharmony_ci				return 0;
168162306a36Sopenharmony_ci		}
168262306a36Sopenharmony_ci	}
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	while(len >= map_bankwidth(map)) {
168562306a36Sopenharmony_ci		map_word datum = map_word_load(map, buf);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci		ret = do_write_oneword(map, &cfi->chips[chipnum],
168862306a36Sopenharmony_ci				       ofs, datum, FL_WRITING);
168962306a36Sopenharmony_ci		if (ret)
169062306a36Sopenharmony_ci			return ret;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci		ofs += map_bankwidth(map);
169362306a36Sopenharmony_ci		buf += map_bankwidth(map);
169462306a36Sopenharmony_ci		(*retlen) += map_bankwidth(map);
169562306a36Sopenharmony_ci		len -= map_bankwidth(map);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci		if (ofs >> cfi->chipshift) {
169862306a36Sopenharmony_ci			chipnum ++;
169962306a36Sopenharmony_ci			ofs = 0;
170062306a36Sopenharmony_ci			if (chipnum == cfi->numchips)
170162306a36Sopenharmony_ci				return 0;
170262306a36Sopenharmony_ci		}
170362306a36Sopenharmony_ci	}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	if (len & (map_bankwidth(map)-1)) {
170662306a36Sopenharmony_ci		map_word datum;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci		datum = map_word_ff(map);
170962306a36Sopenharmony_ci		datum = map_word_load_partial(map, datum, buf, 0, len);
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci		ret = do_write_oneword(map, &cfi->chips[chipnum],
171262306a36Sopenharmony_ci				       ofs, datum, FL_WRITING);
171362306a36Sopenharmony_ci		if (ret)
171462306a36Sopenharmony_ci			return ret;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci		(*retlen) += len;
171762306a36Sopenharmony_ci	}
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	return 0;
172062306a36Sopenharmony_ci}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_cistatic int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
172462306a36Sopenharmony_ci				    unsigned long adr, const struct kvec **pvec,
172562306a36Sopenharmony_ci				    unsigned long *pvec_seek, int len)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
172862306a36Sopenharmony_ci	map_word status, write_cmd, datum;
172962306a36Sopenharmony_ci	unsigned long cmd_adr;
173062306a36Sopenharmony_ci	int ret, wbufsize, word_gap, words;
173162306a36Sopenharmony_ci	const struct kvec *vec;
173262306a36Sopenharmony_ci	unsigned long vec_seek;
173362306a36Sopenharmony_ci	unsigned long initial_adr;
173462306a36Sopenharmony_ci	int initial_len = len;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
173762306a36Sopenharmony_ci	adr += chip->start;
173862306a36Sopenharmony_ci	initial_adr = adr;
173962306a36Sopenharmony_ci	cmd_adr = adr & ~(wbufsize-1);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	/* Sharp LH28F640BF chips need the first address for the
174262306a36Sopenharmony_ci	 * Page Buffer Program command. See Table 5 of
174362306a36Sopenharmony_ci	 * LH28F320BF, LH28F640BF, LH28F128BF Series (Appendix FUM00701) */
174462306a36Sopenharmony_ci	if (is_LH28F640BF(cfi))
174562306a36Sopenharmony_ci		cmd_adr = adr;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	/* Let's determine this according to the interleave only once */
174862306a36Sopenharmony_ci	write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
175162306a36Sopenharmony_ci	ret = get_chip(map, chip, cmd_adr, FL_WRITING);
175262306a36Sopenharmony_ci	if (ret) {
175362306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
175462306a36Sopenharmony_ci		return ret;
175562306a36Sopenharmony_ci	}
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, initial_adr, initial_len);
175862306a36Sopenharmony_ci	ENABLE_VPP(map);
175962306a36Sopenharmony_ci	xip_disable(map, chip, cmd_adr);
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	/* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set
176262306a36Sopenharmony_ci	   [...], the device will not accept any more Write to Buffer commands".
176362306a36Sopenharmony_ci	   So we must check here and reset those bits if they're set. Otherwise
176462306a36Sopenharmony_ci	   we're just pissing in the wind */
176562306a36Sopenharmony_ci	if (chip->state != FL_STATUS) {
176662306a36Sopenharmony_ci		map_write(map, CMD(0x70), cmd_adr);
176762306a36Sopenharmony_ci		chip->state = FL_STATUS;
176862306a36Sopenharmony_ci	}
176962306a36Sopenharmony_ci	status = map_read(map, cmd_adr);
177062306a36Sopenharmony_ci	if (map_word_bitsset(map, status, CMD(0x30))) {
177162306a36Sopenharmony_ci		xip_enable(map, chip, cmd_adr);
177262306a36Sopenharmony_ci		printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]);
177362306a36Sopenharmony_ci		xip_disable(map, chip, cmd_adr);
177462306a36Sopenharmony_ci		map_write(map, CMD(0x50), cmd_adr);
177562306a36Sopenharmony_ci		map_write(map, CMD(0x70), cmd_adr);
177662306a36Sopenharmony_ci	}
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	chip->state = FL_WRITING_TO_BUFFER;
177962306a36Sopenharmony_ci	map_write(map, write_cmd, cmd_adr);
178062306a36Sopenharmony_ci	ret = WAIT_TIMEOUT(map, chip, cmd_adr, 0, 0);
178162306a36Sopenharmony_ci	if (ret) {
178262306a36Sopenharmony_ci		/* Argh. Not ready for write to buffer */
178362306a36Sopenharmony_ci		map_word Xstatus = map_read(map, cmd_adr);
178462306a36Sopenharmony_ci		map_write(map, CMD(0x70), cmd_adr);
178562306a36Sopenharmony_ci		chip->state = FL_STATUS;
178662306a36Sopenharmony_ci		status = map_read(map, cmd_adr);
178762306a36Sopenharmony_ci		map_write(map, CMD(0x50), cmd_adr);
178862306a36Sopenharmony_ci		map_write(map, CMD(0x70), cmd_adr);
178962306a36Sopenharmony_ci		xip_enable(map, chip, cmd_adr);
179062306a36Sopenharmony_ci		printk(KERN_ERR "%s: Chip not ready for buffer write. Xstatus = %lx, status = %lx\n",
179162306a36Sopenharmony_ci				map->name, Xstatus.x[0], status.x[0]);
179262306a36Sopenharmony_ci		goto out;
179362306a36Sopenharmony_ci	}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	/* Figure out the number of words to write */
179662306a36Sopenharmony_ci	word_gap = (-adr & (map_bankwidth(map)-1));
179762306a36Sopenharmony_ci	words = DIV_ROUND_UP(len - word_gap, map_bankwidth(map));
179862306a36Sopenharmony_ci	if (!word_gap) {
179962306a36Sopenharmony_ci		words--;
180062306a36Sopenharmony_ci	} else {
180162306a36Sopenharmony_ci		word_gap = map_bankwidth(map) - word_gap;
180262306a36Sopenharmony_ci		adr -= word_gap;
180362306a36Sopenharmony_ci		datum = map_word_ff(map);
180462306a36Sopenharmony_ci	}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	/* Write length of data to come */
180762306a36Sopenharmony_ci	map_write(map, CMD(words), cmd_adr );
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	/* Write data */
181062306a36Sopenharmony_ci	vec = *pvec;
181162306a36Sopenharmony_ci	vec_seek = *pvec_seek;
181262306a36Sopenharmony_ci	do {
181362306a36Sopenharmony_ci		int n = map_bankwidth(map) - word_gap;
181462306a36Sopenharmony_ci		if (n > vec->iov_len - vec_seek)
181562306a36Sopenharmony_ci			n = vec->iov_len - vec_seek;
181662306a36Sopenharmony_ci		if (n > len)
181762306a36Sopenharmony_ci			n = len;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci		if (!word_gap && len < map_bankwidth(map))
182062306a36Sopenharmony_ci			datum = map_word_ff(map);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci		datum = map_word_load_partial(map, datum,
182362306a36Sopenharmony_ci					      vec->iov_base + vec_seek,
182462306a36Sopenharmony_ci					      word_gap, n);
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci		len -= n;
182762306a36Sopenharmony_ci		word_gap += n;
182862306a36Sopenharmony_ci		if (!len || word_gap == map_bankwidth(map)) {
182962306a36Sopenharmony_ci			map_write(map, datum, adr);
183062306a36Sopenharmony_ci			adr += map_bankwidth(map);
183162306a36Sopenharmony_ci			word_gap = 0;
183262306a36Sopenharmony_ci		}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci		vec_seek += n;
183562306a36Sopenharmony_ci		if (vec_seek == vec->iov_len) {
183662306a36Sopenharmony_ci			vec++;
183762306a36Sopenharmony_ci			vec_seek = 0;
183862306a36Sopenharmony_ci		}
183962306a36Sopenharmony_ci	} while (len);
184062306a36Sopenharmony_ci	*pvec = vec;
184162306a36Sopenharmony_ci	*pvec_seek = vec_seek;
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	/* GO GO GO */
184462306a36Sopenharmony_ci	map_write(map, CMD(0xd0), cmd_adr);
184562306a36Sopenharmony_ci	chip->state = FL_WRITING;
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr,
184862306a36Sopenharmony_ci				   initial_adr, initial_len,
184962306a36Sopenharmony_ci				   chip->buffer_write_time,
185062306a36Sopenharmony_ci				   chip->buffer_write_time_max);
185162306a36Sopenharmony_ci	if (ret) {
185262306a36Sopenharmony_ci		map_write(map, CMD(0x70), cmd_adr);
185362306a36Sopenharmony_ci		chip->state = FL_STATUS;
185462306a36Sopenharmony_ci		xip_enable(map, chip, cmd_adr);
185562306a36Sopenharmony_ci		printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name);
185662306a36Sopenharmony_ci		goto out;
185762306a36Sopenharmony_ci	}
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	/* check for errors */
186062306a36Sopenharmony_ci	status = map_read(map, cmd_adr);
186162306a36Sopenharmony_ci	if (map_word_bitsset(map, status, CMD(0x1a))) {
186262306a36Sopenharmony_ci		unsigned long chipstatus = MERGESTATUS(status);
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci		/* reset status */
186562306a36Sopenharmony_ci		map_write(map, CMD(0x50), cmd_adr);
186662306a36Sopenharmony_ci		map_write(map, CMD(0x70), cmd_adr);
186762306a36Sopenharmony_ci		xip_enable(map, chip, cmd_adr);
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci		if (chipstatus & 0x02) {
187062306a36Sopenharmony_ci			ret = -EROFS;
187162306a36Sopenharmony_ci		} else if (chipstatus & 0x08) {
187262306a36Sopenharmony_ci			printk(KERN_ERR "%s: buffer write error (bad VPP)\n", map->name);
187362306a36Sopenharmony_ci			ret = -EIO;
187462306a36Sopenharmony_ci		} else {
187562306a36Sopenharmony_ci			printk(KERN_ERR "%s: buffer write error (status 0x%lx)\n", map->name, chipstatus);
187662306a36Sopenharmony_ci			ret = -EINVAL;
187762306a36Sopenharmony_ci		}
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci		goto out;
188062306a36Sopenharmony_ci	}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	xip_enable(map, chip, cmd_adr);
188362306a36Sopenharmony_ci out:	DISABLE_VPP(map);
188462306a36Sopenharmony_ci	put_chip(map, chip, cmd_adr);
188562306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
188662306a36Sopenharmony_ci	return ret;
188762306a36Sopenharmony_ci}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_cistatic int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs,
189062306a36Sopenharmony_ci				unsigned long count, loff_t to, size_t *retlen)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
189362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
189462306a36Sopenharmony_ci	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
189562306a36Sopenharmony_ci	int ret;
189662306a36Sopenharmony_ci	int chipnum;
189762306a36Sopenharmony_ci	unsigned long ofs, vec_seek, i;
189862306a36Sopenharmony_ci	size_t len = 0;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	for (i = 0; i < count; i++)
190162306a36Sopenharmony_ci		len += vecs[i].iov_len;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	if (!len)
190462306a36Sopenharmony_ci		return 0;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	chipnum = to >> cfi->chipshift;
190762306a36Sopenharmony_ci	ofs = to - (chipnum << cfi->chipshift);
190862306a36Sopenharmony_ci	vec_seek = 0;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	do {
191162306a36Sopenharmony_ci		/* We must not cross write block boundaries */
191262306a36Sopenharmony_ci		int size = wbufsize - (ofs & (wbufsize-1));
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci		if (size > len)
191562306a36Sopenharmony_ci			size = len;
191662306a36Sopenharmony_ci		ret = do_write_buffer(map, &cfi->chips[chipnum],
191762306a36Sopenharmony_ci				      ofs, &vecs, &vec_seek, size);
191862306a36Sopenharmony_ci		if (ret)
191962306a36Sopenharmony_ci			return ret;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci		ofs += size;
192262306a36Sopenharmony_ci		(*retlen) += size;
192362306a36Sopenharmony_ci		len -= size;
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci		if (ofs >> cfi->chipshift) {
192662306a36Sopenharmony_ci			chipnum ++;
192762306a36Sopenharmony_ci			ofs = 0;
192862306a36Sopenharmony_ci			if (chipnum == cfi->numchips)
192962306a36Sopenharmony_ci				return 0;
193062306a36Sopenharmony_ci		}
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci		/* Be nice and reschedule with the chip in a usable state for other
193362306a36Sopenharmony_ci		   processes. */
193462306a36Sopenharmony_ci		cond_resched();
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	} while (len);
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	return 0;
193962306a36Sopenharmony_ci}
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_cistatic int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to,
194262306a36Sopenharmony_ci				       size_t len, size_t *retlen, const u_char *buf)
194362306a36Sopenharmony_ci{
194462306a36Sopenharmony_ci	struct kvec vec;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	vec.iov_base = (void *) buf;
194762306a36Sopenharmony_ci	vec.iov_len = len;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	return cfi_intelext_writev(mtd, &vec, 1, to, retlen);
195062306a36Sopenharmony_ci}
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_cistatic int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
195362306a36Sopenharmony_ci				      unsigned long adr, int len, void *thunk)
195462306a36Sopenharmony_ci{
195562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
195662306a36Sopenharmony_ci	map_word status;
195762306a36Sopenharmony_ci	int retries = 3;
195862306a36Sopenharmony_ci	int ret;
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	adr += chip->start;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci retry:
196362306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
196462306a36Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_ERASING);
196562306a36Sopenharmony_ci	if (ret) {
196662306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
196762306a36Sopenharmony_ci		return ret;
196862306a36Sopenharmony_ci	}
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, len);
197162306a36Sopenharmony_ci	ENABLE_VPP(map);
197262306a36Sopenharmony_ci	xip_disable(map, chip, adr);
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	/* Clear the status register first */
197562306a36Sopenharmony_ci	map_write(map, CMD(0x50), adr);
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	/* Now erase */
197862306a36Sopenharmony_ci	map_write(map, CMD(0x20), adr);
197962306a36Sopenharmony_ci	map_write(map, CMD(0xD0), adr);
198062306a36Sopenharmony_ci	chip->state = FL_ERASING;
198162306a36Sopenharmony_ci	chip->erase_suspended = 0;
198262306a36Sopenharmony_ci	chip->in_progress_block_addr = adr;
198362306a36Sopenharmony_ci	chip->in_progress_block_mask = ~(len - 1);
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci	ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
198662306a36Sopenharmony_ci				   adr, len,
198762306a36Sopenharmony_ci				   chip->erase_time,
198862306a36Sopenharmony_ci				   chip->erase_time_max);
198962306a36Sopenharmony_ci	if (ret) {
199062306a36Sopenharmony_ci		map_write(map, CMD(0x70), adr);
199162306a36Sopenharmony_ci		chip->state = FL_STATUS;
199262306a36Sopenharmony_ci		xip_enable(map, chip, adr);
199362306a36Sopenharmony_ci		printk(KERN_ERR "%s: block erase error: (status timeout)\n", map->name);
199462306a36Sopenharmony_ci		goto out;
199562306a36Sopenharmony_ci	}
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	/* We've broken this before. It doesn't hurt to be safe */
199862306a36Sopenharmony_ci	map_write(map, CMD(0x70), adr);
199962306a36Sopenharmony_ci	chip->state = FL_STATUS;
200062306a36Sopenharmony_ci	status = map_read(map, adr);
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	/* check for errors */
200362306a36Sopenharmony_ci	if (map_word_bitsset(map, status, CMD(0x3a))) {
200462306a36Sopenharmony_ci		unsigned long chipstatus = MERGESTATUS(status);
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci		/* Reset the error bits */
200762306a36Sopenharmony_ci		map_write(map, CMD(0x50), adr);
200862306a36Sopenharmony_ci		map_write(map, CMD(0x70), adr);
200962306a36Sopenharmony_ci		xip_enable(map, chip, adr);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci		if ((chipstatus & 0x30) == 0x30) {
201262306a36Sopenharmony_ci			printk(KERN_ERR "%s: block erase error: (bad command sequence, status 0x%lx)\n", map->name, chipstatus);
201362306a36Sopenharmony_ci			ret = -EINVAL;
201462306a36Sopenharmony_ci		} else if (chipstatus & 0x02) {
201562306a36Sopenharmony_ci			/* Protection bit set */
201662306a36Sopenharmony_ci			ret = -EROFS;
201762306a36Sopenharmony_ci		} else if (chipstatus & 0x8) {
201862306a36Sopenharmony_ci			/* Voltage */
201962306a36Sopenharmony_ci			printk(KERN_ERR "%s: block erase error: (bad VPP)\n", map->name);
202062306a36Sopenharmony_ci			ret = -EIO;
202162306a36Sopenharmony_ci		} else if (chipstatus & 0x20 && retries--) {
202262306a36Sopenharmony_ci			printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus);
202362306a36Sopenharmony_ci			DISABLE_VPP(map);
202462306a36Sopenharmony_ci			put_chip(map, chip, adr);
202562306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
202662306a36Sopenharmony_ci			goto retry;
202762306a36Sopenharmony_ci		} else {
202862306a36Sopenharmony_ci			printk(KERN_ERR "%s: block erase failed at 0x%08lx (status 0x%lx)\n", map->name, adr, chipstatus);
202962306a36Sopenharmony_ci			ret = -EIO;
203062306a36Sopenharmony_ci		}
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci		goto out;
203362306a36Sopenharmony_ci	}
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	xip_enable(map, chip, adr);
203662306a36Sopenharmony_ci out:	DISABLE_VPP(map);
203762306a36Sopenharmony_ci	put_chip(map, chip, adr);
203862306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
203962306a36Sopenharmony_ci	return ret;
204062306a36Sopenharmony_ci}
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_cistatic int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
204362306a36Sopenharmony_ci{
204462306a36Sopenharmony_ci	return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr,
204562306a36Sopenharmony_ci				instr->len, NULL);
204662306a36Sopenharmony_ci}
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_cistatic void cfi_intelext_sync (struct mtd_info *mtd)
204962306a36Sopenharmony_ci{
205062306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
205162306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
205262306a36Sopenharmony_ci	int i;
205362306a36Sopenharmony_ci	struct flchip *chip;
205462306a36Sopenharmony_ci	int ret = 0;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	for (i=0; !ret && i<cfi->numchips; i++) {
205762306a36Sopenharmony_ci		chip = &cfi->chips[i];
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
206062306a36Sopenharmony_ci		ret = get_chip(map, chip, chip->start, FL_SYNCING);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci		if (!ret) {
206362306a36Sopenharmony_ci			chip->oldstate = chip->state;
206462306a36Sopenharmony_ci			chip->state = FL_SYNCING;
206562306a36Sopenharmony_ci			/* No need to wake_up() on this state change -
206662306a36Sopenharmony_ci			 * as the whole point is that nobody can do anything
206762306a36Sopenharmony_ci			 * with the chip now anyway.
206862306a36Sopenharmony_ci			 */
206962306a36Sopenharmony_ci		}
207062306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
207162306a36Sopenharmony_ci	}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	/* Unlock the chips again */
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	for (i--; i >=0; i--) {
207662306a36Sopenharmony_ci		chip = &cfi->chips[i];
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci		if (chip->state == FL_SYNCING) {
208162306a36Sopenharmony_ci			chip->state = chip->oldstate;
208262306a36Sopenharmony_ci			chip->oldstate = FL_READY;
208362306a36Sopenharmony_ci			wake_up(&chip->wq);
208462306a36Sopenharmony_ci		}
208562306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
208662306a36Sopenharmony_ci	}
208762306a36Sopenharmony_ci}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_cistatic int __xipram do_getlockstatus_oneblock(struct map_info *map,
209062306a36Sopenharmony_ci						struct flchip *chip,
209162306a36Sopenharmony_ci						unsigned long adr,
209262306a36Sopenharmony_ci						int len, void *thunk)
209362306a36Sopenharmony_ci{
209462306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
209562306a36Sopenharmony_ci	int status, ofs_factor = cfi->interleave * cfi->device_type;
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	adr += chip->start;
209862306a36Sopenharmony_ci	xip_disable(map, chip, adr+(2*ofs_factor));
209962306a36Sopenharmony_ci	map_write(map, CMD(0x90), adr+(2*ofs_factor));
210062306a36Sopenharmony_ci	chip->state = FL_JEDEC_QUERY;
210162306a36Sopenharmony_ci	status = cfi_read_query(map, adr+(2*ofs_factor));
210262306a36Sopenharmony_ci	xip_enable(map, chip, 0);
210362306a36Sopenharmony_ci	return status;
210462306a36Sopenharmony_ci}
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS
210762306a36Sopenharmony_cistatic int __xipram do_printlockstatus_oneblock(struct map_info *map,
210862306a36Sopenharmony_ci						struct flchip *chip,
210962306a36Sopenharmony_ci						unsigned long adr,
211062306a36Sopenharmony_ci						int len, void *thunk)
211162306a36Sopenharmony_ci{
211262306a36Sopenharmony_ci	printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
211362306a36Sopenharmony_ci	       adr, do_getlockstatus_oneblock(map, chip, adr, len, thunk));
211462306a36Sopenharmony_ci	return 0;
211562306a36Sopenharmony_ci}
211662306a36Sopenharmony_ci#endif
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_LOCK		((void *) 1)
211962306a36Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_UNLOCK	((void *) 2)
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_cistatic int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
212262306a36Sopenharmony_ci				       unsigned long adr, int len, void *thunk)
212362306a36Sopenharmony_ci{
212462306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
212562306a36Sopenharmony_ci	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
212662306a36Sopenharmony_ci	int mdelay;
212762306a36Sopenharmony_ci	int ret;
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	adr += chip->start;
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
213262306a36Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_LOCKING);
213362306a36Sopenharmony_ci	if (ret) {
213462306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
213562306a36Sopenharmony_ci		return ret;
213662306a36Sopenharmony_ci	}
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci	ENABLE_VPP(map);
213962306a36Sopenharmony_ci	xip_disable(map, chip, adr);
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	map_write(map, CMD(0x60), adr);
214262306a36Sopenharmony_ci	if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) {
214362306a36Sopenharmony_ci		map_write(map, CMD(0x01), adr);
214462306a36Sopenharmony_ci		chip->state = FL_LOCKING;
214562306a36Sopenharmony_ci	} else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) {
214662306a36Sopenharmony_ci		map_write(map, CMD(0xD0), adr);
214762306a36Sopenharmony_ci		chip->state = FL_UNLOCKING;
214862306a36Sopenharmony_ci	} else
214962306a36Sopenharmony_ci		BUG();
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	/*
215262306a36Sopenharmony_ci	 * If Instant Individual Block Locking supported then no need
215362306a36Sopenharmony_ci	 * to delay.
215462306a36Sopenharmony_ci	 */
215562306a36Sopenharmony_ci	/*
215662306a36Sopenharmony_ci	 * Unlocking may take up to 1.4 seconds on some Intel flashes. So
215762306a36Sopenharmony_ci	 * lets use a max of 1.5 seconds (1500ms) as timeout.
215862306a36Sopenharmony_ci	 *
215962306a36Sopenharmony_ci	 * See "Clear Block Lock-Bits Time" on page 40 in
216062306a36Sopenharmony_ci	 * "3 Volt Intel StrataFlash Memory" 28F128J3,28F640J3,28F320J3 manual
216162306a36Sopenharmony_ci	 * from February 2003
216262306a36Sopenharmony_ci	 */
216362306a36Sopenharmony_ci	mdelay = (!extp || !(extp->FeatureSupport & (1 << 5))) ? 1500 : 0;
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	ret = WAIT_TIMEOUT(map, chip, adr, mdelay, mdelay * 1000);
216662306a36Sopenharmony_ci	if (ret) {
216762306a36Sopenharmony_ci		map_write(map, CMD(0x70), adr);
216862306a36Sopenharmony_ci		chip->state = FL_STATUS;
216962306a36Sopenharmony_ci		xip_enable(map, chip, adr);
217062306a36Sopenharmony_ci		printk(KERN_ERR "%s: block unlock error: (status timeout)\n", map->name);
217162306a36Sopenharmony_ci		goto out;
217262306a36Sopenharmony_ci	}
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	xip_enable(map, chip, adr);
217562306a36Sopenharmony_ci out:	DISABLE_VPP(map);
217662306a36Sopenharmony_ci	put_chip(map, chip, adr);
217762306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
217862306a36Sopenharmony_ci	return ret;
217962306a36Sopenharmony_ci}
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_cistatic int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
218262306a36Sopenharmony_ci{
218362306a36Sopenharmony_ci	int ret;
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS
218662306a36Sopenharmony_ci	printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
218762306a36Sopenharmony_ci	       __func__, ofs, len);
218862306a36Sopenharmony_ci	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
218962306a36Sopenharmony_ci		ofs, len, NULL);
219062306a36Sopenharmony_ci#endif
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
219362306a36Sopenharmony_ci		ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS
219662306a36Sopenharmony_ci	printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
219762306a36Sopenharmony_ci	       __func__, ret);
219862306a36Sopenharmony_ci	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
219962306a36Sopenharmony_ci		ofs, len, NULL);
220062306a36Sopenharmony_ci#endif
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	return ret;
220362306a36Sopenharmony_ci}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_cistatic int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
220662306a36Sopenharmony_ci{
220762306a36Sopenharmony_ci	int ret;
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS
221062306a36Sopenharmony_ci	printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
221162306a36Sopenharmony_ci	       __func__, ofs, len);
221262306a36Sopenharmony_ci	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
221362306a36Sopenharmony_ci		ofs, len, NULL);
221462306a36Sopenharmony_ci#endif
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
221762306a36Sopenharmony_ci					ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK);
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS
222062306a36Sopenharmony_ci	printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
222162306a36Sopenharmony_ci	       __func__, ret);
222262306a36Sopenharmony_ci	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
222362306a36Sopenharmony_ci		ofs, len, NULL);
222462306a36Sopenharmony_ci#endif
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	return ret;
222762306a36Sopenharmony_ci}
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_cistatic int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs,
223062306a36Sopenharmony_ci				  uint64_t len)
223162306a36Sopenharmony_ci{
223262306a36Sopenharmony_ci	return cfi_varsize_frob(mtd, do_getlockstatus_oneblock,
223362306a36Sopenharmony_ci				ofs, len, NULL) ? 1 : 0;
223462306a36Sopenharmony_ci}
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci#ifdef CONFIG_MTD_OTP
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_citypedef int (*otp_op_t)(struct map_info *map, struct flchip *chip,
223962306a36Sopenharmony_ci			u_long data_offset, u_char *buf, u_int size,
224062306a36Sopenharmony_ci			u_long prot_offset, u_int groupno, u_int groupsize);
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_cistatic int __xipram
224362306a36Sopenharmony_cido_otp_read(struct map_info *map, struct flchip *chip, u_long offset,
224462306a36Sopenharmony_ci	    u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
224562306a36Sopenharmony_ci{
224662306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
224762306a36Sopenharmony_ci	int ret;
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
225062306a36Sopenharmony_ci	ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
225162306a36Sopenharmony_ci	if (ret) {
225262306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
225362306a36Sopenharmony_ci		return ret;
225462306a36Sopenharmony_ci	}
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	/* let's ensure we're not reading back cached data from array mode */
225762306a36Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, chip->start + offset, size);
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	xip_disable(map, chip, chip->start);
226062306a36Sopenharmony_ci	if (chip->state != FL_JEDEC_QUERY) {
226162306a36Sopenharmony_ci		map_write(map, CMD(0x90), chip->start);
226262306a36Sopenharmony_ci		chip->state = FL_JEDEC_QUERY;
226362306a36Sopenharmony_ci	}
226462306a36Sopenharmony_ci	map_copy_from(map, buf, chip->start + offset, size);
226562306a36Sopenharmony_ci	xip_enable(map, chip, chip->start);
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	/* then ensure we don't keep OTP data in the cache */
226862306a36Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, chip->start + offset, size);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	put_chip(map, chip, chip->start);
227162306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
227262306a36Sopenharmony_ci	return 0;
227362306a36Sopenharmony_ci}
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_cistatic int
227662306a36Sopenharmony_cido_otp_write(struct map_info *map, struct flchip *chip, u_long offset,
227762306a36Sopenharmony_ci	     u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
227862306a36Sopenharmony_ci{
227962306a36Sopenharmony_ci	int ret;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	while (size) {
228262306a36Sopenharmony_ci		unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1);
228362306a36Sopenharmony_ci		int gap = offset - bus_ofs;
228462306a36Sopenharmony_ci		int n = min_t(int, size, map_bankwidth(map)-gap);
228562306a36Sopenharmony_ci		map_word datum = map_word_ff(map);
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_ci		datum = map_word_load_partial(map, datum, buf, gap, n);
228862306a36Sopenharmony_ci		ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE);
228962306a36Sopenharmony_ci		if (ret)
229062306a36Sopenharmony_ci			return ret;
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci		offset += n;
229362306a36Sopenharmony_ci		buf += n;
229462306a36Sopenharmony_ci		size -= n;
229562306a36Sopenharmony_ci	}
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	return 0;
229862306a36Sopenharmony_ci}
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_cistatic int
230162306a36Sopenharmony_cido_otp_lock(struct map_info *map, struct flchip *chip, u_long offset,
230262306a36Sopenharmony_ci	    u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
230362306a36Sopenharmony_ci{
230462306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
230562306a36Sopenharmony_ci	map_word datum;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	/* make sure area matches group boundaries */
230862306a36Sopenharmony_ci	if (size != grpsz)
230962306a36Sopenharmony_ci		return -EXDEV;
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	datum = map_word_ff(map);
231262306a36Sopenharmony_ci	datum = map_word_clr(map, datum, CMD(1 << grpno));
231362306a36Sopenharmony_ci	return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE);
231462306a36Sopenharmony_ci}
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_cistatic int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
231762306a36Sopenharmony_ci				 size_t *retlen, u_char *buf,
231862306a36Sopenharmony_ci				 otp_op_t action, int user_regs)
231962306a36Sopenharmony_ci{
232062306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
232162306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
232262306a36Sopenharmony_ci	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
232362306a36Sopenharmony_ci	struct flchip *chip;
232462306a36Sopenharmony_ci	struct cfi_intelext_otpinfo *otp;
232562306a36Sopenharmony_ci	u_long devsize, reg_prot_offset, data_offset;
232662306a36Sopenharmony_ci	u_int chip_num, chip_step, field, reg_fact_size, reg_user_size;
232762306a36Sopenharmony_ci	u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups;
232862306a36Sopenharmony_ci	int ret;
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	*retlen = 0;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	/* Check that we actually have some OTP registers */
233362306a36Sopenharmony_ci	if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields)
233462306a36Sopenharmony_ci		return -ENODATA;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	/* we need real chips here not virtual ones */
233762306a36Sopenharmony_ci	devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave;
233862306a36Sopenharmony_ci	chip_step = devsize >> cfi->chipshift;
233962306a36Sopenharmony_ci	chip_num = 0;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	/* Some chips have OTP located in the _top_ partition only.
234262306a36Sopenharmony_ci	   For example: Intel 28F256L18T (T means top-parameter device) */
234362306a36Sopenharmony_ci	if (cfi->mfr == CFI_MFR_INTEL) {
234462306a36Sopenharmony_ci		switch (cfi->id) {
234562306a36Sopenharmony_ci		case 0x880b:
234662306a36Sopenharmony_ci		case 0x880c:
234762306a36Sopenharmony_ci		case 0x880d:
234862306a36Sopenharmony_ci			chip_num = chip_step - 1;
234962306a36Sopenharmony_ci		}
235062306a36Sopenharmony_ci	}
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	for ( ; chip_num < cfi->numchips; chip_num += chip_step) {
235362306a36Sopenharmony_ci		chip = &cfi->chips[chip_num];
235462306a36Sopenharmony_ci		otp = (struct cfi_intelext_otpinfo *)&extp->extra[0];
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci		/* first OTP region */
235762306a36Sopenharmony_ci		field = 0;
235862306a36Sopenharmony_ci		reg_prot_offset = extp->ProtRegAddr;
235962306a36Sopenharmony_ci		reg_fact_groups = 1;
236062306a36Sopenharmony_ci		reg_fact_size = 1 << extp->FactProtRegSize;
236162306a36Sopenharmony_ci		reg_user_groups = 1;
236262306a36Sopenharmony_ci		reg_user_size = 1 << extp->UserProtRegSize;
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci		while (len > 0) {
236562306a36Sopenharmony_ci			/* flash geometry fixup */
236662306a36Sopenharmony_ci			data_offset = reg_prot_offset + 1;
236762306a36Sopenharmony_ci			data_offset *= cfi->interleave * cfi->device_type;
236862306a36Sopenharmony_ci			reg_prot_offset *= cfi->interleave * cfi->device_type;
236962306a36Sopenharmony_ci			reg_fact_size *= cfi->interleave;
237062306a36Sopenharmony_ci			reg_user_size *= cfi->interleave;
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci			if (user_regs) {
237362306a36Sopenharmony_ci				groups = reg_user_groups;
237462306a36Sopenharmony_ci				groupsize = reg_user_size;
237562306a36Sopenharmony_ci				/* skip over factory reg area */
237662306a36Sopenharmony_ci				groupno = reg_fact_groups;
237762306a36Sopenharmony_ci				data_offset += reg_fact_groups * reg_fact_size;
237862306a36Sopenharmony_ci			} else {
237962306a36Sopenharmony_ci				groups = reg_fact_groups;
238062306a36Sopenharmony_ci				groupsize = reg_fact_size;
238162306a36Sopenharmony_ci				groupno = 0;
238262306a36Sopenharmony_ci			}
238362306a36Sopenharmony_ci
238462306a36Sopenharmony_ci			while (len > 0 && groups > 0) {
238562306a36Sopenharmony_ci				if (!action) {
238662306a36Sopenharmony_ci					/*
238762306a36Sopenharmony_ci					 * Special case: if action is NULL
238862306a36Sopenharmony_ci					 * we fill buf with otp_info records.
238962306a36Sopenharmony_ci					 */
239062306a36Sopenharmony_ci					struct otp_info *otpinfo;
239162306a36Sopenharmony_ci					map_word lockword;
239262306a36Sopenharmony_ci					len -= sizeof(struct otp_info);
239362306a36Sopenharmony_ci					if (len <= 0)
239462306a36Sopenharmony_ci						return -ENOSPC;
239562306a36Sopenharmony_ci					ret = do_otp_read(map, chip,
239662306a36Sopenharmony_ci							  reg_prot_offset,
239762306a36Sopenharmony_ci							  (u_char *)&lockword,
239862306a36Sopenharmony_ci							  map_bankwidth(map),
239962306a36Sopenharmony_ci							  0, 0,  0);
240062306a36Sopenharmony_ci					if (ret)
240162306a36Sopenharmony_ci						return ret;
240262306a36Sopenharmony_ci					otpinfo = (struct otp_info *)buf;
240362306a36Sopenharmony_ci					otpinfo->start = from;
240462306a36Sopenharmony_ci					otpinfo->length = groupsize;
240562306a36Sopenharmony_ci					otpinfo->locked =
240662306a36Sopenharmony_ci					   !map_word_bitsset(map, lockword,
240762306a36Sopenharmony_ci							     CMD(1 << groupno));
240862306a36Sopenharmony_ci					from += groupsize;
240962306a36Sopenharmony_ci					buf += sizeof(*otpinfo);
241062306a36Sopenharmony_ci					*retlen += sizeof(*otpinfo);
241162306a36Sopenharmony_ci				} else if (from >= groupsize) {
241262306a36Sopenharmony_ci					from -= groupsize;
241362306a36Sopenharmony_ci					data_offset += groupsize;
241462306a36Sopenharmony_ci				} else {
241562306a36Sopenharmony_ci					int size = groupsize;
241662306a36Sopenharmony_ci					data_offset += from;
241762306a36Sopenharmony_ci					size -= from;
241862306a36Sopenharmony_ci					from = 0;
241962306a36Sopenharmony_ci					if (size > len)
242062306a36Sopenharmony_ci						size = len;
242162306a36Sopenharmony_ci					ret = action(map, chip, data_offset,
242262306a36Sopenharmony_ci						     buf, size, reg_prot_offset,
242362306a36Sopenharmony_ci						     groupno, groupsize);
242462306a36Sopenharmony_ci					if (ret < 0)
242562306a36Sopenharmony_ci						return ret;
242662306a36Sopenharmony_ci					buf += size;
242762306a36Sopenharmony_ci					len -= size;
242862306a36Sopenharmony_ci					*retlen += size;
242962306a36Sopenharmony_ci					data_offset += size;
243062306a36Sopenharmony_ci				}
243162306a36Sopenharmony_ci				groupno++;
243262306a36Sopenharmony_ci				groups--;
243362306a36Sopenharmony_ci			}
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci			/* next OTP region */
243662306a36Sopenharmony_ci			if (++field == extp->NumProtectionFields)
243762306a36Sopenharmony_ci				break;
243862306a36Sopenharmony_ci			reg_prot_offset = otp->ProtRegAddr;
243962306a36Sopenharmony_ci			reg_fact_groups = otp->FactGroups;
244062306a36Sopenharmony_ci			reg_fact_size = 1 << otp->FactProtRegSize;
244162306a36Sopenharmony_ci			reg_user_groups = otp->UserGroups;
244262306a36Sopenharmony_ci			reg_user_size = 1 << otp->UserProtRegSize;
244362306a36Sopenharmony_ci			otp++;
244462306a36Sopenharmony_ci		}
244562306a36Sopenharmony_ci	}
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	return 0;
244862306a36Sopenharmony_ci}
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_cistatic int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
245162306a36Sopenharmony_ci					   size_t len, size_t *retlen,
245262306a36Sopenharmony_ci					    u_char *buf)
245362306a36Sopenharmony_ci{
245462306a36Sopenharmony_ci	return cfi_intelext_otp_walk(mtd, from, len, retlen,
245562306a36Sopenharmony_ci				     buf, do_otp_read, 0);
245662306a36Sopenharmony_ci}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_cistatic int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
245962306a36Sopenharmony_ci					   size_t len, size_t *retlen,
246062306a36Sopenharmony_ci					    u_char *buf)
246162306a36Sopenharmony_ci{
246262306a36Sopenharmony_ci	return cfi_intelext_otp_walk(mtd, from, len, retlen,
246362306a36Sopenharmony_ci				     buf, do_otp_read, 1);
246462306a36Sopenharmony_ci}
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_cistatic int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
246762306a36Sopenharmony_ci					    size_t len, size_t *retlen,
246862306a36Sopenharmony_ci					    const u_char *buf)
246962306a36Sopenharmony_ci{
247062306a36Sopenharmony_ci	return cfi_intelext_otp_walk(mtd, from, len, retlen,
247162306a36Sopenharmony_ci				     (u_char *)buf, do_otp_write, 1);
247262306a36Sopenharmony_ci}
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_cistatic int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
247562306a36Sopenharmony_ci					   loff_t from, size_t len)
247662306a36Sopenharmony_ci{
247762306a36Sopenharmony_ci	size_t retlen;
247862306a36Sopenharmony_ci	return cfi_intelext_otp_walk(mtd, from, len, &retlen,
247962306a36Sopenharmony_ci				     NULL, do_otp_lock, 1);
248062306a36Sopenharmony_ci}
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_cistatic int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, size_t len,
248362306a36Sopenharmony_ci					   size_t *retlen, struct otp_info *buf)
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci{
248662306a36Sopenharmony_ci	return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
248762306a36Sopenharmony_ci				     NULL, 0);
248862306a36Sopenharmony_ci}
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_cistatic int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, size_t len,
249162306a36Sopenharmony_ci					   size_t *retlen, struct otp_info *buf)
249262306a36Sopenharmony_ci{
249362306a36Sopenharmony_ci	return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
249462306a36Sopenharmony_ci				     NULL, 1);
249562306a36Sopenharmony_ci}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci#endif
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_cistatic void cfi_intelext_save_locks(struct mtd_info *mtd)
250062306a36Sopenharmony_ci{
250162306a36Sopenharmony_ci	struct mtd_erase_region_info *region;
250262306a36Sopenharmony_ci	int block, status, i;
250362306a36Sopenharmony_ci	unsigned long adr;
250462306a36Sopenharmony_ci	size_t len;
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	for (i = 0; i < mtd->numeraseregions; i++) {
250762306a36Sopenharmony_ci		region = &mtd->eraseregions[i];
250862306a36Sopenharmony_ci		if (!region->lockmap)
250962306a36Sopenharmony_ci			continue;
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci		for (block = 0; block < region->numblocks; block++){
251262306a36Sopenharmony_ci			len = region->erasesize;
251362306a36Sopenharmony_ci			adr = region->offset + block * len;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci			status = cfi_varsize_frob(mtd,
251662306a36Sopenharmony_ci					do_getlockstatus_oneblock, adr, len, NULL);
251762306a36Sopenharmony_ci			if (status)
251862306a36Sopenharmony_ci				set_bit(block, region->lockmap);
251962306a36Sopenharmony_ci			else
252062306a36Sopenharmony_ci				clear_bit(block, region->lockmap);
252162306a36Sopenharmony_ci		}
252262306a36Sopenharmony_ci	}
252362306a36Sopenharmony_ci}
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_cistatic int cfi_intelext_suspend(struct mtd_info *mtd)
252662306a36Sopenharmony_ci{
252762306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
252862306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
252962306a36Sopenharmony_ci	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
253062306a36Sopenharmony_ci	int i;
253162306a36Sopenharmony_ci	struct flchip *chip;
253262306a36Sopenharmony_ci	int ret = 0;
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	if ((mtd->flags & MTD_POWERUP_LOCK)
253562306a36Sopenharmony_ci	    && extp && (extp->FeatureSupport & (1 << 5)))
253662306a36Sopenharmony_ci		cfi_intelext_save_locks(mtd);
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	for (i=0; !ret && i<cfi->numchips; i++) {
253962306a36Sopenharmony_ci		chip = &cfi->chips[i];
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci		switch (chip->state) {
254462306a36Sopenharmony_ci		case FL_READY:
254562306a36Sopenharmony_ci		case FL_STATUS:
254662306a36Sopenharmony_ci		case FL_CFI_QUERY:
254762306a36Sopenharmony_ci		case FL_JEDEC_QUERY:
254862306a36Sopenharmony_ci			if (chip->oldstate == FL_READY) {
254962306a36Sopenharmony_ci				/* place the chip in a known state before suspend */
255062306a36Sopenharmony_ci				map_write(map, CMD(0xFF), cfi->chips[i].start);
255162306a36Sopenharmony_ci				chip->oldstate = chip->state;
255262306a36Sopenharmony_ci				chip->state = FL_PM_SUSPENDED;
255362306a36Sopenharmony_ci				/* No need to wake_up() on this state change -
255462306a36Sopenharmony_ci				 * as the whole point is that nobody can do anything
255562306a36Sopenharmony_ci				 * with the chip now anyway.
255662306a36Sopenharmony_ci				 */
255762306a36Sopenharmony_ci			} else {
255862306a36Sopenharmony_ci				/* There seems to be an operation pending. We must wait for it. */
255962306a36Sopenharmony_ci				printk(KERN_NOTICE "Flash device refused suspend due to pending operation (oldstate %d)\n", chip->oldstate);
256062306a36Sopenharmony_ci				ret = -EAGAIN;
256162306a36Sopenharmony_ci			}
256262306a36Sopenharmony_ci			break;
256362306a36Sopenharmony_ci		default:
256462306a36Sopenharmony_ci			/* Should we actually wait? Once upon a time these routines weren't
256562306a36Sopenharmony_ci			   allowed to. Or should we return -EAGAIN, because the upper layers
256662306a36Sopenharmony_ci			   ought to have already shut down anything which was using the device
256762306a36Sopenharmony_ci			   anyway? The latter for now. */
256862306a36Sopenharmony_ci			printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->state);
256962306a36Sopenharmony_ci			ret = -EAGAIN;
257062306a36Sopenharmony_ci			break;
257162306a36Sopenharmony_ci		case FL_PM_SUSPENDED:
257262306a36Sopenharmony_ci			break;
257362306a36Sopenharmony_ci		}
257462306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
257562306a36Sopenharmony_ci	}
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	/* Unlock the chips again */
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci	if (ret) {
258062306a36Sopenharmony_ci		for (i--; i >=0; i--) {
258162306a36Sopenharmony_ci			chip = &cfi->chips[i];
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci			if (chip->state == FL_PM_SUSPENDED) {
258662306a36Sopenharmony_ci				/* No need to force it into a known state here,
258762306a36Sopenharmony_ci				   because we're returning failure, and it didn't
258862306a36Sopenharmony_ci				   get power cycled */
258962306a36Sopenharmony_ci				chip->state = chip->oldstate;
259062306a36Sopenharmony_ci				chip->oldstate = FL_READY;
259162306a36Sopenharmony_ci				wake_up(&chip->wq);
259262306a36Sopenharmony_ci			}
259362306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
259462306a36Sopenharmony_ci		}
259562306a36Sopenharmony_ci	}
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	return ret;
259862306a36Sopenharmony_ci}
259962306a36Sopenharmony_ci
260062306a36Sopenharmony_cistatic void cfi_intelext_restore_locks(struct mtd_info *mtd)
260162306a36Sopenharmony_ci{
260262306a36Sopenharmony_ci	struct mtd_erase_region_info *region;
260362306a36Sopenharmony_ci	int block, i;
260462306a36Sopenharmony_ci	unsigned long adr;
260562306a36Sopenharmony_ci	size_t len;
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ci	for (i = 0; i < mtd->numeraseregions; i++) {
260862306a36Sopenharmony_ci		region = &mtd->eraseregions[i];
260962306a36Sopenharmony_ci		if (!region->lockmap)
261062306a36Sopenharmony_ci			continue;
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci		for_each_clear_bit(block, region->lockmap, region->numblocks) {
261362306a36Sopenharmony_ci			len = region->erasesize;
261462306a36Sopenharmony_ci			adr = region->offset + block * len;
261562306a36Sopenharmony_ci			cfi_intelext_unlock(mtd, adr, len);
261662306a36Sopenharmony_ci		}
261762306a36Sopenharmony_ci	}
261862306a36Sopenharmony_ci}
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_cistatic void cfi_intelext_resume(struct mtd_info *mtd)
262162306a36Sopenharmony_ci{
262262306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
262362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
262462306a36Sopenharmony_ci	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
262562306a36Sopenharmony_ci	int i;
262662306a36Sopenharmony_ci	struct flchip *chip;
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	for (i=0; i<cfi->numchips; i++) {
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci		chip = &cfi->chips[i];
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci		/* Go to known state. Chip may have been power cycled */
263562306a36Sopenharmony_ci		if (chip->state == FL_PM_SUSPENDED) {
263662306a36Sopenharmony_ci			/* Refresh LH28F640BF Partition Config. Register */
263762306a36Sopenharmony_ci			fixup_LH28F640BF(mtd);
263862306a36Sopenharmony_ci			map_write(map, CMD(0xFF), cfi->chips[i].start);
263962306a36Sopenharmony_ci			chip->oldstate = chip->state = FL_READY;
264062306a36Sopenharmony_ci			wake_up(&chip->wq);
264162306a36Sopenharmony_ci		}
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
264462306a36Sopenharmony_ci	}
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	if ((mtd->flags & MTD_POWERUP_LOCK)
264762306a36Sopenharmony_ci	    && extp && (extp->FeatureSupport & (1 << 5)))
264862306a36Sopenharmony_ci		cfi_intelext_restore_locks(mtd);
264962306a36Sopenharmony_ci}
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_cistatic int cfi_intelext_reset(struct mtd_info *mtd)
265262306a36Sopenharmony_ci{
265362306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
265462306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
265562306a36Sopenharmony_ci	int i, ret;
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	for (i=0; i < cfi->numchips; i++) {
265862306a36Sopenharmony_ci		struct flchip *chip = &cfi->chips[i];
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_ci		/* force the completion of any ongoing operation
266162306a36Sopenharmony_ci		   and switch to array mode so any bootloader in
266262306a36Sopenharmony_ci		   flash is accessible for soft reboot. */
266362306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
266462306a36Sopenharmony_ci		ret = get_chip(map, chip, chip->start, FL_SHUTDOWN);
266562306a36Sopenharmony_ci		if (!ret) {
266662306a36Sopenharmony_ci			map_write(map, CMD(0xff), chip->start);
266762306a36Sopenharmony_ci			chip->state = FL_SHUTDOWN;
266862306a36Sopenharmony_ci			put_chip(map, chip, chip->start);
266962306a36Sopenharmony_ci		}
267062306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
267162306a36Sopenharmony_ci	}
267262306a36Sopenharmony_ci
267362306a36Sopenharmony_ci	return 0;
267462306a36Sopenharmony_ci}
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_cistatic int cfi_intelext_reboot(struct notifier_block *nb, unsigned long val,
267762306a36Sopenharmony_ci			       void *v)
267862306a36Sopenharmony_ci{
267962306a36Sopenharmony_ci	struct mtd_info *mtd;
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	mtd = container_of(nb, struct mtd_info, reboot_notifier);
268262306a36Sopenharmony_ci	cfi_intelext_reset(mtd);
268362306a36Sopenharmony_ci	return NOTIFY_DONE;
268462306a36Sopenharmony_ci}
268562306a36Sopenharmony_ci
268662306a36Sopenharmony_cistatic void cfi_intelext_destroy(struct mtd_info *mtd)
268762306a36Sopenharmony_ci{
268862306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
268962306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
269062306a36Sopenharmony_ci	struct mtd_erase_region_info *region;
269162306a36Sopenharmony_ci	int i;
269262306a36Sopenharmony_ci	cfi_intelext_reset(mtd);
269362306a36Sopenharmony_ci	unregister_reboot_notifier(&mtd->reboot_notifier);
269462306a36Sopenharmony_ci	kfree(cfi->cmdset_priv);
269562306a36Sopenharmony_ci	kfree(cfi->cfiq);
269662306a36Sopenharmony_ci	kfree(cfi->chips[0].priv);
269762306a36Sopenharmony_ci	kfree(cfi);
269862306a36Sopenharmony_ci	for (i = 0; i < mtd->numeraseregions; i++) {
269962306a36Sopenharmony_ci		region = &mtd->eraseregions[i];
270062306a36Sopenharmony_ci		kfree(region->lockmap);
270162306a36Sopenharmony_ci	}
270262306a36Sopenharmony_ci	kfree(mtd->eraseregions);
270362306a36Sopenharmony_ci}
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
270662306a36Sopenharmony_ciMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
270762306a36Sopenharmony_ciMODULE_DESCRIPTION("MTD chip driver for Intel/Sharp flash chips");
270862306a36Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0003");
270962306a36Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0200");
2710