cfi_util.c revision 23af51ecfb04ff65bae51bd8e2270f4449abc789
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Common Flash Interface support:
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *   Generic utility functions not dependant 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#include <linux/mtd/compatmac.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouseint __xipram cfi_qry_present(struct map_info *map, __u32 base,
28c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse			     struct cfi_private *cfi)
292e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev{
302e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	int osf = cfi->interleave * cfi->device_type;	/* scale factor */
312e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	map_word val[3];
322e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	map_word qry[3];
332e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
342e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	qry[0] = cfi_build_cmd('Q', map, cfi);
352e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	qry[1] = cfi_build_cmd('R', map, cfi);
362e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	qry[2] = cfi_build_cmd('Y', map, cfi);
372e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
382e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	val[0] = map_read(map, base + osf*0x10);
392e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	val[1] = map_read(map, base + osf*0x11);
402e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	val[2] = map_read(map, base + osf*0x12);
412e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
422e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	if (!map_word_equal(map, qry[0], val[0]))
432e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 0;
442e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
452e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	if (!map_word_equal(map, qry[1], val[1]))
462e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 0;
472e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
482e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	if (!map_word_equal(map, qry[2], val[2]))
492e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 0;
502e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
512e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	return 1; 	/* "QRY" found */
522e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev}
53c314dfdc358847eef0fc07ec8682e1acc8cadd00David WoodhouseEXPORT_SYMBOL_GPL(cfi_qry_present);
542e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
55c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouseint __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
56c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse			     struct cfi_private *cfi)
572e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev{
582e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
592e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
60c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	if (cfi_qry_present(map, base, cfi))
612e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 1;
622e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	/* QRY not found probably we deal with some odd CFI chips */
632e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	/* Some revisions of some old Intel chips? */
642e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
652e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
662e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
67c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	if (cfi_qry_present(map, base, cfi))
682e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 1;
692e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	/* ST M29DW chips */
702e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
712e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
72c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	if (cfi_qry_present(map, base, cfi))
732e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev		return 1;
742e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	/* QRY not found */
752e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	return 0;
762e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev}
77c314dfdc358847eef0fc07ec8682e1acc8cadd00David WoodhouseEXPORT_SYMBOL_GPL(cfi_qry_mode_on);
78c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse
79c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhousevoid __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map,
80c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse			       struct cfi_private *cfi)
812e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev{
822e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
832e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
8423af51ecfb04ff65bae51bd8e2270f4449abc789Massimo Cirillo	/* M29W128G flashes require an additional reset command
8523af51ecfb04ff65bae51bd8e2270f4449abc789Massimo Cirillo	   when exit qry mode */
8623af51ecfb04ff65bae51bd8e2270f4449abc789Massimo Cirillo	if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E))
8723af51ecfb04ff65bae51bd8e2270f4449abc789Massimo Cirillo		cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
882e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev}
89c314dfdc358847eef0fc07ec8682e1acc8cadd00David WoodhouseEXPORT_SYMBOL_GPL(cfi_qry_mode_off);
902e489e077a6ad118c4f247faedf330117b107cceAlexey Korolev
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct cfi_extquery *
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_private *cfi = map->fldrv_priv;
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	__u32 base = 0; // cfi->chips[0].start;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ofs_factor = cfi->interleave * cfi->device_type;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_extquery *extp = NULL;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!adr)
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	extp = kmalloc(size, GFP_KERNEL);
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!extp) {
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "Failed to allocate memory\n");
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MTD_XIP
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_disable();
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Switch it into Query Mode */
115c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	cfi_qry_mode_on(base, map, cfi);
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read in the Extended Query Table */
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i<size; i++) {
1181f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner		((unsigned char *)extp)[i] =
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cfi_read_query(map, base+((adr+i)*ofs_factor));
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Make sure it returns to read mode */
123c314dfdc358847eef0fc07ec8682e1acc8cadd00David Woodhouse	cfi_qry_mode_off(base, map, cfi);
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_MTD_XIP
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(void) map_read(map, base);
127ca5c23c3b8882d61bf19b7685f2244501902869fPaulius Zaleckas	xip_iprefetch();
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_enable();
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out:	return extp;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cfi_read_pri);
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct map_info *map = mtd->priv;
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_private *cfi = map->fldrv_priv;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_fixup *f;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (f=fixups; f->fixup; f++) {
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			f->fixup(mtd, f->param);
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cfi_fixup);
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     loff_t ofs, size_t len, void *thunk)
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct map_info *map = mtd->priv;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct cfi_private *cfi = map->fldrv_priv;
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long adr;
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int chipnum, ret = 0;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, first;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct mtd_erase_region_info *regions = mtd->eraseregions;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ofs > mtd->size)
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((len + ofs) > mtd->size)
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check that both start and end of the requested erase are
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * aligned with the erasesize at the appropriate addresses.
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = 0;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1741f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner	/* Skip all erase regions which are ended before the start of
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   the requested erase. Actually, to save on the calculations,
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   we skip to the first erase region which starts after the
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   start of the requested erase, and then go back one.
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
1791f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (i < mtd->numeraseregions && ofs >= regions[i].offset)
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       i++;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i--;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1841f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner	/* OK, now i is pointing at the erase region in which this
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   erase request starts. Check the start of the requested
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   erase range is aligned with the erase size which is in
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   effect here.
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ofs & (regions[i].erasesize-1))
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Remember the erase region we start on */
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	first = i;
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Next, check that the end of the requested erase is aligned
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * with the erase region at that address.
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		i++;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* As before, drop back one to point at the region in which
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   the address actually falls
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i--;
2071f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ofs + len) & (regions[i].erasesize-1))
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chipnum = ofs >> cfi->chipshift;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	adr = ofs - (chipnum << cfi->chipshift);
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i=first;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while(len) {
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int size = regions[i].erasesize;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
2201f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ret)
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return ret;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		adr += size;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ofs += size;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len -= size;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ofs == regions[i].offset + size * regions[i].numblocks)
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			i++;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (adr >> cfi->chipshift) {
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			adr = 0;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			chipnum++;
2341f948b43f7b5cf721cf0d03f507843efc1a9bfadThomas Gleixner
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (chipnum >= cfi->numchips)
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(cfi_varsize_frob);
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
246