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