]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/mtd/chips/cfi_util.c
Linux-2.6.12-rc2
[mirror_ubuntu-artful-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 * $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");