[PATCH] RPC: kick off socket connect operations faster
[linux-2.6/verdex.git] / drivers / mtd / chips / cfi_util.c
blob2b2ede2bfccaeca3f9e0e47f2869d501b1633abf
1 /*
2 * Common Flash Interface support:
3 * Generic utility functions not dependant on command set
5 * Copyright (C) 2002 Red Hat
6 * Copyright (C) 2003 STMicroelectronics Limited
8 * This code is covered by the GPL.
10 * $Id: cfi_util.c,v 1.8 2004/12/14 19:55:56 nico Exp $
14 #include <linux/module.h>
15 #include <linux/types.h>
16 #include <linux/kernel.h>
17 #include <linux/sched.h>
18 #include <asm/io.h>
19 #include <asm/byteorder.h>
21 #include <linux/errno.h>
22 #include <linux/slab.h>
23 #include <linux/delay.h>
24 #include <linux/interrupt.h>
25 #include <linux/mtd/xip.h>
26 #include <linux/mtd/mtd.h>
27 #include <linux/mtd/map.h>
28 #include <linux/mtd/cfi.h>
29 #include <linux/mtd/compatmac.h>
31 struct cfi_extquery *
32 __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
34 struct cfi_private *cfi = map->fldrv_priv;
35 __u32 base = 0; // cfi->chips[0].start;
36 int ofs_factor = cfi->interleave * cfi->device_type;
37 int i;
38 struct cfi_extquery *extp = NULL;
40 printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
41 if (!adr)
42 goto out;
44 extp = kmalloc(size, GFP_KERNEL);
45 if (!extp) {
46 printk(KERN_ERR "Failed to allocate memory\n");
47 goto out;
50 #ifdef CONFIG_MTD_XIP
51 local_irq_disable();
52 #endif
54 /* Switch it into Query Mode */
55 cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
57 /* Read in the Extended Query Table */
58 for (i=0; i<size; i++) {
59 ((unsigned char *)extp)[i] =
60 cfi_read_query(map, base+((adr+i)*ofs_factor));
63 /* Make sure it returns to read mode */
64 cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
65 cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL);
67 #ifdef CONFIG_MTD_XIP
68 (void) map_read(map, base);
69 asm volatile (".rep 8; nop; .endr");
70 local_irq_enable();
71 #endif
73 if (extp->MajorVersion != '1' ||
74 (extp->MinorVersion < '0' || extp->MinorVersion > '3')) {
75 printk(KERN_WARNING " Unknown %s Extended Query "
76 "version %c.%c.\n", name, extp->MajorVersion,
77 extp->MinorVersion);
78 kfree(extp);
79 extp = NULL;
82 out: return extp;
85 EXPORT_SYMBOL(cfi_read_pri);
87 void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
89 struct map_info *map = mtd->priv;
90 struct cfi_private *cfi = map->fldrv_priv;
91 struct cfi_fixup *f;
93 for (f=fixups; f->fixup; f++) {
94 if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
95 ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) {
96 f->fixup(mtd, f->param);
101 EXPORT_SYMBOL(cfi_fixup);
103 int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
104 loff_t ofs, size_t len, void *thunk)
106 struct map_info *map = mtd->priv;
107 struct cfi_private *cfi = map->fldrv_priv;
108 unsigned long adr;
109 int chipnum, ret = 0;
110 int i, first;
111 struct mtd_erase_region_info *regions = mtd->eraseregions;
113 if (ofs > mtd->size)
114 return -EINVAL;
116 if ((len + ofs) > mtd->size)
117 return -EINVAL;
119 /* Check that both start and end of the requested erase are
120 * aligned with the erasesize at the appropriate addresses.
123 i = 0;
125 /* Skip all erase regions which are ended before the start of
126 the requested erase. Actually, to save on the calculations,
127 we skip to the first erase region which starts after the
128 start of the requested erase, and then go back one.
131 while (i < mtd->numeraseregions && ofs >= regions[i].offset)
132 i++;
133 i--;
135 /* OK, now i is pointing at the erase region in which this
136 erase request starts. Check the start of the requested
137 erase range is aligned with the erase size which is in
138 effect here.
141 if (ofs & (regions[i].erasesize-1))
142 return -EINVAL;
144 /* Remember the erase region we start on */
145 first = i;
147 /* Next, check that the end of the requested erase is aligned
148 * with the erase region at that address.
151 while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
152 i++;
154 /* As before, drop back one to point at the region in which
155 the address actually falls
157 i--;
159 if ((ofs + len) & (regions[i].erasesize-1))
160 return -EINVAL;
162 chipnum = ofs >> cfi->chipshift;
163 adr = ofs - (chipnum << cfi->chipshift);
165 i=first;
167 while(len) {
168 int size = regions[i].erasesize;
170 ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
172 if (ret)
173 return ret;
175 adr += size;
176 ofs += size;
177 len -= size;
179 if (ofs == regions[i].offset + size * regions[i].numblocks)
180 i++;
182 if (adr >> cfi->chipshift) {
183 adr = 0;
184 chipnum++;
186 if (chipnum >= cfi->numchips)
187 break;
191 return 0;
194 EXPORT_SYMBOL(cfi_varsize_frob);
196 MODULE_LICENSE("GPL");