Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / mtd / chips / cfi_util.c
1 /*
2  * Common Flash Interface support:
3  *   Generic utility functions not dependant on command set
4  *
5  * Copyright (C) 2002 Red Hat
6  * Copyright (C) 2003 STMicroelectronics Limited
7  *
8  * This code is covered by the GPL.
9  */
10
11 #include <linux/module.h>
12 #include <linux/types.h>
13 #include <linux/kernel.h>
14 #include <asm/io.h>
15 #include <asm/byteorder.h>
16
17 #include <linux/errno.h>
18 #include <linux/slab.h>
19 #include <linux/delay.h>
20 #include <linux/interrupt.h>
21 #include <linux/mtd/xip.h>
22 #include <linux/mtd/mtd.h>
23 #include <linux/mtd/map.h>
24 #include <linux/mtd/cfi.h>
25 #include <linux/mtd/compatmac.h>
26
27 int __xipram cfi_qry_present(struct map_info *map, __u32 base,
28                              struct cfi_private *cfi)
29 {
30         int osf = cfi->interleave * cfi->device_type;   /* scale factor */
31         map_word val[3];
32         map_word qry[3];
33
34         qry[0] = cfi_build_cmd('Q', map, cfi);
35         qry[1] = cfi_build_cmd('R', map, cfi);
36         qry[2] = cfi_build_cmd('Y', map, cfi);
37
38         val[0] = map_read(map, base + osf*0x10);
39         val[1] = map_read(map, base + osf*0x11);
40         val[2] = map_read(map, base + osf*0x12);
41
42         if (!map_word_equal(map, qry[0], val[0]))
43                 return 0;
44
45         if (!map_word_equal(map, qry[1], val[1]))
46                 return 0;
47
48         if (!map_word_equal(map, qry[2], val[2]))
49                 return 0;
50
51         return 1;       /* "QRY" found */
52 }
53 EXPORT_SYMBOL_GPL(cfi_qry_present);
54
55 int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
56                              struct cfi_private *cfi)
57 {
58         cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
59         cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
60         if (cfi_qry_present(map, base, cfi))
61                 return 1;
62         /* QRY not found probably we deal with some odd CFI chips */
63         /* Some revisions of some old Intel chips? */
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);
66         cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
67         if (cfi_qry_present(map, base, cfi))
68                 return 1;
69         /* ST M29DW chips */
70         cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
71         cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
72         if (cfi_qry_present(map, base, cfi))
73                 return 1;
74         /* QRY not found */
75         return 0;
76 }
77 EXPORT_SYMBOL_GPL(cfi_qry_mode_on);
78
79 void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map,
80                                struct cfi_private *cfi)
81 {
82         cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
83         cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
84         /* M29W128G flashes require an additional reset command
85            when exit qry mode */
86         if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E))
87                 cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
88 }
89 EXPORT_SYMBOL_GPL(cfi_qry_mode_off);
90
91 struct cfi_extquery *
92 __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
93 {
94         struct cfi_private *cfi = map->fldrv_priv;
95         __u32 base = 0; // cfi->chips[0].start;
96         int ofs_factor = cfi->interleave * cfi->device_type;
97         int i;
98         struct cfi_extquery *extp = NULL;
99
100         printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
101         if (!adr)
102                 goto out;
103
104         extp = kmalloc(size, GFP_KERNEL);
105         if (!extp) {
106                 printk(KERN_ERR "Failed to allocate memory\n");
107                 goto out;
108         }
109
110 #ifdef CONFIG_MTD_XIP
111         local_irq_disable();
112 #endif
113
114         /* Switch it into Query Mode */
115         cfi_qry_mode_on(base, map, cfi);
116         /* Read in the Extended Query Table */
117         for (i=0; i<size; i++) {
118                 ((unsigned char *)extp)[i] =
119                         cfi_read_query(map, base+((adr+i)*ofs_factor));
120         }
121
122         /* Make sure it returns to read mode */
123         cfi_qry_mode_off(base, map, cfi);
124
125 #ifdef CONFIG_MTD_XIP
126         (void) map_read(map, base);
127         xip_iprefetch();
128         local_irq_enable();
129 #endif
130
131  out:   return extp;
132 }
133
134 EXPORT_SYMBOL(cfi_read_pri);
135
136 void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
137 {
138         struct map_info *map = mtd->priv;
139         struct cfi_private *cfi = map->fldrv_priv;
140         struct cfi_fixup *f;
141
142         for (f=fixups; f->fixup; f++) {
143                 if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
144                     ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
145                         f->fixup(mtd, f->param);
146                 }
147         }
148 }
149
150 EXPORT_SYMBOL(cfi_fixup);
151
152 int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
153                                      loff_t ofs, size_t len, void *thunk)
154 {
155         struct map_info *map = mtd->priv;
156         struct cfi_private *cfi = map->fldrv_priv;
157         unsigned long adr;
158         int chipnum, ret = 0;
159         int i, first;
160         struct mtd_erase_region_info *regions = mtd->eraseregions;
161
162         if (ofs > mtd->size)
163                 return -EINVAL;
164
165         if ((len + ofs) > mtd->size)
166                 return -EINVAL;
167
168         /* Check that both start and end of the requested erase are
169          * aligned with the erasesize at the appropriate addresses.
170          */
171
172         i = 0;
173
174         /* Skip all erase regions which are ended before the start of
175            the requested erase. Actually, to save on the calculations,
176            we skip to the first erase region which starts after the
177            start of the requested erase, and then go back one.
178         */
179
180         while (i < mtd->numeraseregions && ofs >= regions[i].offset)
181                i++;
182         i--;
183
184         /* OK, now i is pointing at the erase region in which this
185            erase request starts. Check the start of the requested
186            erase range is aligned with the erase size which is in
187            effect here.
188         */
189
190         if (ofs & (regions[i].erasesize-1))
191                 return -EINVAL;
192
193         /* Remember the erase region we start on */
194         first = i;
195
196         /* Next, check that the end of the requested erase is aligned
197          * with the erase region at that address.
198          */
199
200         while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
201                 i++;
202
203         /* As before, drop back one to point at the region in which
204            the address actually falls
205         */
206         i--;
207
208         if ((ofs + len) & (regions[i].erasesize-1))
209                 return -EINVAL;
210
211         chipnum = ofs >> cfi->chipshift;
212         adr = ofs - (chipnum << cfi->chipshift);
213
214         i=first;
215
216         while(len) {
217                 int size = regions[i].erasesize;
218
219                 ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
220
221                 if (ret)
222                         return ret;
223
224                 adr += size;
225                 ofs += size;
226                 len -= size;
227
228                 if (ofs == regions[i].offset + size * regions[i].numblocks)
229                         i++;
230
231                 if (adr >> cfi->chipshift) {
232                         adr = 0;
233                         chipnum++;
234
235                         if (chipnum >= cfi->numchips)
236                         break;
237                 }
238         }
239
240         return 0;
241 }
242
243 EXPORT_SYMBOL(cfi_varsize_frob);
244
245 MODULE_LICENSE("GPL");