Merge branch 'master' of /home/tglx/work/mtd/git/linux-2.6.git/
[sfrench/cifs-2.6.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  * $Id: cfi_util.c,v 1.9 2005/07/20 21:01:14 tpoynor 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  out:   return extp;
74 }
75
76 EXPORT_SYMBOL(cfi_read_pri);
77
78 void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
79 {
80         struct map_info *map = mtd->priv;
81         struct cfi_private *cfi = map->fldrv_priv;
82         struct cfi_fixup *f;
83
84         for (f=fixups; f->fixup; f++) {
85                 if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
86                     ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
87                         f->fixup(mtd, f->param);
88                 }
89         }
90 }
91
92 EXPORT_SYMBOL(cfi_fixup);
93
94 int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
95                                      loff_t ofs, size_t len, void *thunk)
96 {
97         struct map_info *map = mtd->priv;
98         struct cfi_private *cfi = map->fldrv_priv;
99         unsigned long adr;
100         int chipnum, ret = 0;
101         int i, first;
102         struct mtd_erase_region_info *regions = mtd->eraseregions;
103
104         if (ofs > mtd->size)
105                 return -EINVAL;
106
107         if ((len + ofs) > mtd->size)
108                 return -EINVAL;
109
110         /* Check that both start and end of the requested erase are
111          * aligned with the erasesize at the appropriate addresses.
112          */
113
114         i = 0;
115
116         /* Skip all erase regions which are ended before the start of 
117            the requested erase. Actually, to save on the calculations,
118            we skip to the first erase region which starts after the
119            start of the requested erase, and then go back one.
120         */
121         
122         while (i < mtd->numeraseregions && ofs >= regions[i].offset)
123                i++;
124         i--;
125
126         /* OK, now i is pointing at the erase region in which this 
127            erase request starts. Check the start of the requested
128            erase range is aligned with the erase size which is in
129            effect here.
130         */
131
132         if (ofs & (regions[i].erasesize-1))
133                 return -EINVAL;
134
135         /* Remember the erase region we start on */
136         first = i;
137
138         /* Next, check that the end of the requested erase is aligned
139          * with the erase region at that address.
140          */
141
142         while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
143                 i++;
144
145         /* As before, drop back one to point at the region in which
146            the address actually falls
147         */
148         i--;
149         
150         if ((ofs + len) & (regions[i].erasesize-1))
151                 return -EINVAL;
152
153         chipnum = ofs >> cfi->chipshift;
154         adr = ofs - (chipnum << cfi->chipshift);
155
156         i=first;
157
158         while(len) {
159                 int size = regions[i].erasesize;
160
161                 ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
162                 
163                 if (ret)
164                         return ret;
165
166                 adr += size;
167                 ofs += size;
168                 len -= size;
169
170                 if (ofs == regions[i].offset + size * regions[i].numblocks)
171                         i++;
172
173                 if (adr >> cfi->chipshift) {
174                         adr = 0;
175                         chipnum++;
176                         
177                         if (chipnum >= cfi->numchips)
178                         break;
179                 }
180         }
181
182         return 0;
183 }
184
185 EXPORT_SYMBOL(cfi_varsize_frob);
186
187 MODULE_LICENSE("GPL");