18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Common Flash Interface support: 38c2ecf20Sopenharmony_ci * Generic utility functions not dependent on command set 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2002 Red Hat 68c2ecf20Sopenharmony_ci * Copyright (C) 2003 STMicroelectronics Limited 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This code is covered by the GPL. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <asm/io.h> 158c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/mtd/xip.h> 228c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 238c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 248c2ecf20Sopenharmony_ci#include <linux/mtd/cfi.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_civoid cfi_udelay(int us) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci if (us >= 1000) { 298c2ecf20Sopenharmony_ci msleep(DIV_ROUND_UP(us, 1000)); 308c2ecf20Sopenharmony_ci } else { 318c2ecf20Sopenharmony_ci udelay(us); 328c2ecf20Sopenharmony_ci cond_resched(); 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfi_udelay); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * Returns the command address according to the given geometry. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ciuint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, 418c2ecf20Sopenharmony_ci struct map_info *map, struct cfi_private *cfi) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned bankwidth = map_bankwidth(map); 448c2ecf20Sopenharmony_ci unsigned interleave = cfi_interleave(cfi); 458c2ecf20Sopenharmony_ci unsigned type = cfi->device_type; 468c2ecf20Sopenharmony_ci uint32_t addr; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci addr = (cmd_ofs * type) * interleave; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* Modify the unlock address if we are in compatibility mode. 518c2ecf20Sopenharmony_ci * For 16bit devices on 8 bit busses 528c2ecf20Sopenharmony_ci * and 32bit devices on 16 bit busses 538c2ecf20Sopenharmony_ci * set the low bit of the alternating bit sequence of the address. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci if (((type * interleave) > bankwidth) && ((cmd_ofs & 0xff) == 0xaa)) 568c2ecf20Sopenharmony_ci addr |= (type >> 1)*interleave; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return addr; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfi_build_cmd_addr); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * Transforms the CFI command for the given geometry (bus width & interleave). 648c2ecf20Sopenharmony_ci * It looks too long to be inline, but in the common case it should almost all 658c2ecf20Sopenharmony_ci * get optimised away. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cimap_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci map_word val = { {0} }; 708c2ecf20Sopenharmony_ci int wordwidth, words_per_bus, chip_mode, chips_per_word; 718c2ecf20Sopenharmony_ci unsigned long onecmd; 728c2ecf20Sopenharmony_ci int i; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* We do it this way to give the compiler a fighting chance 758c2ecf20Sopenharmony_ci of optimising away all the crap for 'bankwidth' larger than 768c2ecf20Sopenharmony_ci an unsigned long, in the common case where that support is 778c2ecf20Sopenharmony_ci disabled */ 788c2ecf20Sopenharmony_ci if (map_bankwidth_is_large(map)) { 798c2ecf20Sopenharmony_ci wordwidth = sizeof(unsigned long); 808c2ecf20Sopenharmony_ci words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1 818c2ecf20Sopenharmony_ci } else { 828c2ecf20Sopenharmony_ci wordwidth = map_bankwidth(map); 838c2ecf20Sopenharmony_ci words_per_bus = 1; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci chip_mode = map_bankwidth(map) / cfi_interleave(cfi); 878c2ecf20Sopenharmony_ci chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* First, determine what the bit-pattern should be for a single 908c2ecf20Sopenharmony_ci device, according to chip mode and endianness... */ 918c2ecf20Sopenharmony_ci switch (chip_mode) { 928c2ecf20Sopenharmony_ci default: BUG(); 938c2ecf20Sopenharmony_ci case 1: 948c2ecf20Sopenharmony_ci onecmd = cmd; 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci case 2: 978c2ecf20Sopenharmony_ci onecmd = cpu_to_cfi16(map, cmd); 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci case 4: 1008c2ecf20Sopenharmony_ci onecmd = cpu_to_cfi32(map, cmd); 1018c2ecf20Sopenharmony_ci break; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Now replicate it across the size of an unsigned long, or 1058c2ecf20Sopenharmony_ci just to the bus width as appropriate */ 1068c2ecf20Sopenharmony_ci switch (chips_per_word) { 1078c2ecf20Sopenharmony_ci default: BUG(); 1088c2ecf20Sopenharmony_ci#if BITS_PER_LONG >= 64 1098c2ecf20Sopenharmony_ci case 8: 1108c2ecf20Sopenharmony_ci onecmd |= (onecmd << (chip_mode * 32)); 1118c2ecf20Sopenharmony_ci#endif 1128c2ecf20Sopenharmony_ci fallthrough; 1138c2ecf20Sopenharmony_ci case 4: 1148c2ecf20Sopenharmony_ci onecmd |= (onecmd << (chip_mode * 16)); 1158c2ecf20Sopenharmony_ci fallthrough; 1168c2ecf20Sopenharmony_ci case 2: 1178c2ecf20Sopenharmony_ci onecmd |= (onecmd << (chip_mode * 8)); 1188c2ecf20Sopenharmony_ci fallthrough; 1198c2ecf20Sopenharmony_ci case 1: 1208c2ecf20Sopenharmony_ci ; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* And finally, for the multi-word case, replicate it 1248c2ecf20Sopenharmony_ci in all words in the structure */ 1258c2ecf20Sopenharmony_ci for (i=0; i < words_per_bus; i++) { 1268c2ecf20Sopenharmony_ci val.x[i] = onecmd; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return val; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfi_build_cmd); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ciunsigned long cfi_merge_status(map_word val, struct map_info *map, 1348c2ecf20Sopenharmony_ci struct cfi_private *cfi) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int wordwidth, words_per_bus, chip_mode, chips_per_word; 1378c2ecf20Sopenharmony_ci unsigned long onestat, res = 0; 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* We do it this way to give the compiler a fighting chance 1418c2ecf20Sopenharmony_ci of optimising away all the crap for 'bankwidth' larger than 1428c2ecf20Sopenharmony_ci an unsigned long, in the common case where that support is 1438c2ecf20Sopenharmony_ci disabled */ 1448c2ecf20Sopenharmony_ci if (map_bankwidth_is_large(map)) { 1458c2ecf20Sopenharmony_ci wordwidth = sizeof(unsigned long); 1468c2ecf20Sopenharmony_ci words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1 1478c2ecf20Sopenharmony_ci } else { 1488c2ecf20Sopenharmony_ci wordwidth = map_bankwidth(map); 1498c2ecf20Sopenharmony_ci words_per_bus = 1; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci chip_mode = map_bankwidth(map) / cfi_interleave(cfi); 1538c2ecf20Sopenharmony_ci chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci onestat = val.x[0]; 1568c2ecf20Sopenharmony_ci /* Or all status words together */ 1578c2ecf20Sopenharmony_ci for (i=1; i < words_per_bus; i++) { 1588c2ecf20Sopenharmony_ci onestat |= val.x[i]; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci res = onestat; 1628c2ecf20Sopenharmony_ci switch(chips_per_word) { 1638c2ecf20Sopenharmony_ci default: BUG(); 1648c2ecf20Sopenharmony_ci#if BITS_PER_LONG >= 64 1658c2ecf20Sopenharmony_ci case 8: 1668c2ecf20Sopenharmony_ci res |= (onestat >> (chip_mode * 32)); 1678c2ecf20Sopenharmony_ci#endif 1688c2ecf20Sopenharmony_ci fallthrough; 1698c2ecf20Sopenharmony_ci case 4: 1708c2ecf20Sopenharmony_ci res |= (onestat >> (chip_mode * 16)); 1718c2ecf20Sopenharmony_ci fallthrough; 1728c2ecf20Sopenharmony_ci case 2: 1738c2ecf20Sopenharmony_ci res |= (onestat >> (chip_mode * 8)); 1748c2ecf20Sopenharmony_ci fallthrough; 1758c2ecf20Sopenharmony_ci case 1: 1768c2ecf20Sopenharmony_ci ; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Last, determine what the bit-pattern should be for a single 1808c2ecf20Sopenharmony_ci device, according to chip mode and endianness... */ 1818c2ecf20Sopenharmony_ci switch (chip_mode) { 1828c2ecf20Sopenharmony_ci case 1: 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci case 2: 1858c2ecf20Sopenharmony_ci res = cfi16_to_cpu(map, res); 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci case 4: 1888c2ecf20Sopenharmony_ci res = cfi32_to_cpu(map, res); 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci default: BUG(); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci return res; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfi_merge_status); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * Sends a CFI command to a bank of flash for the given geometry. 1988c2ecf20Sopenharmony_ci * 1998c2ecf20Sopenharmony_ci * Returns the offset in flash where the command was written. 2008c2ecf20Sopenharmony_ci * If prev_val is non-null, it will be set to the value at the command address, 2018c2ecf20Sopenharmony_ci * before the command was written. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ciuint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base, 2048c2ecf20Sopenharmony_ci struct map_info *map, struct cfi_private *cfi, 2058c2ecf20Sopenharmony_ci int type, map_word *prev_val) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci map_word val; 2088c2ecf20Sopenharmony_ci uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, map, cfi); 2098c2ecf20Sopenharmony_ci val = cfi_build_cmd(cmd, map, cfi); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (prev_val) 2128c2ecf20Sopenharmony_ci *prev_val = map_read(map, addr); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci map_write(map, val, addr); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return addr - base; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfi_send_gen_cmd); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ciint __xipram cfi_qry_present(struct map_info *map, __u32 base, 2218c2ecf20Sopenharmony_ci struct cfi_private *cfi) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int osf = cfi->interleave * cfi->device_type; /* scale factor */ 2248c2ecf20Sopenharmony_ci map_word val[3]; 2258c2ecf20Sopenharmony_ci map_word qry[3]; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci qry[0] = cfi_build_cmd('Q', map, cfi); 2288c2ecf20Sopenharmony_ci qry[1] = cfi_build_cmd('R', map, cfi); 2298c2ecf20Sopenharmony_ci qry[2] = cfi_build_cmd('Y', map, cfi); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci val[0] = map_read(map, base + osf*0x10); 2328c2ecf20Sopenharmony_ci val[1] = map_read(map, base + osf*0x11); 2338c2ecf20Sopenharmony_ci val[2] = map_read(map, base + osf*0x12); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (!map_word_equal(map, qry[0], val[0])) 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (!map_word_equal(map, qry[1], val[1])) 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (!map_word_equal(map, qry[2], val[2])) 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return 1; /* "QRY" found */ 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_qry_present); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ciint __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map, 2498c2ecf20Sopenharmony_ci struct cfi_private *cfi) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); 2528c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); 2538c2ecf20Sopenharmony_ci if (cfi_qry_present(map, base, cfi)) 2548c2ecf20Sopenharmony_ci return 1; 2558c2ecf20Sopenharmony_ci /* QRY not found probably we deal with some odd CFI chips */ 2568c2ecf20Sopenharmony_ci /* Some revisions of some old Intel chips? */ 2578c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); 2588c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); 2598c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); 2608c2ecf20Sopenharmony_ci if (cfi_qry_present(map, base, cfi)) 2618c2ecf20Sopenharmony_ci return 1; 2628c2ecf20Sopenharmony_ci /* ST M29DW chips */ 2638c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); 2648c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL); 2658c2ecf20Sopenharmony_ci if (cfi_qry_present(map, base, cfi)) 2668c2ecf20Sopenharmony_ci return 1; 2678c2ecf20Sopenharmony_ci /* some old SST chips, e.g. 39VF160x/39VF320x */ 2688c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); 2698c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL); 2708c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL); 2718c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL); 2728c2ecf20Sopenharmony_ci if (cfi_qry_present(map, base, cfi)) 2738c2ecf20Sopenharmony_ci return 1; 2748c2ecf20Sopenharmony_ci /* SST 39VF640xB */ 2758c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); 2768c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xAA, 0x555, base, map, cfi, cfi->device_type, NULL); 2778c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x55, 0x2AA, base, map, cfi, cfi->device_type, NULL); 2788c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL); 2798c2ecf20Sopenharmony_ci if (cfi_qry_present(map, base, cfi)) 2808c2ecf20Sopenharmony_ci return 1; 2818c2ecf20Sopenharmony_ci /* QRY not found */ 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_qry_mode_on); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_civoid __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map, 2878c2ecf20Sopenharmony_ci struct cfi_private *cfi) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); 2908c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); 2918c2ecf20Sopenharmony_ci /* M29W128G flashes require an additional reset command 2928c2ecf20Sopenharmony_ci when exit qry mode */ 2938c2ecf20Sopenharmony_ci if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E)) 2948c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_qry_mode_off); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistruct cfi_extquery * 2998c2ecf20Sopenharmony_ci__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3028c2ecf20Sopenharmony_ci __u32 base = 0; // cfi->chips[0].start; 3038c2ecf20Sopenharmony_ci int ofs_factor = cfi->interleave * cfi->device_type; 3048c2ecf20Sopenharmony_ci int i; 3058c2ecf20Sopenharmony_ci struct cfi_extquery *extp = NULL; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!adr) 3088c2ecf20Sopenharmony_ci goto out; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci extp = kmalloc(size, GFP_KERNEL); 3138c2ecf20Sopenharmony_ci if (!extp) 3148c2ecf20Sopenharmony_ci goto out; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci#ifdef CONFIG_MTD_XIP 3178c2ecf20Sopenharmony_ci local_irq_disable(); 3188c2ecf20Sopenharmony_ci#endif 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* Switch it into Query Mode */ 3218c2ecf20Sopenharmony_ci cfi_qry_mode_on(base, map, cfi); 3228c2ecf20Sopenharmony_ci /* Read in the Extended Query Table */ 3238c2ecf20Sopenharmony_ci for (i=0; i<size; i++) { 3248c2ecf20Sopenharmony_ci ((unsigned char *)extp)[i] = 3258c2ecf20Sopenharmony_ci cfi_read_query(map, base+((adr+i)*ofs_factor)); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* Make sure it returns to read mode */ 3298c2ecf20Sopenharmony_ci cfi_qry_mode_off(base, map, cfi); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci#ifdef CONFIG_MTD_XIP 3328c2ecf20Sopenharmony_ci (void) map_read(map, base); 3338c2ecf20Sopenharmony_ci xip_iprefetch(); 3348c2ecf20Sopenharmony_ci local_irq_enable(); 3358c2ecf20Sopenharmony_ci#endif 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci out: return extp; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfi_read_pri); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_civoid cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3458c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3468c2ecf20Sopenharmony_ci struct cfi_fixup *f; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci for (f=fixups; f->fixup; f++) { 3498c2ecf20Sopenharmony_ci if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && 3508c2ecf20Sopenharmony_ci ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { 3518c2ecf20Sopenharmony_ci f->fixup(mtd); 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfi_fixup); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ciint cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, 3598c2ecf20Sopenharmony_ci loff_t ofs, size_t len, void *thunk) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3628c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3638c2ecf20Sopenharmony_ci unsigned long adr; 3648c2ecf20Sopenharmony_ci int chipnum, ret = 0; 3658c2ecf20Sopenharmony_ci int i, first; 3668c2ecf20Sopenharmony_ci struct mtd_erase_region_info *regions = mtd->eraseregions; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Check that both start and end of the requested erase are 3698c2ecf20Sopenharmony_ci * aligned with the erasesize at the appropriate addresses. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci i = 0; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* Skip all erase regions which are ended before the start of 3758c2ecf20Sopenharmony_ci the requested erase. Actually, to save on the calculations, 3768c2ecf20Sopenharmony_ci we skip to the first erase region which starts after the 3778c2ecf20Sopenharmony_ci start of the requested erase, and then go back one. 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci while (i < mtd->numeraseregions && ofs >= regions[i].offset) 3818c2ecf20Sopenharmony_ci i++; 3828c2ecf20Sopenharmony_ci i--; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* OK, now i is pointing at the erase region in which this 3858c2ecf20Sopenharmony_ci erase request starts. Check the start of the requested 3868c2ecf20Sopenharmony_ci erase range is aligned with the erase size which is in 3878c2ecf20Sopenharmony_ci effect here. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (ofs & (regions[i].erasesize-1)) 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* Remember the erase region we start on */ 3948c2ecf20Sopenharmony_ci first = i; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Next, check that the end of the requested erase is aligned 3978c2ecf20Sopenharmony_ci * with the erase region at that address. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset) 4018c2ecf20Sopenharmony_ci i++; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* As before, drop back one to point at the region in which 4048c2ecf20Sopenharmony_ci the address actually falls 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci i--; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if ((ofs + len) & (regions[i].erasesize-1)) 4098c2ecf20Sopenharmony_ci return -EINVAL; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci chipnum = ofs >> cfi->chipshift; 4128c2ecf20Sopenharmony_ci adr = ofs - (chipnum << cfi->chipshift); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci i=first; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci while(len) { 4178c2ecf20Sopenharmony_ci int size = regions[i].erasesize; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (ret) 4228c2ecf20Sopenharmony_ci return ret; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci adr += size; 4258c2ecf20Sopenharmony_ci ofs += size; 4268c2ecf20Sopenharmony_ci len -= size; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (ofs == regions[i].offset + size * regions[i].numblocks) 4298c2ecf20Sopenharmony_ci i++; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (adr >> cfi->chipshift) { 4328c2ecf20Sopenharmony_ci adr = 0; 4338c2ecf20Sopenharmony_ci chipnum++; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfi_varsize_frob); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 446