2 * Routines common to all CFI-type probes.
3 * (C) 2001-2003 Red Hat, Inc.
5 * $Id: gen_probe.c,v 1.24 2005/11/07 11:14:23 gleixner Exp $
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>
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
);
22 struct mtd_info
*mtd_do_chip_probe(struct map_info
*map
, struct chip_probe
*cp
)
24 struct mtd_info
*mtd
= NULL
;
25 struct cfi_private
*cfi
;
27 /* First probe the map to see if we have CFI stuff there. */
28 cfi
= genprobe_ident_chips(map
, cp
);
33 map
->fldrv_priv
= cfi
;
34 /* OK we liked it. Now find a driver for the command set it talks */
36 mtd
= check_cmd_set(map
, 1); /* First the primary cmdset */
38 mtd
= check_cmd_set(map
, 0); /* Then the secondary */
43 printk(KERN_WARNING
"gen_probe: No supported Vendor Command Set found\n");
47 map
->fldrv_priv
= NULL
;
50 EXPORT_SYMBOL(mtd_do_chip_probe
);
53 static struct cfi_private
*genprobe_ident_chips(struct map_info
*map
, struct chip_probe
*cp
)
55 struct cfi_private cfi
;
56 struct cfi_private
*retcfi
;
57 unsigned long *chip_map
;
61 memset(&cfi
, 0, sizeof(cfi
));
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",
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.
76 if (cfi
.cfiq
->NumEraseRegions
== 0) {
77 printk(KERN_WARNING
"Number of erase regions is zero\n");
82 cfi
.chipshift
= cfi
.cfiq
->DevSize
;
84 if (cfi_interleave_is_1(&cfi
)) {
86 } else if (cfi_interleave_is_2(&cfi
)) {
88 } else if (cfi_interleave_is_4((&cfi
))) {
90 } else if (cfi_interleave_is_8(&cfi
)) {
99 * Allocate memory for bitmap of valid chips.
100 * Align bitmap storage size to full byte.
102 max_chips
= map
->size
>> cfi
.chipshift
;
103 mapsize
= (max_chips
/ 8) + ((max_chips
% 8) ? 1 : 0);
104 chip_map
= kmalloc(mapsize
, GFP_KERNEL
);
106 printk(KERN_WARNING
"%s: kmalloc failed for CFI chip map\n", map
->name
);
110 memset (chip_map
, 0, mapsize
);
112 set_bit(0, chip_map
); /* Mark first chip valid */
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
120 for (i
= 1; i
< max_chips
; i
++) {
121 cp
->probe_chip(map
, i
<< cfi
.chipshift
, chip_map
, &cfi
);
125 * Now allocate the space for the structures we need to return to
126 * our caller, and copy the appropriate data into them.
129 retcfi
= kmalloc(sizeof(struct cfi_private
) + cfi
.numchips
* sizeof(struct flchip
), GFP_KERNEL
);
132 printk(KERN_WARNING
"%s: kmalloc failed for CFI private structure\n", map
->name
);
138 memcpy(retcfi
, &cfi
, sizeof(cfi
));
139 memset(&retcfi
->chips
[0], 0, sizeof(struct flchip
) * cfi
.numchips
);
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
++];
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
;
158 static int genprobe_new_chip(struct map_info
*map
, struct chip_probe
*cp
,
159 struct cfi_private
*cfi
)
161 int min_chips
= (map_bankwidth(map
)/4?:1); /* At most 4-bytes wide. */
162 int max_chips
= map_bankwidth(map
); /* And minimum 1 */
165 for (nr_chips
= max_chips
; nr_chips
>= min_chips
; nr_chips
>>= 1) {
167 if (!cfi_interleave_supported(nr_chips
))
170 cfi
->interleave
= nr_chips
;
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
;
176 for (; type
<= CFI_DEVICETYPE_X32
; type
<<=1) {
177 cfi
->device_type
= type
;
179 if (cp
->probe_chip(map
, 0, NULL
, cfi
))
186 typedef struct mtd_info
*cfi_cmdset_fn_t(struct map_info
*, int);
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
;
192 static inline struct mtd_info
*cfi_cmdset_unknown(struct map_info
*map
,
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)
199 cfi_cmdset_fn_t
*probe_function
;
201 sprintf(probename
, "cfi_cmdset_%4.4X", type
);
203 probe_function
= inter_module_get_request(probename
, probename
);
205 if (probe_function
) {
206 struct mtd_info
*mtd
;
208 mtd
= (*probe_function
)(map
, primary
);
209 /* If it was happy, it'll have increased its own use count */
210 inter_module_put(probename
);
214 printk(KERN_NOTICE
"Support for command set %04X not present\n",
220 static struct mtd_info
*check_cmd_set(struct map_info
*map
, int primary
)
222 struct cfi_private
*cfi
= map
->fldrv_priv
;
223 __u16 type
= primary
?cfi
->cfiq
->P_ID
:cfi
->cfiq
->A_ID
;
225 if (type
== P_ID_NONE
|| type
== P_ID_RESERVED
)
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.
235 #ifdef CONFIG_MTD_CFI_INTELEXT
239 return cfi_cmdset_0001(map
, primary
);
241 #ifdef CONFIG_MTD_CFI_AMDSTD
243 return cfi_cmdset_0002(map
, primary
);
245 #ifdef CONFIG_MTD_CFI_STAA
247 return cfi_cmdset_0020(map
, primary
);
251 return cfi_cmdset_unknown(map
, primary
);
254 MODULE_LICENSE("GPL");
255 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
256 MODULE_DESCRIPTION("Helper routines for flash chip probe code");