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