162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Common Flash Interface support:
462306a36Sopenharmony_ci *   AMD & Fujitsu Standard Vendor Command Set (ID 0x0002)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
762306a36Sopenharmony_ci * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com>
862306a36Sopenharmony_ci * Copyright (C) 2005 MontaVista Software Inc. <source@mvista.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * 2_by_8 routines added by Simon Munton
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * 4_by_16 work by Carolyn J. Smith
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * XIP support hooks by Vitaly Wool (based on code for Intel flash
1562306a36Sopenharmony_ci * by Nicolas Pitre)
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * 25/09/2008 Christopher Moore: TopBottom fixup for many Macronix with CFI V1.0
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/types.h>
2462306a36Sopenharmony_ci#include <linux/kernel.h>
2562306a36Sopenharmony_ci#include <linux/sched.h>
2662306a36Sopenharmony_ci#include <asm/io.h>
2762306a36Sopenharmony_ci#include <asm/byteorder.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <linux/errno.h>
3062306a36Sopenharmony_ci#include <linux/slab.h>
3162306a36Sopenharmony_ci#include <linux/delay.h>
3262306a36Sopenharmony_ci#include <linux/interrupt.h>
3362306a36Sopenharmony_ci#include <linux/reboot.h>
3462306a36Sopenharmony_ci#include <linux/of.h>
3562306a36Sopenharmony_ci#include <linux/mtd/map.h>
3662306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
3762306a36Sopenharmony_ci#include <linux/mtd/cfi.h>
3862306a36Sopenharmony_ci#include <linux/mtd/xip.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define AMD_BOOTLOC_BUG
4162306a36Sopenharmony_ci#define FORCE_WORD_WRITE 0
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define MAX_RETRIES 3
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define SST49LF004B		0x0060
4662306a36Sopenharmony_ci#define SST49LF040B		0x0050
4762306a36Sopenharmony_ci#define SST49LF008A		0x005a
4862306a36Sopenharmony_ci#define AT49BV6416		0x00d6
4962306a36Sopenharmony_ci#define S29GL064N_MN12		0x0c01
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * Status Register bit description. Used by flash devices that don't
5362306a36Sopenharmony_ci * support DQ polling (e.g. HyperFlash)
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_ci#define CFI_SR_DRB		BIT(7)
5662306a36Sopenharmony_ci#define CFI_SR_ESB		BIT(5)
5762306a36Sopenharmony_ci#define CFI_SR_PSB		BIT(4)
5862306a36Sopenharmony_ci#define CFI_SR_WBASB		BIT(3)
5962306a36Sopenharmony_ci#define CFI_SR_SLSB		BIT(1)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cienum cfi_quirks {
6262306a36Sopenharmony_ci	CFI_QUIRK_DQ_TRUE_DATA = BIT(0),
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
6662306a36Sopenharmony_cistatic int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
6762306a36Sopenharmony_ci#if !FORCE_WORD_WRITE
6862306a36Sopenharmony_cistatic int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
6962306a36Sopenharmony_ci#endif
7062306a36Sopenharmony_cistatic int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);
7162306a36Sopenharmony_cistatic int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);
7262306a36Sopenharmony_cistatic void cfi_amdstd_sync (struct mtd_info *);
7362306a36Sopenharmony_cistatic int cfi_amdstd_suspend (struct mtd_info *);
7462306a36Sopenharmony_cistatic void cfi_amdstd_resume (struct mtd_info *);
7562306a36Sopenharmony_cistatic int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *);
7662306a36Sopenharmony_cistatic int cfi_amdstd_get_fact_prot_info(struct mtd_info *, size_t,
7762306a36Sopenharmony_ci					 size_t *, struct otp_info *);
7862306a36Sopenharmony_cistatic int cfi_amdstd_get_user_prot_info(struct mtd_info *, size_t,
7962306a36Sopenharmony_ci					 size_t *, struct otp_info *);
8062306a36Sopenharmony_cistatic int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
8162306a36Sopenharmony_cistatic int cfi_amdstd_read_fact_prot_reg(struct mtd_info *, loff_t, size_t,
8262306a36Sopenharmony_ci					 size_t *, u_char *);
8362306a36Sopenharmony_cistatic int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t,
8462306a36Sopenharmony_ci					 size_t *, u_char *);
8562306a36Sopenharmony_cistatic int cfi_amdstd_write_user_prot_reg(struct mtd_info *, loff_t, size_t,
8662306a36Sopenharmony_ci					  size_t *, const u_char *);
8762306a36Sopenharmony_cistatic int cfi_amdstd_lock_user_prot_reg(struct mtd_info *, loff_t, size_t);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
9062306a36Sopenharmony_ci				  size_t *retlen, const u_char *buf);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void cfi_amdstd_destroy(struct mtd_info *);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0002(struct map_info *, int);
9562306a36Sopenharmony_cistatic struct mtd_info *cfi_amdstd_setup (struct mtd_info *);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
9862306a36Sopenharmony_cistatic void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
9962306a36Sopenharmony_ci#include "fwh_lock.h"
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
10262306a36Sopenharmony_cistatic int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
10562306a36Sopenharmony_cistatic int cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
10662306a36Sopenharmony_cistatic int cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic struct mtd_chip_driver cfi_amdstd_chipdrv = {
10962306a36Sopenharmony_ci	.probe		= NULL, /* Not usable directly */
11062306a36Sopenharmony_ci	.destroy	= cfi_amdstd_destroy,
11162306a36Sopenharmony_ci	.name		= "cfi_cmdset_0002",
11262306a36Sopenharmony_ci	.module		= THIS_MODULE
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * Use status register to poll for Erase/write completion when DQ is not
11762306a36Sopenharmony_ci * supported. This is indicated by Bit[1:0] of SoftwareFeatures field in
11862306a36Sopenharmony_ci * CFI Primary Vendor-Specific Extended Query table 1.5
11962306a36Sopenharmony_ci */
12062306a36Sopenharmony_cistatic int cfi_use_status_reg(struct cfi_private *cfi)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
12362306a36Sopenharmony_ci	u8 poll_mask = CFI_POLL_STATUS_REG | CFI_POLL_DQ;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return extp && extp->MinorVersion >= '5' &&
12662306a36Sopenharmony_ci		(extp->SoftwareFeatures & poll_mask) == CFI_POLL_STATUS_REG;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int cfi_check_err_status(struct map_info *map, struct flchip *chip,
13062306a36Sopenharmony_ci				unsigned long adr)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
13362306a36Sopenharmony_ci	map_word status;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (!cfi_use_status_reg(cfi))
13662306a36Sopenharmony_ci		return 0;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,
13962306a36Sopenharmony_ci			 cfi->device_type, NULL);
14062306a36Sopenharmony_ci	status = map_read(map, adr);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* The error bits are invalid while the chip's busy */
14362306a36Sopenharmony_ci	if (!map_word_bitsset(map, status, CMD(CFI_SR_DRB)))
14462306a36Sopenharmony_ci		return 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (map_word_bitsset(map, status, CMD(0x3a))) {
14762306a36Sopenharmony_ci		unsigned long chipstatus = MERGESTATUS(status);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		if (chipstatus & CFI_SR_ESB)
15062306a36Sopenharmony_ci			pr_err("%s erase operation failed, status %lx\n",
15162306a36Sopenharmony_ci			       map->name, chipstatus);
15262306a36Sopenharmony_ci		if (chipstatus & CFI_SR_PSB)
15362306a36Sopenharmony_ci			pr_err("%s program operation failed, status %lx\n",
15462306a36Sopenharmony_ci			       map->name, chipstatus);
15562306a36Sopenharmony_ci		if (chipstatus & CFI_SR_WBASB)
15662306a36Sopenharmony_ci			pr_err("%s buffer program command aborted, status %lx\n",
15762306a36Sopenharmony_ci			       map->name, chipstatus);
15862306a36Sopenharmony_ci		if (chipstatus & CFI_SR_SLSB)
15962306a36Sopenharmony_ci			pr_err("%s sector write protected, status %lx\n",
16062306a36Sopenharmony_ci			       map->name, chipstatus);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		/* Erase/Program status bits are set on the operation failure */
16362306a36Sopenharmony_ci		if (chipstatus & (CFI_SR_ESB | CFI_SR_PSB))
16462306a36Sopenharmony_ci			return 1;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/* #define DEBUG_CFI_FEATURES */
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES
17362306a36Sopenharmony_cistatic void cfi_tell_features(struct cfi_pri_amdstd *extp)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	const char* erase_suspend[3] = {
17662306a36Sopenharmony_ci		"Not supported", "Read only", "Read/write"
17762306a36Sopenharmony_ci	};
17862306a36Sopenharmony_ci	const char* top_bottom[6] = {
17962306a36Sopenharmony_ci		"No WP", "8x8KiB sectors at top & bottom, no WP",
18062306a36Sopenharmony_ci		"Bottom boot", "Top boot",
18162306a36Sopenharmony_ci		"Uniform, Bottom WP", "Uniform, Top WP"
18262306a36Sopenharmony_ci	};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	printk("  Silicon revision: %d\n", extp->SiliconRevision >> 1);
18562306a36Sopenharmony_ci	printk("  Address sensitive unlock: %s\n",
18662306a36Sopenharmony_ci	       (extp->SiliconRevision & 1) ? "Not required" : "Required");
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend))
18962306a36Sopenharmony_ci		printk("  Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]);
19062306a36Sopenharmony_ci	else
19162306a36Sopenharmony_ci		printk("  Erase Suspend: Unknown value %d\n", extp->EraseSuspend);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (extp->BlkProt == 0)
19462306a36Sopenharmony_ci		printk("  Block protection: Not supported\n");
19562306a36Sopenharmony_ci	else
19662306a36Sopenharmony_ci		printk("  Block protection: %d sectors per group\n", extp->BlkProt);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	printk("  Temporary block unprotect: %s\n",
20062306a36Sopenharmony_ci	       extp->TmpBlkUnprotect ? "Supported" : "Not supported");
20162306a36Sopenharmony_ci	printk("  Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot);
20262306a36Sopenharmony_ci	printk("  Number of simultaneous operations: %d\n", extp->SimultaneousOps);
20362306a36Sopenharmony_ci	printk("  Burst mode: %s\n",
20462306a36Sopenharmony_ci	       extp->BurstMode ? "Supported" : "Not supported");
20562306a36Sopenharmony_ci	if (extp->PageMode == 0)
20662306a36Sopenharmony_ci		printk("  Page mode: Not supported\n");
20762306a36Sopenharmony_ci	else
20862306a36Sopenharmony_ci		printk("  Page mode: %d word page\n", extp->PageMode << 2);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	printk("  Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n",
21162306a36Sopenharmony_ci	       extp->VppMin >> 4, extp->VppMin & 0xf);
21262306a36Sopenharmony_ci	printk("  Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n",
21362306a36Sopenharmony_ci	       extp->VppMax >> 4, extp->VppMax & 0xf);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (extp->TopBottom < ARRAY_SIZE(top_bottom))
21662306a36Sopenharmony_ci		printk("  Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]);
21762306a36Sopenharmony_ci	else
21862306a36Sopenharmony_ci		printk("  Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci#endif
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci#ifdef AMD_BOOTLOC_BUG
22362306a36Sopenharmony_ci/* Wheee. Bring me the head of someone at AMD. */
22462306a36Sopenharmony_cistatic void fixup_amd_bootblock(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_amdstd *extp = cfi->cmdset_priv;
22962306a36Sopenharmony_ci	__u8 major = extp->MajorVersion;
23062306a36Sopenharmony_ci	__u8 minor = extp->MinorVersion;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (((major << 8) | minor) < 0x3131) {
23362306a36Sopenharmony_ci		/* CFI version 1.0 => don't trust bootloc */
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		pr_debug("%s: JEDEC Vendor ID is 0x%02X Device ID is 0x%02X\n",
23662306a36Sopenharmony_ci			map->name, cfi->mfr, cfi->id);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		/* AFAICS all 29LV400 with a bottom boot block have a device ID
23962306a36Sopenharmony_ci		 * of 0x22BA in 16-bit mode and 0xBA in 8-bit mode.
24062306a36Sopenharmony_ci		 * These were badly detected as they have the 0x80 bit set
24162306a36Sopenharmony_ci		 * so treat them as a special case.
24262306a36Sopenharmony_ci		 */
24362306a36Sopenharmony_ci		if (((cfi->id == 0xBA) || (cfi->id == 0x22BA)) &&
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci			/* Macronix added CFI to their 2nd generation
24662306a36Sopenharmony_ci			 * MX29LV400C B/T but AFAICS no other 29LV400 (AMD,
24762306a36Sopenharmony_ci			 * Fujitsu, Spansion, EON, ESI and older Macronix)
24862306a36Sopenharmony_ci			 * has CFI.
24962306a36Sopenharmony_ci			 *
25062306a36Sopenharmony_ci			 * Therefore also check the manufacturer.
25162306a36Sopenharmony_ci			 * This reduces the risk of false detection due to
25262306a36Sopenharmony_ci			 * the 8-bit device ID.
25362306a36Sopenharmony_ci			 */
25462306a36Sopenharmony_ci			(cfi->mfr == CFI_MFR_MACRONIX)) {
25562306a36Sopenharmony_ci			pr_debug("%s: Macronix MX29LV400C with bottom boot block"
25662306a36Sopenharmony_ci				" detected\n", map->name);
25762306a36Sopenharmony_ci			extp->TopBottom = 2;	/* bottom boot */
25862306a36Sopenharmony_ci		} else
25962306a36Sopenharmony_ci		if (cfi->id & 0x80) {
26062306a36Sopenharmony_ci			printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id);
26162306a36Sopenharmony_ci			extp->TopBottom = 3;	/* top boot */
26262306a36Sopenharmony_ci		} else {
26362306a36Sopenharmony_ci			extp->TopBottom = 2;	/* bottom boot */
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		pr_debug("%s: AMD CFI PRI V%c.%c has no boot block field;"
26762306a36Sopenharmony_ci			" deduced %s from Device ID\n", map->name, major, minor,
26862306a36Sopenharmony_ci			extp->TopBottom == 2 ? "bottom" : "top");
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci#endif
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci#if !FORCE_WORD_WRITE
27462306a36Sopenharmony_cistatic void fixup_use_write_buffers(struct mtd_info *mtd)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
27762306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (cfi->mfr == CFI_MFR_AMD && cfi->id == 0x2201)
28062306a36Sopenharmony_ci		return;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (cfi->cfiq->BufWriteTimeoutTyp) {
28362306a36Sopenharmony_ci		pr_debug("Using buffer write method\n");
28462306a36Sopenharmony_ci		mtd->_write = cfi_amdstd_write_buffers;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci#endif /* !FORCE_WORD_WRITE */
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/* Atmel chips don't use the same PRI format as AMD chips */
29062306a36Sopenharmony_cistatic void fixup_convert_atmel_pri(struct mtd_info *mtd)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
29362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
29462306a36Sopenharmony_ci	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
29562306a36Sopenharmony_ci	struct cfi_pri_atmel atmel_pri;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	memcpy(&atmel_pri, extp, sizeof(atmel_pri));
29862306a36Sopenharmony_ci	memset((char *)extp + 5, 0, sizeof(*extp) - 5);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (atmel_pri.Features & 0x02)
30162306a36Sopenharmony_ci		extp->EraseSuspend = 2;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* Some chips got it backwards... */
30462306a36Sopenharmony_ci	if (cfi->id == AT49BV6416) {
30562306a36Sopenharmony_ci		if (atmel_pri.BottomBoot)
30662306a36Sopenharmony_ci			extp->TopBottom = 3;
30762306a36Sopenharmony_ci		else
30862306a36Sopenharmony_ci			extp->TopBottom = 2;
30962306a36Sopenharmony_ci	} else {
31062306a36Sopenharmony_ci		if (atmel_pri.BottomBoot)
31162306a36Sopenharmony_ci			extp->TopBottom = 2;
31262306a36Sopenharmony_ci		else
31362306a36Sopenharmony_ci			extp->TopBottom = 3;
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* burst write mode not supported */
31762306a36Sopenharmony_ci	cfi->cfiq->BufWriteTimeoutTyp = 0;
31862306a36Sopenharmony_ci	cfi->cfiq->BufWriteTimeoutMax = 0;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic void fixup_use_secsi(struct mtd_info *mtd)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	/* Setup for chips with a secsi area */
32462306a36Sopenharmony_ci	mtd->_read_user_prot_reg = cfi_amdstd_secsi_read;
32562306a36Sopenharmony_ci	mtd->_read_fact_prot_reg = cfi_amdstd_secsi_read;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic void fixup_use_erase_chip(struct mtd_info *mtd)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
33162306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
33262306a36Sopenharmony_ci	if ((cfi->cfiq->NumEraseRegions == 1) &&
33362306a36Sopenharmony_ci		((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) {
33462306a36Sopenharmony_ci		mtd->_erase = cfi_amdstd_erase_chip;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/*
34062306a36Sopenharmony_ci * Some Atmel chips (e.g. the AT49BV6416) power-up with all sectors
34162306a36Sopenharmony_ci * locked by default.
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_cistatic void fixup_use_atmel_lock(struct mtd_info *mtd)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	mtd->_lock = cfi_atmel_lock;
34662306a36Sopenharmony_ci	mtd->_unlock = cfi_atmel_unlock;
34762306a36Sopenharmony_ci	mtd->flags |= MTD_POWERUP_LOCK;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic void fixup_old_sst_eraseregion(struct mtd_info *mtd)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
35362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/*
35662306a36Sopenharmony_ci	 * These flashes report two separate eraseblock regions based on the
35762306a36Sopenharmony_ci	 * sector_erase-size and block_erase-size, although they both operate on the
35862306a36Sopenharmony_ci	 * same memory. This is not allowed according to CFI, so we just pick the
35962306a36Sopenharmony_ci	 * sector_erase-size.
36062306a36Sopenharmony_ci	 */
36162306a36Sopenharmony_ci	cfi->cfiq->NumEraseRegions = 1;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic void fixup_sst39vf(struct mtd_info *mtd)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
36762306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	fixup_old_sst_eraseregion(mtd);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	cfi->addr_unlock1 = 0x5555;
37262306a36Sopenharmony_ci	cfi->addr_unlock2 = 0x2AAA;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic void fixup_sst39vf_rev_b(struct mtd_info *mtd)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
37862306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	fixup_old_sst_eraseregion(mtd);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	cfi->addr_unlock1 = 0x555;
38362306a36Sopenharmony_ci	cfi->addr_unlock2 = 0x2AA;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	cfi->sector_erase_cmd = CMD(0x50);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
39162306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	fixup_sst39vf_rev_b(mtd);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/*
39662306a36Sopenharmony_ci	 * CFI reports 1024 sectors (0x03ff+1) of 64KBytes (0x0100*256) where
39762306a36Sopenharmony_ci	 * it should report a size of 8KBytes (0x0020*256).
39862306a36Sopenharmony_ci	 */
39962306a36Sopenharmony_ci	cfi->cfiq->EraseRegionInfo[0] = 0x002003ff;
40062306a36Sopenharmony_ci	pr_warn("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n",
40162306a36Sopenharmony_ci		mtd->name);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic void fixup_s29gl064n_sectors(struct mtd_info *mtd)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
40762306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) {
41062306a36Sopenharmony_ci		cfi->cfiq->EraseRegionInfo[0] |= 0x0040;
41162306a36Sopenharmony_ci		pr_warn("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n",
41262306a36Sopenharmony_ci			mtd->name);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic void fixup_s29gl032n_sectors(struct mtd_info *mtd)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
41962306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) {
42262306a36Sopenharmony_ci		cfi->cfiq->EraseRegionInfo[1] &= ~0x0040;
42362306a36Sopenharmony_ci		pr_warn("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n",
42462306a36Sopenharmony_ci			mtd->name);
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void fixup_s29ns512p_sectors(struct mtd_info *mtd)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
43162306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	/*
43462306a36Sopenharmony_ci	 *  S29NS512P flash uses more than 8bits to report number of sectors,
43562306a36Sopenharmony_ci	 * which is not permitted by CFI.
43662306a36Sopenharmony_ci	 */
43762306a36Sopenharmony_ci	cfi->cfiq->EraseRegionInfo[0] = 0x020001ff;
43862306a36Sopenharmony_ci	pr_warn("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n",
43962306a36Sopenharmony_ci		mtd->name);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void fixup_quirks(struct mtd_info *mtd)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
44562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (cfi->mfr == CFI_MFR_AMD && cfi->id == S29GL064N_MN12)
44862306a36Sopenharmony_ci		cfi->quirks |= CFI_QUIRK_DQ_TRUE_DATA;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci/* Used to fix CFI-Tables of chips without Extended Query Tables */
45262306a36Sopenharmony_cistatic struct cfi_fixup cfi_nopri_fixup_table[] = {
45362306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x234a, fixup_sst39vf }, /* SST39VF1602 */
45462306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x234b, fixup_sst39vf }, /* SST39VF1601 */
45562306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x235a, fixup_sst39vf }, /* SST39VF3202 */
45662306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x235b, fixup_sst39vf }, /* SST39VF3201 */
45762306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x235c, fixup_sst39vf_rev_b }, /* SST39VF3202B */
45862306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x235d, fixup_sst39vf_rev_b }, /* SST39VF3201B */
45962306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x236c, fixup_sst39vf_rev_b }, /* SST39VF6402B */
46062306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x236d, fixup_sst39vf_rev_b }, /* SST39VF6401B */
46162306a36Sopenharmony_ci	{ 0, 0, NULL }
46262306a36Sopenharmony_ci};
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic struct cfi_fixup cfi_fixup_table[] = {
46562306a36Sopenharmony_ci	{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri },
46662306a36Sopenharmony_ci#ifdef AMD_BOOTLOC_BUG
46762306a36Sopenharmony_ci	{ CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock },
46862306a36Sopenharmony_ci	{ CFI_MFR_AMIC, CFI_ID_ANY, fixup_amd_bootblock },
46962306a36Sopenharmony_ci	{ CFI_MFR_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock },
47062306a36Sopenharmony_ci#endif
47162306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x0050, fixup_use_secsi },
47262306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x0053, fixup_use_secsi },
47362306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x0055, fixup_use_secsi },
47462306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x0056, fixup_use_secsi },
47562306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x005C, fixup_use_secsi },
47662306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x005F, fixup_use_secsi },
47762306a36Sopenharmony_ci	{ CFI_MFR_AMD, S29GL064N_MN12, fixup_s29gl064n_sectors },
47862306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors },
47962306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors },
48062306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors },
48162306a36Sopenharmony_ci	{ CFI_MFR_AMD, 0x3f00, fixup_s29ns512p_sectors },
48262306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x536a, fixup_sst38vf640x_sectorsize }, /* SST38VF6402 */
48362306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x536b, fixup_sst38vf640x_sectorsize }, /* SST38VF6401 */
48462306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x536c, fixup_sst38vf640x_sectorsize }, /* SST38VF6404 */
48562306a36Sopenharmony_ci	{ CFI_MFR_SST, 0x536d, fixup_sst38vf640x_sectorsize }, /* SST38VF6403 */
48662306a36Sopenharmony_ci#if !FORCE_WORD_WRITE
48762306a36Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers },
48862306a36Sopenharmony_ci#endif
48962306a36Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_quirks },
49062306a36Sopenharmony_ci	{ 0, 0, NULL }
49162306a36Sopenharmony_ci};
49262306a36Sopenharmony_cistatic struct cfi_fixup jedec_fixup_table[] = {
49362306a36Sopenharmony_ci	{ CFI_MFR_SST, SST49LF004B, fixup_use_fwh_lock },
49462306a36Sopenharmony_ci	{ CFI_MFR_SST, SST49LF040B, fixup_use_fwh_lock },
49562306a36Sopenharmony_ci	{ CFI_MFR_SST, SST49LF008A, fixup_use_fwh_lock },
49662306a36Sopenharmony_ci	{ 0, 0, NULL }
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic struct cfi_fixup fixup_table[] = {
50062306a36Sopenharmony_ci	/* The CFI vendor ids and the JEDEC vendor IDs appear
50162306a36Sopenharmony_ci	 * to be common.  It is like the devices id's are as
50262306a36Sopenharmony_ci	 * well.  This table is to pick all cases where
50362306a36Sopenharmony_ci	 * we know that is the case.
50462306a36Sopenharmony_ci	 */
50562306a36Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip },
50662306a36Sopenharmony_ci	{ CFI_MFR_ATMEL, AT49BV6416, fixup_use_atmel_lock },
50762306a36Sopenharmony_ci	{ 0, 0, NULL }
50862306a36Sopenharmony_ci};
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic void cfi_fixup_major_minor(struct cfi_private *cfi,
51262306a36Sopenharmony_ci				  struct cfi_pri_amdstd *extp)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	if (cfi->mfr == CFI_MFR_SAMSUNG) {
51562306a36Sopenharmony_ci		if ((extp->MajorVersion == '0' && extp->MinorVersion == '0') ||
51662306a36Sopenharmony_ci		    (extp->MajorVersion == '3' && extp->MinorVersion == '3')) {
51762306a36Sopenharmony_ci			/*
51862306a36Sopenharmony_ci			 * Samsung K8P2815UQB and K8D6x16UxM chips
51962306a36Sopenharmony_ci			 * report major=0 / minor=0.
52062306a36Sopenharmony_ci			 * K8D3x16UxC chips report major=3 / minor=3.
52162306a36Sopenharmony_ci			 */
52262306a36Sopenharmony_ci			printk(KERN_NOTICE "  Fixing Samsung's Amd/Fujitsu"
52362306a36Sopenharmony_ci			       " Extended Query version to 1.%c\n",
52462306a36Sopenharmony_ci			       extp->MinorVersion);
52562306a36Sopenharmony_ci			extp->MajorVersion = '1';
52662306a36Sopenharmony_ci		}
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/*
53062306a36Sopenharmony_ci	 * SST 38VF640x chips report major=0xFF / minor=0xFF.
53162306a36Sopenharmony_ci	 */
53262306a36Sopenharmony_ci	if (cfi->mfr == CFI_MFR_SST && (cfi->id >> 4) == 0x0536) {
53362306a36Sopenharmony_ci		extp->MajorVersion = '1';
53462306a36Sopenharmony_ci		extp->MinorVersion = '0';
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic int is_m29ew(struct cfi_private *cfi)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	if (cfi->mfr == CFI_MFR_INTEL &&
54162306a36Sopenharmony_ci	    ((cfi->device_type == CFI_DEVICETYPE_X8 && (cfi->id & 0xff) == 0x7e) ||
54262306a36Sopenharmony_ci	     (cfi->device_type == CFI_DEVICETYPE_X16 && cfi->id == 0x227e)))
54362306a36Sopenharmony_ci		return 1;
54462306a36Sopenharmony_ci	return 0;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/*
54862306a36Sopenharmony_ci * From TN-13-07: Patching the Linux Kernel and U-Boot for M29 Flash, page 20:
54962306a36Sopenharmony_ci * Some revisions of the M29EW suffer from erase suspend hang ups. In
55062306a36Sopenharmony_ci * particular, it can occur when the sequence
55162306a36Sopenharmony_ci * Erase Confirm -> Suspend -> Program -> Resume
55262306a36Sopenharmony_ci * causes a lockup due to internal timing issues. The consequence is that the
55362306a36Sopenharmony_ci * erase cannot be resumed without inserting a dummy command after programming
55462306a36Sopenharmony_ci * and prior to resuming. [...] The work-around is to issue a dummy write cycle
55562306a36Sopenharmony_ci * that writes an F0 command code before the RESUME command.
55662306a36Sopenharmony_ci */
55762306a36Sopenharmony_cistatic void cfi_fixup_m29ew_erase_suspend(struct map_info *map,
55862306a36Sopenharmony_ci					  unsigned long adr)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
56162306a36Sopenharmony_ci	/* before resume, insert a dummy 0xF0 cycle for Micron M29EW devices */
56262306a36Sopenharmony_ci	if (is_m29ew(cfi))
56362306a36Sopenharmony_ci		map_write(map, CMD(0xF0), adr);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci/*
56762306a36Sopenharmony_ci * From TN-13-07: Patching the Linux Kernel and U-Boot for M29 Flash, page 22:
56862306a36Sopenharmony_ci *
56962306a36Sopenharmony_ci * Some revisions of the M29EW (for example, A1 and A2 step revisions)
57062306a36Sopenharmony_ci * are affected by a problem that could cause a hang up when an ERASE SUSPEND
57162306a36Sopenharmony_ci * command is issued after an ERASE RESUME operation without waiting for a
57262306a36Sopenharmony_ci * minimum delay.  The result is that once the ERASE seems to be completed
57362306a36Sopenharmony_ci * (no bits are toggling), the contents of the Flash memory block on which
57462306a36Sopenharmony_ci * the erase was ongoing could be inconsistent with the expected values
57562306a36Sopenharmony_ci * (typically, the array value is stuck to the 0xC0, 0xC4, 0x80, or 0x84
57662306a36Sopenharmony_ci * values), causing a consequent failure of the ERASE operation.
57762306a36Sopenharmony_ci * The occurrence of this issue could be high, especially when file system
57862306a36Sopenharmony_ci * operations on the Flash are intensive.  As a result, it is recommended
57962306a36Sopenharmony_ci * that a patch be applied.  Intensive file system operations can cause many
58062306a36Sopenharmony_ci * calls to the garbage routine to free Flash space (also by erasing physical
58162306a36Sopenharmony_ci * Flash blocks) and as a result, many consecutive SUSPEND and RESUME
58262306a36Sopenharmony_ci * commands can occur.  The problem disappears when a delay is inserted after
58362306a36Sopenharmony_ci * the RESUME command by using the udelay() function available in Linux.
58462306a36Sopenharmony_ci * The DELAY value must be tuned based on the customer's platform.
58562306a36Sopenharmony_ci * The maximum value that fixes the problem in all cases is 500us.
58662306a36Sopenharmony_ci * But, in our experience, a delay of 30 µs to 50 µs is sufficient
58762306a36Sopenharmony_ci * in most cases.
58862306a36Sopenharmony_ci * We have chosen 500µs because this latency is acceptable.
58962306a36Sopenharmony_ci */
59062306a36Sopenharmony_cistatic void cfi_fixup_m29ew_delay_after_resume(struct cfi_private *cfi)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	/*
59362306a36Sopenharmony_ci	 * Resolving the Delay After Resume Issue see Micron TN-13-07
59462306a36Sopenharmony_ci	 * Worst case delay must be 500µs but 30-50µs should be ok as well
59562306a36Sopenharmony_ci	 */
59662306a36Sopenharmony_ci	if (is_m29ew(cfi))
59762306a36Sopenharmony_ci		cfi_udelay(500);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
60362306a36Sopenharmony_ci	struct device_node __maybe_unused *np = map->device_node;
60462306a36Sopenharmony_ci	struct mtd_info *mtd;
60562306a36Sopenharmony_ci	int i;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
60862306a36Sopenharmony_ci	if (!mtd)
60962306a36Sopenharmony_ci		return NULL;
61062306a36Sopenharmony_ci	mtd->priv = map;
61162306a36Sopenharmony_ci	mtd->type = MTD_NORFLASH;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* Fill in the default mtd operations */
61462306a36Sopenharmony_ci	mtd->_erase   = cfi_amdstd_erase_varsize;
61562306a36Sopenharmony_ci	mtd->_write   = cfi_amdstd_write_words;
61662306a36Sopenharmony_ci	mtd->_read    = cfi_amdstd_read;
61762306a36Sopenharmony_ci	mtd->_sync    = cfi_amdstd_sync;
61862306a36Sopenharmony_ci	mtd->_suspend = cfi_amdstd_suspend;
61962306a36Sopenharmony_ci	mtd->_resume  = cfi_amdstd_resume;
62062306a36Sopenharmony_ci	mtd->_read_user_prot_reg = cfi_amdstd_read_user_prot_reg;
62162306a36Sopenharmony_ci	mtd->_read_fact_prot_reg = cfi_amdstd_read_fact_prot_reg;
62262306a36Sopenharmony_ci	mtd->_get_fact_prot_info = cfi_amdstd_get_fact_prot_info;
62362306a36Sopenharmony_ci	mtd->_get_user_prot_info = cfi_amdstd_get_user_prot_info;
62462306a36Sopenharmony_ci	mtd->_write_user_prot_reg = cfi_amdstd_write_user_prot_reg;
62562306a36Sopenharmony_ci	mtd->_lock_user_prot_reg = cfi_amdstd_lock_user_prot_reg;
62662306a36Sopenharmony_ci	mtd->flags   = MTD_CAP_NORFLASH;
62762306a36Sopenharmony_ci	mtd->name    = map->name;
62862306a36Sopenharmony_ci	mtd->writesize = 1;
62962306a36Sopenharmony_ci	mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	pr_debug("MTD %s(): write buffer size %d\n", __func__,
63262306a36Sopenharmony_ci			mtd->writebufsize);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	mtd->_panic_write = cfi_amdstd_panic_write;
63562306a36Sopenharmony_ci	mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	if (cfi->cfi_mode==CFI_MODE_CFI){
63862306a36Sopenharmony_ci		unsigned char bootloc;
63962306a36Sopenharmony_ci		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
64062306a36Sopenharmony_ci		struct cfi_pri_amdstd *extp;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci		extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");
64362306a36Sopenharmony_ci		if (extp) {
64462306a36Sopenharmony_ci			/*
64562306a36Sopenharmony_ci			 * It's a real CFI chip, not one for which the probe
64662306a36Sopenharmony_ci			 * routine faked a CFI structure.
64762306a36Sopenharmony_ci			 */
64862306a36Sopenharmony_ci			cfi_fixup_major_minor(cfi, extp);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci			/*
65162306a36Sopenharmony_ci			 * Valid primary extension versions are: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5
65262306a36Sopenharmony_ci			 * see: http://cs.ozerki.net/zap/pub/axim-x5/docs/cfi_r20.pdf, page 19
65362306a36Sopenharmony_ci			 *      http://www.spansion.com/Support/AppNotes/cfi_100_20011201.pdf
65462306a36Sopenharmony_ci			 *      http://www.spansion.com/Support/Datasheets/s29ws-p_00_a12_e.pdf
65562306a36Sopenharmony_ci			 *      http://www.spansion.com/Support/Datasheets/S29GL_128S_01GS_00_02_e.pdf
65662306a36Sopenharmony_ci			 */
65762306a36Sopenharmony_ci			if (extp->MajorVersion != '1' ||
65862306a36Sopenharmony_ci			    (extp->MajorVersion == '1' && (extp->MinorVersion < '0' || extp->MinorVersion > '5'))) {
65962306a36Sopenharmony_ci				printk(KERN_ERR "  Unknown Amd/Fujitsu Extended Query "
66062306a36Sopenharmony_ci				       "version %c.%c (%#02x/%#02x).\n",
66162306a36Sopenharmony_ci				       extp->MajorVersion, extp->MinorVersion,
66262306a36Sopenharmony_ci				       extp->MajorVersion, extp->MinorVersion);
66362306a36Sopenharmony_ci				kfree(extp);
66462306a36Sopenharmony_ci				kfree(mtd);
66562306a36Sopenharmony_ci				return NULL;
66662306a36Sopenharmony_ci			}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci			printk(KERN_INFO "  Amd/Fujitsu Extended Query version %c.%c.\n",
66962306a36Sopenharmony_ci			       extp->MajorVersion, extp->MinorVersion);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci			/* Install our own private info structure */
67262306a36Sopenharmony_ci			cfi->cmdset_priv = extp;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci			/* Apply cfi device specific fixups */
67562306a36Sopenharmony_ci			cfi_fixup(mtd, cfi_fixup_table);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES
67862306a36Sopenharmony_ci			/* Tell the user about it in lots of lovely detail */
67962306a36Sopenharmony_ci			cfi_tell_features(extp);
68062306a36Sopenharmony_ci#endif
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci#ifdef CONFIG_OF
68362306a36Sopenharmony_ci			if (np && of_property_read_bool(
68462306a36Sopenharmony_ci				    np, "use-advanced-sector-protection")
68562306a36Sopenharmony_ci			    && extp->BlkProtUnprot == 8) {
68662306a36Sopenharmony_ci				printk(KERN_INFO "  Advanced Sector Protection (PPB Locking) supported\n");
68762306a36Sopenharmony_ci				mtd->_lock = cfi_ppb_lock;
68862306a36Sopenharmony_ci				mtd->_unlock = cfi_ppb_unlock;
68962306a36Sopenharmony_ci				mtd->_is_locked = cfi_ppb_is_locked;
69062306a36Sopenharmony_ci			}
69162306a36Sopenharmony_ci#endif
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci			bootloc = extp->TopBottom;
69462306a36Sopenharmony_ci			if ((bootloc < 2) || (bootloc > 5)) {
69562306a36Sopenharmony_ci				printk(KERN_WARNING "%s: CFI contains unrecognised boot "
69662306a36Sopenharmony_ci				       "bank location (%d). Assuming bottom.\n",
69762306a36Sopenharmony_ci				       map->name, bootloc);
69862306a36Sopenharmony_ci				bootloc = 2;
69962306a36Sopenharmony_ci			}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci			if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {
70262306a36Sopenharmony_ci				printk(KERN_WARNING "%s: Swapping erase regions for top-boot CFI table.\n", map->name);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci				for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) {
70562306a36Sopenharmony_ci					int j = (cfi->cfiq->NumEraseRegions-1)-i;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci					swap(cfi->cfiq->EraseRegionInfo[i],
70862306a36Sopenharmony_ci					     cfi->cfiq->EraseRegionInfo[j]);
70962306a36Sopenharmony_ci				}
71062306a36Sopenharmony_ci			}
71162306a36Sopenharmony_ci			/* Set the default CFI lock/unlock addresses */
71262306a36Sopenharmony_ci			cfi->addr_unlock1 = 0x555;
71362306a36Sopenharmony_ci			cfi->addr_unlock2 = 0x2aa;
71462306a36Sopenharmony_ci		}
71562306a36Sopenharmony_ci		cfi_fixup(mtd, cfi_nopri_fixup_table);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		if (!cfi->addr_unlock1 || !cfi->addr_unlock2) {
71862306a36Sopenharmony_ci			kfree(mtd);
71962306a36Sopenharmony_ci			return NULL;
72062306a36Sopenharmony_ci		}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	} /* CFI mode */
72362306a36Sopenharmony_ci	else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
72462306a36Sopenharmony_ci		/* Apply jedec specific fixups */
72562306a36Sopenharmony_ci		cfi_fixup(mtd, jedec_fixup_table);
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci	/* Apply generic fixups */
72862306a36Sopenharmony_ci	cfi_fixup(mtd, fixup_table);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	for (i=0; i< cfi->numchips; i++) {
73162306a36Sopenharmony_ci		cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
73262306a36Sopenharmony_ci		cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
73362306a36Sopenharmony_ci		cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp;
73462306a36Sopenharmony_ci		/*
73562306a36Sopenharmony_ci		 * First calculate the timeout max according to timeout field
73662306a36Sopenharmony_ci		 * of struct cfi_ident that probed from chip's CFI aera, if
73762306a36Sopenharmony_ci		 * available. Specify a minimum of 2000us, in case the CFI data
73862306a36Sopenharmony_ci		 * is wrong.
73962306a36Sopenharmony_ci		 */
74062306a36Sopenharmony_ci		if (cfi->cfiq->BufWriteTimeoutTyp &&
74162306a36Sopenharmony_ci		    cfi->cfiq->BufWriteTimeoutMax)
74262306a36Sopenharmony_ci			cfi->chips[i].buffer_write_time_max =
74362306a36Sopenharmony_ci				1 << (cfi->cfiq->BufWriteTimeoutTyp +
74462306a36Sopenharmony_ci				      cfi->cfiq->BufWriteTimeoutMax);
74562306a36Sopenharmony_ci		else
74662306a36Sopenharmony_ci			cfi->chips[i].buffer_write_time_max = 0;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci		cfi->chips[i].buffer_write_time_max =
74962306a36Sopenharmony_ci			max(cfi->chips[i].buffer_write_time_max, 2000);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci		cfi->chips[i].ref_point_counter = 0;
75262306a36Sopenharmony_ci		init_waitqueue_head(&(cfi->chips[i].wq));
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	map->fldrv = &cfi_amdstd_chipdrv;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return cfi_amdstd_setup(mtd);
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0006(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002")));
76062306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002")));
76162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0002);
76262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0006);
76362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0701);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
76862306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
76962306a36Sopenharmony_ci	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
77062306a36Sopenharmony_ci	unsigned long offset = 0;
77162306a36Sopenharmony_ci	int i,j;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	printk(KERN_NOTICE "number of %s chips: %d\n",
77462306a36Sopenharmony_ci	       (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips);
77562306a36Sopenharmony_ci	/* Select the correct geometry setup */
77662306a36Sopenharmony_ci	mtd->size = devsize * cfi->numchips;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
77962306a36Sopenharmony_ci	mtd->eraseregions = kmalloc_array(mtd->numeraseregions,
78062306a36Sopenharmony_ci					  sizeof(struct mtd_erase_region_info),
78162306a36Sopenharmony_ci					  GFP_KERNEL);
78262306a36Sopenharmony_ci	if (!mtd->eraseregions)
78362306a36Sopenharmony_ci		goto setup_err;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
78662306a36Sopenharmony_ci		unsigned long ernum, ersize;
78762306a36Sopenharmony_ci		ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
78862306a36Sopenharmony_ci		ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		if (mtd->erasesize < ersize) {
79162306a36Sopenharmony_ci			mtd->erasesize = ersize;
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci		for (j=0; j<cfi->numchips; j++) {
79462306a36Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
79562306a36Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
79662306a36Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
79762306a36Sopenharmony_ci		}
79862306a36Sopenharmony_ci		offset += (ersize * ernum);
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci	if (offset != devsize) {
80162306a36Sopenharmony_ci		/* Argh */
80262306a36Sopenharmony_ci		printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
80362306a36Sopenharmony_ci		goto setup_err;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	__module_get(THIS_MODULE);
80762306a36Sopenharmony_ci	register_reboot_notifier(&mtd->reboot_notifier);
80862306a36Sopenharmony_ci	return mtd;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci setup_err:
81162306a36Sopenharmony_ci	kfree(mtd->eraseregions);
81262306a36Sopenharmony_ci	kfree(mtd);
81362306a36Sopenharmony_ci	kfree(cfi->cmdset_priv);
81462306a36Sopenharmony_ci	return NULL;
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci/*
81862306a36Sopenharmony_ci * Return true if the chip is ready and has the correct value.
81962306a36Sopenharmony_ci *
82062306a36Sopenharmony_ci * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
82162306a36Sopenharmony_ci * non-suspended sector) and is indicated by no toggle bits toggling.
82262306a36Sopenharmony_ci *
82362306a36Sopenharmony_ci * Error are indicated by toggling bits or bits held with the wrong value,
82462306a36Sopenharmony_ci * or with bits toggling.
82562306a36Sopenharmony_ci *
82662306a36Sopenharmony_ci * Note that anything more complicated than checking if no bits are toggling
82762306a36Sopenharmony_ci * (including checking DQ5 for an error status) is tricky to get working
82862306a36Sopenharmony_ci * correctly and is therefore not done	(particularly with interleaved chips
82962306a36Sopenharmony_ci * as each chip must be checked independently of the others).
83062306a36Sopenharmony_ci */
83162306a36Sopenharmony_cistatic int __xipram chip_ready(struct map_info *map, struct flchip *chip,
83262306a36Sopenharmony_ci			       unsigned long addr, map_word *expected)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
83562306a36Sopenharmony_ci	map_word oldd, curd;
83662306a36Sopenharmony_ci	int ret;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (cfi_use_status_reg(cfi)) {
83962306a36Sopenharmony_ci		map_word ready = CMD(CFI_SR_DRB);
84062306a36Sopenharmony_ci		/*
84162306a36Sopenharmony_ci		 * For chips that support status register, check device
84262306a36Sopenharmony_ci		 * ready bit
84362306a36Sopenharmony_ci		 */
84462306a36Sopenharmony_ci		cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,
84562306a36Sopenharmony_ci				 cfi->device_type, NULL);
84662306a36Sopenharmony_ci		curd = map_read(map, addr);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci		return map_word_andequal(map, curd, ready, ready);
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	oldd = map_read(map, addr);
85262306a36Sopenharmony_ci	curd = map_read(map, addr);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	ret = map_word_equal(map, oldd, curd);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if (!ret || !expected)
85762306a36Sopenharmony_ci		return ret;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	return map_word_equal(map, curd, *expected);
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic int __xipram chip_good(struct map_info *map, struct flchip *chip,
86362306a36Sopenharmony_ci			      unsigned long addr, map_word *expected)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
86662306a36Sopenharmony_ci	map_word *datum = expected;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	if (cfi->quirks & CFI_QUIRK_DQ_TRUE_DATA)
86962306a36Sopenharmony_ci		datum = NULL;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	return chip_ready(map, chip, addr, datum);
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
87762306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
87862306a36Sopenharmony_ci	unsigned long timeo;
87962306a36Sopenharmony_ci	struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci resettime:
88262306a36Sopenharmony_ci	timeo = jiffies + HZ;
88362306a36Sopenharmony_ci retry:
88462306a36Sopenharmony_ci	switch (chip->state) {
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	case FL_STATUS:
88762306a36Sopenharmony_ci		for (;;) {
88862306a36Sopenharmony_ci			if (chip_ready(map, chip, adr, NULL))
88962306a36Sopenharmony_ci				break;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci			if (time_after(jiffies, timeo)) {
89262306a36Sopenharmony_ci				printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
89362306a36Sopenharmony_ci				return -EIO;
89462306a36Sopenharmony_ci			}
89562306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
89662306a36Sopenharmony_ci			cfi_udelay(1);
89762306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
89862306a36Sopenharmony_ci			/* Someone else might have been playing with it. */
89962306a36Sopenharmony_ci			goto retry;
90062306a36Sopenharmony_ci		}
90162306a36Sopenharmony_ci		return 0;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	case FL_READY:
90462306a36Sopenharmony_ci	case FL_CFI_QUERY:
90562306a36Sopenharmony_ci	case FL_JEDEC_QUERY:
90662306a36Sopenharmony_ci		return 0;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	case FL_ERASING:
90962306a36Sopenharmony_ci		if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
91062306a36Sopenharmony_ci		    !(mode == FL_READY || mode == FL_POINT ||
91162306a36Sopenharmony_ci		    (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
91262306a36Sopenharmony_ci			goto sleep;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci		/* Do not allow suspend iff read/write to EB address */
91562306a36Sopenharmony_ci		if ((adr & chip->in_progress_block_mask) ==
91662306a36Sopenharmony_ci		    chip->in_progress_block_addr)
91762306a36Sopenharmony_ci			goto sleep;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci		/* Erase suspend */
92062306a36Sopenharmony_ci		/* It's harmless to issue the Erase-Suspend and Erase-Resume
92162306a36Sopenharmony_ci		 * commands when the erase algorithm isn't in progress. */
92262306a36Sopenharmony_ci		map_write(map, CMD(0xB0), chip->in_progress_block_addr);
92362306a36Sopenharmony_ci		chip->oldstate = FL_ERASING;
92462306a36Sopenharmony_ci		chip->state = FL_ERASE_SUSPENDING;
92562306a36Sopenharmony_ci		chip->erase_suspended = 1;
92662306a36Sopenharmony_ci		for (;;) {
92762306a36Sopenharmony_ci			if (chip_ready(map, chip, adr, NULL))
92862306a36Sopenharmony_ci				break;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci			if (time_after(jiffies, timeo)) {
93162306a36Sopenharmony_ci				/* Should have suspended the erase by now.
93262306a36Sopenharmony_ci				 * Send an Erase-Resume command as either
93362306a36Sopenharmony_ci				 * there was an error (so leave the erase
93462306a36Sopenharmony_ci				 * routine to recover from it) or we trying to
93562306a36Sopenharmony_ci				 * use the erase-in-progress sector. */
93662306a36Sopenharmony_ci				put_chip(map, chip, adr);
93762306a36Sopenharmony_ci				printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);
93862306a36Sopenharmony_ci				return -EIO;
93962306a36Sopenharmony_ci			}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
94262306a36Sopenharmony_ci			cfi_udelay(1);
94362306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
94462306a36Sopenharmony_ci			/* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
94562306a36Sopenharmony_ci			   So we can just loop here. */
94662306a36Sopenharmony_ci		}
94762306a36Sopenharmony_ci		chip->state = FL_READY;
94862306a36Sopenharmony_ci		return 0;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	case FL_XIP_WHILE_ERASING:
95162306a36Sopenharmony_ci		if (mode != FL_READY && mode != FL_POINT &&
95262306a36Sopenharmony_ci		    (!cfip || !(cfip->EraseSuspend&2)))
95362306a36Sopenharmony_ci			goto sleep;
95462306a36Sopenharmony_ci		chip->oldstate = chip->state;
95562306a36Sopenharmony_ci		chip->state = FL_READY;
95662306a36Sopenharmony_ci		return 0;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	case FL_SHUTDOWN:
95962306a36Sopenharmony_ci		/* The machine is rebooting */
96062306a36Sopenharmony_ci		return -EIO;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	case FL_POINT:
96362306a36Sopenharmony_ci		/* Only if there's no operation suspended... */
96462306a36Sopenharmony_ci		if (mode == FL_READY && chip->oldstate == FL_READY)
96562306a36Sopenharmony_ci			return 0;
96662306a36Sopenharmony_ci		fallthrough;
96762306a36Sopenharmony_ci	default:
96862306a36Sopenharmony_ci	sleep:
96962306a36Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
97062306a36Sopenharmony_ci		add_wait_queue(&chip->wq, &wait);
97162306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
97262306a36Sopenharmony_ci		schedule();
97362306a36Sopenharmony_ci		remove_wait_queue(&chip->wq, &wait);
97462306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
97562306a36Sopenharmony_ci		goto resettime;
97662306a36Sopenharmony_ci	}
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	switch(chip->oldstate) {
98562306a36Sopenharmony_ci	case FL_ERASING:
98662306a36Sopenharmony_ci		cfi_fixup_m29ew_erase_suspend(map,
98762306a36Sopenharmony_ci			chip->in_progress_block_addr);
98862306a36Sopenharmony_ci		map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
98962306a36Sopenharmony_ci		cfi_fixup_m29ew_delay_after_resume(cfi);
99062306a36Sopenharmony_ci		chip->oldstate = FL_READY;
99162306a36Sopenharmony_ci		chip->state = FL_ERASING;
99262306a36Sopenharmony_ci		break;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	case FL_XIP_WHILE_ERASING:
99562306a36Sopenharmony_ci		chip->state = chip->oldstate;
99662306a36Sopenharmony_ci		chip->oldstate = FL_READY;
99762306a36Sopenharmony_ci		break;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	case FL_READY:
100062306a36Sopenharmony_ci	case FL_STATUS:
100162306a36Sopenharmony_ci		break;
100262306a36Sopenharmony_ci	default:
100362306a36Sopenharmony_ci		printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci	wake_up(&chip->wq);
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci#ifdef CONFIG_MTD_XIP
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci/*
101162306a36Sopenharmony_ci * No interrupt what so ever can be serviced while the flash isn't in array
101262306a36Sopenharmony_ci * mode.  This is ensured by the xip_disable() and xip_enable() functions
101362306a36Sopenharmony_ci * enclosing any code path where the flash is known not to be in array mode.
101462306a36Sopenharmony_ci * And within a XIP disabled code path, only functions marked with __xipram
101562306a36Sopenharmony_ci * may be called and nothing else (it's a good thing to inspect generated
101662306a36Sopenharmony_ci * assembly to make sure inline functions were actually inlined and that gcc
101762306a36Sopenharmony_ci * didn't emit calls to its own support functions). Also configuring MTD CFI
101862306a36Sopenharmony_ci * support to a single buswidth and a single interleave is also recommended.
101962306a36Sopenharmony_ci */
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_cistatic void xip_disable(struct map_info *map, struct flchip *chip,
102262306a36Sopenharmony_ci			unsigned long adr)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	/* TODO: chips with no XIP use should ignore and return */
102562306a36Sopenharmony_ci	(void) map_read(map, adr); /* ensure mmu mapping is up to date */
102662306a36Sopenharmony_ci	local_irq_disable();
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_cistatic void __xipram xip_enable(struct map_info *map, struct flchip *chip,
103062306a36Sopenharmony_ci				unsigned long adr)
103162306a36Sopenharmony_ci{
103262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	if (chip->state != FL_POINT && chip->state != FL_READY) {
103562306a36Sopenharmony_ci		map_write(map, CMD(0xf0), adr);
103662306a36Sopenharmony_ci		chip->state = FL_READY;
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci	(void) map_read(map, adr);
103962306a36Sopenharmony_ci	xip_iprefetch();
104062306a36Sopenharmony_ci	local_irq_enable();
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci/*
104462306a36Sopenharmony_ci * When a delay is required for the flash operation to complete, the
104562306a36Sopenharmony_ci * xip_udelay() function is polling for both the given timeout and pending
104662306a36Sopenharmony_ci * (but still masked) hardware interrupts.  Whenever there is an interrupt
104762306a36Sopenharmony_ci * pending then the flash erase operation is suspended, array mode restored
104862306a36Sopenharmony_ci * and interrupts unmasked.  Task scheduling might also happen at that
104962306a36Sopenharmony_ci * point.  The CPU eventually returns from the interrupt or the call to
105062306a36Sopenharmony_ci * schedule() and the suspended flash operation is resumed for the remaining
105162306a36Sopenharmony_ci * of the delay period.
105262306a36Sopenharmony_ci *
105362306a36Sopenharmony_ci * Warning: this function _will_ fool interrupt latency tracing tools.
105462306a36Sopenharmony_ci */
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
105762306a36Sopenharmony_ci				unsigned long adr, int usec)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
106062306a36Sopenharmony_ci	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
106162306a36Sopenharmony_ci	map_word status, OK = CMD(0x80);
106262306a36Sopenharmony_ci	unsigned long suspended, start = xip_currtime();
106362306a36Sopenharmony_ci	flstate_t oldstate;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	do {
106662306a36Sopenharmony_ci		cpu_relax();
106762306a36Sopenharmony_ci		if (xip_irqpending() && extp &&
106862306a36Sopenharmony_ci		    ((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) &&
106962306a36Sopenharmony_ci		    (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
107062306a36Sopenharmony_ci			/*
107162306a36Sopenharmony_ci			 * Let's suspend the erase operation when supported.
107262306a36Sopenharmony_ci			 * Note that we currently don't try to suspend
107362306a36Sopenharmony_ci			 * interleaved chips if there is already another
107462306a36Sopenharmony_ci			 * operation suspended (imagine what happens
107562306a36Sopenharmony_ci			 * when one chip was already done with the current
107662306a36Sopenharmony_ci			 * operation while another chip suspended it, then
107762306a36Sopenharmony_ci			 * we resume the whole thing at once).  Yes, it
107862306a36Sopenharmony_ci			 * can happen!
107962306a36Sopenharmony_ci			 */
108062306a36Sopenharmony_ci			map_write(map, CMD(0xb0), adr);
108162306a36Sopenharmony_ci			usec -= xip_elapsed_since(start);
108262306a36Sopenharmony_ci			suspended = xip_currtime();
108362306a36Sopenharmony_ci			do {
108462306a36Sopenharmony_ci				if (xip_elapsed_since(suspended) > 100000) {
108562306a36Sopenharmony_ci					/*
108662306a36Sopenharmony_ci					 * The chip doesn't want to suspend
108762306a36Sopenharmony_ci					 * after waiting for 100 msecs.
108862306a36Sopenharmony_ci					 * This is a critical error but there
108962306a36Sopenharmony_ci					 * is not much we can do here.
109062306a36Sopenharmony_ci					 */
109162306a36Sopenharmony_ci					return;
109262306a36Sopenharmony_ci				}
109362306a36Sopenharmony_ci				status = map_read(map, adr);
109462306a36Sopenharmony_ci			} while (!map_word_andequal(map, status, OK, OK));
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci			/* Suspend succeeded */
109762306a36Sopenharmony_ci			oldstate = chip->state;
109862306a36Sopenharmony_ci			if (!map_word_bitsset(map, status, CMD(0x40)))
109962306a36Sopenharmony_ci				break;
110062306a36Sopenharmony_ci			chip->state = FL_XIP_WHILE_ERASING;
110162306a36Sopenharmony_ci			chip->erase_suspended = 1;
110262306a36Sopenharmony_ci			map_write(map, CMD(0xf0), adr);
110362306a36Sopenharmony_ci			(void) map_read(map, adr);
110462306a36Sopenharmony_ci			xip_iprefetch();
110562306a36Sopenharmony_ci			local_irq_enable();
110662306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
110762306a36Sopenharmony_ci			xip_iprefetch();
110862306a36Sopenharmony_ci			cond_resched();
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci			/*
111162306a36Sopenharmony_ci			 * We're back.  However someone else might have
111262306a36Sopenharmony_ci			 * decided to go write to the chip if we are in
111362306a36Sopenharmony_ci			 * a suspended erase state.  If so let's wait
111462306a36Sopenharmony_ci			 * until it's done.
111562306a36Sopenharmony_ci			 */
111662306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
111762306a36Sopenharmony_ci			while (chip->state != FL_XIP_WHILE_ERASING) {
111862306a36Sopenharmony_ci				DECLARE_WAITQUEUE(wait, current);
111962306a36Sopenharmony_ci				set_current_state(TASK_UNINTERRUPTIBLE);
112062306a36Sopenharmony_ci				add_wait_queue(&chip->wq, &wait);
112162306a36Sopenharmony_ci				mutex_unlock(&chip->mutex);
112262306a36Sopenharmony_ci				schedule();
112362306a36Sopenharmony_ci				remove_wait_queue(&chip->wq, &wait);
112462306a36Sopenharmony_ci				mutex_lock(&chip->mutex);
112562306a36Sopenharmony_ci			}
112662306a36Sopenharmony_ci			/* Disallow XIP again */
112762306a36Sopenharmony_ci			local_irq_disable();
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci			/* Correct Erase Suspend Hangups for M29EW */
113062306a36Sopenharmony_ci			cfi_fixup_m29ew_erase_suspend(map, adr);
113162306a36Sopenharmony_ci			/* Resume the write or erase operation */
113262306a36Sopenharmony_ci			map_write(map, cfi->sector_erase_cmd, adr);
113362306a36Sopenharmony_ci			chip->state = oldstate;
113462306a36Sopenharmony_ci			start = xip_currtime();
113562306a36Sopenharmony_ci		} else if (usec >= 1000000/HZ) {
113662306a36Sopenharmony_ci			/*
113762306a36Sopenharmony_ci			 * Try to save on CPU power when waiting delay
113862306a36Sopenharmony_ci			 * is at least a system timer tick period.
113962306a36Sopenharmony_ci			 * No need to be extremely accurate here.
114062306a36Sopenharmony_ci			 */
114162306a36Sopenharmony_ci			xip_cpu_idle();
114262306a36Sopenharmony_ci		}
114362306a36Sopenharmony_ci		status = map_read(map, adr);
114462306a36Sopenharmony_ci	} while (!map_word_andequal(map, status, OK, OK)
114562306a36Sopenharmony_ci		 && xip_elapsed_since(start) < usec);
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci#define UDELAY(map, chip, adr, usec)  xip_udelay(map, chip, adr, usec)
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci/*
115162306a36Sopenharmony_ci * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
115262306a36Sopenharmony_ci * the flash is actively programming or erasing since we have to poll for
115362306a36Sopenharmony_ci * the operation to complete anyway.  We can't do that in a generic way with
115462306a36Sopenharmony_ci * a XIP setup so do it before the actual flash operation in this case
115562306a36Sopenharmony_ci * and stub it out from INVALIDATE_CACHE_UDELAY.
115662306a36Sopenharmony_ci */
115762306a36Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(map, from, size)  \
115862306a36Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, from, size)
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
116162306a36Sopenharmony_ci	UDELAY(map, chip, adr, usec)
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/*
116462306a36Sopenharmony_ci * Extra notes:
116562306a36Sopenharmony_ci *
116662306a36Sopenharmony_ci * Activating this XIP support changes the way the code works a bit.  For
116762306a36Sopenharmony_ci * example the code to suspend the current process when concurrent access
116862306a36Sopenharmony_ci * happens is never executed because xip_udelay() will always return with the
116962306a36Sopenharmony_ci * same chip state as it was entered with.  This is why there is no care for
117062306a36Sopenharmony_ci * the presence of add_wait_queue() or schedule() calls from within a couple
117162306a36Sopenharmony_ci * xip_disable()'d  areas of code, like in do_erase_oneblock for example.
117262306a36Sopenharmony_ci * The queueing and scheduling are always happening within xip_udelay().
117362306a36Sopenharmony_ci *
117462306a36Sopenharmony_ci * Similarly, get_chip() and put_chip() just happen to always be executed
117562306a36Sopenharmony_ci * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state
117662306a36Sopenharmony_ci * is in array mode, therefore never executing many cases therein and not
117762306a36Sopenharmony_ci * causing any problem with XIP.
117862306a36Sopenharmony_ci */
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci#else
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci#define xip_disable(map, chip, adr)
118362306a36Sopenharmony_ci#define xip_enable(map, chip, adr)
118462306a36Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(x...)
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci#define UDELAY(map, chip, adr, usec)  \
118762306a36Sopenharmony_cido {  \
118862306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);  \
118962306a36Sopenharmony_ci	cfi_udelay(usec);  \
119062306a36Sopenharmony_ci	mutex_lock(&chip->mutex);  \
119162306a36Sopenharmony_ci} while (0)
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
119462306a36Sopenharmony_cido {  \
119562306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);  \
119662306a36Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, adr, len);  \
119762306a36Sopenharmony_ci	cfi_udelay(usec);  \
119862306a36Sopenharmony_ci	mutex_lock(&chip->mutex);  \
119962306a36Sopenharmony_ci} while (0)
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci#endif
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_cistatic inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	unsigned long cmd_addr;
120662306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
120762306a36Sopenharmony_ci	int ret;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	adr += chip->start;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	/* Ensure cmd read/writes are aligned. */
121262306a36Sopenharmony_ci	cmd_addr = adr & ~(map_bankwidth(map)-1);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
121562306a36Sopenharmony_ci	ret = get_chip(map, chip, cmd_addr, FL_READY);
121662306a36Sopenharmony_ci	if (ret) {
121762306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
121862306a36Sopenharmony_ci		return ret;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	if (chip->state != FL_POINT && chip->state != FL_READY) {
122262306a36Sopenharmony_ci		map_write(map, CMD(0xf0), cmd_addr);
122362306a36Sopenharmony_ci		chip->state = FL_READY;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	map_copy_from(map, buf, adr, len);
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	put_chip(map, chip, cmd_addr);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
123162306a36Sopenharmony_ci	return 0;
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_cistatic int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
123662306a36Sopenharmony_ci{
123762306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
123862306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
123962306a36Sopenharmony_ci	unsigned long ofs;
124062306a36Sopenharmony_ci	int chipnum;
124162306a36Sopenharmony_ci	int ret = 0;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	/* ofs: offset within the first chip that the first read should start */
124462306a36Sopenharmony_ci	chipnum = (from >> cfi->chipshift);
124562306a36Sopenharmony_ci	ofs = from - (chipnum <<  cfi->chipshift);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	while (len) {
124862306a36Sopenharmony_ci		unsigned long thislen;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci		if (chipnum >= cfi->numchips)
125162306a36Sopenharmony_ci			break;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		if ((len + ofs -1) >> cfi->chipshift)
125462306a36Sopenharmony_ci			thislen = (1<<cfi->chipshift) - ofs;
125562306a36Sopenharmony_ci		else
125662306a36Sopenharmony_ci			thislen = len;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci		ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf);
125962306a36Sopenharmony_ci		if (ret)
126062306a36Sopenharmony_ci			break;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci		*retlen += thislen;
126362306a36Sopenharmony_ci		len -= thislen;
126462306a36Sopenharmony_ci		buf += thislen;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci		ofs = 0;
126762306a36Sopenharmony_ci		chipnum++;
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci	return ret;
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_citypedef int (*otp_op_t)(struct map_info *map, struct flchip *chip,
127362306a36Sopenharmony_ci			loff_t adr, size_t len, u_char *buf, size_t grouplen);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic inline void otp_enter(struct map_info *map, struct flchip *chip,
127662306a36Sopenharmony_ci			     loff_t adr, size_t len)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
128162306a36Sopenharmony_ci			 cfi->device_type, NULL);
128262306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
128362306a36Sopenharmony_ci			 cfi->device_type, NULL);
128462306a36Sopenharmony_ci	cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi,
128562306a36Sopenharmony_ci			 cfi->device_type, NULL);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, chip->start + adr, len);
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic inline void otp_exit(struct map_info *map, struct flchip *chip,
129162306a36Sopenharmony_ci			    loff_t adr, size_t len)
129262306a36Sopenharmony_ci{
129362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
129662306a36Sopenharmony_ci			 cfi->device_type, NULL);
129762306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
129862306a36Sopenharmony_ci			 cfi->device_type, NULL);
129962306a36Sopenharmony_ci	cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi,
130062306a36Sopenharmony_ci			 cfi->device_type, NULL);
130162306a36Sopenharmony_ci	cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi,
130262306a36Sopenharmony_ci			 cfi->device_type, NULL);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, chip->start + adr, len);
130562306a36Sopenharmony_ci}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_cistatic inline int do_read_secsi_onechip(struct map_info *map,
130862306a36Sopenharmony_ci					struct flchip *chip, loff_t adr,
130962306a36Sopenharmony_ci					size_t len, u_char *buf,
131062306a36Sopenharmony_ci					size_t grouplen)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci retry:
131562306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	if (chip->state != FL_READY){
131862306a36Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
131962306a36Sopenharmony_ci		add_wait_queue(&chip->wq, &wait);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci		schedule();
132462306a36Sopenharmony_ci		remove_wait_queue(&chip->wq, &wait);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci		goto retry;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	adr += chip->start;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	chip->state = FL_READY;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	otp_enter(map, chip, adr, len);
133462306a36Sopenharmony_ci	map_copy_from(map, buf, adr, len);
133562306a36Sopenharmony_ci	otp_exit(map, chip, adr, len);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	wake_up(&chip->wq);
133862306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	return 0;
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_cistatic int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
134662306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
134762306a36Sopenharmony_ci	unsigned long ofs;
134862306a36Sopenharmony_ci	int chipnum;
134962306a36Sopenharmony_ci	int ret = 0;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	/* ofs: offset within the first chip that the first read should start */
135262306a36Sopenharmony_ci	/* 8 secsi bytes per chip */
135362306a36Sopenharmony_ci	chipnum=from>>3;
135462306a36Sopenharmony_ci	ofs=from & 7;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	while (len) {
135762306a36Sopenharmony_ci		unsigned long thislen;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci		if (chipnum >= cfi->numchips)
136062306a36Sopenharmony_ci			break;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci		if ((len + ofs -1) >> 3)
136362306a36Sopenharmony_ci			thislen = (1<<3) - ofs;
136462306a36Sopenharmony_ci		else
136562306a36Sopenharmony_ci			thislen = len;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci		ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs,
136862306a36Sopenharmony_ci					    thislen, buf, 0);
136962306a36Sopenharmony_ci		if (ret)
137062306a36Sopenharmony_ci			break;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci		*retlen += thislen;
137362306a36Sopenharmony_ci		len -= thislen;
137462306a36Sopenharmony_ci		buf += thislen;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci		ofs = 0;
137762306a36Sopenharmony_ci		chipnum++;
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci	return ret;
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cistatic int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
138362306a36Sopenharmony_ci				     unsigned long adr, map_word datum,
138462306a36Sopenharmony_ci				     int mode);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cistatic int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr,
138762306a36Sopenharmony_ci			size_t len, u_char *buf, size_t grouplen)
138862306a36Sopenharmony_ci{
138962306a36Sopenharmony_ci	int ret;
139062306a36Sopenharmony_ci	while (len) {
139162306a36Sopenharmony_ci		unsigned long bus_ofs = adr & ~(map_bankwidth(map)-1);
139262306a36Sopenharmony_ci		int gap = adr - bus_ofs;
139362306a36Sopenharmony_ci		int n = min_t(int, len, map_bankwidth(map) - gap);
139462306a36Sopenharmony_ci		map_word datum = map_word_ff(map);
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci		if (n != map_bankwidth(map)) {
139762306a36Sopenharmony_ci			/* partial write of a word, load old contents */
139862306a36Sopenharmony_ci			otp_enter(map, chip, bus_ofs, map_bankwidth(map));
139962306a36Sopenharmony_ci			datum = map_read(map, bus_ofs);
140062306a36Sopenharmony_ci			otp_exit(map, chip, bus_ofs, map_bankwidth(map));
140162306a36Sopenharmony_ci		}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		datum = map_word_load_partial(map, datum, buf, gap, n);
140462306a36Sopenharmony_ci		ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE);
140562306a36Sopenharmony_ci		if (ret)
140662306a36Sopenharmony_ci			return ret;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		adr += n;
140962306a36Sopenharmony_ci		buf += n;
141062306a36Sopenharmony_ci		len -= n;
141162306a36Sopenharmony_ci	}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	return 0;
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr,
141762306a36Sopenharmony_ci		       size_t len, u_char *buf, size_t grouplen)
141862306a36Sopenharmony_ci{
141962306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
142062306a36Sopenharmony_ci	uint8_t lockreg;
142162306a36Sopenharmony_ci	unsigned long timeo;
142262306a36Sopenharmony_ci	int ret;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	/* make sure area matches group boundaries */
142562306a36Sopenharmony_ci	if ((adr != 0) || (len != grouplen))
142662306a36Sopenharmony_ci		return -EINVAL;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
142962306a36Sopenharmony_ci	ret = get_chip(map, chip, chip->start, FL_LOCKING);
143062306a36Sopenharmony_ci	if (ret) {
143162306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
143262306a36Sopenharmony_ci		return ret;
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci	chip->state = FL_LOCKING;
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	/* Enter lock register command */
143762306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
143862306a36Sopenharmony_ci			 cfi->device_type, NULL);
143962306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
144062306a36Sopenharmony_ci			 cfi->device_type, NULL);
144162306a36Sopenharmony_ci	cfi_send_gen_cmd(0x40, cfi->addr_unlock1, chip->start, map, cfi,
144262306a36Sopenharmony_ci			 cfi->device_type, NULL);
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	/* read lock register */
144562306a36Sopenharmony_ci	lockreg = cfi_read_query(map, 0);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	/* set bit 0 to protect extended memory block */
144862306a36Sopenharmony_ci	lockreg &= ~0x01;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	/* set bit 0 to protect extended memory block */
145162306a36Sopenharmony_ci	/* write lock register */
145262306a36Sopenharmony_ci	map_write(map, CMD(0xA0), chip->start);
145362306a36Sopenharmony_ci	map_write(map, CMD(lockreg), chip->start);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	/* wait for chip to become ready */
145662306a36Sopenharmony_ci	timeo = jiffies + msecs_to_jiffies(2);
145762306a36Sopenharmony_ci	for (;;) {
145862306a36Sopenharmony_ci		if (chip_ready(map, chip, adr, NULL))
145962306a36Sopenharmony_ci			break;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci		if (time_after(jiffies, timeo)) {
146262306a36Sopenharmony_ci			pr_err("Waiting for chip to be ready timed out.\n");
146362306a36Sopenharmony_ci			ret = -EIO;
146462306a36Sopenharmony_ci			break;
146562306a36Sopenharmony_ci		}
146662306a36Sopenharmony_ci		UDELAY(map, chip, 0, 1);
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	/* exit protection commands */
147062306a36Sopenharmony_ci	map_write(map, CMD(0x90), chip->start);
147162306a36Sopenharmony_ci	map_write(map, CMD(0x00), chip->start);
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	chip->state = FL_READY;
147462306a36Sopenharmony_ci	put_chip(map, chip, chip->start);
147562306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	return ret;
147862306a36Sopenharmony_ci}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_cistatic int cfi_amdstd_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
148162306a36Sopenharmony_ci			       size_t *retlen, u_char *buf,
148262306a36Sopenharmony_ci			       otp_op_t action, int user_regs)
148362306a36Sopenharmony_ci{
148462306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
148562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
148662306a36Sopenharmony_ci	int ofs_factor = cfi->interleave * cfi->device_type;
148762306a36Sopenharmony_ci	unsigned long base;
148862306a36Sopenharmony_ci	int chipnum;
148962306a36Sopenharmony_ci	struct flchip *chip;
149062306a36Sopenharmony_ci	uint8_t otp, lockreg;
149162306a36Sopenharmony_ci	int ret;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	size_t user_size, factory_size, otpsize;
149462306a36Sopenharmony_ci	loff_t user_offset, factory_offset, otpoffset;
149562306a36Sopenharmony_ci	int user_locked = 0, otplocked;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	*retlen = 0;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	for (chipnum = 0; chipnum < cfi->numchips; chipnum++) {
150062306a36Sopenharmony_ci		chip = &cfi->chips[chipnum];
150162306a36Sopenharmony_ci		factory_size = 0;
150262306a36Sopenharmony_ci		user_size = 0;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci		/* Micron M29EW family */
150562306a36Sopenharmony_ci		if (is_m29ew(cfi)) {
150662306a36Sopenharmony_ci			base = chip->start;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci			/* check whether secsi area is factory locked
150962306a36Sopenharmony_ci			   or user lockable */
151062306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
151162306a36Sopenharmony_ci			ret = get_chip(map, chip, base, FL_CFI_QUERY);
151262306a36Sopenharmony_ci			if (ret) {
151362306a36Sopenharmony_ci				mutex_unlock(&chip->mutex);
151462306a36Sopenharmony_ci				return ret;
151562306a36Sopenharmony_ci			}
151662306a36Sopenharmony_ci			cfi_qry_mode_on(base, map, cfi);
151762306a36Sopenharmony_ci			otp = cfi_read_query(map, base + 0x3 * ofs_factor);
151862306a36Sopenharmony_ci			cfi_qry_mode_off(base, map, cfi);
151962306a36Sopenharmony_ci			put_chip(map, chip, base);
152062306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci			if (otp & 0x80) {
152362306a36Sopenharmony_ci				/* factory locked */
152462306a36Sopenharmony_ci				factory_offset = 0;
152562306a36Sopenharmony_ci				factory_size = 0x100;
152662306a36Sopenharmony_ci			} else {
152762306a36Sopenharmony_ci				/* customer lockable */
152862306a36Sopenharmony_ci				user_offset = 0;
152962306a36Sopenharmony_ci				user_size = 0x100;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci				mutex_lock(&chip->mutex);
153262306a36Sopenharmony_ci				ret = get_chip(map, chip, base, FL_LOCKING);
153362306a36Sopenharmony_ci				if (ret) {
153462306a36Sopenharmony_ci					mutex_unlock(&chip->mutex);
153562306a36Sopenharmony_ci					return ret;
153662306a36Sopenharmony_ci				}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci				/* Enter lock register command */
153962306a36Sopenharmony_ci				cfi_send_gen_cmd(0xAA, cfi->addr_unlock1,
154062306a36Sopenharmony_ci						 chip->start, map, cfi,
154162306a36Sopenharmony_ci						 cfi->device_type, NULL);
154262306a36Sopenharmony_ci				cfi_send_gen_cmd(0x55, cfi->addr_unlock2,
154362306a36Sopenharmony_ci						 chip->start, map, cfi,
154462306a36Sopenharmony_ci						 cfi->device_type, NULL);
154562306a36Sopenharmony_ci				cfi_send_gen_cmd(0x40, cfi->addr_unlock1,
154662306a36Sopenharmony_ci						 chip->start, map, cfi,
154762306a36Sopenharmony_ci						 cfi->device_type, NULL);
154862306a36Sopenharmony_ci				/* read lock register */
154962306a36Sopenharmony_ci				lockreg = cfi_read_query(map, 0);
155062306a36Sopenharmony_ci				/* exit protection commands */
155162306a36Sopenharmony_ci				map_write(map, CMD(0x90), chip->start);
155262306a36Sopenharmony_ci				map_write(map, CMD(0x00), chip->start);
155362306a36Sopenharmony_ci				put_chip(map, chip, chip->start);
155462306a36Sopenharmony_ci				mutex_unlock(&chip->mutex);
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci				user_locked = ((lockreg & 0x01) == 0x00);
155762306a36Sopenharmony_ci			}
155862306a36Sopenharmony_ci		}
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci		otpsize = user_regs ? user_size : factory_size;
156162306a36Sopenharmony_ci		if (!otpsize)
156262306a36Sopenharmony_ci			continue;
156362306a36Sopenharmony_ci		otpoffset = user_regs ? user_offset : factory_offset;
156462306a36Sopenharmony_ci		otplocked = user_regs ? user_locked : 1;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci		if (!action) {
156762306a36Sopenharmony_ci			/* return otpinfo */
156862306a36Sopenharmony_ci			struct otp_info *otpinfo;
156962306a36Sopenharmony_ci			len -= sizeof(*otpinfo);
157062306a36Sopenharmony_ci			if (len <= 0)
157162306a36Sopenharmony_ci				return -ENOSPC;
157262306a36Sopenharmony_ci			otpinfo = (struct otp_info *)buf;
157362306a36Sopenharmony_ci			otpinfo->start = from;
157462306a36Sopenharmony_ci			otpinfo->length = otpsize;
157562306a36Sopenharmony_ci			otpinfo->locked = otplocked;
157662306a36Sopenharmony_ci			buf += sizeof(*otpinfo);
157762306a36Sopenharmony_ci			*retlen += sizeof(*otpinfo);
157862306a36Sopenharmony_ci			from += otpsize;
157962306a36Sopenharmony_ci		} else if ((from < otpsize) && (len > 0)) {
158062306a36Sopenharmony_ci			size_t size;
158162306a36Sopenharmony_ci			size = (len < otpsize - from) ? len : otpsize - from;
158262306a36Sopenharmony_ci			ret = action(map, chip, otpoffset + from, size, buf,
158362306a36Sopenharmony_ci				     otpsize);
158462306a36Sopenharmony_ci			if (ret < 0)
158562306a36Sopenharmony_ci				return ret;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci			buf += size;
158862306a36Sopenharmony_ci			len -= size;
158962306a36Sopenharmony_ci			*retlen += size;
159062306a36Sopenharmony_ci			from = 0;
159162306a36Sopenharmony_ci		} else {
159262306a36Sopenharmony_ci			from -= otpsize;
159362306a36Sopenharmony_ci		}
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci	return 0;
159662306a36Sopenharmony_ci}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_cistatic int cfi_amdstd_get_fact_prot_info(struct mtd_info *mtd, size_t len,
159962306a36Sopenharmony_ci					 size_t *retlen, struct otp_info *buf)
160062306a36Sopenharmony_ci{
160162306a36Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
160262306a36Sopenharmony_ci				   NULL, 0);
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int cfi_amdstd_get_user_prot_info(struct mtd_info *mtd, size_t len,
160662306a36Sopenharmony_ci					 size_t *retlen, struct otp_info *buf)
160762306a36Sopenharmony_ci{
160862306a36Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
160962306a36Sopenharmony_ci				   NULL, 1);
161062306a36Sopenharmony_ci}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_cistatic int cfi_amdstd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
161362306a36Sopenharmony_ci					 size_t len, size_t *retlen,
161462306a36Sopenharmony_ci					 u_char *buf)
161562306a36Sopenharmony_ci{
161662306a36Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, from, len, retlen,
161762306a36Sopenharmony_ci				   buf, do_read_secsi_onechip, 0);
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_cistatic int cfi_amdstd_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
162162306a36Sopenharmony_ci					 size_t len, size_t *retlen,
162262306a36Sopenharmony_ci					 u_char *buf)
162362306a36Sopenharmony_ci{
162462306a36Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, from, len, retlen,
162562306a36Sopenharmony_ci				   buf, do_read_secsi_onechip, 1);
162662306a36Sopenharmony_ci}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_cistatic int cfi_amdstd_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
162962306a36Sopenharmony_ci					  size_t len, size_t *retlen,
163062306a36Sopenharmony_ci					  const u_char *buf)
163162306a36Sopenharmony_ci{
163262306a36Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, from, len, retlen, (u_char *)buf,
163362306a36Sopenharmony_ci				   do_otp_write, 1);
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_cistatic int cfi_amdstd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
163762306a36Sopenharmony_ci					 size_t len)
163862306a36Sopenharmony_ci{
163962306a36Sopenharmony_ci	size_t retlen;
164062306a36Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, from, len, &retlen, NULL,
164162306a36Sopenharmony_ci				   do_otp_lock, 1);
164262306a36Sopenharmony_ci}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_cistatic int __xipram do_write_oneword_once(struct map_info *map,
164562306a36Sopenharmony_ci					  struct flchip *chip,
164662306a36Sopenharmony_ci					  unsigned long adr, map_word datum,
164762306a36Sopenharmony_ci					  int mode, struct cfi_private *cfi)
164862306a36Sopenharmony_ci{
164962306a36Sopenharmony_ci	unsigned long timeo;
165062306a36Sopenharmony_ci	/*
165162306a36Sopenharmony_ci	 * We use a 1ms + 1 jiffies generic timeout for writes (most devices
165262306a36Sopenharmony_ci	 * have a max write time of a few hundreds usec). However, we should
165362306a36Sopenharmony_ci	 * use the maximum timeout value given by the chip at probe time
165462306a36Sopenharmony_ci	 * instead.  Unfortunately, struct flchip does have a field for
165562306a36Sopenharmony_ci	 * maximum timeout, only for typical which can be far too short
165662306a36Sopenharmony_ci	 * depending of the conditions.	 The ' + 1' is to avoid having a
165762306a36Sopenharmony_ci	 * timeout of 0 jiffies if HZ is smaller than 1000.
165862306a36Sopenharmony_ci	 */
165962306a36Sopenharmony_ci	unsigned long uWriteTimeout = (HZ / 1000) + 1;
166062306a36Sopenharmony_ci	int ret = 0;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
166362306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
166462306a36Sopenharmony_ci	cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
166562306a36Sopenharmony_ci	map_write(map, datum, adr);
166662306a36Sopenharmony_ci	chip->state = mode;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	INVALIDATE_CACHE_UDELAY(map, chip,
166962306a36Sopenharmony_ci				adr, map_bankwidth(map),
167062306a36Sopenharmony_ci				chip->word_write_time);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	/* See comment above for timeout value. */
167362306a36Sopenharmony_ci	timeo = jiffies + uWriteTimeout;
167462306a36Sopenharmony_ci	for (;;) {
167562306a36Sopenharmony_ci		if (chip->state != mode) {
167662306a36Sopenharmony_ci			/* Someone's suspended the write. Sleep */
167762306a36Sopenharmony_ci			DECLARE_WAITQUEUE(wait, current);
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
168062306a36Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
168162306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
168262306a36Sopenharmony_ci			schedule();
168362306a36Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
168462306a36Sopenharmony_ci			timeo = jiffies + (HZ / 2); /* FIXME */
168562306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
168662306a36Sopenharmony_ci			continue;
168762306a36Sopenharmony_ci		}
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci		/*
169062306a36Sopenharmony_ci		 * We check "time_after" and "!chip_good" before checking
169162306a36Sopenharmony_ci		 * "chip_good" to avoid the failure due to scheduling.
169262306a36Sopenharmony_ci		 */
169362306a36Sopenharmony_ci		if (time_after(jiffies, timeo) &&
169462306a36Sopenharmony_ci		    !chip_good(map, chip, adr, &datum)) {
169562306a36Sopenharmony_ci			xip_enable(map, chip, adr);
169662306a36Sopenharmony_ci			printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
169762306a36Sopenharmony_ci			xip_disable(map, chip, adr);
169862306a36Sopenharmony_ci			ret = -EIO;
169962306a36Sopenharmony_ci			break;
170062306a36Sopenharmony_ci		}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci		if (chip_good(map, chip, adr, &datum)) {
170362306a36Sopenharmony_ci			if (cfi_check_err_status(map, chip, adr))
170462306a36Sopenharmony_ci				ret = -EIO;
170562306a36Sopenharmony_ci			break;
170662306a36Sopenharmony_ci		}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci		/* Latency issues. Drop the lock, wait a while and retry */
170962306a36Sopenharmony_ci		UDELAY(map, chip, adr, 1);
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	return ret;
171362306a36Sopenharmony_ci}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_cistatic int __xipram do_write_oneword_start(struct map_info *map,
171662306a36Sopenharmony_ci					   struct flchip *chip,
171762306a36Sopenharmony_ci					   unsigned long adr, int mode)
171862306a36Sopenharmony_ci{
171962306a36Sopenharmony_ci	int ret;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	ret = get_chip(map, chip, adr, mode);
172462306a36Sopenharmony_ci	if (ret) {
172562306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
172662306a36Sopenharmony_ci		return ret;
172762306a36Sopenharmony_ci	}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	if (mode == FL_OTP_WRITE)
173062306a36Sopenharmony_ci		otp_enter(map, chip, adr, map_bankwidth(map));
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	return ret;
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_cistatic void __xipram do_write_oneword_done(struct map_info *map,
173662306a36Sopenharmony_ci					   struct flchip *chip,
173762306a36Sopenharmony_ci					   unsigned long adr, int mode)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	if (mode == FL_OTP_WRITE)
174062306a36Sopenharmony_ci		otp_exit(map, chip, adr, map_bankwidth(map));
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	chip->state = FL_READY;
174362306a36Sopenharmony_ci	DISABLE_VPP(map);
174462306a36Sopenharmony_ci	put_chip(map, chip, adr);
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
174762306a36Sopenharmony_ci}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_cistatic int __xipram do_write_oneword_retry(struct map_info *map,
175062306a36Sopenharmony_ci					   struct flchip *chip,
175162306a36Sopenharmony_ci					   unsigned long adr, map_word datum,
175262306a36Sopenharmony_ci					   int mode)
175362306a36Sopenharmony_ci{
175462306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
175562306a36Sopenharmony_ci	int ret = 0;
175662306a36Sopenharmony_ci	map_word oldd;
175762306a36Sopenharmony_ci	int retry_cnt = 0;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	/*
176062306a36Sopenharmony_ci	 * Check for a NOP for the case when the datum to write is already
176162306a36Sopenharmony_ci	 * present - it saves time and works around buggy chips that corrupt
176262306a36Sopenharmony_ci	 * data at other locations when 0xff is written to a location that
176362306a36Sopenharmony_ci	 * already contains 0xff.
176462306a36Sopenharmony_ci	 */
176562306a36Sopenharmony_ci	oldd = map_read(map, adr);
176662306a36Sopenharmony_ci	if (map_word_equal(map, oldd, datum)) {
176762306a36Sopenharmony_ci		pr_debug("MTD %s(): NOP\n", __func__);
176862306a36Sopenharmony_ci		return ret;
176962306a36Sopenharmony_ci	}
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
177262306a36Sopenharmony_ci	ENABLE_VPP(map);
177362306a36Sopenharmony_ci	xip_disable(map, chip, adr);
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci retry:
177662306a36Sopenharmony_ci	ret = do_write_oneword_once(map, chip, adr, datum, mode, cfi);
177762306a36Sopenharmony_ci	if (ret) {
177862306a36Sopenharmony_ci		/* reset on all failures. */
177962306a36Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
178062306a36Sopenharmony_ci		/* FIXME - should have reset delay before continuing */
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci		if (++retry_cnt <= MAX_RETRIES) {
178362306a36Sopenharmony_ci			ret = 0;
178462306a36Sopenharmony_ci			goto retry;
178562306a36Sopenharmony_ci		}
178662306a36Sopenharmony_ci	}
178762306a36Sopenharmony_ci	xip_enable(map, chip, adr);
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	return ret;
179062306a36Sopenharmony_ci}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_cistatic int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
179362306a36Sopenharmony_ci				     unsigned long adr, map_word datum,
179462306a36Sopenharmony_ci				     int mode)
179562306a36Sopenharmony_ci{
179662306a36Sopenharmony_ci	int ret;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	adr += chip->start;
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", __func__, adr,
180162306a36Sopenharmony_ci		 datum.x[0]);
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	ret = do_write_oneword_start(map, chip, adr, mode);
180462306a36Sopenharmony_ci	if (ret)
180562306a36Sopenharmony_ci		return ret;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	ret = do_write_oneword_retry(map, chip, adr, datum, mode);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	do_write_oneword_done(map, chip, adr, mode);
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	return ret;
181262306a36Sopenharmony_ci}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_cistatic int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
181662306a36Sopenharmony_ci				  size_t *retlen, const u_char *buf)
181762306a36Sopenharmony_ci{
181862306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
181962306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
182062306a36Sopenharmony_ci	int ret;
182162306a36Sopenharmony_ci	int chipnum;
182262306a36Sopenharmony_ci	unsigned long ofs, chipstart;
182362306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	chipnum = to >> cfi->chipshift;
182662306a36Sopenharmony_ci	ofs = to  - (chipnum << cfi->chipshift);
182762306a36Sopenharmony_ci	chipstart = cfi->chips[chipnum].start;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	/* If it's not bus-aligned, do the first byte write */
183062306a36Sopenharmony_ci	if (ofs & (map_bankwidth(map)-1)) {
183162306a36Sopenharmony_ci		unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1);
183262306a36Sopenharmony_ci		int i = ofs - bus_ofs;
183362306a36Sopenharmony_ci		int n = 0;
183462306a36Sopenharmony_ci		map_word tmp_buf;
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci retry:
183762306a36Sopenharmony_ci		mutex_lock(&cfi->chips[chipnum].mutex);
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci		if (cfi->chips[chipnum].state != FL_READY) {
184062306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
184162306a36Sopenharmony_ci			add_wait_queue(&cfi->chips[chipnum].wq, &wait);
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci			mutex_unlock(&cfi->chips[chipnum].mutex);
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci			schedule();
184662306a36Sopenharmony_ci			remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
184762306a36Sopenharmony_ci			goto retry;
184862306a36Sopenharmony_ci		}
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci		/* Load 'tmp_buf' with old contents of flash */
185162306a36Sopenharmony_ci		tmp_buf = map_read(map, bus_ofs+chipstart);
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci		mutex_unlock(&cfi->chips[chipnum].mutex);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci		/* Number of bytes to copy from buffer */
185662306a36Sopenharmony_ci		n = min_t(int, len, map_bankwidth(map)-i);
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci		tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci		ret = do_write_oneword(map, &cfi->chips[chipnum],
186162306a36Sopenharmony_ci				       bus_ofs, tmp_buf, FL_WRITING);
186262306a36Sopenharmony_ci		if (ret)
186362306a36Sopenharmony_ci			return ret;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci		ofs += n;
186662306a36Sopenharmony_ci		buf += n;
186762306a36Sopenharmony_ci		(*retlen) += n;
186862306a36Sopenharmony_ci		len -= n;
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci		if (ofs >> cfi->chipshift) {
187162306a36Sopenharmony_ci			chipnum ++;
187262306a36Sopenharmony_ci			ofs = 0;
187362306a36Sopenharmony_ci			if (chipnum == cfi->numchips)
187462306a36Sopenharmony_ci				return 0;
187562306a36Sopenharmony_ci		}
187662306a36Sopenharmony_ci	}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	/* We are now aligned, write as much as possible */
187962306a36Sopenharmony_ci	while(len >= map_bankwidth(map)) {
188062306a36Sopenharmony_ci		map_word datum;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci		datum = map_word_load(map, buf);
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci		ret = do_write_oneword(map, &cfi->chips[chipnum],
188562306a36Sopenharmony_ci				       ofs, datum, FL_WRITING);
188662306a36Sopenharmony_ci		if (ret)
188762306a36Sopenharmony_ci			return ret;
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci		ofs += map_bankwidth(map);
189062306a36Sopenharmony_ci		buf += map_bankwidth(map);
189162306a36Sopenharmony_ci		(*retlen) += map_bankwidth(map);
189262306a36Sopenharmony_ci		len -= map_bankwidth(map);
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci		if (ofs >> cfi->chipshift) {
189562306a36Sopenharmony_ci			chipnum ++;
189662306a36Sopenharmony_ci			ofs = 0;
189762306a36Sopenharmony_ci			if (chipnum == cfi->numchips)
189862306a36Sopenharmony_ci				return 0;
189962306a36Sopenharmony_ci			chipstart = cfi->chips[chipnum].start;
190062306a36Sopenharmony_ci		}
190162306a36Sopenharmony_ci	}
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	/* Write the trailing bytes if any */
190462306a36Sopenharmony_ci	if (len & (map_bankwidth(map)-1)) {
190562306a36Sopenharmony_ci		map_word tmp_buf;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci retry1:
190862306a36Sopenharmony_ci		mutex_lock(&cfi->chips[chipnum].mutex);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci		if (cfi->chips[chipnum].state != FL_READY) {
191162306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
191262306a36Sopenharmony_ci			add_wait_queue(&cfi->chips[chipnum].wq, &wait);
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci			mutex_unlock(&cfi->chips[chipnum].mutex);
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci			schedule();
191762306a36Sopenharmony_ci			remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
191862306a36Sopenharmony_ci			goto retry1;
191962306a36Sopenharmony_ci		}
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci		tmp_buf = map_read(map, ofs + chipstart);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci		mutex_unlock(&cfi->chips[chipnum].mutex);
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci		tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci		ret = do_write_oneword(map, &cfi->chips[chipnum],
192862306a36Sopenharmony_ci				       ofs, tmp_buf, FL_WRITING);
192962306a36Sopenharmony_ci		if (ret)
193062306a36Sopenharmony_ci			return ret;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci		(*retlen) += len;
193362306a36Sopenharmony_ci	}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	return 0;
193662306a36Sopenharmony_ci}
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci#if !FORCE_WORD_WRITE
193962306a36Sopenharmony_cistatic int __xipram do_write_buffer_wait(struct map_info *map,
194062306a36Sopenharmony_ci					 struct flchip *chip, unsigned long adr,
194162306a36Sopenharmony_ci					 map_word datum)
194262306a36Sopenharmony_ci{
194362306a36Sopenharmony_ci	unsigned long timeo;
194462306a36Sopenharmony_ci	unsigned long u_write_timeout;
194562306a36Sopenharmony_ci	int ret = 0;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	/*
194862306a36Sopenharmony_ci	 * Timeout is calculated according to CFI data, if available.
194962306a36Sopenharmony_ci	 * See more comments in cfi_cmdset_0002().
195062306a36Sopenharmony_ci	 */
195162306a36Sopenharmony_ci	u_write_timeout = usecs_to_jiffies(chip->buffer_write_time_max);
195262306a36Sopenharmony_ci	timeo = jiffies + u_write_timeout;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	for (;;) {
195562306a36Sopenharmony_ci		if (chip->state != FL_WRITING) {
195662306a36Sopenharmony_ci			/* Someone's suspended the write. Sleep */
195762306a36Sopenharmony_ci			DECLARE_WAITQUEUE(wait, current);
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
196062306a36Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
196162306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
196262306a36Sopenharmony_ci			schedule();
196362306a36Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
196462306a36Sopenharmony_ci			timeo = jiffies + (HZ / 2); /* FIXME */
196562306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
196662306a36Sopenharmony_ci			continue;
196762306a36Sopenharmony_ci		}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci		/*
197062306a36Sopenharmony_ci		 * We check "time_after" and "!chip_good" before checking
197162306a36Sopenharmony_ci		 * "chip_good" to avoid the failure due to scheduling.
197262306a36Sopenharmony_ci		 */
197362306a36Sopenharmony_ci		if (time_after(jiffies, timeo) &&
197462306a36Sopenharmony_ci		    !chip_good(map, chip, adr, &datum)) {
197562306a36Sopenharmony_ci			pr_err("MTD %s(): software timeout, address:0x%.8lx.\n",
197662306a36Sopenharmony_ci			       __func__, adr);
197762306a36Sopenharmony_ci			ret = -EIO;
197862306a36Sopenharmony_ci			break;
197962306a36Sopenharmony_ci		}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci		if (chip_good(map, chip, adr, &datum)) {
198262306a36Sopenharmony_ci			if (cfi_check_err_status(map, chip, adr))
198362306a36Sopenharmony_ci				ret = -EIO;
198462306a36Sopenharmony_ci			break;
198562306a36Sopenharmony_ci		}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci		/* Latency issues. Drop the lock, wait a while and retry */
198862306a36Sopenharmony_ci		UDELAY(map, chip, adr, 1);
198962306a36Sopenharmony_ci	}
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	return ret;
199262306a36Sopenharmony_ci}
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_cistatic void __xipram do_write_buffer_reset(struct map_info *map,
199562306a36Sopenharmony_ci					   struct flchip *chip,
199662306a36Sopenharmony_ci					   struct cfi_private *cfi)
199762306a36Sopenharmony_ci{
199862306a36Sopenharmony_ci	/*
199962306a36Sopenharmony_ci	 * Recovery from write-buffer programming failures requires
200062306a36Sopenharmony_ci	 * the write-to-buffer-reset sequence.  Since the last part
200162306a36Sopenharmony_ci	 * of the sequence also works as a normal reset, we can run
200262306a36Sopenharmony_ci	 * the same commands regardless of why we are here.
200362306a36Sopenharmony_ci	 * See e.g.
200462306a36Sopenharmony_ci	 * http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf
200562306a36Sopenharmony_ci	 */
200662306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
200762306a36Sopenharmony_ci			 cfi->device_type, NULL);
200862306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
200962306a36Sopenharmony_ci			 cfi->device_type, NULL);
201062306a36Sopenharmony_ci	cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi,
201162306a36Sopenharmony_ci			 cfi->device_type, NULL);
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	/* FIXME - should have reset delay before continuing */
201462306a36Sopenharmony_ci}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci/*
201762306a36Sopenharmony_ci * FIXME: interleaved mode not tested, and probably not supported!
201862306a36Sopenharmony_ci */
201962306a36Sopenharmony_cistatic int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
202062306a36Sopenharmony_ci				    unsigned long adr, const u_char *buf,
202162306a36Sopenharmony_ci				    int len)
202262306a36Sopenharmony_ci{
202362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
202462306a36Sopenharmony_ci	int ret;
202562306a36Sopenharmony_ci	unsigned long cmd_adr;
202662306a36Sopenharmony_ci	int z, words;
202762306a36Sopenharmony_ci	map_word datum;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	adr += chip->start;
203062306a36Sopenharmony_ci	cmd_adr = adr;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
203362306a36Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_WRITING);
203462306a36Sopenharmony_ci	if (ret) {
203562306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
203662306a36Sopenharmony_ci		return ret;
203762306a36Sopenharmony_ci	}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	datum = map_word_load(map, buf);
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
204262306a36Sopenharmony_ci		 __func__, adr, datum.x[0]);
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, len);
204562306a36Sopenharmony_ci	ENABLE_VPP(map);
204662306a36Sopenharmony_ci	xip_disable(map, chip, cmd_adr);
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
204962306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	/* Write Buffer Load */
205262306a36Sopenharmony_ci	map_write(map, CMD(0x25), cmd_adr);
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	chip->state = FL_WRITING_TO_BUFFER;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	/* Write length of data to come */
205762306a36Sopenharmony_ci	words = len / map_bankwidth(map);
205862306a36Sopenharmony_ci	map_write(map, CMD(words - 1), cmd_adr);
205962306a36Sopenharmony_ci	/* Write data */
206062306a36Sopenharmony_ci	z = 0;
206162306a36Sopenharmony_ci	while(z < words * map_bankwidth(map)) {
206262306a36Sopenharmony_ci		datum = map_word_load(map, buf);
206362306a36Sopenharmony_ci		map_write(map, datum, adr + z);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci		z += map_bankwidth(map);
206662306a36Sopenharmony_ci		buf += map_bankwidth(map);
206762306a36Sopenharmony_ci	}
206862306a36Sopenharmony_ci	z -= map_bankwidth(map);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	adr += z;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	/* Write Buffer Program Confirm: GO GO GO */
207362306a36Sopenharmony_ci	map_write(map, CMD(0x29), cmd_adr);
207462306a36Sopenharmony_ci	chip->state = FL_WRITING;
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	INVALIDATE_CACHE_UDELAY(map, chip,
207762306a36Sopenharmony_ci				adr, map_bankwidth(map),
207862306a36Sopenharmony_ci				chip->word_write_time);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	ret = do_write_buffer_wait(map, chip, adr, datum);
208162306a36Sopenharmony_ci	if (ret)
208262306a36Sopenharmony_ci		do_write_buffer_reset(map, chip, cfi);
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	xip_enable(map, chip, adr);
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	chip->state = FL_READY;
208762306a36Sopenharmony_ci	DISABLE_VPP(map);
208862306a36Sopenharmony_ci	put_chip(map, chip, adr);
208962306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	return ret;
209262306a36Sopenharmony_ci}
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_cistatic int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
209662306a36Sopenharmony_ci				    size_t *retlen, const u_char *buf)
209762306a36Sopenharmony_ci{
209862306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
209962306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
210062306a36Sopenharmony_ci	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
210162306a36Sopenharmony_ci	int ret;
210262306a36Sopenharmony_ci	int chipnum;
210362306a36Sopenharmony_ci	unsigned long ofs;
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	chipnum = to >> cfi->chipshift;
210662306a36Sopenharmony_ci	ofs = to  - (chipnum << cfi->chipshift);
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	/* If it's not bus-aligned, do the first word write */
210962306a36Sopenharmony_ci	if (ofs & (map_bankwidth(map)-1)) {
211062306a36Sopenharmony_ci		size_t local_len = (-ofs)&(map_bankwidth(map)-1);
211162306a36Sopenharmony_ci		if (local_len > len)
211262306a36Sopenharmony_ci			local_len = len;
211362306a36Sopenharmony_ci		ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
211462306a36Sopenharmony_ci					     local_len, retlen, buf);
211562306a36Sopenharmony_ci		if (ret)
211662306a36Sopenharmony_ci			return ret;
211762306a36Sopenharmony_ci		ofs += local_len;
211862306a36Sopenharmony_ci		buf += local_len;
211962306a36Sopenharmony_ci		len -= local_len;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci		if (ofs >> cfi->chipshift) {
212262306a36Sopenharmony_ci			chipnum ++;
212362306a36Sopenharmony_ci			ofs = 0;
212462306a36Sopenharmony_ci			if (chipnum == cfi->numchips)
212562306a36Sopenharmony_ci				return 0;
212662306a36Sopenharmony_ci		}
212762306a36Sopenharmony_ci	}
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	/* Write buffer is worth it only if more than one word to write... */
213062306a36Sopenharmony_ci	while (len >= map_bankwidth(map) * 2) {
213162306a36Sopenharmony_ci		/* We must not cross write block boundaries */
213262306a36Sopenharmony_ci		int size = wbufsize - (ofs & (wbufsize-1));
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci		if (size > len)
213562306a36Sopenharmony_ci			size = len;
213662306a36Sopenharmony_ci		if (size % map_bankwidth(map))
213762306a36Sopenharmony_ci			size -= size % map_bankwidth(map);
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci		ret = do_write_buffer(map, &cfi->chips[chipnum],
214062306a36Sopenharmony_ci				      ofs, buf, size);
214162306a36Sopenharmony_ci		if (ret)
214262306a36Sopenharmony_ci			return ret;
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci		ofs += size;
214562306a36Sopenharmony_ci		buf += size;
214662306a36Sopenharmony_ci		(*retlen) += size;
214762306a36Sopenharmony_ci		len -= size;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci		if (ofs >> cfi->chipshift) {
215062306a36Sopenharmony_ci			chipnum ++;
215162306a36Sopenharmony_ci			ofs = 0;
215262306a36Sopenharmony_ci			if (chipnum == cfi->numchips)
215362306a36Sopenharmony_ci				return 0;
215462306a36Sopenharmony_ci		}
215562306a36Sopenharmony_ci	}
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	if (len) {
215862306a36Sopenharmony_ci		size_t retlen_dregs = 0;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci		ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
216162306a36Sopenharmony_ci					     len, &retlen_dregs, buf);
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci		*retlen += retlen_dregs;
216462306a36Sopenharmony_ci		return ret;
216562306a36Sopenharmony_ci	}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	return 0;
216862306a36Sopenharmony_ci}
216962306a36Sopenharmony_ci#endif /* !FORCE_WORD_WRITE */
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci/*
217262306a36Sopenharmony_ci * Wait for the flash chip to become ready to write data
217362306a36Sopenharmony_ci *
217462306a36Sopenharmony_ci * This is only called during the panic_write() path. When panic_write()
217562306a36Sopenharmony_ci * is called, the kernel is in the process of a panic, and will soon be
217662306a36Sopenharmony_ci * dead. Therefore we don't take any locks, and attempt to get access
217762306a36Sopenharmony_ci * to the chip as soon as possible.
217862306a36Sopenharmony_ci */
217962306a36Sopenharmony_cistatic int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
218062306a36Sopenharmony_ci				 unsigned long adr)
218162306a36Sopenharmony_ci{
218262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
218362306a36Sopenharmony_ci	int retries = 10;
218462306a36Sopenharmony_ci	int i;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	/*
218762306a36Sopenharmony_ci	 * If the driver thinks the chip is idle, and no toggle bits
218862306a36Sopenharmony_ci	 * are changing, then the chip is actually idle for sure.
218962306a36Sopenharmony_ci	 */
219062306a36Sopenharmony_ci	if (chip->state == FL_READY && chip_ready(map, chip, adr, NULL))
219162306a36Sopenharmony_ci		return 0;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	/*
219462306a36Sopenharmony_ci	 * Try several times to reset the chip and then wait for it
219562306a36Sopenharmony_ci	 * to become idle. The upper limit of a few milliseconds of
219662306a36Sopenharmony_ci	 * delay isn't a big problem: the kernel is dying anyway. It
219762306a36Sopenharmony_ci	 * is more important to save the messages.
219862306a36Sopenharmony_ci	 */
219962306a36Sopenharmony_ci	while (retries > 0) {
220062306a36Sopenharmony_ci		const unsigned long timeo = (HZ / 1000) + 1;
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci		/* send the reset command */
220362306a36Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci		/* wait for the chip to become ready */
220662306a36Sopenharmony_ci		for (i = 0; i < jiffies_to_usecs(timeo); i++) {
220762306a36Sopenharmony_ci			if (chip_ready(map, chip, adr, NULL))
220862306a36Sopenharmony_ci				return 0;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci			udelay(1);
221162306a36Sopenharmony_ci		}
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci		retries--;
221462306a36Sopenharmony_ci	}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	/* the chip never became ready */
221762306a36Sopenharmony_ci	return -EBUSY;
221862306a36Sopenharmony_ci}
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci/*
222162306a36Sopenharmony_ci * Write out one word of data to a single flash chip during a kernel panic
222262306a36Sopenharmony_ci *
222362306a36Sopenharmony_ci * This is only called during the panic_write() path. When panic_write()
222462306a36Sopenharmony_ci * is called, the kernel is in the process of a panic, and will soon be
222562306a36Sopenharmony_ci * dead. Therefore we don't take any locks, and attempt to get access
222662306a36Sopenharmony_ci * to the chip as soon as possible.
222762306a36Sopenharmony_ci *
222862306a36Sopenharmony_ci * The implementation of this routine is intentionally similar to
222962306a36Sopenharmony_ci * do_write_oneword(), in order to ease code maintenance.
223062306a36Sopenharmony_ci */
223162306a36Sopenharmony_cistatic int do_panic_write_oneword(struct map_info *map, struct flchip *chip,
223262306a36Sopenharmony_ci				  unsigned long adr, map_word datum)
223362306a36Sopenharmony_ci{
223462306a36Sopenharmony_ci	const unsigned long uWriteTimeout = (HZ / 1000) + 1;
223562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
223662306a36Sopenharmony_ci	int retry_cnt = 0;
223762306a36Sopenharmony_ci	map_word oldd;
223862306a36Sopenharmony_ci	int ret;
223962306a36Sopenharmony_ci	int i;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	adr += chip->start;
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	ret = cfi_amdstd_panic_wait(map, chip, adr);
224462306a36Sopenharmony_ci	if (ret)
224562306a36Sopenharmony_ci		return ret;
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	pr_debug("MTD %s(): PANIC WRITE 0x%.8lx(0x%.8lx)\n",
224862306a36Sopenharmony_ci			__func__, adr, datum.x[0]);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	/*
225162306a36Sopenharmony_ci	 * Check for a NOP for the case when the datum to write is already
225262306a36Sopenharmony_ci	 * present - it saves time and works around buggy chips that corrupt
225362306a36Sopenharmony_ci	 * data at other locations when 0xff is written to a location that
225462306a36Sopenharmony_ci	 * already contains 0xff.
225562306a36Sopenharmony_ci	 */
225662306a36Sopenharmony_ci	oldd = map_read(map, adr);
225762306a36Sopenharmony_ci	if (map_word_equal(map, oldd, datum)) {
225862306a36Sopenharmony_ci		pr_debug("MTD %s(): NOP\n", __func__);
225962306a36Sopenharmony_ci		goto op_done;
226062306a36Sopenharmony_ci	}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	ENABLE_VPP(map);
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ciretry:
226562306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
226662306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
226762306a36Sopenharmony_ci	cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
226862306a36Sopenharmony_ci	map_write(map, datum, adr);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) {
227162306a36Sopenharmony_ci		if (chip_ready(map, chip, adr, NULL))
227262306a36Sopenharmony_ci			break;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci		udelay(1);
227562306a36Sopenharmony_ci	}
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	if (!chip_ready(map, chip, adr, &datum) ||
227862306a36Sopenharmony_ci	    cfi_check_err_status(map, chip, adr)) {
227962306a36Sopenharmony_ci		/* reset on all failures. */
228062306a36Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
228162306a36Sopenharmony_ci		/* FIXME - should have reset delay before continuing */
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci		if (++retry_cnt <= MAX_RETRIES)
228462306a36Sopenharmony_ci			goto retry;
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci		ret = -EIO;
228762306a36Sopenharmony_ci	}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ciop_done:
229062306a36Sopenharmony_ci	DISABLE_VPP(map);
229162306a36Sopenharmony_ci	return ret;
229262306a36Sopenharmony_ci}
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci/*
229562306a36Sopenharmony_ci * Write out some data during a kernel panic
229662306a36Sopenharmony_ci *
229762306a36Sopenharmony_ci * This is used by the mtdoops driver to save the dying messages from a
229862306a36Sopenharmony_ci * kernel which has panic'd.
229962306a36Sopenharmony_ci *
230062306a36Sopenharmony_ci * This routine ignores all of the locking used throughout the rest of the
230162306a36Sopenharmony_ci * driver, in order to ensure that the data gets written out no matter what
230262306a36Sopenharmony_ci * state this driver (and the flash chip itself) was in when the kernel crashed.
230362306a36Sopenharmony_ci *
230462306a36Sopenharmony_ci * The implementation of this routine is intentionally similar to
230562306a36Sopenharmony_ci * cfi_amdstd_write_words(), in order to ease code maintenance.
230662306a36Sopenharmony_ci */
230762306a36Sopenharmony_cistatic int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
230862306a36Sopenharmony_ci				  size_t *retlen, const u_char *buf)
230962306a36Sopenharmony_ci{
231062306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
231162306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
231262306a36Sopenharmony_ci	unsigned long ofs, chipstart;
231362306a36Sopenharmony_ci	int ret;
231462306a36Sopenharmony_ci	int chipnum;
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	chipnum = to >> cfi->chipshift;
231762306a36Sopenharmony_ci	ofs = to - (chipnum << cfi->chipshift);
231862306a36Sopenharmony_ci	chipstart = cfi->chips[chipnum].start;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	/* If it's not bus aligned, do the first byte write */
232162306a36Sopenharmony_ci	if (ofs & (map_bankwidth(map) - 1)) {
232262306a36Sopenharmony_ci		unsigned long bus_ofs = ofs & ~(map_bankwidth(map) - 1);
232362306a36Sopenharmony_ci		int i = ofs - bus_ofs;
232462306a36Sopenharmony_ci		int n = 0;
232562306a36Sopenharmony_ci		map_word tmp_buf;
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci		ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], bus_ofs);
232862306a36Sopenharmony_ci		if (ret)
232962306a36Sopenharmony_ci			return ret;
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci		/* Load 'tmp_buf' with old contents of flash */
233262306a36Sopenharmony_ci		tmp_buf = map_read(map, bus_ofs + chipstart);
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci		/* Number of bytes to copy from buffer */
233562306a36Sopenharmony_ci		n = min_t(int, len, map_bankwidth(map) - i);
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci		tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci		ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
234062306a36Sopenharmony_ci					     bus_ofs, tmp_buf);
234162306a36Sopenharmony_ci		if (ret)
234262306a36Sopenharmony_ci			return ret;
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci		ofs += n;
234562306a36Sopenharmony_ci		buf += n;
234662306a36Sopenharmony_ci		(*retlen) += n;
234762306a36Sopenharmony_ci		len -= n;
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci		if (ofs >> cfi->chipshift) {
235062306a36Sopenharmony_ci			chipnum++;
235162306a36Sopenharmony_ci			ofs = 0;
235262306a36Sopenharmony_ci			if (chipnum == cfi->numchips)
235362306a36Sopenharmony_ci				return 0;
235462306a36Sopenharmony_ci		}
235562306a36Sopenharmony_ci	}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	/* We are now aligned, write as much as possible */
235862306a36Sopenharmony_ci	while (len >= map_bankwidth(map)) {
235962306a36Sopenharmony_ci		map_word datum;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci		datum = map_word_load(map, buf);
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci		ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
236462306a36Sopenharmony_ci					     ofs, datum);
236562306a36Sopenharmony_ci		if (ret)
236662306a36Sopenharmony_ci			return ret;
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci		ofs += map_bankwidth(map);
236962306a36Sopenharmony_ci		buf += map_bankwidth(map);
237062306a36Sopenharmony_ci		(*retlen) += map_bankwidth(map);
237162306a36Sopenharmony_ci		len -= map_bankwidth(map);
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_ci		if (ofs >> cfi->chipshift) {
237462306a36Sopenharmony_ci			chipnum++;
237562306a36Sopenharmony_ci			ofs = 0;
237662306a36Sopenharmony_ci			if (chipnum == cfi->numchips)
237762306a36Sopenharmony_ci				return 0;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci			chipstart = cfi->chips[chipnum].start;
238062306a36Sopenharmony_ci		}
238162306a36Sopenharmony_ci	}
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	/* Write the trailing bytes if any */
238462306a36Sopenharmony_ci	if (len & (map_bankwidth(map) - 1)) {
238562306a36Sopenharmony_ci		map_word tmp_buf;
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci		ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], ofs);
238862306a36Sopenharmony_ci		if (ret)
238962306a36Sopenharmony_ci			return ret;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci		tmp_buf = map_read(map, ofs + chipstart);
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci		tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci		ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
239662306a36Sopenharmony_ci					     ofs, tmp_buf);
239762306a36Sopenharmony_ci		if (ret)
239862306a36Sopenharmony_ci			return ret;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci		(*retlen) += len;
240162306a36Sopenharmony_ci	}
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	return 0;
240462306a36Sopenharmony_ci}
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci/*
240862306a36Sopenharmony_ci * Handle devices with one erase region, that only implement
240962306a36Sopenharmony_ci * the chip erase command.
241062306a36Sopenharmony_ci */
241162306a36Sopenharmony_cistatic int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
241262306a36Sopenharmony_ci{
241362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
241462306a36Sopenharmony_ci	unsigned long timeo = jiffies + HZ;
241562306a36Sopenharmony_ci	unsigned long int adr;
241662306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
241762306a36Sopenharmony_ci	int ret;
241862306a36Sopenharmony_ci	int retry_cnt = 0;
241962306a36Sopenharmony_ci	map_word datum = map_word_ff(map);
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	adr = cfi->addr_unlock1;
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
242462306a36Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_ERASING);
242562306a36Sopenharmony_ci	if (ret) {
242662306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
242762306a36Sopenharmony_ci		return ret;
242862306a36Sopenharmony_ci	}
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	pr_debug("MTD %s(): ERASE 0x%.8lx\n",
243162306a36Sopenharmony_ci	       __func__, chip->start);
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, map->size);
243462306a36Sopenharmony_ci	ENABLE_VPP(map);
243562306a36Sopenharmony_ci	xip_disable(map, chip, adr);
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci retry:
243862306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
243962306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
244062306a36Sopenharmony_ci	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
244162306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
244262306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
244362306a36Sopenharmony_ci	cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	chip->state = FL_ERASING;
244662306a36Sopenharmony_ci	chip->erase_suspended = 0;
244762306a36Sopenharmony_ci	chip->in_progress_block_addr = adr;
244862306a36Sopenharmony_ci	chip->in_progress_block_mask = ~(map->size - 1);
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	INVALIDATE_CACHE_UDELAY(map, chip,
245162306a36Sopenharmony_ci				adr, map->size,
245262306a36Sopenharmony_ci				chip->erase_time*500);
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	timeo = jiffies + (HZ*20);
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	for (;;) {
245762306a36Sopenharmony_ci		if (chip->state != FL_ERASING) {
245862306a36Sopenharmony_ci			/* Someone's suspended the erase. Sleep */
245962306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
246062306a36Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
246162306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
246262306a36Sopenharmony_ci			schedule();
246362306a36Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
246462306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
246562306a36Sopenharmony_ci			continue;
246662306a36Sopenharmony_ci		}
246762306a36Sopenharmony_ci		if (chip->erase_suspended) {
246862306a36Sopenharmony_ci			/* This erase was suspended and resumed.
246962306a36Sopenharmony_ci			   Adjust the timeout */
247062306a36Sopenharmony_ci			timeo = jiffies + (HZ*20); /* FIXME */
247162306a36Sopenharmony_ci			chip->erase_suspended = 0;
247262306a36Sopenharmony_ci		}
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci		if (chip_ready(map, chip, adr, &datum)) {
247562306a36Sopenharmony_ci			if (cfi_check_err_status(map, chip, adr))
247662306a36Sopenharmony_ci				ret = -EIO;
247762306a36Sopenharmony_ci			break;
247862306a36Sopenharmony_ci		}
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci		if (time_after(jiffies, timeo)) {
248162306a36Sopenharmony_ci			printk(KERN_WARNING "MTD %s(): software timeout\n",
248262306a36Sopenharmony_ci			       __func__);
248362306a36Sopenharmony_ci			ret = -EIO;
248462306a36Sopenharmony_ci			break;
248562306a36Sopenharmony_ci		}
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci		/* Latency issues. Drop the lock, wait a while and retry */
248862306a36Sopenharmony_ci		UDELAY(map, chip, adr, 1000000/HZ);
248962306a36Sopenharmony_ci	}
249062306a36Sopenharmony_ci	/* Did we succeed? */
249162306a36Sopenharmony_ci	if (ret) {
249262306a36Sopenharmony_ci		/* reset on all failures. */
249362306a36Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
249462306a36Sopenharmony_ci		/* FIXME - should have reset delay before continuing */
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci		if (++retry_cnt <= MAX_RETRIES) {
249762306a36Sopenharmony_ci			ret = 0;
249862306a36Sopenharmony_ci			goto retry;
249962306a36Sopenharmony_ci		}
250062306a36Sopenharmony_ci	}
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	chip->state = FL_READY;
250362306a36Sopenharmony_ci	xip_enable(map, chip, adr);
250462306a36Sopenharmony_ci	DISABLE_VPP(map);
250562306a36Sopenharmony_ci	put_chip(map, chip, adr);
250662306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	return ret;
250962306a36Sopenharmony_ci}
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_cistatic int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
251362306a36Sopenharmony_ci{
251462306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
251562306a36Sopenharmony_ci	unsigned long timeo = jiffies + HZ;
251662306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
251762306a36Sopenharmony_ci	int ret;
251862306a36Sopenharmony_ci	int retry_cnt = 0;
251962306a36Sopenharmony_ci	map_word datum = map_word_ff(map);
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_ci	adr += chip->start;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
252462306a36Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_ERASING);
252562306a36Sopenharmony_ci	if (ret) {
252662306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
252762306a36Sopenharmony_ci		return ret;
252862306a36Sopenharmony_ci	}
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	pr_debug("MTD %s(): ERASE 0x%.8lx\n",
253162306a36Sopenharmony_ci		 __func__, adr);
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, len);
253462306a36Sopenharmony_ci	ENABLE_VPP(map);
253562306a36Sopenharmony_ci	xip_disable(map, chip, adr);
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci retry:
253862306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
253962306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
254062306a36Sopenharmony_ci	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
254162306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
254262306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
254362306a36Sopenharmony_ci	map_write(map, cfi->sector_erase_cmd, adr);
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci	chip->state = FL_ERASING;
254662306a36Sopenharmony_ci	chip->erase_suspended = 0;
254762306a36Sopenharmony_ci	chip->in_progress_block_addr = adr;
254862306a36Sopenharmony_ci	chip->in_progress_block_mask = ~(len - 1);
254962306a36Sopenharmony_ci
255062306a36Sopenharmony_ci	INVALIDATE_CACHE_UDELAY(map, chip,
255162306a36Sopenharmony_ci				adr, len,
255262306a36Sopenharmony_ci				chip->erase_time*500);
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci	timeo = jiffies + (HZ*20);
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci	for (;;) {
255762306a36Sopenharmony_ci		if (chip->state != FL_ERASING) {
255862306a36Sopenharmony_ci			/* Someone's suspended the erase. Sleep */
255962306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
256062306a36Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
256162306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
256262306a36Sopenharmony_ci			schedule();
256362306a36Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
256462306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
256562306a36Sopenharmony_ci			continue;
256662306a36Sopenharmony_ci		}
256762306a36Sopenharmony_ci		if (chip->erase_suspended) {
256862306a36Sopenharmony_ci			/* This erase was suspended and resumed.
256962306a36Sopenharmony_ci			   Adjust the timeout */
257062306a36Sopenharmony_ci			timeo = jiffies + (HZ*20); /* FIXME */
257162306a36Sopenharmony_ci			chip->erase_suspended = 0;
257262306a36Sopenharmony_ci		}
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci		if (chip_ready(map, chip, adr, &datum)) {
257562306a36Sopenharmony_ci			if (cfi_check_err_status(map, chip, adr))
257662306a36Sopenharmony_ci				ret = -EIO;
257762306a36Sopenharmony_ci			break;
257862306a36Sopenharmony_ci		}
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci		if (time_after(jiffies, timeo)) {
258162306a36Sopenharmony_ci			printk(KERN_WARNING "MTD %s(): software timeout\n",
258262306a36Sopenharmony_ci			       __func__);
258362306a36Sopenharmony_ci			ret = -EIO;
258462306a36Sopenharmony_ci			break;
258562306a36Sopenharmony_ci		}
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci		/* Latency issues. Drop the lock, wait a while and retry */
258862306a36Sopenharmony_ci		UDELAY(map, chip, adr, 1000000/HZ);
258962306a36Sopenharmony_ci	}
259062306a36Sopenharmony_ci	/* Did we succeed? */
259162306a36Sopenharmony_ci	if (ret) {
259262306a36Sopenharmony_ci		/* reset on all failures. */
259362306a36Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
259462306a36Sopenharmony_ci		/* FIXME - should have reset delay before continuing */
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci		if (++retry_cnt <= MAX_RETRIES) {
259762306a36Sopenharmony_ci			ret = 0;
259862306a36Sopenharmony_ci			goto retry;
259962306a36Sopenharmony_ci		}
260062306a36Sopenharmony_ci	}
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	chip->state = FL_READY;
260362306a36Sopenharmony_ci	xip_enable(map, chip, adr);
260462306a36Sopenharmony_ci	DISABLE_VPP(map);
260562306a36Sopenharmony_ci	put_chip(map, chip, adr);
260662306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
260762306a36Sopenharmony_ci	return ret;
260862306a36Sopenharmony_ci}
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_cistatic int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
261262306a36Sopenharmony_ci{
261362306a36Sopenharmony_ci	return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr,
261462306a36Sopenharmony_ci				instr->len, NULL);
261562306a36Sopenharmony_ci}
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_cistatic int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
261962306a36Sopenharmony_ci{
262062306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
262162306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	if (instr->addr != 0)
262462306a36Sopenharmony_ci		return -EINVAL;
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci	if (instr->len != mtd->size)
262762306a36Sopenharmony_ci		return -EINVAL;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	return do_erase_chip(map, &cfi->chips[0]);
263062306a36Sopenharmony_ci}
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_cistatic int do_atmel_lock(struct map_info *map, struct flchip *chip,
263362306a36Sopenharmony_ci			 unsigned long adr, int len, void *thunk)
263462306a36Sopenharmony_ci{
263562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
263662306a36Sopenharmony_ci	int ret;
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
263962306a36Sopenharmony_ci	ret = get_chip(map, chip, adr + chip->start, FL_LOCKING);
264062306a36Sopenharmony_ci	if (ret)
264162306a36Sopenharmony_ci		goto out_unlock;
264262306a36Sopenharmony_ci	chip->state = FL_LOCKING;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	pr_debug("MTD %s(): LOCK 0x%08lx len %d\n", __func__, adr, len);
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
264762306a36Sopenharmony_ci			 cfi->device_type, NULL);
264862306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
264962306a36Sopenharmony_ci			 cfi->device_type, NULL);
265062306a36Sopenharmony_ci	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi,
265162306a36Sopenharmony_ci			 cfi->device_type, NULL);
265262306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
265362306a36Sopenharmony_ci			 cfi->device_type, NULL);
265462306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
265562306a36Sopenharmony_ci			 cfi->device_type, NULL);
265662306a36Sopenharmony_ci	map_write(map, CMD(0x40), chip->start + adr);
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	chip->state = FL_READY;
265962306a36Sopenharmony_ci	put_chip(map, chip, adr + chip->start);
266062306a36Sopenharmony_ci	ret = 0;
266162306a36Sopenharmony_ci
266262306a36Sopenharmony_ciout_unlock:
266362306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
266462306a36Sopenharmony_ci	return ret;
266562306a36Sopenharmony_ci}
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_cistatic int do_atmel_unlock(struct map_info *map, struct flchip *chip,
266862306a36Sopenharmony_ci			   unsigned long adr, int len, void *thunk)
266962306a36Sopenharmony_ci{
267062306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
267162306a36Sopenharmony_ci	int ret;
267262306a36Sopenharmony_ci
267362306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
267462306a36Sopenharmony_ci	ret = get_chip(map, chip, adr + chip->start, FL_UNLOCKING);
267562306a36Sopenharmony_ci	if (ret)
267662306a36Sopenharmony_ci		goto out_unlock;
267762306a36Sopenharmony_ci	chip->state = FL_UNLOCKING;
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	pr_debug("MTD %s(): LOCK 0x%08lx len %d\n", __func__, adr, len);
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
268262306a36Sopenharmony_ci			 cfi->device_type, NULL);
268362306a36Sopenharmony_ci	map_write(map, CMD(0x70), adr);
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	chip->state = FL_READY;
268662306a36Sopenharmony_ci	put_chip(map, chip, adr + chip->start);
268762306a36Sopenharmony_ci	ret = 0;
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ciout_unlock:
269062306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
269162306a36Sopenharmony_ci	return ret;
269262306a36Sopenharmony_ci}
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_cistatic int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
269562306a36Sopenharmony_ci{
269662306a36Sopenharmony_ci	return cfi_varsize_frob(mtd, do_atmel_lock, ofs, len, NULL);
269762306a36Sopenharmony_ci}
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_cistatic int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
270062306a36Sopenharmony_ci{
270162306a36Sopenharmony_ci	return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL);
270262306a36Sopenharmony_ci}
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci/*
270562306a36Sopenharmony_ci * Advanced Sector Protection - PPB (Persistent Protection Bit) locking
270662306a36Sopenharmony_ci */
270762306a36Sopenharmony_ci
270862306a36Sopenharmony_cistruct ppb_lock {
270962306a36Sopenharmony_ci	struct flchip *chip;
271062306a36Sopenharmony_ci	unsigned long adr;
271162306a36Sopenharmony_ci	int locked;
271262306a36Sopenharmony_ci};
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_LOCK		((void *)1)
271562306a36Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_UNLOCK	((void *)2)
271662306a36Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_GETLOCK	((void *)3)
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_cistatic int __maybe_unused do_ppb_xxlock(struct map_info *map,
271962306a36Sopenharmony_ci					struct flchip *chip,
272062306a36Sopenharmony_ci					unsigned long adr, int len, void *thunk)
272162306a36Sopenharmony_ci{
272262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
272362306a36Sopenharmony_ci	unsigned long timeo;
272462306a36Sopenharmony_ci	int ret;
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	adr += chip->start;
272762306a36Sopenharmony_ci	mutex_lock(&chip->mutex);
272862306a36Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_LOCKING);
272962306a36Sopenharmony_ci	if (ret) {
273062306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
273162306a36Sopenharmony_ci		return ret;
273262306a36Sopenharmony_ci	}
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci	pr_debug("MTD %s(): XXLOCK 0x%08lx len %d\n", __func__, adr, len);
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
273762306a36Sopenharmony_ci			 cfi->device_type, NULL);
273862306a36Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
273962306a36Sopenharmony_ci			 cfi->device_type, NULL);
274062306a36Sopenharmony_ci	/* PPB entry command */
274162306a36Sopenharmony_ci	cfi_send_gen_cmd(0xC0, cfi->addr_unlock1, chip->start, map, cfi,
274262306a36Sopenharmony_ci			 cfi->device_type, NULL);
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci	if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) {
274562306a36Sopenharmony_ci		chip->state = FL_LOCKING;
274662306a36Sopenharmony_ci		map_write(map, CMD(0xA0), adr);
274762306a36Sopenharmony_ci		map_write(map, CMD(0x00), adr);
274862306a36Sopenharmony_ci	} else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) {
274962306a36Sopenharmony_ci		/*
275062306a36Sopenharmony_ci		 * Unlocking of one specific sector is not supported, so we
275162306a36Sopenharmony_ci		 * have to unlock all sectors of this device instead
275262306a36Sopenharmony_ci		 */
275362306a36Sopenharmony_ci		chip->state = FL_UNLOCKING;
275462306a36Sopenharmony_ci		map_write(map, CMD(0x80), chip->start);
275562306a36Sopenharmony_ci		map_write(map, CMD(0x30), chip->start);
275662306a36Sopenharmony_ci	} else if (thunk == DO_XXLOCK_ONEBLOCK_GETLOCK) {
275762306a36Sopenharmony_ci		chip->state = FL_JEDEC_QUERY;
275862306a36Sopenharmony_ci		/* Return locked status: 0->locked, 1->unlocked */
275962306a36Sopenharmony_ci		ret = !cfi_read_query(map, adr);
276062306a36Sopenharmony_ci	} else
276162306a36Sopenharmony_ci		BUG();
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_ci	/*
276462306a36Sopenharmony_ci	 * Wait for some time as unlocking of all sectors takes quite long
276562306a36Sopenharmony_ci	 */
276662306a36Sopenharmony_ci	timeo = jiffies + msecs_to_jiffies(2000);	/* 2s max (un)locking */
276762306a36Sopenharmony_ci	for (;;) {
276862306a36Sopenharmony_ci		if (chip_ready(map, chip, adr, NULL))
276962306a36Sopenharmony_ci			break;
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ci		if (time_after(jiffies, timeo)) {
277262306a36Sopenharmony_ci			printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
277362306a36Sopenharmony_ci			ret = -EIO;
277462306a36Sopenharmony_ci			break;
277562306a36Sopenharmony_ci		}
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci		UDELAY(map, chip, adr, 1);
277862306a36Sopenharmony_ci	}
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci	/* Exit BC commands */
278162306a36Sopenharmony_ci	map_write(map, CMD(0x90), chip->start);
278262306a36Sopenharmony_ci	map_write(map, CMD(0x00), chip->start);
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci	chip->state = FL_READY;
278562306a36Sopenharmony_ci	put_chip(map, chip, adr);
278662306a36Sopenharmony_ci	mutex_unlock(&chip->mutex);
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci	return ret;
278962306a36Sopenharmony_ci}
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_cistatic int __maybe_unused cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs,
279262306a36Sopenharmony_ci				       uint64_t len)
279362306a36Sopenharmony_ci{
279462306a36Sopenharmony_ci	return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len,
279562306a36Sopenharmony_ci				DO_XXLOCK_ONEBLOCK_LOCK);
279662306a36Sopenharmony_ci}
279762306a36Sopenharmony_ci
279862306a36Sopenharmony_cistatic int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs,
279962306a36Sopenharmony_ci					 uint64_t len)
280062306a36Sopenharmony_ci{
280162306a36Sopenharmony_ci	struct mtd_erase_region_info *regions = mtd->eraseregions;
280262306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
280362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
280462306a36Sopenharmony_ci	struct ppb_lock *sect;
280562306a36Sopenharmony_ci	unsigned long adr;
280662306a36Sopenharmony_ci	loff_t offset;
280762306a36Sopenharmony_ci	uint64_t length;
280862306a36Sopenharmony_ci	int chipnum;
280962306a36Sopenharmony_ci	int i;
281062306a36Sopenharmony_ci	int sectors;
281162306a36Sopenharmony_ci	int ret;
281262306a36Sopenharmony_ci	int max_sectors;
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	/*
281562306a36Sopenharmony_ci	 * PPB unlocking always unlocks all sectors of the flash chip.
281662306a36Sopenharmony_ci	 * We need to re-lock all previously locked sectors. So lets
281762306a36Sopenharmony_ci	 * first check the locking status of all sectors and save
281862306a36Sopenharmony_ci	 * it for future use.
281962306a36Sopenharmony_ci	 */
282062306a36Sopenharmony_ci	max_sectors = 0;
282162306a36Sopenharmony_ci	for (i = 0; i < mtd->numeraseregions; i++)
282262306a36Sopenharmony_ci		max_sectors += regions[i].numblocks;
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	sect = kcalloc(max_sectors, sizeof(struct ppb_lock), GFP_KERNEL);
282562306a36Sopenharmony_ci	if (!sect)
282662306a36Sopenharmony_ci		return -ENOMEM;
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci	/*
282962306a36Sopenharmony_ci	 * This code to walk all sectors is a slightly modified version
283062306a36Sopenharmony_ci	 * of the cfi_varsize_frob() code.
283162306a36Sopenharmony_ci	 */
283262306a36Sopenharmony_ci	i = 0;
283362306a36Sopenharmony_ci	chipnum = 0;
283462306a36Sopenharmony_ci	adr = 0;
283562306a36Sopenharmony_ci	sectors = 0;
283662306a36Sopenharmony_ci	offset = 0;
283762306a36Sopenharmony_ci	length = mtd->size;
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci	while (length) {
284062306a36Sopenharmony_ci		int size = regions[i].erasesize;
284162306a36Sopenharmony_ci
284262306a36Sopenharmony_ci		/*
284362306a36Sopenharmony_ci		 * Only test sectors that shall not be unlocked. The other
284462306a36Sopenharmony_ci		 * sectors shall be unlocked, so lets keep their locking
284562306a36Sopenharmony_ci		 * status at "unlocked" (locked=0) for the final re-locking.
284662306a36Sopenharmony_ci		 */
284762306a36Sopenharmony_ci		if ((offset < ofs) || (offset >= (ofs + len))) {
284862306a36Sopenharmony_ci			sect[sectors].chip = &cfi->chips[chipnum];
284962306a36Sopenharmony_ci			sect[sectors].adr = adr;
285062306a36Sopenharmony_ci			sect[sectors].locked = do_ppb_xxlock(
285162306a36Sopenharmony_ci				map, &cfi->chips[chipnum], adr, 0,
285262306a36Sopenharmony_ci				DO_XXLOCK_ONEBLOCK_GETLOCK);
285362306a36Sopenharmony_ci		}
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_ci		adr += size;
285662306a36Sopenharmony_ci		offset += size;
285762306a36Sopenharmony_ci		length -= size;
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci		if (offset == regions[i].offset + size * regions[i].numblocks)
286062306a36Sopenharmony_ci			i++;
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci		if (adr >> cfi->chipshift) {
286362306a36Sopenharmony_ci			if (offset >= (ofs + len))
286462306a36Sopenharmony_ci				break;
286562306a36Sopenharmony_ci			adr = 0;
286662306a36Sopenharmony_ci			chipnum++;
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci			if (chipnum >= cfi->numchips)
286962306a36Sopenharmony_ci				break;
287062306a36Sopenharmony_ci		}
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_ci		sectors++;
287362306a36Sopenharmony_ci		if (sectors >= max_sectors) {
287462306a36Sopenharmony_ci			printk(KERN_ERR "Only %d sectors for PPB locking supported!\n",
287562306a36Sopenharmony_ci			       max_sectors);
287662306a36Sopenharmony_ci			kfree(sect);
287762306a36Sopenharmony_ci			return -EINVAL;
287862306a36Sopenharmony_ci		}
287962306a36Sopenharmony_ci	}
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci	/* Now unlock the whole chip */
288262306a36Sopenharmony_ci	ret = cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len,
288362306a36Sopenharmony_ci			       DO_XXLOCK_ONEBLOCK_UNLOCK);
288462306a36Sopenharmony_ci	if (ret) {
288562306a36Sopenharmony_ci		kfree(sect);
288662306a36Sopenharmony_ci		return ret;
288762306a36Sopenharmony_ci	}
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ci	/*
289062306a36Sopenharmony_ci	 * PPB unlocking always unlocks all sectors of the flash chip.
289162306a36Sopenharmony_ci	 * We need to re-lock all previously locked sectors.
289262306a36Sopenharmony_ci	 */
289362306a36Sopenharmony_ci	for (i = 0; i < sectors; i++) {
289462306a36Sopenharmony_ci		if (sect[i].locked)
289562306a36Sopenharmony_ci			do_ppb_xxlock(map, sect[i].chip, sect[i].adr, 0,
289662306a36Sopenharmony_ci				      DO_XXLOCK_ONEBLOCK_LOCK);
289762306a36Sopenharmony_ci	}
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	kfree(sect);
290062306a36Sopenharmony_ci	return ret;
290162306a36Sopenharmony_ci}
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_cistatic int __maybe_unused cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs,
290462306a36Sopenharmony_ci					    uint64_t len)
290562306a36Sopenharmony_ci{
290662306a36Sopenharmony_ci	return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len,
290762306a36Sopenharmony_ci				DO_XXLOCK_ONEBLOCK_GETLOCK) ? 1 : 0;
290862306a36Sopenharmony_ci}
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_cistatic void cfi_amdstd_sync (struct mtd_info *mtd)
291162306a36Sopenharmony_ci{
291262306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
291362306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
291462306a36Sopenharmony_ci	int i;
291562306a36Sopenharmony_ci	struct flchip *chip;
291662306a36Sopenharmony_ci	int ret = 0;
291762306a36Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	for (i=0; !ret && i<cfi->numchips; i++) {
292062306a36Sopenharmony_ci		chip = &cfi->chips[i];
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_ci	retry:
292362306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_ci		switch(chip->state) {
292662306a36Sopenharmony_ci		case FL_READY:
292762306a36Sopenharmony_ci		case FL_STATUS:
292862306a36Sopenharmony_ci		case FL_CFI_QUERY:
292962306a36Sopenharmony_ci		case FL_JEDEC_QUERY:
293062306a36Sopenharmony_ci			chip->oldstate = chip->state;
293162306a36Sopenharmony_ci			chip->state = FL_SYNCING;
293262306a36Sopenharmony_ci			/* No need to wake_up() on this state change -
293362306a36Sopenharmony_ci			 * as the whole point is that nobody can do anything
293462306a36Sopenharmony_ci			 * with the chip now anyway.
293562306a36Sopenharmony_ci			 */
293662306a36Sopenharmony_ci			fallthrough;
293762306a36Sopenharmony_ci		case FL_SYNCING:
293862306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
293962306a36Sopenharmony_ci			break;
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci		default:
294262306a36Sopenharmony_ci			/* Not an idle state */
294362306a36Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
294462306a36Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
294762306a36Sopenharmony_ci
294862306a36Sopenharmony_ci			schedule();
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci			goto retry;
295362306a36Sopenharmony_ci		}
295462306a36Sopenharmony_ci	}
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	/* Unlock the chips again */
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	for (i--; i >=0; i--) {
295962306a36Sopenharmony_ci		chip = &cfi->chips[i];
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
296262306a36Sopenharmony_ci
296362306a36Sopenharmony_ci		if (chip->state == FL_SYNCING) {
296462306a36Sopenharmony_ci			chip->state = chip->oldstate;
296562306a36Sopenharmony_ci			wake_up(&chip->wq);
296662306a36Sopenharmony_ci		}
296762306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
296862306a36Sopenharmony_ci	}
296962306a36Sopenharmony_ci}
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_cistatic int cfi_amdstd_suspend(struct mtd_info *mtd)
297362306a36Sopenharmony_ci{
297462306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
297562306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
297662306a36Sopenharmony_ci	int i;
297762306a36Sopenharmony_ci	struct flchip *chip;
297862306a36Sopenharmony_ci	int ret = 0;
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	for (i=0; !ret && i<cfi->numchips; i++) {
298162306a36Sopenharmony_ci		chip = &cfi->chips[i];
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci		switch(chip->state) {
298662306a36Sopenharmony_ci		case FL_READY:
298762306a36Sopenharmony_ci		case FL_STATUS:
298862306a36Sopenharmony_ci		case FL_CFI_QUERY:
298962306a36Sopenharmony_ci		case FL_JEDEC_QUERY:
299062306a36Sopenharmony_ci			chip->oldstate = chip->state;
299162306a36Sopenharmony_ci			chip->state = FL_PM_SUSPENDED;
299262306a36Sopenharmony_ci			/* No need to wake_up() on this state change -
299362306a36Sopenharmony_ci			 * as the whole point is that nobody can do anything
299462306a36Sopenharmony_ci			 * with the chip now anyway.
299562306a36Sopenharmony_ci			 */
299662306a36Sopenharmony_ci			break;
299762306a36Sopenharmony_ci		case FL_PM_SUSPENDED:
299862306a36Sopenharmony_ci			break;
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci		default:
300162306a36Sopenharmony_ci			ret = -EAGAIN;
300262306a36Sopenharmony_ci			break;
300362306a36Sopenharmony_ci		}
300462306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
300562306a36Sopenharmony_ci	}
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci	/* Unlock the chips again */
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	if (ret) {
301062306a36Sopenharmony_ci		for (i--; i >=0; i--) {
301162306a36Sopenharmony_ci			chip = &cfi->chips[i];
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci			mutex_lock(&chip->mutex);
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci			if (chip->state == FL_PM_SUSPENDED) {
301662306a36Sopenharmony_ci				chip->state = chip->oldstate;
301762306a36Sopenharmony_ci				wake_up(&chip->wq);
301862306a36Sopenharmony_ci			}
301962306a36Sopenharmony_ci			mutex_unlock(&chip->mutex);
302062306a36Sopenharmony_ci		}
302162306a36Sopenharmony_ci	}
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_ci	return ret;
302462306a36Sopenharmony_ci}
302562306a36Sopenharmony_ci
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_cistatic void cfi_amdstd_resume(struct mtd_info *mtd)
302862306a36Sopenharmony_ci{
302962306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
303062306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
303162306a36Sopenharmony_ci	int i;
303262306a36Sopenharmony_ci	struct flchip *chip;
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	for (i=0; i<cfi->numchips; i++) {
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci		chip = &cfi->chips[i];
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
303962306a36Sopenharmony_ci
304062306a36Sopenharmony_ci		if (chip->state == FL_PM_SUSPENDED) {
304162306a36Sopenharmony_ci			chip->state = FL_READY;
304262306a36Sopenharmony_ci			map_write(map, CMD(0xF0), chip->start);
304362306a36Sopenharmony_ci			wake_up(&chip->wq);
304462306a36Sopenharmony_ci		}
304562306a36Sopenharmony_ci		else
304662306a36Sopenharmony_ci			printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n");
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
304962306a36Sopenharmony_ci	}
305062306a36Sopenharmony_ci}
305162306a36Sopenharmony_ci
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci/*
305462306a36Sopenharmony_ci * Ensure that the flash device is put back into read array mode before
305562306a36Sopenharmony_ci * unloading the driver or rebooting.  On some systems, rebooting while
305662306a36Sopenharmony_ci * the flash is in query/program/erase mode will prevent the CPU from
305762306a36Sopenharmony_ci * fetching the bootloader code, requiring a hard reset or power cycle.
305862306a36Sopenharmony_ci */
305962306a36Sopenharmony_cistatic int cfi_amdstd_reset(struct mtd_info *mtd)
306062306a36Sopenharmony_ci{
306162306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
306262306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
306362306a36Sopenharmony_ci	int i, ret;
306462306a36Sopenharmony_ci	struct flchip *chip;
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci	for (i = 0; i < cfi->numchips; i++) {
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci		chip = &cfi->chips[i];
306962306a36Sopenharmony_ci
307062306a36Sopenharmony_ci		mutex_lock(&chip->mutex);
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_ci		ret = get_chip(map, chip, chip->start, FL_SHUTDOWN);
307362306a36Sopenharmony_ci		if (!ret) {
307462306a36Sopenharmony_ci			map_write(map, CMD(0xF0), chip->start);
307562306a36Sopenharmony_ci			chip->state = FL_SHUTDOWN;
307662306a36Sopenharmony_ci			put_chip(map, chip, chip->start);
307762306a36Sopenharmony_ci		}
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci		mutex_unlock(&chip->mutex);
308062306a36Sopenharmony_ci	}
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci	return 0;
308362306a36Sopenharmony_ci}
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_cistatic int cfi_amdstd_reboot(struct notifier_block *nb, unsigned long val,
308762306a36Sopenharmony_ci			       void *v)
308862306a36Sopenharmony_ci{
308962306a36Sopenharmony_ci	struct mtd_info *mtd;
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci	mtd = container_of(nb, struct mtd_info, reboot_notifier);
309262306a36Sopenharmony_ci	cfi_amdstd_reset(mtd);
309362306a36Sopenharmony_ci	return NOTIFY_DONE;
309462306a36Sopenharmony_ci}
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_cistatic void cfi_amdstd_destroy(struct mtd_info *mtd)
309862306a36Sopenharmony_ci{
309962306a36Sopenharmony_ci	struct map_info *map = mtd->priv;
310062306a36Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci	cfi_amdstd_reset(mtd);
310362306a36Sopenharmony_ci	unregister_reboot_notifier(&mtd->reboot_notifier);
310462306a36Sopenharmony_ci	kfree(cfi->cmdset_priv);
310562306a36Sopenharmony_ci	kfree(cfi->cfiq);
310662306a36Sopenharmony_ci	kfree(cfi);
310762306a36Sopenharmony_ci	kfree(mtd->eraseregions);
310862306a36Sopenharmony_ci}
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
311162306a36Sopenharmony_ciMODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al.");
311262306a36Sopenharmony_ciMODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");
311362306a36Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0006");
311462306a36Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0701");
3115