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