Merge with /shiny/git/linux-2.6/.git
[linux-2.6] / 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  * $Id: cfi_util.c,v 1.8 2004/12/14 19:55:56 nico Exp $
11  *
12  */
13
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>
20
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>
30
31 struct cfi_extquery *
32 __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
33 {
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;
39
40         printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
41         if (!adr)
42                 goto out;
43
44         extp = kmalloc(size, GFP_KERNEL);
45         if (!extp) {
46                 printk(KERN_ERR "Failed to allocate memory\n");
47                 goto out;
48         }
49
50 #ifdef CONFIG_MTD_XIP
51         local_irq_disable();
52 #endif
53
54         /* Switch it into Query Mode */
55         cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
56
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));
61         }
62
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);
66
67 #ifdef CONFIG_MTD_XIP
68         (void) map_read(map, base);
69         asm volatile (".rep 8; nop; .endr");
70         local_irq_enable();
71 #endif
72
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;
80         }
81
82  out:   return extp;
83 }
84
85 EXPORT_SYMBOL(cfi_read_pri);
86
87 void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
88 {
89         struct map_info *map = mtd->priv;
90         struct cfi_private *cfi = map->fldrv_priv;
91         struct cfi_fixup *f;
92
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);
97                 }
98         }
99 }
100
101 EXPORT_SYMBOL(cfi_fixup);
102
103 int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
104                                      loff_t ofs, size_t len, void *thunk)
105 {
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;
112
113         if (ofs > mtd->size)
114                 return -EINVAL;
115
116         if ((len + ofs) > mtd->size)
117                 return -EINVAL;
118
119         /* Check that both start and end of the requested erase are
120          * aligned with the erasesize at the appropriate addresses.
121          */
122
123         i = 0;
124
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.
129         */
130         
131         while (i < mtd->numeraseregions && ofs >= regions[i].offset)
132                i++;
133         i--;
134
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.
139         */
140
141         if (ofs & (regions[i].erasesize-1))
142                 return -EINVAL;
143
144         /* Remember the erase region we start on */
145         first = i;
146
147         /* Next, check that the end of the requested erase is aligned
148          * with the erase region at that address.
149          */
150
151         while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
152                 i++;
153
154         /* As before, drop back one to point at the region in which
155            the address actually falls
156         */
157         i--;
158         
159         if ((ofs + len) & (regions[i].erasesize-1))
160                 return -EINVAL;
161
162         chipnum = ofs >> cfi->chipshift;
163         adr = ofs - (chipnum << cfi->chipshift);
164
165         i=first;
166
167         while(len) {
168                 int size = regions[i].erasesize;
169
170                 ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
171                 
172                 if (ret)
173                         return ret;
174
175                 adr += size;
176                 ofs += size;
177                 len -= size;
178
179                 if (ofs == regions[i].offset + size * regions[i].numblocks)
180                         i++;
181
182                 if (adr >> cfi->chipshift) {
183                         adr = 0;
184                         chipnum++;
185                         
186                         if (chipnum >= cfi->numchips)
187                         break;
188                 }
189         }
190
191         return 0;
192 }
193
194 EXPORT_SYMBOL(cfi_varsize_frob);
195
196 MODULE_LICENSE("GPL");