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