1/*
2 * Routines common to all CFI-type probes.
3 * (C) 2001, 2001 Red Hat, Inc.
4 * GPL'd
5 * $Id: gen_probe.c,v 1.1.1.1 2008/10/15 03:26:35 james26_jang Exp $
6 */
7
8#include <linux/kernel.h>
9#include <linux/mtd/mtd.h>
10#include <linux/mtd/map.h>
11#include <linux/mtd/cfi.h>
12#include <linux/mtd/gen_probe.h>
13
14static struct mtd_info *check_cmd_set(struct map_info *, int);
15static struct cfi_private *genprobe_ident_chips(struct map_info *map,
16						struct chip_probe *cp);
17static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
18			     struct cfi_private *cfi);
19
20struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
21{
22	struct mtd_info *mtd = NULL;
23	struct cfi_private *cfi;
24
25	/* First probe the map to see if we have CFI stuff there. */
26	cfi = genprobe_ident_chips(map, cp);
27
28	if (!cfi)
29		return NULL;
30
31	map->fldrv_priv = cfi;
32	/* OK we liked it. Now find a driver for the command set it talks */
33
34	mtd = check_cmd_set(map, 1); /* First the primary cmdset */
35	if (!mtd)
36		mtd = check_cmd_set(map, 0); /* Then the secondary */
37
38	if (mtd)
39		return mtd;
40
41	printk(KERN_WARNING"cfi_probe: No supported Vendor Command Set found\n");
42
43	kfree(cfi->cfiq);
44	kfree(cfi);
45	map->fldrv_priv = NULL;
46	return NULL;
47}
48EXPORT_SYMBOL(mtd_do_chip_probe);
49
50
51struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
52{
53	unsigned long base=0;
54	struct cfi_private cfi;
55	struct cfi_private *retcfi;
56	struct flchip chip[MAX_CFI_CHIPS];
57	int i;
58
59	memset(&cfi, 0, sizeof(cfi));
60
61	/* Call the probetype-specific code with all permutations of
62	   interleave and device type, etc. */
63	if (!genprobe_new_chip(map, cp, &cfi)) {
64		/* The probe didn't like it */
65		printk(KERN_WARNING "%s: Found no %s device at location zero\n",
66		       cp->name, map->name);
67		return NULL;
68	}
69
70#if 0 /* Let the CFI probe routine do this sanity check. The Intel and AMD
71	 probe routines won't ever return a broken CFI structure anyway,
72	 because they make them up themselves.
73      */
74	if (cfi.cfiq->NumEraseRegions == 0) {
75		printk(KERN_WARNING "Number of erase regions is zero\n");
76		kfree(cfi.cfiq);
77		return NULL;
78	}
79#endif
80	chip[0].start = 0;
81	chip[0].state = FL_READY;
82	cfi.chipshift = cfi.cfiq->DevSize;
83
84	switch(cfi.interleave) {
85#ifdef CFIDEV_INTERLEAVE_1
86	case 1:
87		break;
88#endif
89#ifdef CFIDEV_INTERLEAVE_2
90	case 2:
91		cfi.chipshift++;
92		break;
93#endif
94#ifdef CFIDEV_INTERLEAVE_4
95	case 4:
96		cfi.chipshift+=2;
97		break;
98#endif
99	default:
100		BUG();
101	}
102
103	cfi.numchips = 1;
104
105	/*
106	 * Now probe for other chips, checking sensibly for aliases while
107	 * we're at it. The new_chip probe above should have let the first
108	 * chip in read mode.
109	 */
110
111	for (base = (1<<cfi.chipshift); base + (1<<cfi.chipshift) <= map->size;
112	     base += (1<<cfi.chipshift))
113		cp->probe_chip(map, base, &chip[0], &cfi);
114
115	/*
116	 * Now allocate the space for the structures we need to return to
117	 * our caller, and copy the appropriate data into them.
118	 */
119
120	retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL);
121
122	if (!retcfi) {
123		printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name);
124		kfree(cfi.cfiq);
125		return NULL;
126	}
127
128	memcpy(retcfi, &cfi, sizeof(cfi));
129	memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips);
130
131	/* Fix up the stuff that breaks when you move it */
132	for (i=0; i< retcfi->numchips; i++) {
133		init_waitqueue_head(&retcfi->chips[i].wq);
134		spin_lock_init(&retcfi->chips[i]._spinlock);
135		retcfi->chips[i].mutex = &retcfi->chips[i]._spinlock;
136	}
137
138	return retcfi;
139}
140
141
142static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
143			     struct cfi_private *cfi)
144{
145	switch (map->buswidth) {
146#ifdef CFIDEV_BUSWIDTH_1
147	case CFIDEV_BUSWIDTH_1:
148		cfi->interleave = CFIDEV_INTERLEAVE_1;
149
150		cfi->device_type = CFI_DEVICETYPE_X8;
151		if (cp->probe_chip(map, 0, NULL, cfi))
152			return 1;
153
154		cfi->device_type = CFI_DEVICETYPE_X16;
155		if (cp->probe_chip(map, 0, NULL, cfi))
156			return 1;
157		break;
158#endif /* CFIDEV_BUSWITDH_1 */
159
160#ifdef CFIDEV_BUSWIDTH_2
161	case CFIDEV_BUSWIDTH_2:
162#ifdef CFIDEV_INTERLEAVE_1
163		cfi->interleave = CFIDEV_INTERLEAVE_1;
164
165		cfi->device_type = CFI_DEVICETYPE_X16;
166		if (cp->probe_chip(map, 0, NULL, cfi))
167			return 1;
168#endif /* CFIDEV_INTERLEAVE_1 */
169#ifdef CFIDEV_INTERLEAVE_2
170		cfi->interleave = CFIDEV_INTERLEAVE_2;
171
172		cfi->device_type = CFI_DEVICETYPE_X8;
173		if (cp->probe_chip(map, 0, NULL, cfi))
174			return 1;
175
176		cfi->device_type = CFI_DEVICETYPE_X16;
177		if (cp->probe_chip(map, 0, NULL, cfi))
178			return 1;
179#endif /* CFIDEV_INTERLEAVE_2 */
180		break;
181#endif /* CFIDEV_BUSWIDTH_2 */
182
183#ifdef CFIDEV_BUSWIDTH_4
184	case CFIDEV_BUSWIDTH_4:
185#if defined(CFIDEV_INTERLEAVE_1) && defined(SOMEONE_ACTUALLY_MAKES_THESE)
186                cfi->interleave = CFIDEV_INTERLEAVE_1;
187
188                cfi->device_type = CFI_DEVICETYPE_X32;
189		if (cp->probe_chip(map, 0, NULL, cfi))
190			return 1;
191#endif /* CFIDEV_INTERLEAVE_1 */
192#ifdef CFIDEV_INTERLEAVE_2
193		cfi->interleave = CFIDEV_INTERLEAVE_2;
194
195#ifdef SOMEONE_ACTUALLY_MAKES_THESE
196		cfi->device_type = CFI_DEVICETYPE_X32;
197		if (cp->probe_chip(map, 0, NULL, cfi))
198			return 1;
199#endif
200		cfi->device_type = CFI_DEVICETYPE_X16;
201		if (cp->probe_chip(map, 0, NULL, cfi))
202			return 1;
203
204		cfi->device_type = CFI_DEVICETYPE_X8;
205		if (cp->probe_chip(map, 0, NULL, cfi))
206			return 1;
207#endif /* CFIDEV_INTERLEAVE_2 */
208#ifdef CFIDEV_INTERLEAVE_4
209		cfi->interleave = CFIDEV_INTERLEAVE_4;
210
211#ifdef SOMEONE_ACTUALLY_MAKES_THESE
212		cfi->device_type = CFI_DEVICETYPE_X32;
213		if (cp->probe_chip(map, 0, NULL, cfi))
214			return 1;
215#endif
216		cfi->device_type = CFI_DEVICETYPE_X16;
217		if (cp->probe_chip(map, 0, NULL, cfi))
218			return 1;
219
220		cfi->device_type = CFI_DEVICETYPE_X8;
221		if (cp->probe_chip(map, 0, NULL, cfi))
222			return 1;
223#endif /* CFIDEV_INTERLEAVE_4 */
224		break;
225#endif /* CFIDEV_BUSWIDTH_4 */
226
227	default:
228		printk(KERN_WARNING "genprobe_new_chip called with unsupported buswidth %d\n", map->buswidth);
229		return 0;
230	}
231	return 0;
232}
233
234
235typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int);
236
237extern cfi_cmdset_fn_t cfi_cmdset_0001;
238extern cfi_cmdset_fn_t cfi_cmdset_0002;
239
240static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map,
241						  int primary)
242{
243	struct cfi_private *cfi = map->fldrv_priv;
244	__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
245#if defined(CONFIG_MODULES) && defined(HAVE_INTER_MODULE)
246	char probename[32];
247	cfi_cmdset_fn_t *probe_function;
248
249	sprintf(probename, "cfi_cmdset_%4.4X", type);
250
251	probe_function = inter_module_get_request(probename, probename);
252
253	if (probe_function) {
254		struct mtd_info *mtd;
255
256		mtd = (*probe_function)(map, primary);
257		/* If it was happy, it'll have increased its own use count */
258		inter_module_put(probename);
259		return mtd;
260	}
261#endif
262	printk(KERN_NOTICE "Support for command set %04X not present\n",
263	       type);
264
265	return NULL;
266}
267
268static struct mtd_info *check_cmd_set(struct map_info *map, int primary)
269{
270	struct cfi_private *cfi = map->fldrv_priv;
271	__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
272
273	if (type == P_ID_NONE || type == P_ID_RESERVED)
274		return NULL;
275
276	switch(type){
277		/* Urgh. Ifdefs. The version with weak symbols was
278		 * _much_ nicer. Shame it didn't seem to work on
279		 * anything but x86, really.
280		 * But we can't rely in inter_module_get() because
281		 * that'd mean we depend on link order.
282		 */
283#ifdef CONFIG_MTD_CFI_INTELEXT
284	case 0x0001:
285	case 0x0003:
286		return cfi_cmdset_0001(map, primary);
287#endif
288#ifdef CONFIG_MTD_CFI_AMDSTD
289	case 0x0002:
290		return cfi_cmdset_0002(map, primary);
291#endif
292#ifdef CONFIG_MTD_CFI_SSTSTD
293	case 0x0701:
294		return cfi_cmdset_0701(map, primary);
295#endif
296	}
297
298	return cfi_cmdset_unknown(map, primary);
299}
300
301MODULE_LICENSE("GPL");
302MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
303MODULE_DESCRIPTION("Helper routines for flash chip probe code");
304