diff options
Diffstat (limited to 'drivers/mtd/chips/gen_probe.c')
-rw-r--r-- | drivers/mtd/chips/gen_probe.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c new file mode 100644 index 000000000000..fc982c4671f0 --- /dev/null +++ b/drivers/mtd/chips/gen_probe.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * Routines common to all CFI-type probes. | ||
3 | * (C) 2001-2003 Red Hat, Inc. | ||
4 | * GPL'd | ||
5 | * $Id: gen_probe.c,v 1.21 2004/08/14 15:14:05 dwmw2 Exp $ | ||
6 | */ | ||
7 | |||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/slab.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/mtd/mtd.h> | ||
12 | #include <linux/mtd/map.h> | ||
13 | #include <linux/mtd/cfi.h> | ||
14 | #include <linux/mtd/gen_probe.h> | ||
15 | |||
16 | static struct mtd_info *check_cmd_set(struct map_info *, int); | ||
17 | static struct cfi_private *genprobe_ident_chips(struct map_info *map, | ||
18 | struct chip_probe *cp); | ||
19 | static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, | ||
20 | struct cfi_private *cfi); | ||
21 | |||
22 | struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp) | ||
23 | { | ||
24 | struct mtd_info *mtd = NULL; | ||
25 | struct cfi_private *cfi; | ||
26 | |||
27 | /* First probe the map to see if we have CFI stuff there. */ | ||
28 | cfi = genprobe_ident_chips(map, cp); | ||
29 | |||
30 | if (!cfi) | ||
31 | return NULL; | ||
32 | |||
33 | map->fldrv_priv = cfi; | ||
34 | /* OK we liked it. Now find a driver for the command set it talks */ | ||
35 | |||
36 | mtd = check_cmd_set(map, 1); /* First the primary cmdset */ | ||
37 | if (!mtd) | ||
38 | mtd = check_cmd_set(map, 0); /* Then the secondary */ | ||
39 | |||
40 | if (mtd) | ||
41 | return mtd; | ||
42 | |||
43 | printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n"); | ||
44 | |||
45 | kfree(cfi->cfiq); | ||
46 | kfree(cfi); | ||
47 | map->fldrv_priv = NULL; | ||
48 | return NULL; | ||
49 | } | ||
50 | EXPORT_SYMBOL(mtd_do_chip_probe); | ||
51 | |||
52 | |||
53 | static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) | ||
54 | { | ||
55 | struct cfi_private cfi; | ||
56 | struct cfi_private *retcfi; | ||
57 | unsigned long *chip_map; | ||
58 | int i, j, mapsize; | ||
59 | int max_chips; | ||
60 | |||
61 | memset(&cfi, 0, sizeof(cfi)); | ||
62 | |||
63 | /* Call the probetype-specific code with all permutations of | ||
64 | interleave and device type, etc. */ | ||
65 | if (!genprobe_new_chip(map, cp, &cfi)) { | ||
66 | /* The probe didn't like it */ | ||
67 | printk(KERN_DEBUG "%s: Found no %s device at location zero\n", | ||
68 | cp->name, map->name); | ||
69 | return NULL; | ||
70 | } | ||
71 | |||
72 | #if 0 /* Let the CFI probe routine do this sanity check. The Intel and AMD | ||
73 | probe routines won't ever return a broken CFI structure anyway, | ||
74 | because they make them up themselves. | ||
75 | */ | ||
76 | if (cfi.cfiq->NumEraseRegions == 0) { | ||
77 | printk(KERN_WARNING "Number of erase regions is zero\n"); | ||
78 | kfree(cfi.cfiq); | ||
79 | return NULL; | ||
80 | } | ||
81 | #endif | ||
82 | cfi.chipshift = cfi.cfiq->DevSize; | ||
83 | |||
84 | if (cfi_interleave_is_1(&cfi)) { | ||
85 | ; | ||
86 | } else if (cfi_interleave_is_2(&cfi)) { | ||
87 | cfi.chipshift++; | ||
88 | } else if (cfi_interleave_is_4((&cfi))) { | ||
89 | cfi.chipshift += 2; | ||
90 | } else if (cfi_interleave_is_8(&cfi)) { | ||
91 | cfi.chipshift += 3; | ||
92 | } else { | ||
93 | BUG(); | ||
94 | } | ||
95 | |||
96 | cfi.numchips = 1; | ||
97 | |||
98 | /* | ||
99 | * Allocate memory for bitmap of valid chips. | ||
100 | * Align bitmap storage size to full byte. | ||
101 | */ | ||
102 | max_chips = map->size >> cfi.chipshift; | ||
103 | mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0); | ||
104 | chip_map = kmalloc(mapsize, GFP_KERNEL); | ||
105 | if (!chip_map) { | ||
106 | printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); | ||
107 | kfree(cfi.cfiq); | ||
108 | return NULL; | ||
109 | } | ||
110 | memset (chip_map, 0, mapsize); | ||
111 | |||
112 | set_bit(0, chip_map); /* Mark first chip valid */ | ||
113 | |||
114 | /* | ||
115 | * Now probe for other chips, checking sensibly for aliases while | ||
116 | * we're at it. The new_chip probe above should have let the first | ||
117 | * chip in read mode. | ||
118 | */ | ||
119 | |||
120 | for (i = 1; i < max_chips; i++) { | ||
121 | cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi); | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * Now allocate the space for the structures we need to return to | ||
126 | * our caller, and copy the appropriate data into them. | ||
127 | */ | ||
128 | |||
129 | retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL); | ||
130 | |||
131 | if (!retcfi) { | ||
132 | printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name); | ||
133 | kfree(cfi.cfiq); | ||
134 | kfree(chip_map); | ||
135 | return NULL; | ||
136 | } | ||
137 | |||
138 | memcpy(retcfi, &cfi, sizeof(cfi)); | ||
139 | memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips); | ||
140 | |||
141 | for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) { | ||
142 | if(test_bit(i, chip_map)) { | ||
143 | struct flchip *pchip = &retcfi->chips[j++]; | ||
144 | |||
145 | pchip->start = (i << cfi.chipshift); | ||
146 | pchip->state = FL_READY; | ||
147 | init_waitqueue_head(&pchip->wq); | ||
148 | spin_lock_init(&pchip->_spinlock); | ||
149 | pchip->mutex = &pchip->_spinlock; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | kfree(chip_map); | ||
154 | return retcfi; | ||
155 | } | ||
156 | |||
157 | |||
158 | static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, | ||
159 | struct cfi_private *cfi) | ||
160 | { | ||
161 | int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */ | ||
162 | int max_chips = map_bankwidth(map); /* And minimum 1 */ | ||
163 | int nr_chips, type; | ||
164 | |||
165 | for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) { | ||
166 | |||
167 | if (!cfi_interleave_supported(nr_chips)) | ||
168 | continue; | ||
169 | |||
170 | cfi->interleave = nr_chips; | ||
171 | |||
172 | /* Minimum device size. Don't look for one 8-bit device | ||
173 | in a 16-bit bus, etc. */ | ||
174 | type = map_bankwidth(map) / nr_chips; | ||
175 | |||
176 | for (; type <= CFI_DEVICETYPE_X32; type<<=1) { | ||
177 | cfi->device_type = type; | ||
178 | |||
179 | if (cp->probe_chip(map, 0, NULL, cfi)) | ||
180 | return 1; | ||
181 | } | ||
182 | } | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int); | ||
187 | |||
188 | extern cfi_cmdset_fn_t cfi_cmdset_0001; | ||
189 | extern cfi_cmdset_fn_t cfi_cmdset_0002; | ||
190 | extern cfi_cmdset_fn_t cfi_cmdset_0020; | ||
191 | |||
192 | static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, | ||
193 | int primary) | ||
194 | { | ||
195 | struct cfi_private *cfi = map->fldrv_priv; | ||
196 | __u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID; | ||
197 | #if defined(CONFIG_MODULES) && defined(HAVE_INTER_MODULE) | ||
198 | char probename[32]; | ||
199 | cfi_cmdset_fn_t *probe_function; | ||
200 | |||
201 | sprintf(probename, "cfi_cmdset_%4.4X", type); | ||
202 | |||
203 | probe_function = inter_module_get_request(probename, probename); | ||
204 | |||
205 | if (probe_function) { | ||
206 | struct mtd_info *mtd; | ||
207 | |||
208 | mtd = (*probe_function)(map, primary); | ||
209 | /* If it was happy, it'll have increased its own use count */ | ||
210 | inter_module_put(probename); | ||
211 | return mtd; | ||
212 | } | ||
213 | #endif | ||
214 | printk(KERN_NOTICE "Support for command set %04X not present\n", | ||
215 | type); | ||
216 | |||
217 | return NULL; | ||
218 | } | ||
219 | |||
220 | static struct mtd_info *check_cmd_set(struct map_info *map, int primary) | ||
221 | { | ||
222 | struct cfi_private *cfi = map->fldrv_priv; | ||
223 | __u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID; | ||
224 | |||
225 | if (type == P_ID_NONE || type == P_ID_RESERVED) | ||
226 | return NULL; | ||
227 | |||
228 | switch(type){ | ||
229 | /* Urgh. Ifdefs. The version with weak symbols was | ||
230 | * _much_ nicer. Shame it didn't seem to work on | ||
231 | * anything but x86, really. | ||
232 | * But we can't rely in inter_module_get() because | ||
233 | * that'd mean we depend on link order. | ||
234 | */ | ||
235 | #ifdef CONFIG_MTD_CFI_INTELEXT | ||
236 | case 0x0001: | ||
237 | case 0x0003: | ||
238 | return cfi_cmdset_0001(map, primary); | ||
239 | #endif | ||
240 | #ifdef CONFIG_MTD_CFI_AMDSTD | ||
241 | case 0x0002: | ||
242 | return cfi_cmdset_0002(map, primary); | ||
243 | #endif | ||
244 | #ifdef CONFIG_MTD_CFI_STAA | ||
245 | case 0x0020: | ||
246 | return cfi_cmdset_0020(map, primary); | ||
247 | #endif | ||
248 | } | ||
249 | |||
250 | return cfi_cmdset_unknown(map, primary); | ||
251 | } | ||
252 | |||
253 | MODULE_LICENSE("GPL"); | ||
254 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | ||
255 | MODULE_DESCRIPTION("Helper routines for flash chip probe code"); | ||