1
2/* Linux driver for Disk-On-Chip devices			*/
3/* Probe routines common to all DoC devices			*/
4/* (C) 1999 Machine Vision Holdings, Inc.			*/
5/* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>		*/
6
7/* $Id: docprobe.c,v 1.1.1.1 2007/08/03 18:52:43 Exp $	*/
8
9
10
11/* DOC_PASSIVE_PROBE:
12   In order to ensure that the BIOS checksum is correct at boot time, and
13   hence that the onboard BIOS extension gets executed, the DiskOnChip
14   goes into reset mode when it is read sequentially: all registers
15   return 0xff until the chip is woken up again by writing to the
16   DOCControl register.
17
18   Unfortunately, this means that the probe for the DiskOnChip is unsafe,
19   because one of the first things it does is write to where it thinks
20   the DOCControl register should be - which may well be shared memory
21   for another device. I've had machines which lock up when this is
22   attempted. Hence the possibility to do a passive probe, which will fail
23   to detect a chip in reset mode, but is at least guaranteed not to lock
24   the machine.
25
26   If you have this problem, uncomment the following line:
27#define DOC_PASSIVE_PROBE
28*/
29
30
31/* DOC_SINGLE_DRIVER:
32   Millennium driver has been merged into DOC2000 driver.
33
34   The old Millennium-only driver has been retained just in case there
35   are problems with the new code. If the combined driver doesn't work
36   for you, you can try the old one by undefining DOC_SINGLE_DRIVER
37   below and also enabling it in your configuration. If this fixes the
38   problems, please send a report to the MTD mailing list at
39   <linux-mtd@lists.infradead.org>.
40*/
41#define DOC_SINGLE_DRIVER
42
43#include <linux/kernel.h>
44#include <linux/module.h>
45#include <asm/errno.h>
46#include <asm/io.h>
47#include <linux/delay.h>
48#include <linux/slab.h>
49#include <linux/init.h>
50#include <linux/types.h>
51
52#include <linux/mtd/mtd.h>
53#include <linux/mtd/nand.h>
54#include <linux/mtd/doc2000.h>
55#include <linux/mtd/compatmac.h>
56
57/* Where to look for the devices? */
58#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
59#define CONFIG_MTD_DOCPROBE_ADDRESS 0
60#endif
61
62
63static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
64module_param(doc_config_location, ulong, 0);
65MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
66
67static unsigned long __initdata doc_locations[] = {
68#if defined(__alpha__) || defined(__i386__) || defined(__x86_64__)
69#ifdef CONFIG_MTD_DOCPROBE_HIGH
70	0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
71	0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
72	0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
73	0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
74	0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
75#else /*  CONFIG_MTD_DOCPROBE_HIGH */
76	0xc8000, 0xca000, 0xcc000, 0xce000,
77	0xd0000, 0xd2000, 0xd4000, 0xd6000,
78	0xd8000, 0xda000, 0xdc000, 0xde000,
79	0xe0000, 0xe2000, 0xe4000, 0xe6000,
80	0xe8000, 0xea000, 0xec000, 0xee000,
81#endif /*  CONFIG_MTD_DOCPROBE_HIGH */
82#elif defined(__PPC__)
83	0xe4000000,
84#elif defined(CONFIG_MOMENCO_OCELOT)
85	0x2f000000,
86	0xff000000,
87#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined(CONFIG_MOMENCO_OCELOT_C)
88	0xff000000,
89#else
90#warning Unknown architecture for DiskOnChip. No default probe locations defined
91#endif
92	0xffffffff };
93
94/* doccheck: Probe a given memory window to see if there's a DiskOnChip present */
95
96static inline int __init doccheck(void __iomem *potential, unsigned long physadr)
97{
98	void __iomem *window=potential;
99	unsigned char tmp, tmpb, tmpc, ChipID;
100#ifndef DOC_PASSIVE_PROBE
101	unsigned char tmp2;
102#endif
103
104	/* Routine copied from the Linux DOC driver */
105
106#ifdef CONFIG_MTD_DOCPROBE_55AA
107	/* Check for 0x55 0xAA signature at beginning of window,
108	   this is no longer true once we remove the IPL (for Millennium */
109	if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa)
110		return 0;
111#endif /* CONFIG_MTD_DOCPROBE_55AA */
112
113#ifndef DOC_PASSIVE_PROBE
114	/* It's not possible to cleanly detect the DiskOnChip - the
115	 * bootup procedure will put the device into reset mode, and
116	 * it's not possible to talk to it without actually writing
117	 * to the DOCControl register. So we store the current contents
118	 * of the DOCControl register's location, in case we later decide
119	 * that it's not a DiskOnChip, and want to put it back how we
120	 * found it.
121	 */
122	tmp2 = ReadDOC(window, DOCControl);
123
124	/* Reset the DiskOnChip ASIC */
125	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
126		 window, DOCControl);
127	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
128		 window, DOCControl);
129
130	/* Enable the DiskOnChip ASIC */
131	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
132		 window, DOCControl);
133	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
134		 window, DOCControl);
135#endif /* !DOC_PASSIVE_PROBE */
136
137	/* We need to read the ChipID register four times. For some
138	   newer DiskOnChip 2000 units, the first three reads will
139	   return the DiskOnChip Millennium ident. Don't ask. */
140	ChipID = ReadDOC(window, ChipID);
141
142	switch (ChipID) {
143	case DOC_ChipID_Doc2k:
144		/* Check the TOGGLE bit in the ECC register */
145		tmp  = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
146		tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
147		tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
148		if (tmp != tmpb && tmp == tmpc)
149				return ChipID;
150		break;
151
152	case DOC_ChipID_DocMil:
153		/* Check for the new 2000 with Millennium ASIC */
154		ReadDOC(window, ChipID);
155		ReadDOC(window, ChipID);
156		if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil)
157			ChipID = DOC_ChipID_Doc2kTSOP;
158
159		/* Check the TOGGLE bit in the ECC register */
160		tmp  = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
161		tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
162		tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
163		if (tmp != tmpb && tmp == tmpc)
164				return ChipID;
165		break;
166
167	case DOC_ChipID_DocMilPlus16:
168	case DOC_ChipID_DocMilPlus32:
169	case 0:
170		/* Possible Millennium+, need to do more checks */
171#ifndef DOC_PASSIVE_PROBE
172		/* Possibly release from power down mode */
173		for (tmp = 0; (tmp < 4); tmp++)
174			ReadDOC(window, Mplus_Power);
175
176		/* Reset the DiskOnChip ASIC */
177		tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
178			DOC_MODE_BDECT;
179		WriteDOC(tmp, window, Mplus_DOCControl);
180		WriteDOC(~tmp, window, Mplus_CtrlConfirm);
181
182		mdelay(1);
183		/* Enable the DiskOnChip ASIC */
184		tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
185			DOC_MODE_BDECT;
186		WriteDOC(tmp, window, Mplus_DOCControl);
187		WriteDOC(~tmp, window, Mplus_CtrlConfirm);
188		mdelay(1);
189#endif /* !DOC_PASSIVE_PROBE */
190
191		ChipID = ReadDOC(window, ChipID);
192
193		switch (ChipID) {
194		case DOC_ChipID_DocMilPlus16:
195		case DOC_ChipID_DocMilPlus32:
196			/* Check the TOGGLE bit in the toggle register */
197			tmp  = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
198			tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
199			tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
200			if (tmp != tmpb && tmp == tmpc)
201					return ChipID;
202		default:
203			break;
204		}
205		/* FALL TRHU */
206
207	default:
208
209#ifdef CONFIG_MTD_DOCPROBE_55AA
210		printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
211		       ChipID, physadr);
212#endif
213#ifndef DOC_PASSIVE_PROBE
214		/* Put back the contents of the DOCControl register, in case it's not
215		 * actually a DiskOnChip.
216		 */
217		WriteDOC(tmp2, window, DOCControl);
218#endif
219		return 0;
220	}
221
222	printk(KERN_WARNING "DiskOnChip failed TOGGLE test, dropping.\n");
223
224#ifndef DOC_PASSIVE_PROBE
225	/* Put back the contents of the DOCControl register: it's not a DiskOnChip */
226	WriteDOC(tmp2, window, DOCControl);
227#endif
228	return 0;
229}
230
231static int docfound;
232
233extern void DoC2k_init(struct mtd_info *);
234extern void DoCMil_init(struct mtd_info *);
235extern void DoCMilPlus_init(struct mtd_info *);
236
237static void __init DoC_Probe(unsigned long physadr)
238{
239	void __iomem *docptr;
240	struct DiskOnChip *this;
241	struct mtd_info *mtd;
242	int ChipID;
243	char namebuf[15];
244	char *name = namebuf;
245	void (*initroutine)(struct mtd_info *) = NULL;
246
247	docptr = ioremap(physadr, DOC_IOREMAP_LEN);
248
249	if (!docptr)
250		return;
251
252	if ((ChipID = doccheck(docptr, physadr))) {
253		if (ChipID == DOC_ChipID_Doc2kTSOP) {
254			/* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */
255			printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n");
256			iounmap(docptr);
257			return;
258		}
259		docfound = 1;
260		mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
261
262		if (!mtd) {
263			printk(KERN_WARNING "Cannot allocate memory for data structures. Dropping.\n");
264			iounmap(docptr);
265			return;
266		}
267
268		this = (struct DiskOnChip *)(&mtd[1]);
269
270		memset((char *)mtd,0, sizeof(struct mtd_info));
271		memset((char *)this, 0, sizeof(struct DiskOnChip));
272
273		mtd->priv = this;
274		this->virtadr = docptr;
275		this->physadr = physadr;
276		this->ChipID = ChipID;
277		sprintf(namebuf, "with ChipID %2.2X", ChipID);
278
279		switch(ChipID) {
280		case DOC_ChipID_Doc2kTSOP:
281			name="2000 TSOP";
282			initroutine = symbol_request(DoC2k_init);
283			break;
284
285		case DOC_ChipID_Doc2k:
286			name="2000";
287			initroutine = symbol_request(DoC2k_init);
288			break;
289
290		case DOC_ChipID_DocMil:
291			name="Millennium";
292#ifdef DOC_SINGLE_DRIVER
293			initroutine = symbol_request(DoC2k_init);
294#else
295			initroutine = symbol_request(DoCMil_init);
296#endif /* DOC_SINGLE_DRIVER */
297			break;
298
299		case DOC_ChipID_DocMilPlus16:
300		case DOC_ChipID_DocMilPlus32:
301			name="MillenniumPlus";
302			initroutine = symbol_request(DoCMilPlus_init);
303			break;
304		}
305
306		if (initroutine) {
307			(*initroutine)(mtd);
308			symbol_put_addr(initroutine);
309			return;
310		}
311		printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
312		kfree(mtd);
313	}
314	iounmap(docptr);
315}
316
317
318/****************************************************************************
319 *
320 * Module stuff
321 *
322 ****************************************************************************/
323
324static int __init init_doc(void)
325{
326	int i;
327
328	if (doc_config_location) {
329		printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
330		DoC_Probe(doc_config_location);
331	} else {
332		for (i=0; (doc_locations[i] != 0xffffffff); i++) {
333			DoC_Probe(doc_locations[i]);
334		}
335	}
336	/* No banner message any more. Print a message if no DiskOnChip
337	   found, so the user knows we at least tried. */
338	if (!docfound)
339		printk(KERN_INFO "No recognised DiskOnChip devices found\n");
340	return -EAGAIN;
341}
342
343module_init(init_doc);
344
345MODULE_LICENSE("GPL");
346MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
347MODULE_DESCRIPTION("Probe code for DiskOnChip 2000 and Millennium devices");
348