]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/mtd/chips/cfi_util.c
[MTD] [NOR] Add qry_mode_on()/qry_omde_off() to deal with odd chips
[linux-2.6-omap-h63xx.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 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
54 int __xipram qry_mode_on(uint32_t base, struct map_info *map,
55                                 struct cfi_private *cfi)
56 {
57         cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
58         cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
59         if (qry_present(map, base, cfi))
60                 return 1;
61         /* QRY not found probably we deal with some odd CFI chips */
62         /* Some revisions of some old Intel chips? */
63         cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
64         cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
65         cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
66         if (qry_present(map, base, cfi))
67                 return 1;
68         /* ST M29DW chips */
69         cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
70         cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
71         if (qry_present(map, base, cfi))
72                 return 1;
73         /* QRY not found */
74         return 0;
75 }
76 void __xipram qry_mode_off(uint32_t base, struct map_info *map,
77                                 struct cfi_private *cfi)
78 {
79         cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
80         cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
81 }
82
83 struct cfi_extquery *
84 __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
85 {
86         struct cfi_private *cfi = map->fldrv_priv;
87         __u32 base = 0; // cfi->chips[0].start;
88         int ofs_factor = cfi->interleave * cfi->device_type;
89         int i;
90         struct cfi_extquery *extp = NULL;
91
92         printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
93         if (!adr)
94                 goto out;
95
96         extp = kmalloc(size, GFP_KERNEL);
97         if (!extp) {
98                 printk(KERN_ERR "Failed to allocate memory\n");
99                 goto out;
100         }
101
102 #ifdef CONFIG_MTD_XIP
103         local_irq_disable();
104 #endif
105
106         /* Switch it into Query Mode */
107         qry_mode_on(base, map, cfi);
108         /* Read in the Extended Query Table */
109         for (i=0; i<size; i++) {
110                 ((unsigned char *)extp)[i] =
111                         cfi_read_query(map, base+((adr+i)*ofs_factor));
112         }
113
114         /* Make sure it returns to read mode */
115         qry_mode_off(base, map, cfi);
116
117 #ifdef CONFIG_MTD_XIP
118         (void) map_read(map, base);
119         xip_iprefetch();
120         local_irq_enable();
121 #endif
122
123  out:   return extp;
124 }
125
126 EXPORT_SYMBOL(cfi_read_pri);
127
128 void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
129 {
130         struct map_info *map = mtd->priv;
131         struct cfi_private *cfi = map->fldrv_priv;
132         struct cfi_fixup *f;
133
134         for (f=fixups; f->fixup; f++) {
135                 if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
136                     ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
137                         f->fixup(mtd, f->param);
138                 }
139         }
140 }
141
142 EXPORT_SYMBOL(cfi_fixup);
143
144 int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
145                                      loff_t ofs, size_t len, void *thunk)
146 {
147         struct map_info *map = mtd->priv;
148         struct cfi_private *cfi = map->fldrv_priv;
149         unsigned long adr;
150         int chipnum, ret = 0;
151         int i, first;
152         struct mtd_erase_region_info *regions = mtd->eraseregions;
153
154         if (ofs > mtd->size)
155                 return -EINVAL;
156
157         if ((len + ofs) > mtd->size)
158                 return -EINVAL;
159
160         /* Check that both start and end of the requested erase are
161          * aligned with the erasesize at the appropriate addresses.
162          */
163
164         i = 0;
165
166         /* Skip all erase regions which are ended before the start of
167            the requested erase. Actually, to save on the calculations,
168            we skip to the first erase region which starts after the
169            start of the requested erase, and then go back one.
170         */
171
172         while (i < mtd->numeraseregions && ofs >= regions[i].offset)
173                i++;
174         i--;
175
176         /* OK, now i is pointing at the erase region in which this
177            erase request starts. Check the start of the requested
178            erase range is aligned with the erase size which is in
179            effect here.
180         */
181
182         if (ofs & (regions[i].erasesize-1))
183                 return -EINVAL;
184
185         /* Remember the erase region we start on */
186         first = i;
187
188         /* Next, check that the end of the requested erase is aligned
189          * with the erase region at that address.
190          */
191
192         while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
193                 i++;
194
195         /* As before, drop back one to point at the region in which
196            the address actually falls
197         */
198         i--;
199
200         if ((ofs + len) & (regions[i].erasesize-1))
201                 return -EINVAL;
202
203         chipnum = ofs >> cfi->chipshift;
204         adr = ofs - (chipnum << cfi->chipshift);
205
206         i=first;
207
208         while(len) {
209                 int size = regions[i].erasesize;
210
211                 ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
212
213                 if (ret)
214                         return ret;
215
216                 adr += size;
217                 ofs += size;
218                 len -= size;
219
220                 if (ofs == regions[i].offset + size * regions[i].numblocks)
221                         i++;
222
223                 if (adr >> cfi->chipshift) {
224                         adr = 0;
225                         chipnum++;
226
227                         if (chipnum >= cfi->numchips)
228                         break;
229                 }
230         }
231
232         return 0;
233 }
234
235 EXPORT_SYMBOL(cfi_varsize_frob);
236
237 MODULE_LICENSE("GPL");