18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Common Flash Interface support:
38c2ecf20Sopenharmony_ci *   AMD & Fujitsu Standard Vendor Command Set (ID 0x0002)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com>
78c2ecf20Sopenharmony_ci * Copyright (C) 2005 MontaVista Software Inc. <source@mvista.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * 2_by_8 routines added by Simon Munton
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * 4_by_16 work by Carolyn J. Smith
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * XIP support hooks by Vitaly Wool (based on code for Intel flash
148c2ecf20Sopenharmony_ci * by Nicolas Pitre)
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * 25/09/2008 Christopher Moore: TopBottom fixup for many Macronix with CFI V1.0
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * This code is GPL
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <linux/types.h>
258c2ecf20Sopenharmony_ci#include <linux/kernel.h>
268c2ecf20Sopenharmony_ci#include <linux/sched.h>
278c2ecf20Sopenharmony_ci#include <asm/io.h>
288c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <linux/errno.h>
318c2ecf20Sopenharmony_ci#include <linux/slab.h>
328c2ecf20Sopenharmony_ci#include <linux/delay.h>
338c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
348c2ecf20Sopenharmony_ci#include <linux/reboot.h>
358c2ecf20Sopenharmony_ci#include <linux/of.h>
368c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
378c2ecf20Sopenharmony_ci#include <linux/mtd/map.h>
388c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
398c2ecf20Sopenharmony_ci#include <linux/mtd/cfi.h>
408c2ecf20Sopenharmony_ci#include <linux/mtd/xip.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define AMD_BOOTLOC_BUG
438c2ecf20Sopenharmony_ci#define FORCE_WORD_WRITE 0
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define MAX_RETRIES 3
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define SST49LF004B		0x0060
488c2ecf20Sopenharmony_ci#define SST49LF040B		0x0050
498c2ecf20Sopenharmony_ci#define SST49LF008A		0x005a
508c2ecf20Sopenharmony_ci#define AT49BV6416		0x00d6
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * Status Register bit description. Used by flash devices that don't
548c2ecf20Sopenharmony_ci * support DQ polling (e.g. HyperFlash)
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_ci#define CFI_SR_DRB		BIT(7)
578c2ecf20Sopenharmony_ci#define CFI_SR_ESB		BIT(5)
588c2ecf20Sopenharmony_ci#define CFI_SR_PSB		BIT(4)
598c2ecf20Sopenharmony_ci#define CFI_SR_WBASB		BIT(3)
608c2ecf20Sopenharmony_ci#define CFI_SR_SLSB		BIT(1)
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cienum cfi_quirks {
638c2ecf20Sopenharmony_ci	CFI_QUIRK_DQ_TRUE_DATA = BIT(0),
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
678c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
688c2ecf20Sopenharmony_ci#if !FORCE_WORD_WRITE
698c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
708c2ecf20Sopenharmony_ci#endif
718c2ecf20Sopenharmony_cistatic int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);
728c2ecf20Sopenharmony_cistatic int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);
738c2ecf20Sopenharmony_cistatic void cfi_amdstd_sync (struct mtd_info *);
748c2ecf20Sopenharmony_cistatic int cfi_amdstd_suspend (struct mtd_info *);
758c2ecf20Sopenharmony_cistatic void cfi_amdstd_resume (struct mtd_info *);
768c2ecf20Sopenharmony_cistatic int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *);
778c2ecf20Sopenharmony_cistatic int cfi_amdstd_get_fact_prot_info(struct mtd_info *, size_t,
788c2ecf20Sopenharmony_ci					 size_t *, struct otp_info *);
798c2ecf20Sopenharmony_cistatic int cfi_amdstd_get_user_prot_info(struct mtd_info *, size_t,
808c2ecf20Sopenharmony_ci					 size_t *, struct otp_info *);
818c2ecf20Sopenharmony_cistatic int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
828c2ecf20Sopenharmony_cistatic int cfi_amdstd_read_fact_prot_reg(struct mtd_info *, loff_t, size_t,
838c2ecf20Sopenharmony_ci					 size_t *, u_char *);
848c2ecf20Sopenharmony_cistatic int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t,
858c2ecf20Sopenharmony_ci					 size_t *, u_char *);
868c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_user_prot_reg(struct mtd_info *, loff_t, size_t,
878c2ecf20Sopenharmony_ci					  size_t *, u_char *);
888c2ecf20Sopenharmony_cistatic int cfi_amdstd_lock_user_prot_reg(struct mtd_info *, loff_t, size_t);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
918c2ecf20Sopenharmony_ci				  size_t *retlen, const u_char *buf);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic void cfi_amdstd_destroy(struct mtd_info *);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0002(struct map_info *, int);
968c2ecf20Sopenharmony_cistatic struct mtd_info *cfi_amdstd_setup (struct mtd_info *);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
998c2ecf20Sopenharmony_cistatic void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
1008c2ecf20Sopenharmony_ci#include "fwh_lock.h"
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
1038c2ecf20Sopenharmony_cistatic int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
1068c2ecf20Sopenharmony_cistatic int cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
1078c2ecf20Sopenharmony_cistatic int cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic struct mtd_chip_driver cfi_amdstd_chipdrv = {
1108c2ecf20Sopenharmony_ci	.probe		= NULL, /* Not usable directly */
1118c2ecf20Sopenharmony_ci	.destroy	= cfi_amdstd_destroy,
1128c2ecf20Sopenharmony_ci	.name		= "cfi_cmdset_0002",
1138c2ecf20Sopenharmony_ci	.module		= THIS_MODULE
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/*
1178c2ecf20Sopenharmony_ci * Use status register to poll for Erase/write completion when DQ is not
1188c2ecf20Sopenharmony_ci * supported. This is indicated by Bit[1:0] of SoftwareFeatures field in
1198c2ecf20Sopenharmony_ci * CFI Primary Vendor-Specific Extended Query table 1.5
1208c2ecf20Sopenharmony_ci */
1218c2ecf20Sopenharmony_cistatic int cfi_use_status_reg(struct cfi_private *cfi)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
1248c2ecf20Sopenharmony_ci	u8 poll_mask = CFI_POLL_STATUS_REG | CFI_POLL_DQ;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return extp && extp->MinorVersion >= '5' &&
1278c2ecf20Sopenharmony_ci		(extp->SoftwareFeatures & poll_mask) == CFI_POLL_STATUS_REG;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int cfi_check_err_status(struct map_info *map, struct flchip *chip,
1318c2ecf20Sopenharmony_ci				unsigned long adr)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
1348c2ecf20Sopenharmony_ci	map_word status;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (!cfi_use_status_reg(cfi))
1378c2ecf20Sopenharmony_ci		return 0;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,
1408c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
1418c2ecf20Sopenharmony_ci	status = map_read(map, adr);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* The error bits are invalid while the chip's busy */
1448c2ecf20Sopenharmony_ci	if (!map_word_bitsset(map, status, CMD(CFI_SR_DRB)))
1458c2ecf20Sopenharmony_ci		return 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (map_word_bitsset(map, status, CMD(0x3a))) {
1488c2ecf20Sopenharmony_ci		unsigned long chipstatus = MERGESTATUS(status);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		if (chipstatus & CFI_SR_ESB)
1518c2ecf20Sopenharmony_ci			pr_err("%s erase operation failed, status %lx\n",
1528c2ecf20Sopenharmony_ci			       map->name, chipstatus);
1538c2ecf20Sopenharmony_ci		if (chipstatus & CFI_SR_PSB)
1548c2ecf20Sopenharmony_ci			pr_err("%s program operation failed, status %lx\n",
1558c2ecf20Sopenharmony_ci			       map->name, chipstatus);
1568c2ecf20Sopenharmony_ci		if (chipstatus & CFI_SR_WBASB)
1578c2ecf20Sopenharmony_ci			pr_err("%s buffer program command aborted, status %lx\n",
1588c2ecf20Sopenharmony_ci			       map->name, chipstatus);
1598c2ecf20Sopenharmony_ci		if (chipstatus & CFI_SR_SLSB)
1608c2ecf20Sopenharmony_ci			pr_err("%s sector write protected, status %lx\n",
1618c2ecf20Sopenharmony_ci			       map->name, chipstatus);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		/* Erase/Program status bits are set on the operation failure */
1648c2ecf20Sopenharmony_ci		if (chipstatus & (CFI_SR_ESB | CFI_SR_PSB))
1658c2ecf20Sopenharmony_ci			return 1;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/* #define DEBUG_CFI_FEATURES */
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES
1748c2ecf20Sopenharmony_cistatic void cfi_tell_features(struct cfi_pri_amdstd *extp)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	const char* erase_suspend[3] = {
1778c2ecf20Sopenharmony_ci		"Not supported", "Read only", "Read/write"
1788c2ecf20Sopenharmony_ci	};
1798c2ecf20Sopenharmony_ci	const char* top_bottom[6] = {
1808c2ecf20Sopenharmony_ci		"No WP", "8x8KiB sectors at top & bottom, no WP",
1818c2ecf20Sopenharmony_ci		"Bottom boot", "Top boot",
1828c2ecf20Sopenharmony_ci		"Uniform, Bottom WP", "Uniform, Top WP"
1838c2ecf20Sopenharmony_ci	};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	printk("  Silicon revision: %d\n", extp->SiliconRevision >> 1);
1868c2ecf20Sopenharmony_ci	printk("  Address sensitive unlock: %s\n",
1878c2ecf20Sopenharmony_ci	       (extp->SiliconRevision & 1) ? "Not required" : "Required");
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend))
1908c2ecf20Sopenharmony_ci		printk("  Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]);
1918c2ecf20Sopenharmony_ci	else
1928c2ecf20Sopenharmony_ci		printk("  Erase Suspend: Unknown value %d\n", extp->EraseSuspend);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (extp->BlkProt == 0)
1958c2ecf20Sopenharmony_ci		printk("  Block protection: Not supported\n");
1968c2ecf20Sopenharmony_ci	else
1978c2ecf20Sopenharmony_ci		printk("  Block protection: %d sectors per group\n", extp->BlkProt);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	printk("  Temporary block unprotect: %s\n",
2018c2ecf20Sopenharmony_ci	       extp->TmpBlkUnprotect ? "Supported" : "Not supported");
2028c2ecf20Sopenharmony_ci	printk("  Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot);
2038c2ecf20Sopenharmony_ci	printk("  Number of simultaneous operations: %d\n", extp->SimultaneousOps);
2048c2ecf20Sopenharmony_ci	printk("  Burst mode: %s\n",
2058c2ecf20Sopenharmony_ci	       extp->BurstMode ? "Supported" : "Not supported");
2068c2ecf20Sopenharmony_ci	if (extp->PageMode == 0)
2078c2ecf20Sopenharmony_ci		printk("  Page mode: Not supported\n");
2088c2ecf20Sopenharmony_ci	else
2098c2ecf20Sopenharmony_ci		printk("  Page mode: %d word page\n", extp->PageMode << 2);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	printk("  Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n",
2128c2ecf20Sopenharmony_ci	       extp->VppMin >> 4, extp->VppMin & 0xf);
2138c2ecf20Sopenharmony_ci	printk("  Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n",
2148c2ecf20Sopenharmony_ci	       extp->VppMax >> 4, extp->VppMax & 0xf);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (extp->TopBottom < ARRAY_SIZE(top_bottom))
2178c2ecf20Sopenharmony_ci		printk("  Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]);
2188c2ecf20Sopenharmony_ci	else
2198c2ecf20Sopenharmony_ci		printk("  Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom);
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci#endif
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci#ifdef AMD_BOOTLOC_BUG
2248c2ecf20Sopenharmony_ci/* Wheee. Bring me the head of someone at AMD. */
2258c2ecf20Sopenharmony_cistatic void fixup_amd_bootblock(struct mtd_info *mtd)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
2288c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
2298c2ecf20Sopenharmony_ci	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
2308c2ecf20Sopenharmony_ci	__u8 major = extp->MajorVersion;
2318c2ecf20Sopenharmony_ci	__u8 minor = extp->MinorVersion;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (((major << 8) | minor) < 0x3131) {
2348c2ecf20Sopenharmony_ci		/* CFI version 1.0 => don't trust bootloc */
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		pr_debug("%s: JEDEC Vendor ID is 0x%02X Device ID is 0x%02X\n",
2378c2ecf20Sopenharmony_ci			map->name, cfi->mfr, cfi->id);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		/* AFAICS all 29LV400 with a bottom boot block have a device ID
2408c2ecf20Sopenharmony_ci		 * of 0x22BA in 16-bit mode and 0xBA in 8-bit mode.
2418c2ecf20Sopenharmony_ci		 * These were badly detected as they have the 0x80 bit set
2428c2ecf20Sopenharmony_ci		 * so treat them as a special case.
2438c2ecf20Sopenharmony_ci		 */
2448c2ecf20Sopenharmony_ci		if (((cfi->id == 0xBA) || (cfi->id == 0x22BA)) &&
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci			/* Macronix added CFI to their 2nd generation
2478c2ecf20Sopenharmony_ci			 * MX29LV400C B/T but AFAICS no other 29LV400 (AMD,
2488c2ecf20Sopenharmony_ci			 * Fujitsu, Spansion, EON, ESI and older Macronix)
2498c2ecf20Sopenharmony_ci			 * has CFI.
2508c2ecf20Sopenharmony_ci			 *
2518c2ecf20Sopenharmony_ci			 * Therefore also check the manufacturer.
2528c2ecf20Sopenharmony_ci			 * This reduces the risk of false detection due to
2538c2ecf20Sopenharmony_ci			 * the 8-bit device ID.
2548c2ecf20Sopenharmony_ci			 */
2558c2ecf20Sopenharmony_ci			(cfi->mfr == CFI_MFR_MACRONIX)) {
2568c2ecf20Sopenharmony_ci			pr_debug("%s: Macronix MX29LV400C with bottom boot block"
2578c2ecf20Sopenharmony_ci				" detected\n", map->name);
2588c2ecf20Sopenharmony_ci			extp->TopBottom = 2;	/* bottom boot */
2598c2ecf20Sopenharmony_ci		} else
2608c2ecf20Sopenharmony_ci		if (cfi->id & 0x80) {
2618c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id);
2628c2ecf20Sopenharmony_ci			extp->TopBottom = 3;	/* top boot */
2638c2ecf20Sopenharmony_ci		} else {
2648c2ecf20Sopenharmony_ci			extp->TopBottom = 2;	/* bottom boot */
2658c2ecf20Sopenharmony_ci		}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		pr_debug("%s: AMD CFI PRI V%c.%c has no boot block field;"
2688c2ecf20Sopenharmony_ci			" deduced %s from Device ID\n", map->name, major, minor,
2698c2ecf20Sopenharmony_ci			extp->TopBottom == 2 ? "bottom" : "top");
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci#endif
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci#if !FORCE_WORD_WRITE
2758c2ecf20Sopenharmony_cistatic void fixup_use_write_buffers(struct mtd_info *mtd)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
2788c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
2798c2ecf20Sopenharmony_ci	if (cfi->cfiq->BufWriteTimeoutTyp) {
2808c2ecf20Sopenharmony_ci		pr_debug("Using buffer write method\n");
2818c2ecf20Sopenharmony_ci		mtd->_write = cfi_amdstd_write_buffers;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci#endif /* !FORCE_WORD_WRITE */
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/* Atmel chips don't use the same PRI format as AMD chips */
2878c2ecf20Sopenharmony_cistatic void fixup_convert_atmel_pri(struct mtd_info *mtd)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
2908c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
2918c2ecf20Sopenharmony_ci	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
2928c2ecf20Sopenharmony_ci	struct cfi_pri_atmel atmel_pri;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	memcpy(&atmel_pri, extp, sizeof(atmel_pri));
2958c2ecf20Sopenharmony_ci	memset((char *)extp + 5, 0, sizeof(*extp) - 5);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (atmel_pri.Features & 0x02)
2988c2ecf20Sopenharmony_ci		extp->EraseSuspend = 2;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* Some chips got it backwards... */
3018c2ecf20Sopenharmony_ci	if (cfi->id == AT49BV6416) {
3028c2ecf20Sopenharmony_ci		if (atmel_pri.BottomBoot)
3038c2ecf20Sopenharmony_ci			extp->TopBottom = 3;
3048c2ecf20Sopenharmony_ci		else
3058c2ecf20Sopenharmony_ci			extp->TopBottom = 2;
3068c2ecf20Sopenharmony_ci	} else {
3078c2ecf20Sopenharmony_ci		if (atmel_pri.BottomBoot)
3088c2ecf20Sopenharmony_ci			extp->TopBottom = 2;
3098c2ecf20Sopenharmony_ci		else
3108c2ecf20Sopenharmony_ci			extp->TopBottom = 3;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* burst write mode not supported */
3148c2ecf20Sopenharmony_ci	cfi->cfiq->BufWriteTimeoutTyp = 0;
3158c2ecf20Sopenharmony_ci	cfi->cfiq->BufWriteTimeoutMax = 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic void fixup_use_secsi(struct mtd_info *mtd)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	/* Setup for chips with a secsi area */
3218c2ecf20Sopenharmony_ci	mtd->_read_user_prot_reg = cfi_amdstd_secsi_read;
3228c2ecf20Sopenharmony_ci	mtd->_read_fact_prot_reg = cfi_amdstd_secsi_read;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void fixup_use_erase_chip(struct mtd_info *mtd)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
3288c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
3298c2ecf20Sopenharmony_ci	if ((cfi->cfiq->NumEraseRegions == 1) &&
3308c2ecf20Sopenharmony_ci		((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) {
3318c2ecf20Sopenharmony_ci		mtd->_erase = cfi_amdstd_erase_chip;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/*
3378c2ecf20Sopenharmony_ci * Some Atmel chips (e.g. the AT49BV6416) power-up with all sectors
3388c2ecf20Sopenharmony_ci * locked by default.
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_cistatic void fixup_use_atmel_lock(struct mtd_info *mtd)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	mtd->_lock = cfi_atmel_lock;
3438c2ecf20Sopenharmony_ci	mtd->_unlock = cfi_atmel_unlock;
3448c2ecf20Sopenharmony_ci	mtd->flags |= MTD_POWERUP_LOCK;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void fixup_old_sst_eraseregion(struct mtd_info *mtd)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
3508c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/*
3538c2ecf20Sopenharmony_ci	 * These flashes report two separate eraseblock regions based on the
3548c2ecf20Sopenharmony_ci	 * sector_erase-size and block_erase-size, although they both operate on the
3558c2ecf20Sopenharmony_ci	 * same memory. This is not allowed according to CFI, so we just pick the
3568c2ecf20Sopenharmony_ci	 * sector_erase-size.
3578c2ecf20Sopenharmony_ci	 */
3588c2ecf20Sopenharmony_ci	cfi->cfiq->NumEraseRegions = 1;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic void fixup_sst39vf(struct mtd_info *mtd)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
3648c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	fixup_old_sst_eraseregion(mtd);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	cfi->addr_unlock1 = 0x5555;
3698c2ecf20Sopenharmony_ci	cfi->addr_unlock2 = 0x2AAA;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void fixup_sst39vf_rev_b(struct mtd_info *mtd)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
3758c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	fixup_old_sst_eraseregion(mtd);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	cfi->addr_unlock1 = 0x555;
3808c2ecf20Sopenharmony_ci	cfi->addr_unlock2 = 0x2AA;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	cfi->sector_erase_cmd = CMD(0x50);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
3888c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	fixup_sst39vf_rev_b(mtd);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/*
3938c2ecf20Sopenharmony_ci	 * CFI reports 1024 sectors (0x03ff+1) of 64KBytes (0x0100*256) where
3948c2ecf20Sopenharmony_ci	 * it should report a size of 8KBytes (0x0020*256).
3958c2ecf20Sopenharmony_ci	 */
3968c2ecf20Sopenharmony_ci	cfi->cfiq->EraseRegionInfo[0] = 0x002003ff;
3978c2ecf20Sopenharmony_ci	pr_warn("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n",
3988c2ecf20Sopenharmony_ci		mtd->name);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic void fixup_s29gl064n_sectors(struct mtd_info *mtd)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
4048c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) {
4078c2ecf20Sopenharmony_ci		cfi->cfiq->EraseRegionInfo[0] |= 0x0040;
4088c2ecf20Sopenharmony_ci		pr_warn("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n",
4098c2ecf20Sopenharmony_ci			mtd->name);
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic void fixup_s29gl032n_sectors(struct mtd_info *mtd)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
4168c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) {
4198c2ecf20Sopenharmony_ci		cfi->cfiq->EraseRegionInfo[1] &= ~0x0040;
4208c2ecf20Sopenharmony_ci		pr_warn("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n",
4218c2ecf20Sopenharmony_ci			mtd->name);
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void fixup_s29ns512p_sectors(struct mtd_info *mtd)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
4288c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/*
4318c2ecf20Sopenharmony_ci	 *  S29NS512P flash uses more than 8bits to report number of sectors,
4328c2ecf20Sopenharmony_ci	 * which is not permitted by CFI.
4338c2ecf20Sopenharmony_ci	 */
4348c2ecf20Sopenharmony_ci	cfi->cfiq->EraseRegionInfo[0] = 0x020001ff;
4358c2ecf20Sopenharmony_ci	pr_warn("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n",
4368c2ecf20Sopenharmony_ci		mtd->name);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic void fixup_quirks(struct mtd_info *mtd)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
4428c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (cfi->mfr == CFI_MFR_AMD && cfi->id == 0x0c01)
4458c2ecf20Sopenharmony_ci		cfi->quirks |= CFI_QUIRK_DQ_TRUE_DATA;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci/* Used to fix CFI-Tables of chips without Extended Query Tables */
4498c2ecf20Sopenharmony_cistatic struct cfi_fixup cfi_nopri_fixup_table[] = {
4508c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x234a, fixup_sst39vf }, /* SST39VF1602 */
4518c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x234b, fixup_sst39vf }, /* SST39VF1601 */
4528c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x235a, fixup_sst39vf }, /* SST39VF3202 */
4538c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x235b, fixup_sst39vf }, /* SST39VF3201 */
4548c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x235c, fixup_sst39vf_rev_b }, /* SST39VF3202B */
4558c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x235d, fixup_sst39vf_rev_b }, /* SST39VF3201B */
4568c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x236c, fixup_sst39vf_rev_b }, /* SST39VF6402B */
4578c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x236d, fixup_sst39vf_rev_b }, /* SST39VF6401B */
4588c2ecf20Sopenharmony_ci	{ 0, 0, NULL }
4598c2ecf20Sopenharmony_ci};
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic struct cfi_fixup cfi_fixup_table[] = {
4628c2ecf20Sopenharmony_ci	{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri },
4638c2ecf20Sopenharmony_ci#ifdef AMD_BOOTLOC_BUG
4648c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock },
4658c2ecf20Sopenharmony_ci	{ CFI_MFR_AMIC, CFI_ID_ANY, fixup_amd_bootblock },
4668c2ecf20Sopenharmony_ci	{ CFI_MFR_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock },
4678c2ecf20Sopenharmony_ci#endif
4688c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x0050, fixup_use_secsi },
4698c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x0053, fixup_use_secsi },
4708c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x0055, fixup_use_secsi },
4718c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x0056, fixup_use_secsi },
4728c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x005C, fixup_use_secsi },
4738c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x005F, fixup_use_secsi },
4748c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x0c01, fixup_s29gl064n_sectors },
4758c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors },
4768c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors },
4778c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors },
4788c2ecf20Sopenharmony_ci	{ CFI_MFR_AMD, 0x3f00, fixup_s29ns512p_sectors },
4798c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x536a, fixup_sst38vf640x_sectorsize }, /* SST38VF6402 */
4808c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x536b, fixup_sst38vf640x_sectorsize }, /* SST38VF6401 */
4818c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x536c, fixup_sst38vf640x_sectorsize }, /* SST38VF6404 */
4828c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, 0x536d, fixup_sst38vf640x_sectorsize }, /* SST38VF6403 */
4838c2ecf20Sopenharmony_ci#if !FORCE_WORD_WRITE
4848c2ecf20Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers },
4858c2ecf20Sopenharmony_ci#endif
4868c2ecf20Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_quirks },
4878c2ecf20Sopenharmony_ci	{ 0, 0, NULL }
4888c2ecf20Sopenharmony_ci};
4898c2ecf20Sopenharmony_cistatic struct cfi_fixup jedec_fixup_table[] = {
4908c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, SST49LF004B, fixup_use_fwh_lock },
4918c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, SST49LF040B, fixup_use_fwh_lock },
4928c2ecf20Sopenharmony_ci	{ CFI_MFR_SST, SST49LF008A, fixup_use_fwh_lock },
4938c2ecf20Sopenharmony_ci	{ 0, 0, NULL }
4948c2ecf20Sopenharmony_ci};
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic struct cfi_fixup fixup_table[] = {
4978c2ecf20Sopenharmony_ci	/* The CFI vendor ids and the JEDEC vendor IDs appear
4988c2ecf20Sopenharmony_ci	 * to be common.  It is like the devices id's are as
4998c2ecf20Sopenharmony_ci	 * well.  This table is to pick all cases where
5008c2ecf20Sopenharmony_ci	 * we know that is the case.
5018c2ecf20Sopenharmony_ci	 */
5028c2ecf20Sopenharmony_ci	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip },
5038c2ecf20Sopenharmony_ci	{ CFI_MFR_ATMEL, AT49BV6416, fixup_use_atmel_lock },
5048c2ecf20Sopenharmony_ci	{ 0, 0, NULL }
5058c2ecf20Sopenharmony_ci};
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void cfi_fixup_major_minor(struct cfi_private *cfi,
5098c2ecf20Sopenharmony_ci				  struct cfi_pri_amdstd *extp)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	if (cfi->mfr == CFI_MFR_SAMSUNG) {
5128c2ecf20Sopenharmony_ci		if ((extp->MajorVersion == '0' && extp->MinorVersion == '0') ||
5138c2ecf20Sopenharmony_ci		    (extp->MajorVersion == '3' && extp->MinorVersion == '3')) {
5148c2ecf20Sopenharmony_ci			/*
5158c2ecf20Sopenharmony_ci			 * Samsung K8P2815UQB and K8D6x16UxM chips
5168c2ecf20Sopenharmony_ci			 * report major=0 / minor=0.
5178c2ecf20Sopenharmony_ci			 * K8D3x16UxC chips report major=3 / minor=3.
5188c2ecf20Sopenharmony_ci			 */
5198c2ecf20Sopenharmony_ci			printk(KERN_NOTICE "  Fixing Samsung's Amd/Fujitsu"
5208c2ecf20Sopenharmony_ci			       " Extended Query version to 1.%c\n",
5218c2ecf20Sopenharmony_ci			       extp->MinorVersion);
5228c2ecf20Sopenharmony_ci			extp->MajorVersion = '1';
5238c2ecf20Sopenharmony_ci		}
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/*
5278c2ecf20Sopenharmony_ci	 * SST 38VF640x chips report major=0xFF / minor=0xFF.
5288c2ecf20Sopenharmony_ci	 */
5298c2ecf20Sopenharmony_ci	if (cfi->mfr == CFI_MFR_SST && (cfi->id >> 4) == 0x0536) {
5308c2ecf20Sopenharmony_ci		extp->MajorVersion = '1';
5318c2ecf20Sopenharmony_ci		extp->MinorVersion = '0';
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cistatic int is_m29ew(struct cfi_private *cfi)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	if (cfi->mfr == CFI_MFR_INTEL &&
5388c2ecf20Sopenharmony_ci	    ((cfi->device_type == CFI_DEVICETYPE_X8 && (cfi->id & 0xff) == 0x7e) ||
5398c2ecf20Sopenharmony_ci	     (cfi->device_type == CFI_DEVICETYPE_X16 && cfi->id == 0x227e)))
5408c2ecf20Sopenharmony_ci		return 1;
5418c2ecf20Sopenharmony_ci	return 0;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci/*
5458c2ecf20Sopenharmony_ci * From TN-13-07: Patching the Linux Kernel and U-Boot for M29 Flash, page 20:
5468c2ecf20Sopenharmony_ci * Some revisions of the M29EW suffer from erase suspend hang ups. In
5478c2ecf20Sopenharmony_ci * particular, it can occur when the sequence
5488c2ecf20Sopenharmony_ci * Erase Confirm -> Suspend -> Program -> Resume
5498c2ecf20Sopenharmony_ci * causes a lockup due to internal timing issues. The consequence is that the
5508c2ecf20Sopenharmony_ci * erase cannot be resumed without inserting a dummy command after programming
5518c2ecf20Sopenharmony_ci * and prior to resuming. [...] The work-around is to issue a dummy write cycle
5528c2ecf20Sopenharmony_ci * that writes an F0 command code before the RESUME command.
5538c2ecf20Sopenharmony_ci */
5548c2ecf20Sopenharmony_cistatic void cfi_fixup_m29ew_erase_suspend(struct map_info *map,
5558c2ecf20Sopenharmony_ci					  unsigned long adr)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
5588c2ecf20Sopenharmony_ci	/* before resume, insert a dummy 0xF0 cycle for Micron M29EW devices */
5598c2ecf20Sopenharmony_ci	if (is_m29ew(cfi))
5608c2ecf20Sopenharmony_ci		map_write(map, CMD(0xF0), adr);
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci/*
5648c2ecf20Sopenharmony_ci * From TN-13-07: Patching the Linux Kernel and U-Boot for M29 Flash, page 22:
5658c2ecf20Sopenharmony_ci *
5668c2ecf20Sopenharmony_ci * Some revisions of the M29EW (for example, A1 and A2 step revisions)
5678c2ecf20Sopenharmony_ci * are affected by a problem that could cause a hang up when an ERASE SUSPEND
5688c2ecf20Sopenharmony_ci * command is issued after an ERASE RESUME operation without waiting for a
5698c2ecf20Sopenharmony_ci * minimum delay.  The result is that once the ERASE seems to be completed
5708c2ecf20Sopenharmony_ci * (no bits are toggling), the contents of the Flash memory block on which
5718c2ecf20Sopenharmony_ci * the erase was ongoing could be inconsistent with the expected values
5728c2ecf20Sopenharmony_ci * (typically, the array value is stuck to the 0xC0, 0xC4, 0x80, or 0x84
5738c2ecf20Sopenharmony_ci * values), causing a consequent failure of the ERASE operation.
5748c2ecf20Sopenharmony_ci * The occurrence of this issue could be high, especially when file system
5758c2ecf20Sopenharmony_ci * operations on the Flash are intensive.  As a result, it is recommended
5768c2ecf20Sopenharmony_ci * that a patch be applied.  Intensive file system operations can cause many
5778c2ecf20Sopenharmony_ci * calls to the garbage routine to free Flash space (also by erasing physical
5788c2ecf20Sopenharmony_ci * Flash blocks) and as a result, many consecutive SUSPEND and RESUME
5798c2ecf20Sopenharmony_ci * commands can occur.  The problem disappears when a delay is inserted after
5808c2ecf20Sopenharmony_ci * the RESUME command by using the udelay() function available in Linux.
5818c2ecf20Sopenharmony_ci * The DELAY value must be tuned based on the customer's platform.
5828c2ecf20Sopenharmony_ci * The maximum value that fixes the problem in all cases is 500us.
5838c2ecf20Sopenharmony_ci * But, in our experience, a delay of 30 µs to 50 µs is sufficient
5848c2ecf20Sopenharmony_ci * in most cases.
5858c2ecf20Sopenharmony_ci * We have chosen 500µs because this latency is acceptable.
5868c2ecf20Sopenharmony_ci */
5878c2ecf20Sopenharmony_cistatic void cfi_fixup_m29ew_delay_after_resume(struct cfi_private *cfi)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	/*
5908c2ecf20Sopenharmony_ci	 * Resolving the Delay After Resume Issue see Micron TN-13-07
5918c2ecf20Sopenharmony_ci	 * Worst case delay must be 500µs but 30-50µs should be ok as well
5928c2ecf20Sopenharmony_ci	 */
5938c2ecf20Sopenharmony_ci	if (is_m29ew(cfi))
5948c2ecf20Sopenharmony_ci		cfi_udelay(500);
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
6008c2ecf20Sopenharmony_ci	struct device_node __maybe_unused *np = map->device_node;
6018c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
6028c2ecf20Sopenharmony_ci	int i;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
6058c2ecf20Sopenharmony_ci	if (!mtd)
6068c2ecf20Sopenharmony_ci		return NULL;
6078c2ecf20Sopenharmony_ci	mtd->priv = map;
6088c2ecf20Sopenharmony_ci	mtd->type = MTD_NORFLASH;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	/* Fill in the default mtd operations */
6118c2ecf20Sopenharmony_ci	mtd->_erase   = cfi_amdstd_erase_varsize;
6128c2ecf20Sopenharmony_ci	mtd->_write   = cfi_amdstd_write_words;
6138c2ecf20Sopenharmony_ci	mtd->_read    = cfi_amdstd_read;
6148c2ecf20Sopenharmony_ci	mtd->_sync    = cfi_amdstd_sync;
6158c2ecf20Sopenharmony_ci	mtd->_suspend = cfi_amdstd_suspend;
6168c2ecf20Sopenharmony_ci	mtd->_resume  = cfi_amdstd_resume;
6178c2ecf20Sopenharmony_ci	mtd->_read_user_prot_reg = cfi_amdstd_read_user_prot_reg;
6188c2ecf20Sopenharmony_ci	mtd->_read_fact_prot_reg = cfi_amdstd_read_fact_prot_reg;
6198c2ecf20Sopenharmony_ci	mtd->_get_fact_prot_info = cfi_amdstd_get_fact_prot_info;
6208c2ecf20Sopenharmony_ci	mtd->_get_user_prot_info = cfi_amdstd_get_user_prot_info;
6218c2ecf20Sopenharmony_ci	mtd->_write_user_prot_reg = cfi_amdstd_write_user_prot_reg;
6228c2ecf20Sopenharmony_ci	mtd->_lock_user_prot_reg = cfi_amdstd_lock_user_prot_reg;
6238c2ecf20Sopenharmony_ci	mtd->flags   = MTD_CAP_NORFLASH;
6248c2ecf20Sopenharmony_ci	mtd->name    = map->name;
6258c2ecf20Sopenharmony_ci	mtd->writesize = 1;
6268c2ecf20Sopenharmony_ci	mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	pr_debug("MTD %s(): write buffer size %d\n", __func__,
6298c2ecf20Sopenharmony_ci			mtd->writebufsize);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	mtd->_panic_write = cfi_amdstd_panic_write;
6328c2ecf20Sopenharmony_ci	mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	if (cfi->cfi_mode==CFI_MODE_CFI){
6358c2ecf20Sopenharmony_ci		unsigned char bootloc;
6368c2ecf20Sopenharmony_ci		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
6378c2ecf20Sopenharmony_ci		struct cfi_pri_amdstd *extp;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");
6408c2ecf20Sopenharmony_ci		if (extp) {
6418c2ecf20Sopenharmony_ci			/*
6428c2ecf20Sopenharmony_ci			 * It's a real CFI chip, not one for which the probe
6438c2ecf20Sopenharmony_ci			 * routine faked a CFI structure.
6448c2ecf20Sopenharmony_ci			 */
6458c2ecf20Sopenharmony_ci			cfi_fixup_major_minor(cfi, extp);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci			/*
6488c2ecf20Sopenharmony_ci			 * Valid primary extension versions are: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5
6498c2ecf20Sopenharmony_ci			 * see: http://cs.ozerki.net/zap/pub/axim-x5/docs/cfi_r20.pdf, page 19
6508c2ecf20Sopenharmony_ci			 *      http://www.spansion.com/Support/AppNotes/cfi_100_20011201.pdf
6518c2ecf20Sopenharmony_ci			 *      http://www.spansion.com/Support/Datasheets/s29ws-p_00_a12_e.pdf
6528c2ecf20Sopenharmony_ci			 *      http://www.spansion.com/Support/Datasheets/S29GL_128S_01GS_00_02_e.pdf
6538c2ecf20Sopenharmony_ci			 */
6548c2ecf20Sopenharmony_ci			if (extp->MajorVersion != '1' ||
6558c2ecf20Sopenharmony_ci			    (extp->MajorVersion == '1' && (extp->MinorVersion < '0' || extp->MinorVersion > '5'))) {
6568c2ecf20Sopenharmony_ci				printk(KERN_ERR "  Unknown Amd/Fujitsu Extended Query "
6578c2ecf20Sopenharmony_ci				       "version %c.%c (%#02x/%#02x).\n",
6588c2ecf20Sopenharmony_ci				       extp->MajorVersion, extp->MinorVersion,
6598c2ecf20Sopenharmony_ci				       extp->MajorVersion, extp->MinorVersion);
6608c2ecf20Sopenharmony_ci				kfree(extp);
6618c2ecf20Sopenharmony_ci				kfree(mtd);
6628c2ecf20Sopenharmony_ci				return NULL;
6638c2ecf20Sopenharmony_ci			}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci			printk(KERN_INFO "  Amd/Fujitsu Extended Query version %c.%c.\n",
6668c2ecf20Sopenharmony_ci			       extp->MajorVersion, extp->MinorVersion);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci			/* Install our own private info structure */
6698c2ecf20Sopenharmony_ci			cfi->cmdset_priv = extp;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci			/* Apply cfi device specific fixups */
6728c2ecf20Sopenharmony_ci			cfi_fixup(mtd, cfi_fixup_table);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES
6758c2ecf20Sopenharmony_ci			/* Tell the user about it in lots of lovely detail */
6768c2ecf20Sopenharmony_ci			cfi_tell_features(extp);
6778c2ecf20Sopenharmony_ci#endif
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
6808c2ecf20Sopenharmony_ci			if (np && of_property_read_bool(
6818c2ecf20Sopenharmony_ci				    np, "use-advanced-sector-protection")
6828c2ecf20Sopenharmony_ci			    && extp->BlkProtUnprot == 8) {
6838c2ecf20Sopenharmony_ci				printk(KERN_INFO "  Advanced Sector Protection (PPB Locking) supported\n");
6848c2ecf20Sopenharmony_ci				mtd->_lock = cfi_ppb_lock;
6858c2ecf20Sopenharmony_ci				mtd->_unlock = cfi_ppb_unlock;
6868c2ecf20Sopenharmony_ci				mtd->_is_locked = cfi_ppb_is_locked;
6878c2ecf20Sopenharmony_ci			}
6888c2ecf20Sopenharmony_ci#endif
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci			bootloc = extp->TopBottom;
6918c2ecf20Sopenharmony_ci			if ((bootloc < 2) || (bootloc > 5)) {
6928c2ecf20Sopenharmony_ci				printk(KERN_WARNING "%s: CFI contains unrecognised boot "
6938c2ecf20Sopenharmony_ci				       "bank location (%d). Assuming bottom.\n",
6948c2ecf20Sopenharmony_ci				       map->name, bootloc);
6958c2ecf20Sopenharmony_ci				bootloc = 2;
6968c2ecf20Sopenharmony_ci			}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci			if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {
6998c2ecf20Sopenharmony_ci				printk(KERN_WARNING "%s: Swapping erase regions for top-boot CFI table.\n", map->name);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci				for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) {
7028c2ecf20Sopenharmony_ci					int j = (cfi->cfiq->NumEraseRegions-1)-i;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci					swap(cfi->cfiq->EraseRegionInfo[i],
7058c2ecf20Sopenharmony_ci					     cfi->cfiq->EraseRegionInfo[j]);
7068c2ecf20Sopenharmony_ci				}
7078c2ecf20Sopenharmony_ci			}
7088c2ecf20Sopenharmony_ci			/* Set the default CFI lock/unlock addresses */
7098c2ecf20Sopenharmony_ci			cfi->addr_unlock1 = 0x555;
7108c2ecf20Sopenharmony_ci			cfi->addr_unlock2 = 0x2aa;
7118c2ecf20Sopenharmony_ci		}
7128c2ecf20Sopenharmony_ci		cfi_fixup(mtd, cfi_nopri_fixup_table);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		if (!cfi->addr_unlock1 || !cfi->addr_unlock2) {
7158c2ecf20Sopenharmony_ci			kfree(mtd);
7168c2ecf20Sopenharmony_ci			return NULL;
7178c2ecf20Sopenharmony_ci		}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	} /* CFI mode */
7208c2ecf20Sopenharmony_ci	else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
7218c2ecf20Sopenharmony_ci		/* Apply jedec specific fixups */
7228c2ecf20Sopenharmony_ci		cfi_fixup(mtd, jedec_fixup_table);
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci	/* Apply generic fixups */
7258c2ecf20Sopenharmony_ci	cfi_fixup(mtd, fixup_table);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	for (i=0; i< cfi->numchips; i++) {
7288c2ecf20Sopenharmony_ci		cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
7298c2ecf20Sopenharmony_ci		cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
7308c2ecf20Sopenharmony_ci		cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp;
7318c2ecf20Sopenharmony_ci		/*
7328c2ecf20Sopenharmony_ci		 * First calculate the timeout max according to timeout field
7338c2ecf20Sopenharmony_ci		 * of struct cfi_ident that probed from chip's CFI aera, if
7348c2ecf20Sopenharmony_ci		 * available. Specify a minimum of 2000us, in case the CFI data
7358c2ecf20Sopenharmony_ci		 * is wrong.
7368c2ecf20Sopenharmony_ci		 */
7378c2ecf20Sopenharmony_ci		if (cfi->cfiq->BufWriteTimeoutTyp &&
7388c2ecf20Sopenharmony_ci		    cfi->cfiq->BufWriteTimeoutMax)
7398c2ecf20Sopenharmony_ci			cfi->chips[i].buffer_write_time_max =
7408c2ecf20Sopenharmony_ci				1 << (cfi->cfiq->BufWriteTimeoutTyp +
7418c2ecf20Sopenharmony_ci				      cfi->cfiq->BufWriteTimeoutMax);
7428c2ecf20Sopenharmony_ci		else
7438c2ecf20Sopenharmony_ci			cfi->chips[i].buffer_write_time_max = 0;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci		cfi->chips[i].buffer_write_time_max =
7468c2ecf20Sopenharmony_ci			max(cfi->chips[i].buffer_write_time_max, 2000);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		cfi->chips[i].ref_point_counter = 0;
7498c2ecf20Sopenharmony_ci		init_waitqueue_head(&(cfi->chips[i].wq));
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	map->fldrv = &cfi_amdstd_chipdrv;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	return cfi_amdstd_setup(mtd);
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0006(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002")));
7578c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002")));
7588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0002);
7598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0006);
7608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0701);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_cistatic struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
7658c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
7668c2ecf20Sopenharmony_ci	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
7678c2ecf20Sopenharmony_ci	unsigned long offset = 0;
7688c2ecf20Sopenharmony_ci	int i,j;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	printk(KERN_NOTICE "number of %s chips: %d\n",
7718c2ecf20Sopenharmony_ci	       (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips);
7728c2ecf20Sopenharmony_ci	/* Select the correct geometry setup */
7738c2ecf20Sopenharmony_ci	mtd->size = devsize * cfi->numchips;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
7768c2ecf20Sopenharmony_ci	mtd->eraseregions = kmalloc_array(mtd->numeraseregions,
7778c2ecf20Sopenharmony_ci					  sizeof(struct mtd_erase_region_info),
7788c2ecf20Sopenharmony_ci					  GFP_KERNEL);
7798c2ecf20Sopenharmony_ci	if (!mtd->eraseregions)
7808c2ecf20Sopenharmony_ci		goto setup_err;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
7838c2ecf20Sopenharmony_ci		unsigned long ernum, ersize;
7848c2ecf20Sopenharmony_ci		ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
7858c2ecf20Sopenharmony_ci		ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		if (mtd->erasesize < ersize) {
7888c2ecf20Sopenharmony_ci			mtd->erasesize = ersize;
7898c2ecf20Sopenharmony_ci		}
7908c2ecf20Sopenharmony_ci		for (j=0; j<cfi->numchips; j++) {
7918c2ecf20Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
7928c2ecf20Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
7938c2ecf20Sopenharmony_ci			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
7948c2ecf20Sopenharmony_ci		}
7958c2ecf20Sopenharmony_ci		offset += (ersize * ernum);
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci	if (offset != devsize) {
7988c2ecf20Sopenharmony_ci		/* Argh */
7998c2ecf20Sopenharmony_ci		printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
8008c2ecf20Sopenharmony_ci		goto setup_err;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	__module_get(THIS_MODULE);
8048c2ecf20Sopenharmony_ci	register_reboot_notifier(&mtd->reboot_notifier);
8058c2ecf20Sopenharmony_ci	return mtd;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci setup_err:
8088c2ecf20Sopenharmony_ci	kfree(mtd->eraseregions);
8098c2ecf20Sopenharmony_ci	kfree(mtd);
8108c2ecf20Sopenharmony_ci	kfree(cfi->cmdset_priv);
8118c2ecf20Sopenharmony_ci	return NULL;
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci/*
8158c2ecf20Sopenharmony_ci * Return true if the chip is ready and has the correct value.
8168c2ecf20Sopenharmony_ci *
8178c2ecf20Sopenharmony_ci * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
8188c2ecf20Sopenharmony_ci * non-suspended sector) and is indicated by no toggle bits toggling.
8198c2ecf20Sopenharmony_ci *
8208c2ecf20Sopenharmony_ci * Error are indicated by toggling bits or bits held with the wrong value,
8218c2ecf20Sopenharmony_ci * or with bits toggling.
8228c2ecf20Sopenharmony_ci *
8238c2ecf20Sopenharmony_ci * Note that anything more complicated than checking if no bits are toggling
8248c2ecf20Sopenharmony_ci * (including checking DQ5 for an error status) is tricky to get working
8258c2ecf20Sopenharmony_ci * correctly and is therefore not done	(particularly with interleaved chips
8268c2ecf20Sopenharmony_ci * as each chip must be checked independently of the others).
8278c2ecf20Sopenharmony_ci */
8288c2ecf20Sopenharmony_cistatic int __xipram chip_ready(struct map_info *map, struct flchip *chip,
8298c2ecf20Sopenharmony_ci			       unsigned long addr, map_word *expected)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
8328c2ecf20Sopenharmony_ci	map_word d, t;
8338c2ecf20Sopenharmony_ci	int ret;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (cfi_use_status_reg(cfi)) {
8368c2ecf20Sopenharmony_ci		map_word ready = CMD(CFI_SR_DRB);
8378c2ecf20Sopenharmony_ci		/*
8388c2ecf20Sopenharmony_ci		 * For chips that support status register, check device
8398c2ecf20Sopenharmony_ci		 * ready bit
8408c2ecf20Sopenharmony_ci		 */
8418c2ecf20Sopenharmony_ci		cfi_send_gen_cmd(0x70, cfi->addr_unlock1, chip->start, map, cfi,
8428c2ecf20Sopenharmony_ci				 cfi->device_type, NULL);
8438c2ecf20Sopenharmony_ci		t = map_read(map, addr);
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci		return map_word_andequal(map, t, ready, ready);
8468c2ecf20Sopenharmony_ci	}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	d = map_read(map, addr);
8498c2ecf20Sopenharmony_ci	t = map_read(map, addr);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	ret = map_word_equal(map, d, t);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	if (!ret || !expected)
8548c2ecf20Sopenharmony_ci		return ret;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	return map_word_equal(map, t, *expected);
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic int __xipram chip_good(struct map_info *map, struct flchip *chip,
8608c2ecf20Sopenharmony_ci			      unsigned long addr, map_word *expected)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
8638c2ecf20Sopenharmony_ci	map_word *datum = expected;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (cfi->quirks & CFI_QUIRK_DQ_TRUE_DATA)
8668c2ecf20Sopenharmony_ci		datum = NULL;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	return chip_ready(map, chip, addr, datum);
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cistatic int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
8748c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
8758c2ecf20Sopenharmony_ci	unsigned long timeo;
8768c2ecf20Sopenharmony_ci	struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci resettime:
8798c2ecf20Sopenharmony_ci	timeo = jiffies + HZ;
8808c2ecf20Sopenharmony_ci retry:
8818c2ecf20Sopenharmony_ci	switch (chip->state) {
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	case FL_STATUS:
8848c2ecf20Sopenharmony_ci		for (;;) {
8858c2ecf20Sopenharmony_ci			if (chip_ready(map, chip, adr, NULL))
8868c2ecf20Sopenharmony_ci				break;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci			if (time_after(jiffies, timeo)) {
8898c2ecf20Sopenharmony_ci				printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
8908c2ecf20Sopenharmony_ci				return -EIO;
8918c2ecf20Sopenharmony_ci			}
8928c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
8938c2ecf20Sopenharmony_ci			cfi_udelay(1);
8948c2ecf20Sopenharmony_ci			mutex_lock(&chip->mutex);
8958c2ecf20Sopenharmony_ci			/* Someone else might have been playing with it. */
8968c2ecf20Sopenharmony_ci			goto retry;
8978c2ecf20Sopenharmony_ci		}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	case FL_READY:
9008c2ecf20Sopenharmony_ci	case FL_CFI_QUERY:
9018c2ecf20Sopenharmony_ci	case FL_JEDEC_QUERY:
9028c2ecf20Sopenharmony_ci		return 0;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	case FL_ERASING:
9058c2ecf20Sopenharmony_ci		if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
9068c2ecf20Sopenharmony_ci		    !(mode == FL_READY || mode == FL_POINT ||
9078c2ecf20Sopenharmony_ci		    (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
9088c2ecf20Sopenharmony_ci			goto sleep;
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci		/* Do not allow suspend iff read/write to EB address */
9118c2ecf20Sopenharmony_ci		if ((adr & chip->in_progress_block_mask) ==
9128c2ecf20Sopenharmony_ci		    chip->in_progress_block_addr)
9138c2ecf20Sopenharmony_ci			goto sleep;
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		/* Erase suspend */
9168c2ecf20Sopenharmony_ci		/* It's harmless to issue the Erase-Suspend and Erase-Resume
9178c2ecf20Sopenharmony_ci		 * commands when the erase algorithm isn't in progress. */
9188c2ecf20Sopenharmony_ci		map_write(map, CMD(0xB0), chip->in_progress_block_addr);
9198c2ecf20Sopenharmony_ci		chip->oldstate = FL_ERASING;
9208c2ecf20Sopenharmony_ci		chip->state = FL_ERASE_SUSPENDING;
9218c2ecf20Sopenharmony_ci		chip->erase_suspended = 1;
9228c2ecf20Sopenharmony_ci		for (;;) {
9238c2ecf20Sopenharmony_ci			if (chip_ready(map, chip, adr, NULL))
9248c2ecf20Sopenharmony_ci				break;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci			if (time_after(jiffies, timeo)) {
9278c2ecf20Sopenharmony_ci				/* Should have suspended the erase by now.
9288c2ecf20Sopenharmony_ci				 * Send an Erase-Resume command as either
9298c2ecf20Sopenharmony_ci				 * there was an error (so leave the erase
9308c2ecf20Sopenharmony_ci				 * routine to recover from it) or we trying to
9318c2ecf20Sopenharmony_ci				 * use the erase-in-progress sector. */
9328c2ecf20Sopenharmony_ci				put_chip(map, chip, adr);
9338c2ecf20Sopenharmony_ci				printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);
9348c2ecf20Sopenharmony_ci				return -EIO;
9358c2ecf20Sopenharmony_ci			}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
9388c2ecf20Sopenharmony_ci			cfi_udelay(1);
9398c2ecf20Sopenharmony_ci			mutex_lock(&chip->mutex);
9408c2ecf20Sopenharmony_ci			/* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
9418c2ecf20Sopenharmony_ci			   So we can just loop here. */
9428c2ecf20Sopenharmony_ci		}
9438c2ecf20Sopenharmony_ci		chip->state = FL_READY;
9448c2ecf20Sopenharmony_ci		return 0;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	case FL_XIP_WHILE_ERASING:
9478c2ecf20Sopenharmony_ci		if (mode != FL_READY && mode != FL_POINT &&
9488c2ecf20Sopenharmony_ci		    (!cfip || !(cfip->EraseSuspend&2)))
9498c2ecf20Sopenharmony_ci			goto sleep;
9508c2ecf20Sopenharmony_ci		chip->oldstate = chip->state;
9518c2ecf20Sopenharmony_ci		chip->state = FL_READY;
9528c2ecf20Sopenharmony_ci		return 0;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	case FL_SHUTDOWN:
9558c2ecf20Sopenharmony_ci		/* The machine is rebooting */
9568c2ecf20Sopenharmony_ci		return -EIO;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	case FL_POINT:
9598c2ecf20Sopenharmony_ci		/* Only if there's no operation suspended... */
9608c2ecf20Sopenharmony_ci		if (mode == FL_READY && chip->oldstate == FL_READY)
9618c2ecf20Sopenharmony_ci			return 0;
9628c2ecf20Sopenharmony_ci		fallthrough;
9638c2ecf20Sopenharmony_ci	default:
9648c2ecf20Sopenharmony_ci	sleep:
9658c2ecf20Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
9668c2ecf20Sopenharmony_ci		add_wait_queue(&chip->wq, &wait);
9678c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
9688c2ecf20Sopenharmony_ci		schedule();
9698c2ecf20Sopenharmony_ci		remove_wait_queue(&chip->wq, &wait);
9708c2ecf20Sopenharmony_ci		mutex_lock(&chip->mutex);
9718c2ecf20Sopenharmony_ci		goto resettime;
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_cistatic void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
9778c2ecf20Sopenharmony_ci{
9788c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	switch(chip->oldstate) {
9818c2ecf20Sopenharmony_ci	case FL_ERASING:
9828c2ecf20Sopenharmony_ci		cfi_fixup_m29ew_erase_suspend(map,
9838c2ecf20Sopenharmony_ci			chip->in_progress_block_addr);
9848c2ecf20Sopenharmony_ci		map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
9858c2ecf20Sopenharmony_ci		cfi_fixup_m29ew_delay_after_resume(cfi);
9868c2ecf20Sopenharmony_ci		chip->oldstate = FL_READY;
9878c2ecf20Sopenharmony_ci		chip->state = FL_ERASING;
9888c2ecf20Sopenharmony_ci		break;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	case FL_XIP_WHILE_ERASING:
9918c2ecf20Sopenharmony_ci		chip->state = chip->oldstate;
9928c2ecf20Sopenharmony_ci		chip->oldstate = FL_READY;
9938c2ecf20Sopenharmony_ci		break;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	case FL_READY:
9968c2ecf20Sopenharmony_ci	case FL_STATUS:
9978c2ecf20Sopenharmony_ci		break;
9988c2ecf20Sopenharmony_ci	default:
9998c2ecf20Sopenharmony_ci		printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);
10008c2ecf20Sopenharmony_ci	}
10018c2ecf20Sopenharmony_ci	wake_up(&chip->wq);
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci#ifdef CONFIG_MTD_XIP
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci/*
10078c2ecf20Sopenharmony_ci * No interrupt what so ever can be serviced while the flash isn't in array
10088c2ecf20Sopenharmony_ci * mode.  This is ensured by the xip_disable() and xip_enable() functions
10098c2ecf20Sopenharmony_ci * enclosing any code path where the flash is known not to be in array mode.
10108c2ecf20Sopenharmony_ci * And within a XIP disabled code path, only functions marked with __xipram
10118c2ecf20Sopenharmony_ci * may be called and nothing else (it's a good thing to inspect generated
10128c2ecf20Sopenharmony_ci * assembly to make sure inline functions were actually inlined and that gcc
10138c2ecf20Sopenharmony_ci * didn't emit calls to its own support functions). Also configuring MTD CFI
10148c2ecf20Sopenharmony_ci * support to a single buswidth and a single interleave is also recommended.
10158c2ecf20Sopenharmony_ci */
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_cistatic void xip_disable(struct map_info *map, struct flchip *chip,
10188c2ecf20Sopenharmony_ci			unsigned long adr)
10198c2ecf20Sopenharmony_ci{
10208c2ecf20Sopenharmony_ci	/* TODO: chips with no XIP use should ignore and return */
10218c2ecf20Sopenharmony_ci	(void) map_read(map, adr); /* ensure mmu mapping is up to date */
10228c2ecf20Sopenharmony_ci	local_irq_disable();
10238c2ecf20Sopenharmony_ci}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_cistatic void __xipram xip_enable(struct map_info *map, struct flchip *chip,
10268c2ecf20Sopenharmony_ci				unsigned long adr)
10278c2ecf20Sopenharmony_ci{
10288c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	if (chip->state != FL_POINT && chip->state != FL_READY) {
10318c2ecf20Sopenharmony_ci		map_write(map, CMD(0xf0), adr);
10328c2ecf20Sopenharmony_ci		chip->state = FL_READY;
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci	(void) map_read(map, adr);
10358c2ecf20Sopenharmony_ci	xip_iprefetch();
10368c2ecf20Sopenharmony_ci	local_irq_enable();
10378c2ecf20Sopenharmony_ci}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci/*
10408c2ecf20Sopenharmony_ci * When a delay is required for the flash operation to complete, the
10418c2ecf20Sopenharmony_ci * xip_udelay() function is polling for both the given timeout and pending
10428c2ecf20Sopenharmony_ci * (but still masked) hardware interrupts.  Whenever there is an interrupt
10438c2ecf20Sopenharmony_ci * pending then the flash erase operation is suspended, array mode restored
10448c2ecf20Sopenharmony_ci * and interrupts unmasked.  Task scheduling might also happen at that
10458c2ecf20Sopenharmony_ci * point.  The CPU eventually returns from the interrupt or the call to
10468c2ecf20Sopenharmony_ci * schedule() and the suspended flash operation is resumed for the remaining
10478c2ecf20Sopenharmony_ci * of the delay period.
10488c2ecf20Sopenharmony_ci *
10498c2ecf20Sopenharmony_ci * Warning: this function _will_ fool interrupt latency tracing tools.
10508c2ecf20Sopenharmony_ci */
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_cistatic void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
10538c2ecf20Sopenharmony_ci				unsigned long adr, int usec)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
10568c2ecf20Sopenharmony_ci	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
10578c2ecf20Sopenharmony_ci	map_word status, OK = CMD(0x80);
10588c2ecf20Sopenharmony_ci	unsigned long suspended, start = xip_currtime();
10598c2ecf20Sopenharmony_ci	flstate_t oldstate;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	do {
10628c2ecf20Sopenharmony_ci		cpu_relax();
10638c2ecf20Sopenharmony_ci		if (xip_irqpending() && extp &&
10648c2ecf20Sopenharmony_ci		    ((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) &&
10658c2ecf20Sopenharmony_ci		    (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
10668c2ecf20Sopenharmony_ci			/*
10678c2ecf20Sopenharmony_ci			 * Let's suspend the erase operation when supported.
10688c2ecf20Sopenharmony_ci			 * Note that we currently don't try to suspend
10698c2ecf20Sopenharmony_ci			 * interleaved chips if there is already another
10708c2ecf20Sopenharmony_ci			 * operation suspended (imagine what happens
10718c2ecf20Sopenharmony_ci			 * when one chip was already done with the current
10728c2ecf20Sopenharmony_ci			 * operation while another chip suspended it, then
10738c2ecf20Sopenharmony_ci			 * we resume the whole thing at once).  Yes, it
10748c2ecf20Sopenharmony_ci			 * can happen!
10758c2ecf20Sopenharmony_ci			 */
10768c2ecf20Sopenharmony_ci			map_write(map, CMD(0xb0), adr);
10778c2ecf20Sopenharmony_ci			usec -= xip_elapsed_since(start);
10788c2ecf20Sopenharmony_ci			suspended = xip_currtime();
10798c2ecf20Sopenharmony_ci			do {
10808c2ecf20Sopenharmony_ci				if (xip_elapsed_since(suspended) > 100000) {
10818c2ecf20Sopenharmony_ci					/*
10828c2ecf20Sopenharmony_ci					 * The chip doesn't want to suspend
10838c2ecf20Sopenharmony_ci					 * after waiting for 100 msecs.
10848c2ecf20Sopenharmony_ci					 * This is a critical error but there
10858c2ecf20Sopenharmony_ci					 * is not much we can do here.
10868c2ecf20Sopenharmony_ci					 */
10878c2ecf20Sopenharmony_ci					return;
10888c2ecf20Sopenharmony_ci				}
10898c2ecf20Sopenharmony_ci				status = map_read(map, adr);
10908c2ecf20Sopenharmony_ci			} while (!map_word_andequal(map, status, OK, OK));
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci			/* Suspend succeeded */
10938c2ecf20Sopenharmony_ci			oldstate = chip->state;
10948c2ecf20Sopenharmony_ci			if (!map_word_bitsset(map, status, CMD(0x40)))
10958c2ecf20Sopenharmony_ci				break;
10968c2ecf20Sopenharmony_ci			chip->state = FL_XIP_WHILE_ERASING;
10978c2ecf20Sopenharmony_ci			chip->erase_suspended = 1;
10988c2ecf20Sopenharmony_ci			map_write(map, CMD(0xf0), adr);
10998c2ecf20Sopenharmony_ci			(void) map_read(map, adr);
11008c2ecf20Sopenharmony_ci			xip_iprefetch();
11018c2ecf20Sopenharmony_ci			local_irq_enable();
11028c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
11038c2ecf20Sopenharmony_ci			xip_iprefetch();
11048c2ecf20Sopenharmony_ci			cond_resched();
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci			/*
11078c2ecf20Sopenharmony_ci			 * We're back.  However someone else might have
11088c2ecf20Sopenharmony_ci			 * decided to go write to the chip if we are in
11098c2ecf20Sopenharmony_ci			 * a suspended erase state.  If so let's wait
11108c2ecf20Sopenharmony_ci			 * until it's done.
11118c2ecf20Sopenharmony_ci			 */
11128c2ecf20Sopenharmony_ci			mutex_lock(&chip->mutex);
11138c2ecf20Sopenharmony_ci			while (chip->state != FL_XIP_WHILE_ERASING) {
11148c2ecf20Sopenharmony_ci				DECLARE_WAITQUEUE(wait, current);
11158c2ecf20Sopenharmony_ci				set_current_state(TASK_UNINTERRUPTIBLE);
11168c2ecf20Sopenharmony_ci				add_wait_queue(&chip->wq, &wait);
11178c2ecf20Sopenharmony_ci				mutex_unlock(&chip->mutex);
11188c2ecf20Sopenharmony_ci				schedule();
11198c2ecf20Sopenharmony_ci				remove_wait_queue(&chip->wq, &wait);
11208c2ecf20Sopenharmony_ci				mutex_lock(&chip->mutex);
11218c2ecf20Sopenharmony_ci			}
11228c2ecf20Sopenharmony_ci			/* Disallow XIP again */
11238c2ecf20Sopenharmony_ci			local_irq_disable();
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci			/* Correct Erase Suspend Hangups for M29EW */
11268c2ecf20Sopenharmony_ci			cfi_fixup_m29ew_erase_suspend(map, adr);
11278c2ecf20Sopenharmony_ci			/* Resume the write or erase operation */
11288c2ecf20Sopenharmony_ci			map_write(map, cfi->sector_erase_cmd, adr);
11298c2ecf20Sopenharmony_ci			chip->state = oldstate;
11308c2ecf20Sopenharmony_ci			start = xip_currtime();
11318c2ecf20Sopenharmony_ci		} else if (usec >= 1000000/HZ) {
11328c2ecf20Sopenharmony_ci			/*
11338c2ecf20Sopenharmony_ci			 * Try to save on CPU power when waiting delay
11348c2ecf20Sopenharmony_ci			 * is at least a system timer tick period.
11358c2ecf20Sopenharmony_ci			 * No need to be extremely accurate here.
11368c2ecf20Sopenharmony_ci			 */
11378c2ecf20Sopenharmony_ci			xip_cpu_idle();
11388c2ecf20Sopenharmony_ci		}
11398c2ecf20Sopenharmony_ci		status = map_read(map, adr);
11408c2ecf20Sopenharmony_ci	} while (!map_word_andequal(map, status, OK, OK)
11418c2ecf20Sopenharmony_ci		 && xip_elapsed_since(start) < usec);
11428c2ecf20Sopenharmony_ci}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci#define UDELAY(map, chip, adr, usec)  xip_udelay(map, chip, adr, usec)
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci/*
11478c2ecf20Sopenharmony_ci * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
11488c2ecf20Sopenharmony_ci * the flash is actively programming or erasing since we have to poll for
11498c2ecf20Sopenharmony_ci * the operation to complete anyway.  We can't do that in a generic way with
11508c2ecf20Sopenharmony_ci * a XIP setup so do it before the actual flash operation in this case
11518c2ecf20Sopenharmony_ci * and stub it out from INVALIDATE_CACHE_UDELAY.
11528c2ecf20Sopenharmony_ci */
11538c2ecf20Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(map, from, size)  \
11548c2ecf20Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, from, size)
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
11578c2ecf20Sopenharmony_ci	UDELAY(map, chip, adr, usec)
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci/*
11608c2ecf20Sopenharmony_ci * Extra notes:
11618c2ecf20Sopenharmony_ci *
11628c2ecf20Sopenharmony_ci * Activating this XIP support changes the way the code works a bit.  For
11638c2ecf20Sopenharmony_ci * example the code to suspend the current process when concurrent access
11648c2ecf20Sopenharmony_ci * happens is never executed because xip_udelay() will always return with the
11658c2ecf20Sopenharmony_ci * same chip state as it was entered with.  This is why there is no care for
11668c2ecf20Sopenharmony_ci * the presence of add_wait_queue() or schedule() calls from within a couple
11678c2ecf20Sopenharmony_ci * xip_disable()'d  areas of code, like in do_erase_oneblock for example.
11688c2ecf20Sopenharmony_ci * The queueing and scheduling are always happening within xip_udelay().
11698c2ecf20Sopenharmony_ci *
11708c2ecf20Sopenharmony_ci * Similarly, get_chip() and put_chip() just happen to always be executed
11718c2ecf20Sopenharmony_ci * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state
11728c2ecf20Sopenharmony_ci * is in array mode, therefore never executing many cases therein and not
11738c2ecf20Sopenharmony_ci * causing any problem with XIP.
11748c2ecf20Sopenharmony_ci */
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci#else
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci#define xip_disable(map, chip, adr)
11798c2ecf20Sopenharmony_ci#define xip_enable(map, chip, adr)
11808c2ecf20Sopenharmony_ci#define XIP_INVAL_CACHED_RANGE(x...)
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci#define UDELAY(map, chip, adr, usec)  \
11838c2ecf20Sopenharmony_cido {  \
11848c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);  \
11858c2ecf20Sopenharmony_ci	cfi_udelay(usec);  \
11868c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);  \
11878c2ecf20Sopenharmony_ci} while (0)
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec)  \
11908c2ecf20Sopenharmony_cido {  \
11918c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);  \
11928c2ecf20Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, adr, len);  \
11938c2ecf20Sopenharmony_ci	cfi_udelay(usec);  \
11948c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);  \
11958c2ecf20Sopenharmony_ci} while (0)
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci#endif
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	unsigned long cmd_addr;
12028c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
12038c2ecf20Sopenharmony_ci	int ret;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	adr += chip->start;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	/* Ensure cmd read/writes are aligned. */
12088c2ecf20Sopenharmony_ci	cmd_addr = adr & ~(map_bankwidth(map)-1);
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
12118c2ecf20Sopenharmony_ci	ret = get_chip(map, chip, cmd_addr, FL_READY);
12128c2ecf20Sopenharmony_ci	if (ret) {
12138c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
12148c2ecf20Sopenharmony_ci		return ret;
12158c2ecf20Sopenharmony_ci	}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	if (chip->state != FL_POINT && chip->state != FL_READY) {
12188c2ecf20Sopenharmony_ci		map_write(map, CMD(0xf0), cmd_addr);
12198c2ecf20Sopenharmony_ci		chip->state = FL_READY;
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	map_copy_from(map, buf, adr, len);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	put_chip(map, chip, cmd_addr);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
12278c2ecf20Sopenharmony_ci	return 0;
12288c2ecf20Sopenharmony_ci}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_cistatic int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
12328c2ecf20Sopenharmony_ci{
12338c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
12348c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
12358c2ecf20Sopenharmony_ci	unsigned long ofs;
12368c2ecf20Sopenharmony_ci	int chipnum;
12378c2ecf20Sopenharmony_ci	int ret = 0;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	/* ofs: offset within the first chip that the first read should start */
12408c2ecf20Sopenharmony_ci	chipnum = (from >> cfi->chipshift);
12418c2ecf20Sopenharmony_ci	ofs = from - (chipnum <<  cfi->chipshift);
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	while (len) {
12448c2ecf20Sopenharmony_ci		unsigned long thislen;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci		if (chipnum >= cfi->numchips)
12478c2ecf20Sopenharmony_ci			break;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci		if ((len + ofs -1) >> cfi->chipshift)
12508c2ecf20Sopenharmony_ci			thislen = (1<<cfi->chipshift) - ofs;
12518c2ecf20Sopenharmony_ci		else
12528c2ecf20Sopenharmony_ci			thislen = len;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci		ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf);
12558c2ecf20Sopenharmony_ci		if (ret)
12568c2ecf20Sopenharmony_ci			break;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci		*retlen += thislen;
12598c2ecf20Sopenharmony_ci		len -= thislen;
12608c2ecf20Sopenharmony_ci		buf += thislen;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci		ofs = 0;
12638c2ecf20Sopenharmony_ci		chipnum++;
12648c2ecf20Sopenharmony_ci	}
12658c2ecf20Sopenharmony_ci	return ret;
12668c2ecf20Sopenharmony_ci}
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_citypedef int (*otp_op_t)(struct map_info *map, struct flchip *chip,
12698c2ecf20Sopenharmony_ci			loff_t adr, size_t len, u_char *buf, size_t grouplen);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_cistatic inline void otp_enter(struct map_info *map, struct flchip *chip,
12728c2ecf20Sopenharmony_ci			     loff_t adr, size_t len)
12738c2ecf20Sopenharmony_ci{
12748c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
12778c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
12788c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
12798c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
12808c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi,
12818c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, chip->start + adr, len);
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_cistatic inline void otp_exit(struct map_info *map, struct flchip *chip,
12878c2ecf20Sopenharmony_ci			    loff_t adr, size_t len)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
12928c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
12938c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
12948c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
12958c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi,
12968c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
12978c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi,
12988c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	INVALIDATE_CACHED_RANGE(map, chip->start + adr, len);
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_cistatic inline int do_read_secsi_onechip(struct map_info *map,
13048c2ecf20Sopenharmony_ci					struct flchip *chip, loff_t adr,
13058c2ecf20Sopenharmony_ci					size_t len, u_char *buf,
13068c2ecf20Sopenharmony_ci					size_t grouplen)
13078c2ecf20Sopenharmony_ci{
13088c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci retry:
13118c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	if (chip->state != FL_READY){
13148c2ecf20Sopenharmony_ci		set_current_state(TASK_UNINTERRUPTIBLE);
13158c2ecf20Sopenharmony_ci		add_wait_queue(&chip->wq, &wait);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci		schedule();
13208c2ecf20Sopenharmony_ci		remove_wait_queue(&chip->wq, &wait);
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci		goto retry;
13238c2ecf20Sopenharmony_ci	}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	adr += chip->start;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	chip->state = FL_READY;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	otp_enter(map, chip, adr, len);
13308c2ecf20Sopenharmony_ci	map_copy_from(map, buf, adr, len);
13318c2ecf20Sopenharmony_ci	otp_exit(map, chip, adr, len);
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	wake_up(&chip->wq);
13348c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	return 0;
13378c2ecf20Sopenharmony_ci}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_cistatic int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
13408c2ecf20Sopenharmony_ci{
13418c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
13428c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
13438c2ecf20Sopenharmony_ci	unsigned long ofs;
13448c2ecf20Sopenharmony_ci	int chipnum;
13458c2ecf20Sopenharmony_ci	int ret = 0;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	/* ofs: offset within the first chip that the first read should start */
13488c2ecf20Sopenharmony_ci	/* 8 secsi bytes per chip */
13498c2ecf20Sopenharmony_ci	chipnum=from>>3;
13508c2ecf20Sopenharmony_ci	ofs=from & 7;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	while (len) {
13538c2ecf20Sopenharmony_ci		unsigned long thislen;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci		if (chipnum >= cfi->numchips)
13568c2ecf20Sopenharmony_ci			break;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci		if ((len + ofs -1) >> 3)
13598c2ecf20Sopenharmony_ci			thislen = (1<<3) - ofs;
13608c2ecf20Sopenharmony_ci		else
13618c2ecf20Sopenharmony_ci			thislen = len;
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci		ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs,
13648c2ecf20Sopenharmony_ci					    thislen, buf, 0);
13658c2ecf20Sopenharmony_ci		if (ret)
13668c2ecf20Sopenharmony_ci			break;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci		*retlen += thislen;
13698c2ecf20Sopenharmony_ci		len -= thislen;
13708c2ecf20Sopenharmony_ci		buf += thislen;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci		ofs = 0;
13738c2ecf20Sopenharmony_ci		chipnum++;
13748c2ecf20Sopenharmony_ci	}
13758c2ecf20Sopenharmony_ci	return ret;
13768c2ecf20Sopenharmony_ci}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
13798c2ecf20Sopenharmony_ci				     unsigned long adr, map_word datum,
13808c2ecf20Sopenharmony_ci				     int mode);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_cistatic int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr,
13838c2ecf20Sopenharmony_ci			size_t len, u_char *buf, size_t grouplen)
13848c2ecf20Sopenharmony_ci{
13858c2ecf20Sopenharmony_ci	int ret;
13868c2ecf20Sopenharmony_ci	while (len) {
13878c2ecf20Sopenharmony_ci		unsigned long bus_ofs = adr & ~(map_bankwidth(map)-1);
13888c2ecf20Sopenharmony_ci		int gap = adr - bus_ofs;
13898c2ecf20Sopenharmony_ci		int n = min_t(int, len, map_bankwidth(map) - gap);
13908c2ecf20Sopenharmony_ci		map_word datum = map_word_ff(map);
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci		if (n != map_bankwidth(map)) {
13938c2ecf20Sopenharmony_ci			/* partial write of a word, load old contents */
13948c2ecf20Sopenharmony_ci			otp_enter(map, chip, bus_ofs, map_bankwidth(map));
13958c2ecf20Sopenharmony_ci			datum = map_read(map, bus_ofs);
13968c2ecf20Sopenharmony_ci			otp_exit(map, chip, bus_ofs, map_bankwidth(map));
13978c2ecf20Sopenharmony_ci		}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci		datum = map_word_load_partial(map, datum, buf, gap, n);
14008c2ecf20Sopenharmony_ci		ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE);
14018c2ecf20Sopenharmony_ci		if (ret)
14028c2ecf20Sopenharmony_ci			return ret;
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci		adr += n;
14058c2ecf20Sopenharmony_ci		buf += n;
14068c2ecf20Sopenharmony_ci		len -= n;
14078c2ecf20Sopenharmony_ci	}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	return 0;
14108c2ecf20Sopenharmony_ci}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_cistatic int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr,
14138c2ecf20Sopenharmony_ci		       size_t len, u_char *buf, size_t grouplen)
14148c2ecf20Sopenharmony_ci{
14158c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
14168c2ecf20Sopenharmony_ci	uint8_t lockreg;
14178c2ecf20Sopenharmony_ci	unsigned long timeo;
14188c2ecf20Sopenharmony_ci	int ret;
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	/* make sure area matches group boundaries */
14218c2ecf20Sopenharmony_ci	if ((adr != 0) || (len != grouplen))
14228c2ecf20Sopenharmony_ci		return -EINVAL;
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
14258c2ecf20Sopenharmony_ci	ret = get_chip(map, chip, chip->start, FL_LOCKING);
14268c2ecf20Sopenharmony_ci	if (ret) {
14278c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
14288c2ecf20Sopenharmony_ci		return ret;
14298c2ecf20Sopenharmony_ci	}
14308c2ecf20Sopenharmony_ci	chip->state = FL_LOCKING;
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	/* Enter lock register command */
14338c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
14348c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
14358c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
14368c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
14378c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x40, cfi->addr_unlock1, chip->start, map, cfi,
14388c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	/* read lock register */
14418c2ecf20Sopenharmony_ci	lockreg = cfi_read_query(map, 0);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	/* set bit 0 to protect extended memory block */
14448c2ecf20Sopenharmony_ci	lockreg &= ~0x01;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	/* set bit 0 to protect extended memory block */
14478c2ecf20Sopenharmony_ci	/* write lock register */
14488c2ecf20Sopenharmony_ci	map_write(map, CMD(0xA0), chip->start);
14498c2ecf20Sopenharmony_ci	map_write(map, CMD(lockreg), chip->start);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	/* wait for chip to become ready */
14528c2ecf20Sopenharmony_ci	timeo = jiffies + msecs_to_jiffies(2);
14538c2ecf20Sopenharmony_ci	for (;;) {
14548c2ecf20Sopenharmony_ci		if (chip_ready(map, chip, adr, NULL))
14558c2ecf20Sopenharmony_ci			break;
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeo)) {
14588c2ecf20Sopenharmony_ci			pr_err("Waiting for chip to be ready timed out.\n");
14598c2ecf20Sopenharmony_ci			ret = -EIO;
14608c2ecf20Sopenharmony_ci			break;
14618c2ecf20Sopenharmony_ci		}
14628c2ecf20Sopenharmony_ci		UDELAY(map, chip, 0, 1);
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	/* exit protection commands */
14668c2ecf20Sopenharmony_ci	map_write(map, CMD(0x90), chip->start);
14678c2ecf20Sopenharmony_ci	map_write(map, CMD(0x00), chip->start);
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	chip->state = FL_READY;
14708c2ecf20Sopenharmony_ci	put_chip(map, chip, chip->start);
14718c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	return ret;
14748c2ecf20Sopenharmony_ci}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_cistatic int cfi_amdstd_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
14778c2ecf20Sopenharmony_ci			       size_t *retlen, u_char *buf,
14788c2ecf20Sopenharmony_ci			       otp_op_t action, int user_regs)
14798c2ecf20Sopenharmony_ci{
14808c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
14818c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
14828c2ecf20Sopenharmony_ci	int ofs_factor = cfi->interleave * cfi->device_type;
14838c2ecf20Sopenharmony_ci	unsigned long base;
14848c2ecf20Sopenharmony_ci	int chipnum;
14858c2ecf20Sopenharmony_ci	struct flchip *chip;
14868c2ecf20Sopenharmony_ci	uint8_t otp, lockreg;
14878c2ecf20Sopenharmony_ci	int ret;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	size_t user_size, factory_size, otpsize;
14908c2ecf20Sopenharmony_ci	loff_t user_offset, factory_offset, otpoffset;
14918c2ecf20Sopenharmony_ci	int user_locked = 0, otplocked;
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	*retlen = 0;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	for (chipnum = 0; chipnum < cfi->numchips; chipnum++) {
14968c2ecf20Sopenharmony_ci		chip = &cfi->chips[chipnum];
14978c2ecf20Sopenharmony_ci		factory_size = 0;
14988c2ecf20Sopenharmony_ci		user_size = 0;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci		/* Micron M29EW family */
15018c2ecf20Sopenharmony_ci		if (is_m29ew(cfi)) {
15028c2ecf20Sopenharmony_ci			base = chip->start;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci			/* check whether secsi area is factory locked
15058c2ecf20Sopenharmony_ci			   or user lockable */
15068c2ecf20Sopenharmony_ci			mutex_lock(&chip->mutex);
15078c2ecf20Sopenharmony_ci			ret = get_chip(map, chip, base, FL_CFI_QUERY);
15088c2ecf20Sopenharmony_ci			if (ret) {
15098c2ecf20Sopenharmony_ci				mutex_unlock(&chip->mutex);
15108c2ecf20Sopenharmony_ci				return ret;
15118c2ecf20Sopenharmony_ci			}
15128c2ecf20Sopenharmony_ci			cfi_qry_mode_on(base, map, cfi);
15138c2ecf20Sopenharmony_ci			otp = cfi_read_query(map, base + 0x3 * ofs_factor);
15148c2ecf20Sopenharmony_ci			cfi_qry_mode_off(base, map, cfi);
15158c2ecf20Sopenharmony_ci			put_chip(map, chip, base);
15168c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci			if (otp & 0x80) {
15198c2ecf20Sopenharmony_ci				/* factory locked */
15208c2ecf20Sopenharmony_ci				factory_offset = 0;
15218c2ecf20Sopenharmony_ci				factory_size = 0x100;
15228c2ecf20Sopenharmony_ci			} else {
15238c2ecf20Sopenharmony_ci				/* customer lockable */
15248c2ecf20Sopenharmony_ci				user_offset = 0;
15258c2ecf20Sopenharmony_ci				user_size = 0x100;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci				mutex_lock(&chip->mutex);
15288c2ecf20Sopenharmony_ci				ret = get_chip(map, chip, base, FL_LOCKING);
15298c2ecf20Sopenharmony_ci				if (ret) {
15308c2ecf20Sopenharmony_ci					mutex_unlock(&chip->mutex);
15318c2ecf20Sopenharmony_ci					return ret;
15328c2ecf20Sopenharmony_ci				}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci				/* Enter lock register command */
15358c2ecf20Sopenharmony_ci				cfi_send_gen_cmd(0xAA, cfi->addr_unlock1,
15368c2ecf20Sopenharmony_ci						 chip->start, map, cfi,
15378c2ecf20Sopenharmony_ci						 cfi->device_type, NULL);
15388c2ecf20Sopenharmony_ci				cfi_send_gen_cmd(0x55, cfi->addr_unlock2,
15398c2ecf20Sopenharmony_ci						 chip->start, map, cfi,
15408c2ecf20Sopenharmony_ci						 cfi->device_type, NULL);
15418c2ecf20Sopenharmony_ci				cfi_send_gen_cmd(0x40, cfi->addr_unlock1,
15428c2ecf20Sopenharmony_ci						 chip->start, map, cfi,
15438c2ecf20Sopenharmony_ci						 cfi->device_type, NULL);
15448c2ecf20Sopenharmony_ci				/* read lock register */
15458c2ecf20Sopenharmony_ci				lockreg = cfi_read_query(map, 0);
15468c2ecf20Sopenharmony_ci				/* exit protection commands */
15478c2ecf20Sopenharmony_ci				map_write(map, CMD(0x90), chip->start);
15488c2ecf20Sopenharmony_ci				map_write(map, CMD(0x00), chip->start);
15498c2ecf20Sopenharmony_ci				put_chip(map, chip, chip->start);
15508c2ecf20Sopenharmony_ci				mutex_unlock(&chip->mutex);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci				user_locked = ((lockreg & 0x01) == 0x00);
15538c2ecf20Sopenharmony_ci			}
15548c2ecf20Sopenharmony_ci		}
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci		otpsize = user_regs ? user_size : factory_size;
15578c2ecf20Sopenharmony_ci		if (!otpsize)
15588c2ecf20Sopenharmony_ci			continue;
15598c2ecf20Sopenharmony_ci		otpoffset = user_regs ? user_offset : factory_offset;
15608c2ecf20Sopenharmony_ci		otplocked = user_regs ? user_locked : 1;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci		if (!action) {
15638c2ecf20Sopenharmony_ci			/* return otpinfo */
15648c2ecf20Sopenharmony_ci			struct otp_info *otpinfo;
15658c2ecf20Sopenharmony_ci			len -= sizeof(*otpinfo);
15668c2ecf20Sopenharmony_ci			if (len <= 0)
15678c2ecf20Sopenharmony_ci				return -ENOSPC;
15688c2ecf20Sopenharmony_ci			otpinfo = (struct otp_info *)buf;
15698c2ecf20Sopenharmony_ci			otpinfo->start = from;
15708c2ecf20Sopenharmony_ci			otpinfo->length = otpsize;
15718c2ecf20Sopenharmony_ci			otpinfo->locked = otplocked;
15728c2ecf20Sopenharmony_ci			buf += sizeof(*otpinfo);
15738c2ecf20Sopenharmony_ci			*retlen += sizeof(*otpinfo);
15748c2ecf20Sopenharmony_ci			from += otpsize;
15758c2ecf20Sopenharmony_ci		} else if ((from < otpsize) && (len > 0)) {
15768c2ecf20Sopenharmony_ci			size_t size;
15778c2ecf20Sopenharmony_ci			size = (len < otpsize - from) ? len : otpsize - from;
15788c2ecf20Sopenharmony_ci			ret = action(map, chip, otpoffset + from, size, buf,
15798c2ecf20Sopenharmony_ci				     otpsize);
15808c2ecf20Sopenharmony_ci			if (ret < 0)
15818c2ecf20Sopenharmony_ci				return ret;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci			buf += size;
15848c2ecf20Sopenharmony_ci			len -= size;
15858c2ecf20Sopenharmony_ci			*retlen += size;
15868c2ecf20Sopenharmony_ci			from = 0;
15878c2ecf20Sopenharmony_ci		} else {
15888c2ecf20Sopenharmony_ci			from -= otpsize;
15898c2ecf20Sopenharmony_ci		}
15908c2ecf20Sopenharmony_ci	}
15918c2ecf20Sopenharmony_ci	return 0;
15928c2ecf20Sopenharmony_ci}
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_cistatic int cfi_amdstd_get_fact_prot_info(struct mtd_info *mtd, size_t len,
15958c2ecf20Sopenharmony_ci					 size_t *retlen, struct otp_info *buf)
15968c2ecf20Sopenharmony_ci{
15978c2ecf20Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
15988c2ecf20Sopenharmony_ci				   NULL, 0);
15998c2ecf20Sopenharmony_ci}
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_cistatic int cfi_amdstd_get_user_prot_info(struct mtd_info *mtd, size_t len,
16028c2ecf20Sopenharmony_ci					 size_t *retlen, struct otp_info *buf)
16038c2ecf20Sopenharmony_ci{
16048c2ecf20Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
16058c2ecf20Sopenharmony_ci				   NULL, 1);
16068c2ecf20Sopenharmony_ci}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_cistatic int cfi_amdstd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
16098c2ecf20Sopenharmony_ci					 size_t len, size_t *retlen,
16108c2ecf20Sopenharmony_ci					 u_char *buf)
16118c2ecf20Sopenharmony_ci{
16128c2ecf20Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, from, len, retlen,
16138c2ecf20Sopenharmony_ci				   buf, do_read_secsi_onechip, 0);
16148c2ecf20Sopenharmony_ci}
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_cistatic int cfi_amdstd_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
16178c2ecf20Sopenharmony_ci					 size_t len, size_t *retlen,
16188c2ecf20Sopenharmony_ci					 u_char *buf)
16198c2ecf20Sopenharmony_ci{
16208c2ecf20Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, from, len, retlen,
16218c2ecf20Sopenharmony_ci				   buf, do_read_secsi_onechip, 1);
16228c2ecf20Sopenharmony_ci}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
16258c2ecf20Sopenharmony_ci					  size_t len, size_t *retlen,
16268c2ecf20Sopenharmony_ci					  u_char *buf)
16278c2ecf20Sopenharmony_ci{
16288c2ecf20Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, from, len, retlen, buf,
16298c2ecf20Sopenharmony_ci				   do_otp_write, 1);
16308c2ecf20Sopenharmony_ci}
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_cistatic int cfi_amdstd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
16338c2ecf20Sopenharmony_ci					 size_t len)
16348c2ecf20Sopenharmony_ci{
16358c2ecf20Sopenharmony_ci	size_t retlen;
16368c2ecf20Sopenharmony_ci	return cfi_amdstd_otp_walk(mtd, from, len, &retlen, NULL,
16378c2ecf20Sopenharmony_ci				   do_otp_lock, 1);
16388c2ecf20Sopenharmony_ci}
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword_once(struct map_info *map,
16418c2ecf20Sopenharmony_ci					  struct flchip *chip,
16428c2ecf20Sopenharmony_ci					  unsigned long adr, map_word datum,
16438c2ecf20Sopenharmony_ci					  int mode, struct cfi_private *cfi)
16448c2ecf20Sopenharmony_ci{
16458c2ecf20Sopenharmony_ci	unsigned long timeo = jiffies + HZ;
16468c2ecf20Sopenharmony_ci	/*
16478c2ecf20Sopenharmony_ci	 * We use a 1ms + 1 jiffies generic timeout for writes (most devices
16488c2ecf20Sopenharmony_ci	 * have a max write time of a few hundreds usec). However, we should
16498c2ecf20Sopenharmony_ci	 * use the maximum timeout value given by the chip at probe time
16508c2ecf20Sopenharmony_ci	 * instead.  Unfortunately, struct flchip does have a field for
16518c2ecf20Sopenharmony_ci	 * maximum timeout, only for typical which can be far too short
16528c2ecf20Sopenharmony_ci	 * depending of the conditions.	 The ' + 1' is to avoid having a
16538c2ecf20Sopenharmony_ci	 * timeout of 0 jiffies if HZ is smaller than 1000.
16548c2ecf20Sopenharmony_ci	 */
16558c2ecf20Sopenharmony_ci	unsigned long uWriteTimeout = (HZ / 1000) + 1;
16568c2ecf20Sopenharmony_ci	int ret = 0;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
16598c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
16608c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
16618c2ecf20Sopenharmony_ci	map_write(map, datum, adr);
16628c2ecf20Sopenharmony_ci	chip->state = mode;
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	INVALIDATE_CACHE_UDELAY(map, chip,
16658c2ecf20Sopenharmony_ci				adr, map_bankwidth(map),
16668c2ecf20Sopenharmony_ci				chip->word_write_time);
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	/* See comment above for timeout value. */
16698c2ecf20Sopenharmony_ci	timeo = jiffies + uWriteTimeout;
16708c2ecf20Sopenharmony_ci	for (;;) {
16718c2ecf20Sopenharmony_ci		if (chip->state != mode) {
16728c2ecf20Sopenharmony_ci			/* Someone's suspended the write. Sleep */
16738c2ecf20Sopenharmony_ci			DECLARE_WAITQUEUE(wait, current);
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
16768c2ecf20Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
16778c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
16788c2ecf20Sopenharmony_ci			schedule();
16798c2ecf20Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
16808c2ecf20Sopenharmony_ci			timeo = jiffies + (HZ / 2); /* FIXME */
16818c2ecf20Sopenharmony_ci			mutex_lock(&chip->mutex);
16828c2ecf20Sopenharmony_ci			continue;
16838c2ecf20Sopenharmony_ci		}
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci		/*
16868c2ecf20Sopenharmony_ci		 * We check "time_after" and "!chip_good" before checking
16878c2ecf20Sopenharmony_ci		 * "chip_good" to avoid the failure due to scheduling.
16888c2ecf20Sopenharmony_ci		 */
16898c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeo) &&
16908c2ecf20Sopenharmony_ci		    !chip_good(map, chip, adr, &datum)) {
16918c2ecf20Sopenharmony_ci			xip_enable(map, chip, adr);
16928c2ecf20Sopenharmony_ci			printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
16938c2ecf20Sopenharmony_ci			xip_disable(map, chip, adr);
16948c2ecf20Sopenharmony_ci			ret = -EIO;
16958c2ecf20Sopenharmony_ci			break;
16968c2ecf20Sopenharmony_ci		}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci		if (chip_good(map, chip, adr, &datum)) {
16998c2ecf20Sopenharmony_ci			if (cfi_check_err_status(map, chip, adr))
17008c2ecf20Sopenharmony_ci				ret = -EIO;
17018c2ecf20Sopenharmony_ci			break;
17028c2ecf20Sopenharmony_ci		}
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci		/* Latency issues. Drop the lock, wait a while and retry */
17058c2ecf20Sopenharmony_ci		UDELAY(map, chip, adr, 1);
17068c2ecf20Sopenharmony_ci	}
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	return ret;
17098c2ecf20Sopenharmony_ci}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword_start(struct map_info *map,
17128c2ecf20Sopenharmony_ci					   struct flchip *chip,
17138c2ecf20Sopenharmony_ci					   unsigned long adr, int mode)
17148c2ecf20Sopenharmony_ci{
17158c2ecf20Sopenharmony_ci	int ret;
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	ret = get_chip(map, chip, adr, mode);
17208c2ecf20Sopenharmony_ci	if (ret) {
17218c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
17228c2ecf20Sopenharmony_ci		return ret;
17238c2ecf20Sopenharmony_ci	}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	if (mode == FL_OTP_WRITE)
17268c2ecf20Sopenharmony_ci		otp_enter(map, chip, adr, map_bankwidth(map));
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	return ret;
17298c2ecf20Sopenharmony_ci}
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_cistatic void __xipram do_write_oneword_done(struct map_info *map,
17328c2ecf20Sopenharmony_ci					   struct flchip *chip,
17338c2ecf20Sopenharmony_ci					   unsigned long adr, int mode)
17348c2ecf20Sopenharmony_ci{
17358c2ecf20Sopenharmony_ci	if (mode == FL_OTP_WRITE)
17368c2ecf20Sopenharmony_ci		otp_exit(map, chip, adr, map_bankwidth(map));
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	chip->state = FL_READY;
17398c2ecf20Sopenharmony_ci	DISABLE_VPP(map);
17408c2ecf20Sopenharmony_ci	put_chip(map, chip, adr);
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
17438c2ecf20Sopenharmony_ci}
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword_retry(struct map_info *map,
17468c2ecf20Sopenharmony_ci					   struct flchip *chip,
17478c2ecf20Sopenharmony_ci					   unsigned long adr, map_word datum,
17488c2ecf20Sopenharmony_ci					   int mode)
17498c2ecf20Sopenharmony_ci{
17508c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
17518c2ecf20Sopenharmony_ci	int ret = 0;
17528c2ecf20Sopenharmony_ci	map_word oldd;
17538c2ecf20Sopenharmony_ci	int retry_cnt = 0;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	/*
17568c2ecf20Sopenharmony_ci	 * Check for a NOP for the case when the datum to write is already
17578c2ecf20Sopenharmony_ci	 * present - it saves time and works around buggy chips that corrupt
17588c2ecf20Sopenharmony_ci	 * data at other locations when 0xff is written to a location that
17598c2ecf20Sopenharmony_ci	 * already contains 0xff.
17608c2ecf20Sopenharmony_ci	 */
17618c2ecf20Sopenharmony_ci	oldd = map_read(map, adr);
17628c2ecf20Sopenharmony_ci	if (map_word_equal(map, oldd, datum)) {
17638c2ecf20Sopenharmony_ci		pr_debug("MTD %s(): NOP\n", __func__);
17648c2ecf20Sopenharmony_ci		return ret;
17658c2ecf20Sopenharmony_ci	}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
17688c2ecf20Sopenharmony_ci	ENABLE_VPP(map);
17698c2ecf20Sopenharmony_ci	xip_disable(map, chip, adr);
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci retry:
17728c2ecf20Sopenharmony_ci	ret = do_write_oneword_once(map, chip, adr, datum, mode, cfi);
17738c2ecf20Sopenharmony_ci	if (ret) {
17748c2ecf20Sopenharmony_ci		/* reset on all failures. */
17758c2ecf20Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
17768c2ecf20Sopenharmony_ci		/* FIXME - should have reset delay before continuing */
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci		if (++retry_cnt <= MAX_RETRIES) {
17798c2ecf20Sopenharmony_ci			ret = 0;
17808c2ecf20Sopenharmony_ci			goto retry;
17818c2ecf20Sopenharmony_ci		}
17828c2ecf20Sopenharmony_ci	}
17838c2ecf20Sopenharmony_ci	xip_enable(map, chip, adr);
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	return ret;
17868c2ecf20Sopenharmony_ci}
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_cistatic int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
17898c2ecf20Sopenharmony_ci				     unsigned long adr, map_word datum,
17908c2ecf20Sopenharmony_ci				     int mode)
17918c2ecf20Sopenharmony_ci{
17928c2ecf20Sopenharmony_ci	int ret;
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	adr += chip->start;
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", __func__, adr,
17978c2ecf20Sopenharmony_ci		 datum.x[0]);
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	ret = do_write_oneword_start(map, chip, adr, mode);
18008c2ecf20Sopenharmony_ci	if (ret)
18018c2ecf20Sopenharmony_ci		return ret;
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci	ret = do_write_oneword_retry(map, chip, adr, datum, mode);
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci	do_write_oneword_done(map, chip, adr, mode);
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	return ret;
18088c2ecf20Sopenharmony_ci}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
18128c2ecf20Sopenharmony_ci				  size_t *retlen, const u_char *buf)
18138c2ecf20Sopenharmony_ci{
18148c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
18158c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
18168c2ecf20Sopenharmony_ci	int ret;
18178c2ecf20Sopenharmony_ci	int chipnum;
18188c2ecf20Sopenharmony_ci	unsigned long ofs, chipstart;
18198c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	chipnum = to >> cfi->chipshift;
18228c2ecf20Sopenharmony_ci	ofs = to  - (chipnum << cfi->chipshift);
18238c2ecf20Sopenharmony_ci	chipstart = cfi->chips[chipnum].start;
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	/* If it's not bus-aligned, do the first byte write */
18268c2ecf20Sopenharmony_ci	if (ofs & (map_bankwidth(map)-1)) {
18278c2ecf20Sopenharmony_ci		unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1);
18288c2ecf20Sopenharmony_ci		int i = ofs - bus_ofs;
18298c2ecf20Sopenharmony_ci		int n = 0;
18308c2ecf20Sopenharmony_ci		map_word tmp_buf;
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci retry:
18338c2ecf20Sopenharmony_ci		mutex_lock(&cfi->chips[chipnum].mutex);
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci		if (cfi->chips[chipnum].state != FL_READY) {
18368c2ecf20Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
18378c2ecf20Sopenharmony_ci			add_wait_queue(&cfi->chips[chipnum].wq, &wait);
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci			mutex_unlock(&cfi->chips[chipnum].mutex);
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci			schedule();
18428c2ecf20Sopenharmony_ci			remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
18438c2ecf20Sopenharmony_ci			goto retry;
18448c2ecf20Sopenharmony_ci		}
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci		/* Load 'tmp_buf' with old contents of flash */
18478c2ecf20Sopenharmony_ci		tmp_buf = map_read(map, bus_ofs+chipstart);
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci		mutex_unlock(&cfi->chips[chipnum].mutex);
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci		/* Number of bytes to copy from buffer */
18528c2ecf20Sopenharmony_ci		n = min_t(int, len, map_bankwidth(map)-i);
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci		tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci		ret = do_write_oneword(map, &cfi->chips[chipnum],
18578c2ecf20Sopenharmony_ci				       bus_ofs, tmp_buf, FL_WRITING);
18588c2ecf20Sopenharmony_ci		if (ret)
18598c2ecf20Sopenharmony_ci			return ret;
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci		ofs += n;
18628c2ecf20Sopenharmony_ci		buf += n;
18638c2ecf20Sopenharmony_ci		(*retlen) += n;
18648c2ecf20Sopenharmony_ci		len -= n;
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci		if (ofs >> cfi->chipshift) {
18678c2ecf20Sopenharmony_ci			chipnum ++;
18688c2ecf20Sopenharmony_ci			ofs = 0;
18698c2ecf20Sopenharmony_ci			if (chipnum == cfi->numchips)
18708c2ecf20Sopenharmony_ci				return 0;
18718c2ecf20Sopenharmony_ci		}
18728c2ecf20Sopenharmony_ci	}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	/* We are now aligned, write as much as possible */
18758c2ecf20Sopenharmony_ci	while(len >= map_bankwidth(map)) {
18768c2ecf20Sopenharmony_ci		map_word datum;
18778c2ecf20Sopenharmony_ci
18788c2ecf20Sopenharmony_ci		datum = map_word_load(map, buf);
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci		ret = do_write_oneword(map, &cfi->chips[chipnum],
18818c2ecf20Sopenharmony_ci				       ofs, datum, FL_WRITING);
18828c2ecf20Sopenharmony_ci		if (ret)
18838c2ecf20Sopenharmony_ci			return ret;
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci		ofs += map_bankwidth(map);
18868c2ecf20Sopenharmony_ci		buf += map_bankwidth(map);
18878c2ecf20Sopenharmony_ci		(*retlen) += map_bankwidth(map);
18888c2ecf20Sopenharmony_ci		len -= map_bankwidth(map);
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci		if (ofs >> cfi->chipshift) {
18918c2ecf20Sopenharmony_ci			chipnum ++;
18928c2ecf20Sopenharmony_ci			ofs = 0;
18938c2ecf20Sopenharmony_ci			if (chipnum == cfi->numchips)
18948c2ecf20Sopenharmony_ci				return 0;
18958c2ecf20Sopenharmony_ci			chipstart = cfi->chips[chipnum].start;
18968c2ecf20Sopenharmony_ci		}
18978c2ecf20Sopenharmony_ci	}
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	/* Write the trailing bytes if any */
19008c2ecf20Sopenharmony_ci	if (len & (map_bankwidth(map)-1)) {
19018c2ecf20Sopenharmony_ci		map_word tmp_buf;
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci retry1:
19048c2ecf20Sopenharmony_ci		mutex_lock(&cfi->chips[chipnum].mutex);
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci		if (cfi->chips[chipnum].state != FL_READY) {
19078c2ecf20Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
19088c2ecf20Sopenharmony_ci			add_wait_queue(&cfi->chips[chipnum].wq, &wait);
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci			mutex_unlock(&cfi->chips[chipnum].mutex);
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci			schedule();
19138c2ecf20Sopenharmony_ci			remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
19148c2ecf20Sopenharmony_ci			goto retry1;
19158c2ecf20Sopenharmony_ci		}
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci		tmp_buf = map_read(map, ofs + chipstart);
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci		mutex_unlock(&cfi->chips[chipnum].mutex);
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci		tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci		ret = do_write_oneword(map, &cfi->chips[chipnum],
19248c2ecf20Sopenharmony_ci				       ofs, tmp_buf, FL_WRITING);
19258c2ecf20Sopenharmony_ci		if (ret)
19268c2ecf20Sopenharmony_ci			return ret;
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci		(*retlen) += len;
19298c2ecf20Sopenharmony_ci	}
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci	return 0;
19328c2ecf20Sopenharmony_ci}
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci#if !FORCE_WORD_WRITE
19358c2ecf20Sopenharmony_cistatic int __xipram do_write_buffer_wait(struct map_info *map,
19368c2ecf20Sopenharmony_ci					 struct flchip *chip, unsigned long adr,
19378c2ecf20Sopenharmony_ci					 map_word datum)
19388c2ecf20Sopenharmony_ci{
19398c2ecf20Sopenharmony_ci	unsigned long timeo;
19408c2ecf20Sopenharmony_ci	unsigned long u_write_timeout;
19418c2ecf20Sopenharmony_ci	int ret = 0;
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	/*
19448c2ecf20Sopenharmony_ci	 * Timeout is calculated according to CFI data, if available.
19458c2ecf20Sopenharmony_ci	 * See more comments in cfi_cmdset_0002().
19468c2ecf20Sopenharmony_ci	 */
19478c2ecf20Sopenharmony_ci	u_write_timeout = usecs_to_jiffies(chip->buffer_write_time_max);
19488c2ecf20Sopenharmony_ci	timeo = jiffies + u_write_timeout;
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci	for (;;) {
19518c2ecf20Sopenharmony_ci		if (chip->state != FL_WRITING) {
19528c2ecf20Sopenharmony_ci			/* Someone's suspended the write. Sleep */
19538c2ecf20Sopenharmony_ci			DECLARE_WAITQUEUE(wait, current);
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
19568c2ecf20Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
19578c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
19588c2ecf20Sopenharmony_ci			schedule();
19598c2ecf20Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
19608c2ecf20Sopenharmony_ci			timeo = jiffies + (HZ / 2); /* FIXME */
19618c2ecf20Sopenharmony_ci			mutex_lock(&chip->mutex);
19628c2ecf20Sopenharmony_ci			continue;
19638c2ecf20Sopenharmony_ci		}
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci		/*
19668c2ecf20Sopenharmony_ci		 * We check "time_after" and "!chip_good" before checking
19678c2ecf20Sopenharmony_ci		 * "chip_good" to avoid the failure due to scheduling.
19688c2ecf20Sopenharmony_ci		 */
19698c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeo) &&
19708c2ecf20Sopenharmony_ci		    !chip_good(map, chip, adr, &datum)) {
19718c2ecf20Sopenharmony_ci			pr_err("MTD %s(): software timeout, address:0x%.8lx.\n",
19728c2ecf20Sopenharmony_ci			       __func__, adr);
19738c2ecf20Sopenharmony_ci			ret = -EIO;
19748c2ecf20Sopenharmony_ci			break;
19758c2ecf20Sopenharmony_ci		}
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci		if (chip_good(map, chip, adr, &datum)) {
19788c2ecf20Sopenharmony_ci			if (cfi_check_err_status(map, chip, adr))
19798c2ecf20Sopenharmony_ci				ret = -EIO;
19808c2ecf20Sopenharmony_ci			break;
19818c2ecf20Sopenharmony_ci		}
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci		/* Latency issues. Drop the lock, wait a while and retry */
19848c2ecf20Sopenharmony_ci		UDELAY(map, chip, adr, 1);
19858c2ecf20Sopenharmony_ci	}
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	return ret;
19888c2ecf20Sopenharmony_ci}
19898c2ecf20Sopenharmony_ci
19908c2ecf20Sopenharmony_cistatic void __xipram do_write_buffer_reset(struct map_info *map,
19918c2ecf20Sopenharmony_ci					   struct flchip *chip,
19928c2ecf20Sopenharmony_ci					   struct cfi_private *cfi)
19938c2ecf20Sopenharmony_ci{
19948c2ecf20Sopenharmony_ci	/*
19958c2ecf20Sopenharmony_ci	 * Recovery from write-buffer programming failures requires
19968c2ecf20Sopenharmony_ci	 * the write-to-buffer-reset sequence.  Since the last part
19978c2ecf20Sopenharmony_ci	 * of the sequence also works as a normal reset, we can run
19988c2ecf20Sopenharmony_ci	 * the same commands regardless of why we are here.
19998c2ecf20Sopenharmony_ci	 * See e.g.
20008c2ecf20Sopenharmony_ci	 * http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf
20018c2ecf20Sopenharmony_ci	 */
20028c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
20038c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
20048c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
20058c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
20068c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi,
20078c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	/* FIXME - should have reset delay before continuing */
20108c2ecf20Sopenharmony_ci}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci/*
20138c2ecf20Sopenharmony_ci * FIXME: interleaved mode not tested, and probably not supported!
20148c2ecf20Sopenharmony_ci */
20158c2ecf20Sopenharmony_cistatic int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
20168c2ecf20Sopenharmony_ci				    unsigned long adr, const u_char *buf,
20178c2ecf20Sopenharmony_ci				    int len)
20188c2ecf20Sopenharmony_ci{
20198c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
20208c2ecf20Sopenharmony_ci	int ret;
20218c2ecf20Sopenharmony_ci	unsigned long cmd_adr;
20228c2ecf20Sopenharmony_ci	int z, words;
20238c2ecf20Sopenharmony_ci	map_word datum;
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_ci	adr += chip->start;
20268c2ecf20Sopenharmony_ci	cmd_adr = adr;
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
20298c2ecf20Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_WRITING);
20308c2ecf20Sopenharmony_ci	if (ret) {
20318c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
20328c2ecf20Sopenharmony_ci		return ret;
20338c2ecf20Sopenharmony_ci	}
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	datum = map_word_load(map, buf);
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci	pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
20388c2ecf20Sopenharmony_ci		 __func__, adr, datum.x[0]);
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, len);
20418c2ecf20Sopenharmony_ci	ENABLE_VPP(map);
20428c2ecf20Sopenharmony_ci	xip_disable(map, chip, cmd_adr);
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
20458c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	/* Write Buffer Load */
20488c2ecf20Sopenharmony_ci	map_write(map, CMD(0x25), cmd_adr);
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci	chip->state = FL_WRITING_TO_BUFFER;
20518c2ecf20Sopenharmony_ci
20528c2ecf20Sopenharmony_ci	/* Write length of data to come */
20538c2ecf20Sopenharmony_ci	words = len / map_bankwidth(map);
20548c2ecf20Sopenharmony_ci	map_write(map, CMD(words - 1), cmd_adr);
20558c2ecf20Sopenharmony_ci	/* Write data */
20568c2ecf20Sopenharmony_ci	z = 0;
20578c2ecf20Sopenharmony_ci	while(z < words * map_bankwidth(map)) {
20588c2ecf20Sopenharmony_ci		datum = map_word_load(map, buf);
20598c2ecf20Sopenharmony_ci		map_write(map, datum, adr + z);
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci		z += map_bankwidth(map);
20628c2ecf20Sopenharmony_ci		buf += map_bankwidth(map);
20638c2ecf20Sopenharmony_ci	}
20648c2ecf20Sopenharmony_ci	z -= map_bankwidth(map);
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci	adr += z;
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_ci	/* Write Buffer Program Confirm: GO GO GO */
20698c2ecf20Sopenharmony_ci	map_write(map, CMD(0x29), cmd_adr);
20708c2ecf20Sopenharmony_ci	chip->state = FL_WRITING;
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	INVALIDATE_CACHE_UDELAY(map, chip,
20738c2ecf20Sopenharmony_ci				adr, map_bankwidth(map),
20748c2ecf20Sopenharmony_ci				chip->word_write_time);
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_ci	ret = do_write_buffer_wait(map, chip, adr, datum);
20778c2ecf20Sopenharmony_ci	if (ret)
20788c2ecf20Sopenharmony_ci		do_write_buffer_reset(map, chip, cfi);
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	xip_enable(map, chip, adr);
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	chip->state = FL_READY;
20838c2ecf20Sopenharmony_ci	DISABLE_VPP(map);
20848c2ecf20Sopenharmony_ci	put_chip(map, chip, adr);
20858c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci	return ret;
20888c2ecf20Sopenharmony_ci}
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_cistatic int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
20928c2ecf20Sopenharmony_ci				    size_t *retlen, const u_char *buf)
20938c2ecf20Sopenharmony_ci{
20948c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
20958c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
20968c2ecf20Sopenharmony_ci	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
20978c2ecf20Sopenharmony_ci	int ret;
20988c2ecf20Sopenharmony_ci	int chipnum;
20998c2ecf20Sopenharmony_ci	unsigned long ofs;
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	chipnum = to >> cfi->chipshift;
21028c2ecf20Sopenharmony_ci	ofs = to  - (chipnum << cfi->chipshift);
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci	/* If it's not bus-aligned, do the first word write */
21058c2ecf20Sopenharmony_ci	if (ofs & (map_bankwidth(map)-1)) {
21068c2ecf20Sopenharmony_ci		size_t local_len = (-ofs)&(map_bankwidth(map)-1);
21078c2ecf20Sopenharmony_ci		if (local_len > len)
21088c2ecf20Sopenharmony_ci			local_len = len;
21098c2ecf20Sopenharmony_ci		ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
21108c2ecf20Sopenharmony_ci					     local_len, retlen, buf);
21118c2ecf20Sopenharmony_ci		if (ret)
21128c2ecf20Sopenharmony_ci			return ret;
21138c2ecf20Sopenharmony_ci		ofs += local_len;
21148c2ecf20Sopenharmony_ci		buf += local_len;
21158c2ecf20Sopenharmony_ci		len -= local_len;
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci		if (ofs >> cfi->chipshift) {
21188c2ecf20Sopenharmony_ci			chipnum ++;
21198c2ecf20Sopenharmony_ci			ofs = 0;
21208c2ecf20Sopenharmony_ci			if (chipnum == cfi->numchips)
21218c2ecf20Sopenharmony_ci				return 0;
21228c2ecf20Sopenharmony_ci		}
21238c2ecf20Sopenharmony_ci	}
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	/* Write buffer is worth it only if more than one word to write... */
21268c2ecf20Sopenharmony_ci	while (len >= map_bankwidth(map) * 2) {
21278c2ecf20Sopenharmony_ci		/* We must not cross write block boundaries */
21288c2ecf20Sopenharmony_ci		int size = wbufsize - (ofs & (wbufsize-1));
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_ci		if (size > len)
21318c2ecf20Sopenharmony_ci			size = len;
21328c2ecf20Sopenharmony_ci		if (size % map_bankwidth(map))
21338c2ecf20Sopenharmony_ci			size -= size % map_bankwidth(map);
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci		ret = do_write_buffer(map, &cfi->chips[chipnum],
21368c2ecf20Sopenharmony_ci				      ofs, buf, size);
21378c2ecf20Sopenharmony_ci		if (ret)
21388c2ecf20Sopenharmony_ci			return ret;
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci		ofs += size;
21418c2ecf20Sopenharmony_ci		buf += size;
21428c2ecf20Sopenharmony_ci		(*retlen) += size;
21438c2ecf20Sopenharmony_ci		len -= size;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci		if (ofs >> cfi->chipshift) {
21468c2ecf20Sopenharmony_ci			chipnum ++;
21478c2ecf20Sopenharmony_ci			ofs = 0;
21488c2ecf20Sopenharmony_ci			if (chipnum == cfi->numchips)
21498c2ecf20Sopenharmony_ci				return 0;
21508c2ecf20Sopenharmony_ci		}
21518c2ecf20Sopenharmony_ci	}
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	if (len) {
21548c2ecf20Sopenharmony_ci		size_t retlen_dregs = 0;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci		ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
21578c2ecf20Sopenharmony_ci					     len, &retlen_dregs, buf);
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci		*retlen += retlen_dregs;
21608c2ecf20Sopenharmony_ci		return ret;
21618c2ecf20Sopenharmony_ci	}
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	return 0;
21648c2ecf20Sopenharmony_ci}
21658c2ecf20Sopenharmony_ci#endif /* !FORCE_WORD_WRITE */
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci/*
21688c2ecf20Sopenharmony_ci * Wait for the flash chip to become ready to write data
21698c2ecf20Sopenharmony_ci *
21708c2ecf20Sopenharmony_ci * This is only called during the panic_write() path. When panic_write()
21718c2ecf20Sopenharmony_ci * is called, the kernel is in the process of a panic, and will soon be
21728c2ecf20Sopenharmony_ci * dead. Therefore we don't take any locks, and attempt to get access
21738c2ecf20Sopenharmony_ci * to the chip as soon as possible.
21748c2ecf20Sopenharmony_ci */
21758c2ecf20Sopenharmony_cistatic int cfi_amdstd_panic_wait(struct map_info *map, struct flchip *chip,
21768c2ecf20Sopenharmony_ci				 unsigned long adr)
21778c2ecf20Sopenharmony_ci{
21788c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
21798c2ecf20Sopenharmony_ci	int retries = 10;
21808c2ecf20Sopenharmony_ci	int i;
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci	/*
21838c2ecf20Sopenharmony_ci	 * If the driver thinks the chip is idle, and no toggle bits
21848c2ecf20Sopenharmony_ci	 * are changing, then the chip is actually idle for sure.
21858c2ecf20Sopenharmony_ci	 */
21868c2ecf20Sopenharmony_ci	if (chip->state == FL_READY && chip_ready(map, chip, adr, NULL))
21878c2ecf20Sopenharmony_ci		return 0;
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	/*
21908c2ecf20Sopenharmony_ci	 * Try several times to reset the chip and then wait for it
21918c2ecf20Sopenharmony_ci	 * to become idle. The upper limit of a few milliseconds of
21928c2ecf20Sopenharmony_ci	 * delay isn't a big problem: the kernel is dying anyway. It
21938c2ecf20Sopenharmony_ci	 * is more important to save the messages.
21948c2ecf20Sopenharmony_ci	 */
21958c2ecf20Sopenharmony_ci	while (retries > 0) {
21968c2ecf20Sopenharmony_ci		const unsigned long timeo = (HZ / 1000) + 1;
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci		/* send the reset command */
21998c2ecf20Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci		/* wait for the chip to become ready */
22028c2ecf20Sopenharmony_ci		for (i = 0; i < jiffies_to_usecs(timeo); i++) {
22038c2ecf20Sopenharmony_ci			if (chip_ready(map, chip, adr, NULL))
22048c2ecf20Sopenharmony_ci				return 0;
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci			udelay(1);
22078c2ecf20Sopenharmony_ci		}
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci		retries--;
22108c2ecf20Sopenharmony_ci	}
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	/* the chip never became ready */
22138c2ecf20Sopenharmony_ci	return -EBUSY;
22148c2ecf20Sopenharmony_ci}
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci/*
22178c2ecf20Sopenharmony_ci * Write out one word of data to a single flash chip during a kernel panic
22188c2ecf20Sopenharmony_ci *
22198c2ecf20Sopenharmony_ci * This is only called during the panic_write() path. When panic_write()
22208c2ecf20Sopenharmony_ci * is called, the kernel is in the process of a panic, and will soon be
22218c2ecf20Sopenharmony_ci * dead. Therefore we don't take any locks, and attempt to get access
22228c2ecf20Sopenharmony_ci * to the chip as soon as possible.
22238c2ecf20Sopenharmony_ci *
22248c2ecf20Sopenharmony_ci * The implementation of this routine is intentionally similar to
22258c2ecf20Sopenharmony_ci * do_write_oneword(), in order to ease code maintenance.
22268c2ecf20Sopenharmony_ci */
22278c2ecf20Sopenharmony_cistatic int do_panic_write_oneword(struct map_info *map, struct flchip *chip,
22288c2ecf20Sopenharmony_ci				  unsigned long adr, map_word datum)
22298c2ecf20Sopenharmony_ci{
22308c2ecf20Sopenharmony_ci	const unsigned long uWriteTimeout = (HZ / 1000) + 1;
22318c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
22328c2ecf20Sopenharmony_ci	int retry_cnt = 0;
22338c2ecf20Sopenharmony_ci	map_word oldd;
22348c2ecf20Sopenharmony_ci	int ret;
22358c2ecf20Sopenharmony_ci	int i;
22368c2ecf20Sopenharmony_ci
22378c2ecf20Sopenharmony_ci	adr += chip->start;
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci	ret = cfi_amdstd_panic_wait(map, chip, adr);
22408c2ecf20Sopenharmony_ci	if (ret)
22418c2ecf20Sopenharmony_ci		return ret;
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	pr_debug("MTD %s(): PANIC WRITE 0x%.8lx(0x%.8lx)\n",
22448c2ecf20Sopenharmony_ci			__func__, adr, datum.x[0]);
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci	/*
22478c2ecf20Sopenharmony_ci	 * Check for a NOP for the case when the datum to write is already
22488c2ecf20Sopenharmony_ci	 * present - it saves time and works around buggy chips that corrupt
22498c2ecf20Sopenharmony_ci	 * data at other locations when 0xff is written to a location that
22508c2ecf20Sopenharmony_ci	 * already contains 0xff.
22518c2ecf20Sopenharmony_ci	 */
22528c2ecf20Sopenharmony_ci	oldd = map_read(map, adr);
22538c2ecf20Sopenharmony_ci	if (map_word_equal(map, oldd, datum)) {
22548c2ecf20Sopenharmony_ci		pr_debug("MTD %s(): NOP\n", __func__);
22558c2ecf20Sopenharmony_ci		goto op_done;
22568c2ecf20Sopenharmony_ci	}
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	ENABLE_VPP(map);
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ciretry:
22618c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
22628c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
22638c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
22648c2ecf20Sopenharmony_ci	map_write(map, datum, adr);
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	for (i = 0; i < jiffies_to_usecs(uWriteTimeout); i++) {
22678c2ecf20Sopenharmony_ci		if (chip_ready(map, chip, adr, NULL))
22688c2ecf20Sopenharmony_ci			break;
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_ci		udelay(1);
22718c2ecf20Sopenharmony_ci	}
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci	if (!chip_ready(map, chip, adr, &datum) ||
22748c2ecf20Sopenharmony_ci	    cfi_check_err_status(map, chip, adr)) {
22758c2ecf20Sopenharmony_ci		/* reset on all failures. */
22768c2ecf20Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
22778c2ecf20Sopenharmony_ci		/* FIXME - should have reset delay before continuing */
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci		if (++retry_cnt <= MAX_RETRIES)
22808c2ecf20Sopenharmony_ci			goto retry;
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci		ret = -EIO;
22838c2ecf20Sopenharmony_ci	}
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ciop_done:
22868c2ecf20Sopenharmony_ci	DISABLE_VPP(map);
22878c2ecf20Sopenharmony_ci	return ret;
22888c2ecf20Sopenharmony_ci}
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci/*
22918c2ecf20Sopenharmony_ci * Write out some data during a kernel panic
22928c2ecf20Sopenharmony_ci *
22938c2ecf20Sopenharmony_ci * This is used by the mtdoops driver to save the dying messages from a
22948c2ecf20Sopenharmony_ci * kernel which has panic'd.
22958c2ecf20Sopenharmony_ci *
22968c2ecf20Sopenharmony_ci * This routine ignores all of the locking used throughout the rest of the
22978c2ecf20Sopenharmony_ci * driver, in order to ensure that the data gets written out no matter what
22988c2ecf20Sopenharmony_ci * state this driver (and the flash chip itself) was in when the kernel crashed.
22998c2ecf20Sopenharmony_ci *
23008c2ecf20Sopenharmony_ci * The implementation of this routine is intentionally similar to
23018c2ecf20Sopenharmony_ci * cfi_amdstd_write_words(), in order to ease code maintenance.
23028c2ecf20Sopenharmony_ci */
23038c2ecf20Sopenharmony_cistatic int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
23048c2ecf20Sopenharmony_ci				  size_t *retlen, const u_char *buf)
23058c2ecf20Sopenharmony_ci{
23068c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
23078c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
23088c2ecf20Sopenharmony_ci	unsigned long ofs, chipstart;
23098c2ecf20Sopenharmony_ci	int ret;
23108c2ecf20Sopenharmony_ci	int chipnum;
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	chipnum = to >> cfi->chipshift;
23138c2ecf20Sopenharmony_ci	ofs = to - (chipnum << cfi->chipshift);
23148c2ecf20Sopenharmony_ci	chipstart = cfi->chips[chipnum].start;
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_ci	/* If it's not bus aligned, do the first byte write */
23178c2ecf20Sopenharmony_ci	if (ofs & (map_bankwidth(map) - 1)) {
23188c2ecf20Sopenharmony_ci		unsigned long bus_ofs = ofs & ~(map_bankwidth(map) - 1);
23198c2ecf20Sopenharmony_ci		int i = ofs - bus_ofs;
23208c2ecf20Sopenharmony_ci		int n = 0;
23218c2ecf20Sopenharmony_ci		map_word tmp_buf;
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci		ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], bus_ofs);
23248c2ecf20Sopenharmony_ci		if (ret)
23258c2ecf20Sopenharmony_ci			return ret;
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci		/* Load 'tmp_buf' with old contents of flash */
23288c2ecf20Sopenharmony_ci		tmp_buf = map_read(map, bus_ofs + chipstart);
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci		/* Number of bytes to copy from buffer */
23318c2ecf20Sopenharmony_ci		n = min_t(int, len, map_bankwidth(map) - i);
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci		tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_ci		ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
23368c2ecf20Sopenharmony_ci					     bus_ofs, tmp_buf);
23378c2ecf20Sopenharmony_ci		if (ret)
23388c2ecf20Sopenharmony_ci			return ret;
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci		ofs += n;
23418c2ecf20Sopenharmony_ci		buf += n;
23428c2ecf20Sopenharmony_ci		(*retlen) += n;
23438c2ecf20Sopenharmony_ci		len -= n;
23448c2ecf20Sopenharmony_ci
23458c2ecf20Sopenharmony_ci		if (ofs >> cfi->chipshift) {
23468c2ecf20Sopenharmony_ci			chipnum++;
23478c2ecf20Sopenharmony_ci			ofs = 0;
23488c2ecf20Sopenharmony_ci			if (chipnum == cfi->numchips)
23498c2ecf20Sopenharmony_ci				return 0;
23508c2ecf20Sopenharmony_ci		}
23518c2ecf20Sopenharmony_ci	}
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci	/* We are now aligned, write as much as possible */
23548c2ecf20Sopenharmony_ci	while (len >= map_bankwidth(map)) {
23558c2ecf20Sopenharmony_ci		map_word datum;
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci		datum = map_word_load(map, buf);
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci		ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
23608c2ecf20Sopenharmony_ci					     ofs, datum);
23618c2ecf20Sopenharmony_ci		if (ret)
23628c2ecf20Sopenharmony_ci			return ret;
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci		ofs += map_bankwidth(map);
23658c2ecf20Sopenharmony_ci		buf += map_bankwidth(map);
23668c2ecf20Sopenharmony_ci		(*retlen) += map_bankwidth(map);
23678c2ecf20Sopenharmony_ci		len -= map_bankwidth(map);
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ci		if (ofs >> cfi->chipshift) {
23708c2ecf20Sopenharmony_ci			chipnum++;
23718c2ecf20Sopenharmony_ci			ofs = 0;
23728c2ecf20Sopenharmony_ci			if (chipnum == cfi->numchips)
23738c2ecf20Sopenharmony_ci				return 0;
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_ci			chipstart = cfi->chips[chipnum].start;
23768c2ecf20Sopenharmony_ci		}
23778c2ecf20Sopenharmony_ci	}
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci	/* Write the trailing bytes if any */
23808c2ecf20Sopenharmony_ci	if (len & (map_bankwidth(map) - 1)) {
23818c2ecf20Sopenharmony_ci		map_word tmp_buf;
23828c2ecf20Sopenharmony_ci
23838c2ecf20Sopenharmony_ci		ret = cfi_amdstd_panic_wait(map, &cfi->chips[chipnum], ofs);
23848c2ecf20Sopenharmony_ci		if (ret)
23858c2ecf20Sopenharmony_ci			return ret;
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_ci		tmp_buf = map_read(map, ofs + chipstart);
23888c2ecf20Sopenharmony_ci
23898c2ecf20Sopenharmony_ci		tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci		ret = do_panic_write_oneword(map, &cfi->chips[chipnum],
23928c2ecf20Sopenharmony_ci					     ofs, tmp_buf);
23938c2ecf20Sopenharmony_ci		if (ret)
23948c2ecf20Sopenharmony_ci			return ret;
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci		(*retlen) += len;
23978c2ecf20Sopenharmony_ci	}
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_ci	return 0;
24008c2ecf20Sopenharmony_ci}
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci
24038c2ecf20Sopenharmony_ci/*
24048c2ecf20Sopenharmony_ci * Handle devices with one erase region, that only implement
24058c2ecf20Sopenharmony_ci * the chip erase command.
24068c2ecf20Sopenharmony_ci */
24078c2ecf20Sopenharmony_cistatic int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
24088c2ecf20Sopenharmony_ci{
24098c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
24108c2ecf20Sopenharmony_ci	unsigned long timeo = jiffies + HZ;
24118c2ecf20Sopenharmony_ci	unsigned long int adr;
24128c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
24138c2ecf20Sopenharmony_ci	int ret;
24148c2ecf20Sopenharmony_ci	int retry_cnt = 0;
24158c2ecf20Sopenharmony_ci	map_word datum = map_word_ff(map);
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	adr = cfi->addr_unlock1;
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
24208c2ecf20Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_ERASING);
24218c2ecf20Sopenharmony_ci	if (ret) {
24228c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
24238c2ecf20Sopenharmony_ci		return ret;
24248c2ecf20Sopenharmony_ci	}
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci	pr_debug("MTD %s(): ERASE 0x%.8lx\n",
24278c2ecf20Sopenharmony_ci	       __func__, chip->start);
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, map->size);
24308c2ecf20Sopenharmony_ci	ENABLE_VPP(map);
24318c2ecf20Sopenharmony_ci	xip_disable(map, chip, adr);
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci retry:
24348c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
24358c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
24368c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
24378c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
24388c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
24398c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci	chip->state = FL_ERASING;
24428c2ecf20Sopenharmony_ci	chip->erase_suspended = 0;
24438c2ecf20Sopenharmony_ci	chip->in_progress_block_addr = adr;
24448c2ecf20Sopenharmony_ci	chip->in_progress_block_mask = ~(map->size - 1);
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_ci	INVALIDATE_CACHE_UDELAY(map, chip,
24478c2ecf20Sopenharmony_ci				adr, map->size,
24488c2ecf20Sopenharmony_ci				chip->erase_time*500);
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci	timeo = jiffies + (HZ*20);
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	for (;;) {
24538c2ecf20Sopenharmony_ci		if (chip->state != FL_ERASING) {
24548c2ecf20Sopenharmony_ci			/* Someone's suspended the erase. Sleep */
24558c2ecf20Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
24568c2ecf20Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
24578c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
24588c2ecf20Sopenharmony_ci			schedule();
24598c2ecf20Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
24608c2ecf20Sopenharmony_ci			mutex_lock(&chip->mutex);
24618c2ecf20Sopenharmony_ci			continue;
24628c2ecf20Sopenharmony_ci		}
24638c2ecf20Sopenharmony_ci		if (chip->erase_suspended) {
24648c2ecf20Sopenharmony_ci			/* This erase was suspended and resumed.
24658c2ecf20Sopenharmony_ci			   Adjust the timeout */
24668c2ecf20Sopenharmony_ci			timeo = jiffies + (HZ*20); /* FIXME */
24678c2ecf20Sopenharmony_ci			chip->erase_suspended = 0;
24688c2ecf20Sopenharmony_ci		}
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_ci		if (chip_ready(map, chip, adr, &datum)) {
24718c2ecf20Sopenharmony_ci			if (cfi_check_err_status(map, chip, adr))
24728c2ecf20Sopenharmony_ci				ret = -EIO;
24738c2ecf20Sopenharmony_ci			break;
24748c2ecf20Sopenharmony_ci		}
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeo)) {
24778c2ecf20Sopenharmony_ci			printk(KERN_WARNING "MTD %s(): software timeout\n",
24788c2ecf20Sopenharmony_ci			       __func__);
24798c2ecf20Sopenharmony_ci			ret = -EIO;
24808c2ecf20Sopenharmony_ci			break;
24818c2ecf20Sopenharmony_ci		}
24828c2ecf20Sopenharmony_ci
24838c2ecf20Sopenharmony_ci		/* Latency issues. Drop the lock, wait a while and retry */
24848c2ecf20Sopenharmony_ci		UDELAY(map, chip, adr, 1000000/HZ);
24858c2ecf20Sopenharmony_ci	}
24868c2ecf20Sopenharmony_ci	/* Did we succeed? */
24878c2ecf20Sopenharmony_ci	if (ret) {
24888c2ecf20Sopenharmony_ci		/* reset on all failures. */
24898c2ecf20Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
24908c2ecf20Sopenharmony_ci		/* FIXME - should have reset delay before continuing */
24918c2ecf20Sopenharmony_ci
24928c2ecf20Sopenharmony_ci		if (++retry_cnt <= MAX_RETRIES) {
24938c2ecf20Sopenharmony_ci			ret = 0;
24948c2ecf20Sopenharmony_ci			goto retry;
24958c2ecf20Sopenharmony_ci		}
24968c2ecf20Sopenharmony_ci	}
24978c2ecf20Sopenharmony_ci
24988c2ecf20Sopenharmony_ci	chip->state = FL_READY;
24998c2ecf20Sopenharmony_ci	xip_enable(map, chip, adr);
25008c2ecf20Sopenharmony_ci	DISABLE_VPP(map);
25018c2ecf20Sopenharmony_ci	put_chip(map, chip, adr);
25028c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci	return ret;
25058c2ecf20Sopenharmony_ci}
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_cistatic int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
25098c2ecf20Sopenharmony_ci{
25108c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
25118c2ecf20Sopenharmony_ci	unsigned long timeo = jiffies + HZ;
25128c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
25138c2ecf20Sopenharmony_ci	int ret;
25148c2ecf20Sopenharmony_ci	int retry_cnt = 0;
25158c2ecf20Sopenharmony_ci	map_word datum = map_word_ff(map);
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	adr += chip->start;
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
25208c2ecf20Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_ERASING);
25218c2ecf20Sopenharmony_ci	if (ret) {
25228c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
25238c2ecf20Sopenharmony_ci		return ret;
25248c2ecf20Sopenharmony_ci	}
25258c2ecf20Sopenharmony_ci
25268c2ecf20Sopenharmony_ci	pr_debug("MTD %s(): ERASE 0x%.8lx\n",
25278c2ecf20Sopenharmony_ci		 __func__, adr);
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_ci	XIP_INVAL_CACHED_RANGE(map, adr, len);
25308c2ecf20Sopenharmony_ci	ENABLE_VPP(map);
25318c2ecf20Sopenharmony_ci	xip_disable(map, chip, adr);
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci retry:
25348c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
25358c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
25368c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
25378c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
25388c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
25398c2ecf20Sopenharmony_ci	map_write(map, cfi->sector_erase_cmd, adr);
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	chip->state = FL_ERASING;
25428c2ecf20Sopenharmony_ci	chip->erase_suspended = 0;
25438c2ecf20Sopenharmony_ci	chip->in_progress_block_addr = adr;
25448c2ecf20Sopenharmony_ci	chip->in_progress_block_mask = ~(len - 1);
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	INVALIDATE_CACHE_UDELAY(map, chip,
25478c2ecf20Sopenharmony_ci				adr, len,
25488c2ecf20Sopenharmony_ci				chip->erase_time*500);
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_ci	timeo = jiffies + (HZ*20);
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci	for (;;) {
25538c2ecf20Sopenharmony_ci		if (chip->state != FL_ERASING) {
25548c2ecf20Sopenharmony_ci			/* Someone's suspended the erase. Sleep */
25558c2ecf20Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
25568c2ecf20Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
25578c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
25588c2ecf20Sopenharmony_ci			schedule();
25598c2ecf20Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
25608c2ecf20Sopenharmony_ci			mutex_lock(&chip->mutex);
25618c2ecf20Sopenharmony_ci			continue;
25628c2ecf20Sopenharmony_ci		}
25638c2ecf20Sopenharmony_ci		if (chip->erase_suspended) {
25648c2ecf20Sopenharmony_ci			/* This erase was suspended and resumed.
25658c2ecf20Sopenharmony_ci			   Adjust the timeout */
25668c2ecf20Sopenharmony_ci			timeo = jiffies + (HZ*20); /* FIXME */
25678c2ecf20Sopenharmony_ci			chip->erase_suspended = 0;
25688c2ecf20Sopenharmony_ci		}
25698c2ecf20Sopenharmony_ci
25708c2ecf20Sopenharmony_ci		if (chip_ready(map, chip, adr, &datum)) {
25718c2ecf20Sopenharmony_ci			if (cfi_check_err_status(map, chip, adr))
25728c2ecf20Sopenharmony_ci				ret = -EIO;
25738c2ecf20Sopenharmony_ci			break;
25748c2ecf20Sopenharmony_ci		}
25758c2ecf20Sopenharmony_ci
25768c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeo)) {
25778c2ecf20Sopenharmony_ci			printk(KERN_WARNING "MTD %s(): software timeout\n",
25788c2ecf20Sopenharmony_ci			       __func__);
25798c2ecf20Sopenharmony_ci			ret = -EIO;
25808c2ecf20Sopenharmony_ci			break;
25818c2ecf20Sopenharmony_ci		}
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_ci		/* Latency issues. Drop the lock, wait a while and retry */
25848c2ecf20Sopenharmony_ci		UDELAY(map, chip, adr, 1000000/HZ);
25858c2ecf20Sopenharmony_ci	}
25868c2ecf20Sopenharmony_ci	/* Did we succeed? */
25878c2ecf20Sopenharmony_ci	if (ret) {
25888c2ecf20Sopenharmony_ci		/* reset on all failures. */
25898c2ecf20Sopenharmony_ci		map_write(map, CMD(0xF0), chip->start);
25908c2ecf20Sopenharmony_ci		/* FIXME - should have reset delay before continuing */
25918c2ecf20Sopenharmony_ci
25928c2ecf20Sopenharmony_ci		if (++retry_cnt <= MAX_RETRIES) {
25938c2ecf20Sopenharmony_ci			ret = 0;
25948c2ecf20Sopenharmony_ci			goto retry;
25958c2ecf20Sopenharmony_ci		}
25968c2ecf20Sopenharmony_ci	}
25978c2ecf20Sopenharmony_ci
25988c2ecf20Sopenharmony_ci	chip->state = FL_READY;
25998c2ecf20Sopenharmony_ci	xip_enable(map, chip, adr);
26008c2ecf20Sopenharmony_ci	DISABLE_VPP(map);
26018c2ecf20Sopenharmony_ci	put_chip(map, chip, adr);
26028c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
26038c2ecf20Sopenharmony_ci	return ret;
26048c2ecf20Sopenharmony_ci}
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_ci
26078c2ecf20Sopenharmony_cistatic int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
26088c2ecf20Sopenharmony_ci{
26098c2ecf20Sopenharmony_ci	return cfi_varsize_frob(mtd, do_erase_oneblock, instr->addr,
26108c2ecf20Sopenharmony_ci				instr->len, NULL);
26118c2ecf20Sopenharmony_ci}
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_cistatic int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
26158c2ecf20Sopenharmony_ci{
26168c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
26178c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_ci	if (instr->addr != 0)
26208c2ecf20Sopenharmony_ci		return -EINVAL;
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_ci	if (instr->len != mtd->size)
26238c2ecf20Sopenharmony_ci		return -EINVAL;
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_ci	return do_erase_chip(map, &cfi->chips[0]);
26268c2ecf20Sopenharmony_ci}
26278c2ecf20Sopenharmony_ci
26288c2ecf20Sopenharmony_cistatic int do_atmel_lock(struct map_info *map, struct flchip *chip,
26298c2ecf20Sopenharmony_ci			 unsigned long adr, int len, void *thunk)
26308c2ecf20Sopenharmony_ci{
26318c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
26328c2ecf20Sopenharmony_ci	int ret;
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
26358c2ecf20Sopenharmony_ci	ret = get_chip(map, chip, adr + chip->start, FL_LOCKING);
26368c2ecf20Sopenharmony_ci	if (ret)
26378c2ecf20Sopenharmony_ci		goto out_unlock;
26388c2ecf20Sopenharmony_ci	chip->state = FL_LOCKING;
26398c2ecf20Sopenharmony_ci
26408c2ecf20Sopenharmony_ci	pr_debug("MTD %s(): LOCK 0x%08lx len %d\n", __func__, adr, len);
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
26438c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
26448c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
26458c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
26468c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi,
26478c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
26488c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
26498c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
26508c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
26518c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
26528c2ecf20Sopenharmony_ci	map_write(map, CMD(0x40), chip->start + adr);
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_ci	chip->state = FL_READY;
26558c2ecf20Sopenharmony_ci	put_chip(map, chip, adr + chip->start);
26568c2ecf20Sopenharmony_ci	ret = 0;
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_ciout_unlock:
26598c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
26608c2ecf20Sopenharmony_ci	return ret;
26618c2ecf20Sopenharmony_ci}
26628c2ecf20Sopenharmony_ci
26638c2ecf20Sopenharmony_cistatic int do_atmel_unlock(struct map_info *map, struct flchip *chip,
26648c2ecf20Sopenharmony_ci			   unsigned long adr, int len, void *thunk)
26658c2ecf20Sopenharmony_ci{
26668c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
26678c2ecf20Sopenharmony_ci	int ret;
26688c2ecf20Sopenharmony_ci
26698c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
26708c2ecf20Sopenharmony_ci	ret = get_chip(map, chip, adr + chip->start, FL_UNLOCKING);
26718c2ecf20Sopenharmony_ci	if (ret)
26728c2ecf20Sopenharmony_ci		goto out_unlock;
26738c2ecf20Sopenharmony_ci	chip->state = FL_UNLOCKING;
26748c2ecf20Sopenharmony_ci
26758c2ecf20Sopenharmony_ci	pr_debug("MTD %s(): LOCK 0x%08lx len %d\n", __func__, adr, len);
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
26788c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
26798c2ecf20Sopenharmony_ci	map_write(map, CMD(0x70), adr);
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	chip->state = FL_READY;
26828c2ecf20Sopenharmony_ci	put_chip(map, chip, adr + chip->start);
26838c2ecf20Sopenharmony_ci	ret = 0;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ciout_unlock:
26868c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
26878c2ecf20Sopenharmony_ci	return ret;
26888c2ecf20Sopenharmony_ci}
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_cistatic int cfi_atmel_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
26918c2ecf20Sopenharmony_ci{
26928c2ecf20Sopenharmony_ci	return cfi_varsize_frob(mtd, do_atmel_lock, ofs, len, NULL);
26938c2ecf20Sopenharmony_ci}
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_cistatic int cfi_atmel_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
26968c2ecf20Sopenharmony_ci{
26978c2ecf20Sopenharmony_ci	return cfi_varsize_frob(mtd, do_atmel_unlock, ofs, len, NULL);
26988c2ecf20Sopenharmony_ci}
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ci/*
27018c2ecf20Sopenharmony_ci * Advanced Sector Protection - PPB (Persistent Protection Bit) locking
27028c2ecf20Sopenharmony_ci */
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_cistruct ppb_lock {
27058c2ecf20Sopenharmony_ci	struct flchip *chip;
27068c2ecf20Sopenharmony_ci	unsigned long adr;
27078c2ecf20Sopenharmony_ci	int locked;
27088c2ecf20Sopenharmony_ci};
27098c2ecf20Sopenharmony_ci
27108c2ecf20Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_LOCK		((void *)1)
27118c2ecf20Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_UNLOCK	((void *)2)
27128c2ecf20Sopenharmony_ci#define DO_XXLOCK_ONEBLOCK_GETLOCK	((void *)3)
27138c2ecf20Sopenharmony_ci
27148c2ecf20Sopenharmony_cistatic int __maybe_unused do_ppb_xxlock(struct map_info *map,
27158c2ecf20Sopenharmony_ci					struct flchip *chip,
27168c2ecf20Sopenharmony_ci					unsigned long adr, int len, void *thunk)
27178c2ecf20Sopenharmony_ci{
27188c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
27198c2ecf20Sopenharmony_ci	unsigned long timeo;
27208c2ecf20Sopenharmony_ci	int ret;
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_ci	adr += chip->start;
27238c2ecf20Sopenharmony_ci	mutex_lock(&chip->mutex);
27248c2ecf20Sopenharmony_ci	ret = get_chip(map, chip, adr, FL_LOCKING);
27258c2ecf20Sopenharmony_ci	if (ret) {
27268c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
27278c2ecf20Sopenharmony_ci		return ret;
27288c2ecf20Sopenharmony_ci	}
27298c2ecf20Sopenharmony_ci
27308c2ecf20Sopenharmony_ci	pr_debug("MTD %s(): XXLOCK 0x%08lx len %d\n", __func__, adr, len);
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
27338c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
27348c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
27358c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
27368c2ecf20Sopenharmony_ci	/* PPB entry command */
27378c2ecf20Sopenharmony_ci	cfi_send_gen_cmd(0xC0, cfi->addr_unlock1, chip->start, map, cfi,
27388c2ecf20Sopenharmony_ci			 cfi->device_type, NULL);
27398c2ecf20Sopenharmony_ci
27408c2ecf20Sopenharmony_ci	if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) {
27418c2ecf20Sopenharmony_ci		chip->state = FL_LOCKING;
27428c2ecf20Sopenharmony_ci		map_write(map, CMD(0xA0), adr);
27438c2ecf20Sopenharmony_ci		map_write(map, CMD(0x00), adr);
27448c2ecf20Sopenharmony_ci	} else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) {
27458c2ecf20Sopenharmony_ci		/*
27468c2ecf20Sopenharmony_ci		 * Unlocking of one specific sector is not supported, so we
27478c2ecf20Sopenharmony_ci		 * have to unlock all sectors of this device instead
27488c2ecf20Sopenharmony_ci		 */
27498c2ecf20Sopenharmony_ci		chip->state = FL_UNLOCKING;
27508c2ecf20Sopenharmony_ci		map_write(map, CMD(0x80), chip->start);
27518c2ecf20Sopenharmony_ci		map_write(map, CMD(0x30), chip->start);
27528c2ecf20Sopenharmony_ci	} else if (thunk == DO_XXLOCK_ONEBLOCK_GETLOCK) {
27538c2ecf20Sopenharmony_ci		chip->state = FL_JEDEC_QUERY;
27548c2ecf20Sopenharmony_ci		/* Return locked status: 0->locked, 1->unlocked */
27558c2ecf20Sopenharmony_ci		ret = !cfi_read_query(map, adr);
27568c2ecf20Sopenharmony_ci	} else
27578c2ecf20Sopenharmony_ci		BUG();
27588c2ecf20Sopenharmony_ci
27598c2ecf20Sopenharmony_ci	/*
27608c2ecf20Sopenharmony_ci	 * Wait for some time as unlocking of all sectors takes quite long
27618c2ecf20Sopenharmony_ci	 */
27628c2ecf20Sopenharmony_ci	timeo = jiffies + msecs_to_jiffies(2000);	/* 2s max (un)locking */
27638c2ecf20Sopenharmony_ci	for (;;) {
27648c2ecf20Sopenharmony_ci		if (chip_ready(map, chip, adr, NULL))
27658c2ecf20Sopenharmony_ci			break;
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeo)) {
27688c2ecf20Sopenharmony_ci			printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
27698c2ecf20Sopenharmony_ci			ret = -EIO;
27708c2ecf20Sopenharmony_ci			break;
27718c2ecf20Sopenharmony_ci		}
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_ci		UDELAY(map, chip, adr, 1);
27748c2ecf20Sopenharmony_ci	}
27758c2ecf20Sopenharmony_ci
27768c2ecf20Sopenharmony_ci	/* Exit BC commands */
27778c2ecf20Sopenharmony_ci	map_write(map, CMD(0x90), chip->start);
27788c2ecf20Sopenharmony_ci	map_write(map, CMD(0x00), chip->start);
27798c2ecf20Sopenharmony_ci
27808c2ecf20Sopenharmony_ci	chip->state = FL_READY;
27818c2ecf20Sopenharmony_ci	put_chip(map, chip, adr);
27828c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mutex);
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci	return ret;
27858c2ecf20Sopenharmony_ci}
27868c2ecf20Sopenharmony_ci
27878c2ecf20Sopenharmony_cistatic int __maybe_unused cfi_ppb_lock(struct mtd_info *mtd, loff_t ofs,
27888c2ecf20Sopenharmony_ci				       uint64_t len)
27898c2ecf20Sopenharmony_ci{
27908c2ecf20Sopenharmony_ci	return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len,
27918c2ecf20Sopenharmony_ci				DO_XXLOCK_ONEBLOCK_LOCK);
27928c2ecf20Sopenharmony_ci}
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_cistatic int __maybe_unused cfi_ppb_unlock(struct mtd_info *mtd, loff_t ofs,
27958c2ecf20Sopenharmony_ci					 uint64_t len)
27968c2ecf20Sopenharmony_ci{
27978c2ecf20Sopenharmony_ci	struct mtd_erase_region_info *regions = mtd->eraseregions;
27988c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
27998c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
28008c2ecf20Sopenharmony_ci	struct ppb_lock *sect;
28018c2ecf20Sopenharmony_ci	unsigned long adr;
28028c2ecf20Sopenharmony_ci	loff_t offset;
28038c2ecf20Sopenharmony_ci	uint64_t length;
28048c2ecf20Sopenharmony_ci	int chipnum;
28058c2ecf20Sopenharmony_ci	int i;
28068c2ecf20Sopenharmony_ci	int sectors;
28078c2ecf20Sopenharmony_ci	int ret;
28088c2ecf20Sopenharmony_ci	int max_sectors;
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_ci	/*
28118c2ecf20Sopenharmony_ci	 * PPB unlocking always unlocks all sectors of the flash chip.
28128c2ecf20Sopenharmony_ci	 * We need to re-lock all previously locked sectors. So lets
28138c2ecf20Sopenharmony_ci	 * first check the locking status of all sectors and save
28148c2ecf20Sopenharmony_ci	 * it for future use.
28158c2ecf20Sopenharmony_ci	 */
28168c2ecf20Sopenharmony_ci	max_sectors = 0;
28178c2ecf20Sopenharmony_ci	for (i = 0; i < mtd->numeraseregions; i++)
28188c2ecf20Sopenharmony_ci		max_sectors += regions[i].numblocks;
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci	sect = kcalloc(max_sectors, sizeof(struct ppb_lock), GFP_KERNEL);
28218c2ecf20Sopenharmony_ci	if (!sect)
28228c2ecf20Sopenharmony_ci		return -ENOMEM;
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ci	/*
28258c2ecf20Sopenharmony_ci	 * This code to walk all sectors is a slightly modified version
28268c2ecf20Sopenharmony_ci	 * of the cfi_varsize_frob() code.
28278c2ecf20Sopenharmony_ci	 */
28288c2ecf20Sopenharmony_ci	i = 0;
28298c2ecf20Sopenharmony_ci	chipnum = 0;
28308c2ecf20Sopenharmony_ci	adr = 0;
28318c2ecf20Sopenharmony_ci	sectors = 0;
28328c2ecf20Sopenharmony_ci	offset = 0;
28338c2ecf20Sopenharmony_ci	length = mtd->size;
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci	while (length) {
28368c2ecf20Sopenharmony_ci		int size = regions[i].erasesize;
28378c2ecf20Sopenharmony_ci
28388c2ecf20Sopenharmony_ci		/*
28398c2ecf20Sopenharmony_ci		 * Only test sectors that shall not be unlocked. The other
28408c2ecf20Sopenharmony_ci		 * sectors shall be unlocked, so lets keep their locking
28418c2ecf20Sopenharmony_ci		 * status at "unlocked" (locked=0) for the final re-locking.
28428c2ecf20Sopenharmony_ci		 */
28438c2ecf20Sopenharmony_ci		if ((offset < ofs) || (offset >= (ofs + len))) {
28448c2ecf20Sopenharmony_ci			sect[sectors].chip = &cfi->chips[chipnum];
28458c2ecf20Sopenharmony_ci			sect[sectors].adr = adr;
28468c2ecf20Sopenharmony_ci			sect[sectors].locked = do_ppb_xxlock(
28478c2ecf20Sopenharmony_ci				map, &cfi->chips[chipnum], adr, 0,
28488c2ecf20Sopenharmony_ci				DO_XXLOCK_ONEBLOCK_GETLOCK);
28498c2ecf20Sopenharmony_ci		}
28508c2ecf20Sopenharmony_ci
28518c2ecf20Sopenharmony_ci		adr += size;
28528c2ecf20Sopenharmony_ci		offset += size;
28538c2ecf20Sopenharmony_ci		length -= size;
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci		if (offset == regions[i].offset + size * regions[i].numblocks)
28568c2ecf20Sopenharmony_ci			i++;
28578c2ecf20Sopenharmony_ci
28588c2ecf20Sopenharmony_ci		if (adr >> cfi->chipshift) {
28598c2ecf20Sopenharmony_ci			if (offset >= (ofs + len))
28608c2ecf20Sopenharmony_ci				break;
28618c2ecf20Sopenharmony_ci			adr = 0;
28628c2ecf20Sopenharmony_ci			chipnum++;
28638c2ecf20Sopenharmony_ci
28648c2ecf20Sopenharmony_ci			if (chipnum >= cfi->numchips)
28658c2ecf20Sopenharmony_ci				break;
28668c2ecf20Sopenharmony_ci		}
28678c2ecf20Sopenharmony_ci
28688c2ecf20Sopenharmony_ci		sectors++;
28698c2ecf20Sopenharmony_ci		if (sectors >= max_sectors) {
28708c2ecf20Sopenharmony_ci			printk(KERN_ERR "Only %d sectors for PPB locking supported!\n",
28718c2ecf20Sopenharmony_ci			       max_sectors);
28728c2ecf20Sopenharmony_ci			kfree(sect);
28738c2ecf20Sopenharmony_ci			return -EINVAL;
28748c2ecf20Sopenharmony_ci		}
28758c2ecf20Sopenharmony_ci	}
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_ci	/* Now unlock the whole chip */
28788c2ecf20Sopenharmony_ci	ret = cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len,
28798c2ecf20Sopenharmony_ci			       DO_XXLOCK_ONEBLOCK_UNLOCK);
28808c2ecf20Sopenharmony_ci	if (ret) {
28818c2ecf20Sopenharmony_ci		kfree(sect);
28828c2ecf20Sopenharmony_ci		return ret;
28838c2ecf20Sopenharmony_ci	}
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ci	/*
28868c2ecf20Sopenharmony_ci	 * PPB unlocking always unlocks all sectors of the flash chip.
28878c2ecf20Sopenharmony_ci	 * We need to re-lock all previously locked sectors.
28888c2ecf20Sopenharmony_ci	 */
28898c2ecf20Sopenharmony_ci	for (i = 0; i < sectors; i++) {
28908c2ecf20Sopenharmony_ci		if (sect[i].locked)
28918c2ecf20Sopenharmony_ci			do_ppb_xxlock(map, sect[i].chip, sect[i].adr, 0,
28928c2ecf20Sopenharmony_ci				      DO_XXLOCK_ONEBLOCK_LOCK);
28938c2ecf20Sopenharmony_ci	}
28948c2ecf20Sopenharmony_ci
28958c2ecf20Sopenharmony_ci	kfree(sect);
28968c2ecf20Sopenharmony_ci	return ret;
28978c2ecf20Sopenharmony_ci}
28988c2ecf20Sopenharmony_ci
28998c2ecf20Sopenharmony_cistatic int __maybe_unused cfi_ppb_is_locked(struct mtd_info *mtd, loff_t ofs,
29008c2ecf20Sopenharmony_ci					    uint64_t len)
29018c2ecf20Sopenharmony_ci{
29028c2ecf20Sopenharmony_ci	return cfi_varsize_frob(mtd, do_ppb_xxlock, ofs, len,
29038c2ecf20Sopenharmony_ci				DO_XXLOCK_ONEBLOCK_GETLOCK) ? 1 : 0;
29048c2ecf20Sopenharmony_ci}
29058c2ecf20Sopenharmony_ci
29068c2ecf20Sopenharmony_cistatic void cfi_amdstd_sync (struct mtd_info *mtd)
29078c2ecf20Sopenharmony_ci{
29088c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
29098c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
29108c2ecf20Sopenharmony_ci	int i;
29118c2ecf20Sopenharmony_ci	struct flchip *chip;
29128c2ecf20Sopenharmony_ci	int ret = 0;
29138c2ecf20Sopenharmony_ci	DECLARE_WAITQUEUE(wait, current);
29148c2ecf20Sopenharmony_ci
29158c2ecf20Sopenharmony_ci	for (i=0; !ret && i<cfi->numchips; i++) {
29168c2ecf20Sopenharmony_ci		chip = &cfi->chips[i];
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_ci	retry:
29198c2ecf20Sopenharmony_ci		mutex_lock(&chip->mutex);
29208c2ecf20Sopenharmony_ci
29218c2ecf20Sopenharmony_ci		switch(chip->state) {
29228c2ecf20Sopenharmony_ci		case FL_READY:
29238c2ecf20Sopenharmony_ci		case FL_STATUS:
29248c2ecf20Sopenharmony_ci		case FL_CFI_QUERY:
29258c2ecf20Sopenharmony_ci		case FL_JEDEC_QUERY:
29268c2ecf20Sopenharmony_ci			chip->oldstate = chip->state;
29278c2ecf20Sopenharmony_ci			chip->state = FL_SYNCING;
29288c2ecf20Sopenharmony_ci			/* No need to wake_up() on this state change -
29298c2ecf20Sopenharmony_ci			 * as the whole point is that nobody can do anything
29308c2ecf20Sopenharmony_ci			 * with the chip now anyway.
29318c2ecf20Sopenharmony_ci			 */
29328c2ecf20Sopenharmony_ci			fallthrough;
29338c2ecf20Sopenharmony_ci		case FL_SYNCING:
29348c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
29358c2ecf20Sopenharmony_ci			break;
29368c2ecf20Sopenharmony_ci
29378c2ecf20Sopenharmony_ci		default:
29388c2ecf20Sopenharmony_ci			/* Not an idle state */
29398c2ecf20Sopenharmony_ci			set_current_state(TASK_UNINTERRUPTIBLE);
29408c2ecf20Sopenharmony_ci			add_wait_queue(&chip->wq, &wait);
29418c2ecf20Sopenharmony_ci
29428c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
29438c2ecf20Sopenharmony_ci
29448c2ecf20Sopenharmony_ci			schedule();
29458c2ecf20Sopenharmony_ci
29468c2ecf20Sopenharmony_ci			remove_wait_queue(&chip->wq, &wait);
29478c2ecf20Sopenharmony_ci
29488c2ecf20Sopenharmony_ci			goto retry;
29498c2ecf20Sopenharmony_ci		}
29508c2ecf20Sopenharmony_ci	}
29518c2ecf20Sopenharmony_ci
29528c2ecf20Sopenharmony_ci	/* Unlock the chips again */
29538c2ecf20Sopenharmony_ci
29548c2ecf20Sopenharmony_ci	for (i--; i >=0; i--) {
29558c2ecf20Sopenharmony_ci		chip = &cfi->chips[i];
29568c2ecf20Sopenharmony_ci
29578c2ecf20Sopenharmony_ci		mutex_lock(&chip->mutex);
29588c2ecf20Sopenharmony_ci
29598c2ecf20Sopenharmony_ci		if (chip->state == FL_SYNCING) {
29608c2ecf20Sopenharmony_ci			chip->state = chip->oldstate;
29618c2ecf20Sopenharmony_ci			wake_up(&chip->wq);
29628c2ecf20Sopenharmony_ci		}
29638c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
29648c2ecf20Sopenharmony_ci	}
29658c2ecf20Sopenharmony_ci}
29668c2ecf20Sopenharmony_ci
29678c2ecf20Sopenharmony_ci
29688c2ecf20Sopenharmony_cistatic int cfi_amdstd_suspend(struct mtd_info *mtd)
29698c2ecf20Sopenharmony_ci{
29708c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
29718c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
29728c2ecf20Sopenharmony_ci	int i;
29738c2ecf20Sopenharmony_ci	struct flchip *chip;
29748c2ecf20Sopenharmony_ci	int ret = 0;
29758c2ecf20Sopenharmony_ci
29768c2ecf20Sopenharmony_ci	for (i=0; !ret && i<cfi->numchips; i++) {
29778c2ecf20Sopenharmony_ci		chip = &cfi->chips[i];
29788c2ecf20Sopenharmony_ci
29798c2ecf20Sopenharmony_ci		mutex_lock(&chip->mutex);
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_ci		switch(chip->state) {
29828c2ecf20Sopenharmony_ci		case FL_READY:
29838c2ecf20Sopenharmony_ci		case FL_STATUS:
29848c2ecf20Sopenharmony_ci		case FL_CFI_QUERY:
29858c2ecf20Sopenharmony_ci		case FL_JEDEC_QUERY:
29868c2ecf20Sopenharmony_ci			chip->oldstate = chip->state;
29878c2ecf20Sopenharmony_ci			chip->state = FL_PM_SUSPENDED;
29888c2ecf20Sopenharmony_ci			/* No need to wake_up() on this state change -
29898c2ecf20Sopenharmony_ci			 * as the whole point is that nobody can do anything
29908c2ecf20Sopenharmony_ci			 * with the chip now anyway.
29918c2ecf20Sopenharmony_ci			 */
29928c2ecf20Sopenharmony_ci		case FL_PM_SUSPENDED:
29938c2ecf20Sopenharmony_ci			break;
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci		default:
29968c2ecf20Sopenharmony_ci			ret = -EAGAIN;
29978c2ecf20Sopenharmony_ci			break;
29988c2ecf20Sopenharmony_ci		}
29998c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
30008c2ecf20Sopenharmony_ci	}
30018c2ecf20Sopenharmony_ci
30028c2ecf20Sopenharmony_ci	/* Unlock the chips again */
30038c2ecf20Sopenharmony_ci
30048c2ecf20Sopenharmony_ci	if (ret) {
30058c2ecf20Sopenharmony_ci		for (i--; i >=0; i--) {
30068c2ecf20Sopenharmony_ci			chip = &cfi->chips[i];
30078c2ecf20Sopenharmony_ci
30088c2ecf20Sopenharmony_ci			mutex_lock(&chip->mutex);
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_ci			if (chip->state == FL_PM_SUSPENDED) {
30118c2ecf20Sopenharmony_ci				chip->state = chip->oldstate;
30128c2ecf20Sopenharmony_ci				wake_up(&chip->wq);
30138c2ecf20Sopenharmony_ci			}
30148c2ecf20Sopenharmony_ci			mutex_unlock(&chip->mutex);
30158c2ecf20Sopenharmony_ci		}
30168c2ecf20Sopenharmony_ci	}
30178c2ecf20Sopenharmony_ci
30188c2ecf20Sopenharmony_ci	return ret;
30198c2ecf20Sopenharmony_ci}
30208c2ecf20Sopenharmony_ci
30218c2ecf20Sopenharmony_ci
30228c2ecf20Sopenharmony_cistatic void cfi_amdstd_resume(struct mtd_info *mtd)
30238c2ecf20Sopenharmony_ci{
30248c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
30258c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
30268c2ecf20Sopenharmony_ci	int i;
30278c2ecf20Sopenharmony_ci	struct flchip *chip;
30288c2ecf20Sopenharmony_ci
30298c2ecf20Sopenharmony_ci	for (i=0; i<cfi->numchips; i++) {
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ci		chip = &cfi->chips[i];
30328c2ecf20Sopenharmony_ci
30338c2ecf20Sopenharmony_ci		mutex_lock(&chip->mutex);
30348c2ecf20Sopenharmony_ci
30358c2ecf20Sopenharmony_ci		if (chip->state == FL_PM_SUSPENDED) {
30368c2ecf20Sopenharmony_ci			chip->state = FL_READY;
30378c2ecf20Sopenharmony_ci			map_write(map, CMD(0xF0), chip->start);
30388c2ecf20Sopenharmony_ci			wake_up(&chip->wq);
30398c2ecf20Sopenharmony_ci		}
30408c2ecf20Sopenharmony_ci		else
30418c2ecf20Sopenharmony_ci			printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n");
30428c2ecf20Sopenharmony_ci
30438c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
30448c2ecf20Sopenharmony_ci	}
30458c2ecf20Sopenharmony_ci}
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_ci
30488c2ecf20Sopenharmony_ci/*
30498c2ecf20Sopenharmony_ci * Ensure that the flash device is put back into read array mode before
30508c2ecf20Sopenharmony_ci * unloading the driver or rebooting.  On some systems, rebooting while
30518c2ecf20Sopenharmony_ci * the flash is in query/program/erase mode will prevent the CPU from
30528c2ecf20Sopenharmony_ci * fetching the bootloader code, requiring a hard reset or power cycle.
30538c2ecf20Sopenharmony_ci */
30548c2ecf20Sopenharmony_cistatic int cfi_amdstd_reset(struct mtd_info *mtd)
30558c2ecf20Sopenharmony_ci{
30568c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
30578c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
30588c2ecf20Sopenharmony_ci	int i, ret;
30598c2ecf20Sopenharmony_ci	struct flchip *chip;
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ci	for (i = 0; i < cfi->numchips; i++) {
30628c2ecf20Sopenharmony_ci
30638c2ecf20Sopenharmony_ci		chip = &cfi->chips[i];
30648c2ecf20Sopenharmony_ci
30658c2ecf20Sopenharmony_ci		mutex_lock(&chip->mutex);
30668c2ecf20Sopenharmony_ci
30678c2ecf20Sopenharmony_ci		ret = get_chip(map, chip, chip->start, FL_SHUTDOWN);
30688c2ecf20Sopenharmony_ci		if (!ret) {
30698c2ecf20Sopenharmony_ci			map_write(map, CMD(0xF0), chip->start);
30708c2ecf20Sopenharmony_ci			chip->state = FL_SHUTDOWN;
30718c2ecf20Sopenharmony_ci			put_chip(map, chip, chip->start);
30728c2ecf20Sopenharmony_ci		}
30738c2ecf20Sopenharmony_ci
30748c2ecf20Sopenharmony_ci		mutex_unlock(&chip->mutex);
30758c2ecf20Sopenharmony_ci	}
30768c2ecf20Sopenharmony_ci
30778c2ecf20Sopenharmony_ci	return 0;
30788c2ecf20Sopenharmony_ci}
30798c2ecf20Sopenharmony_ci
30808c2ecf20Sopenharmony_ci
30818c2ecf20Sopenharmony_cistatic int cfi_amdstd_reboot(struct notifier_block *nb, unsigned long val,
30828c2ecf20Sopenharmony_ci			       void *v)
30838c2ecf20Sopenharmony_ci{
30848c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
30858c2ecf20Sopenharmony_ci
30868c2ecf20Sopenharmony_ci	mtd = container_of(nb, struct mtd_info, reboot_notifier);
30878c2ecf20Sopenharmony_ci	cfi_amdstd_reset(mtd);
30888c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
30898c2ecf20Sopenharmony_ci}
30908c2ecf20Sopenharmony_ci
30918c2ecf20Sopenharmony_ci
30928c2ecf20Sopenharmony_cistatic void cfi_amdstd_destroy(struct mtd_info *mtd)
30938c2ecf20Sopenharmony_ci{
30948c2ecf20Sopenharmony_ci	struct map_info *map = mtd->priv;
30958c2ecf20Sopenharmony_ci	struct cfi_private *cfi = map->fldrv_priv;
30968c2ecf20Sopenharmony_ci
30978c2ecf20Sopenharmony_ci	cfi_amdstd_reset(mtd);
30988c2ecf20Sopenharmony_ci	unregister_reboot_notifier(&mtd->reboot_notifier);
30998c2ecf20Sopenharmony_ci	kfree(cfi->cmdset_priv);
31008c2ecf20Sopenharmony_ci	kfree(cfi->cfiq);
31018c2ecf20Sopenharmony_ci	kfree(cfi);
31028c2ecf20Sopenharmony_ci	kfree(mtd->eraseregions);
31038c2ecf20Sopenharmony_ci}
31048c2ecf20Sopenharmony_ci
31058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
31068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al.");
31078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");
31088c2ecf20Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0006");
31098c2ecf20Sopenharmony_ciMODULE_ALIAS("cfi_cmdset_0701");
3110