11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Common Flash Interface support:
325985edcedea6396277003854657b5f3cb31a628Lucas De Marchi *   Generic utility functions not dependent on command set
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2002 Red Hat
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2003 STMicroelectronics Limited
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This code is covered by the GPL.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/xip.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/map.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/cfi.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouseint __xipram cfi_qry_present(struct map_info *map, __u32 base,
27c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse			     struct cfi_private *cfi)
282e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev{
292e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	int osf = cfi->interleave * cfi->device_type;	/* scale factor */
302e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	map_word val[3];
312e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	map_word qry[3];
322e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
332e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	qry[0] = cfi_build_cmd('Q', map, cfi);
342e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	qry[1] = cfi_build_cmd('R', map, cfi);
352e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	qry[2] = cfi_build_cmd('Y', map, cfi);
362e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
372e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	val[0] = map_read(map, base + osf*0x10);
382e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	val[1] = map_read(map, base + osf*0x11);
392e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	val[2] = map_read(map, base + osf*0x12);
402e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
412e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	if (!map_word_equal(map, qry[0], val[0]))
422e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 0;
432e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
442e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	if (!map_word_equal(map, qry[1], val[1]))
452e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 0;
462e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
472e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	if (!map_word_equal(map, qry[2], val[2]))
482e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 0;
492e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
502e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	return 1; 	/* "QRY" found */
512e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev}
52c314dfdc358847eef0fc07ec8682e1acc8cadd00David WoodhouseEXPORT_SYMBOL_GPL(cfi_qry_present);
532e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
54c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouseint __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
55c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse			     struct cfi_private *cfi)
562e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev{
572e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
582e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
59c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	if (cfi_qry_present(map, base, cfi))
602e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 1;
612e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	/* QRY not found probably we deal with some odd CFI chips */
622e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	/* Some revisions of some old Intel chips? */
632e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
642e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
652e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
66c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	if (cfi_qry_present(map, base, cfi))
672e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 1;
682e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	/* ST M29DW chips */
692e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
702e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
714a58948669702639db7acecfa2105c3172d85c93Guillaume LECERF	if (cfi_qry_present(map, base, cfi))
724a58948669702639db7acecfa2105c3172d85c93Guillaume LECERF		return 1;
734a58948669702639db7acecfa2105c3172d85c93Guillaume LECERF	/* some old SST chips, e.g. 39VF160x/39VF320x */
744a58948669702639db7acecfa2105c3172d85c93Guillaume LECERF	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
754a58948669702639db7acecfa2105c3172d85c93Guillaume LECERF	cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
764a58948669702639db7acecfa2105c3172d85c93Guillaume LECERF	cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
774a58948669702639db7acecfa2105c3172d85c93Guillaume LECERF	cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
78fc61015f72808f2fd83657909d02f4d29404b8c6Guillaume LECERF	if (cfi_qry_present(map, base, cfi))
79fc61015f72808f2fd83657909d02f4d29404b8c6Guillaume LECERF		return 1;
80fc61015f72808f2fd83657909d02f4d29404b8c6Guillaume LECERF	/* SST 39VF640xB */
81fc61015f72808f2fd83657909d02f4d29404b8c6Guillaume LECERF	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
82fc61015f72808f2fd83657909d02f4d29404b8c6Guillaume LECERF	cfi_send_gen_cmd(0xAA, 0x555, base, map, cfi, cfi->device_type, NULL);
83fc61015f72808f2fd83657909d02f4d29404b8c6Guillaume LECERF	cfi_send_gen_cmd(0x55, 0x2AA, base, map, cfi, cfi->device_type, NULL);
84fc61015f72808f2fd83657909d02f4d29404b8c6Guillaume LECERF	cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
85c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	if (cfi_qry_present(map, base, cfi))
862e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 1;
872e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	/* QRY not found */
882e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	return 0;
892e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev}
90c314dfdc358847eef0fc07ec8682e1acc8cadd00David WoodhouseEXPORT_SYMBOL_GPL(cfi_qry_mode_on);
91c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse
92c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhousevoid __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map,
93c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse			       struct cfi_private *cfi)
942e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev{
952e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
962e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
9723af51ecfb04ff65bae51bd8e2270f4449abc789Massimo Cirillo	/* M29W128G flashes require an additional reset command
9823af51ecfb04ff65bae51bd8e2270f4449abc789Massimo Cirillo	   when exit qry mode */
9923af51ecfb04ff65bae51bd8e2270f4449abc789Massimo Cirillo	if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E))
10023af51ecfb04ff65bae51bd8e2270f4449abc789Massimo Cirillo		cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
1012e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev}
102c314dfdc358847eef0fc07ec8682e1acc8cadd00David WoodhouseEXPORT_SYMBOL_GPL(cfi_qry_mode_off);
1032e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct cfi_extquery *
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_private *cfi = map->fldrv_priv;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u32 base = 0; // cfi->chips[0].start;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ofs_factor = cfi->interleave * cfi->device_type;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_extquery *extp = NULL;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!adr)
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
116087444da61ed972b3c2bfbf7dcf317cb4475f143Guillaume LECERF	printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr);
117087444da61ed972b3c2bfbf7dcf317cb4475f143Guillaume LECERF
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	extp = kmalloc(size, GFP_KERNEL);
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!extp) {
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "Failed to allocate memory\n");
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MTD_XIP
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_disable();
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switch it into Query Mode */
129c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	cfi_qry_mode_on(base, map, cfi);
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read in the Extended Query Table */
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i<size; i++) {
1321f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner		((unsigned char *)extp)[i] =
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cfi_read_query(map, base+((adr+i)*ofs_factor));
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Make sure it returns to read mode */
137c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	cfi_qry_mode_off(base, map, cfi);
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MTD_XIP
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(void) map_read(map, base);
141ca5c23c3b8882d61bf19b7685f2244501902869fPaulius Zaleckas	xip_iprefetch();
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_enable();
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out:	return extp;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cfi_read_pri);
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct map_info *map = mtd->priv;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_private *cfi = map->fldrv_priv;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_fixup *f;
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (f=fixups; f->fixup; f++) {
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
159cc31822250236ec173bb2aa149ebe2ba35405db2Guillaume LECERF			f->fixup(mtd);
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cfi_fixup);
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     loff_t ofs, size_t len, void *thunk)
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct map_info *map = mtd->priv;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_private *cfi = map->fldrv_priv;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long adr;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int chipnum, ret = 0;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, first;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct mtd_erase_region_info *regions = mtd->eraseregions;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check that both start and end of the requested erase are
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * aligned with the erasesize at the appropriate addresses.
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = 0;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner	/* Skip all erase regions which are ended before the start of
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   the requested erase. Actually, to save on the calculations,
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   we skip to the first erase region which starts after the
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   start of the requested erase, and then go back one.
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
1871f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (i < mtd->numeraseregions && ofs >= regions[i].offset)
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       i++;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i--;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1921f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner	/* OK, now i is pointing at the erase region in which this
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   erase request starts. Check the start of the requested
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   erase range is aligned with the erase size which is in
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   effect here.
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ofs & (regions[i].erasesize-1))
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Remember the erase region we start on */
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	first = i;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Next, check that the end of the requested erase is aligned
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * with the erase region at that address.
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i++;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* As before, drop back one to point at the region in which
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   the address actually falls
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i--;
2151f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ofs + len) & (regions[i].erasesize-1))
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chipnum = ofs >> cfi->chipshift;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adr = ofs - (chipnum << cfi->chipshift);
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i=first;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while(len) {
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int size = regions[i].erasesize;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
2281f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ret)
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return ret;
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		adr += size;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ofs += size;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len -= size;
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ofs == regions[i].offset + size * regions[i].numblocks)
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			i++;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (adr >> cfi->chipshift) {
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			adr = 0;
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			chipnum++;
2421f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (chipnum >= cfi->numchips)
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cfi_varsize_frob);
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
254