blob: d374603493a7da77c6f0004643809e0ac5862374 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001
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
Linus Torvalds1da177e2005-04-16 15:20:36 -07007
8/* DOC_PASSIVE_PROBE:
Thomas Gleixnere5580fb2005-11-07 11:15:40 +00009 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.
Linus Torvalds1da177e2005-04-16 15:20:36 -070014
Thomas Gleixnere5580fb2005-11-07 11:15:40 +000015 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
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 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
Thomas Gleixnere5580fb2005-11-07 11:15:40 +000033 for you, you can try the old one by undefining DOC_SINGLE_DRIVER
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 below and also enabling it in your configuration. If this fixes the
Thomas Gleixnere5580fb2005-11-07 11:15:40 +000035 problems, please send a report to the MTD mailing list at
Linus Torvalds1da177e2005-04-16 15:20:36 -070036 <linux-mtd@lists.infradead.org>.
37*/
38#define DOC_SINGLE_DRIVER
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53/* Where to look for the devices? */
54#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
55#define CONFIG_MTD_DOCPROBE_ADDRESS 0
56#endif
57
58
59static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
60module_param(doc_config_location, ulong, 0);
61MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
62
63static unsigned long __initdata doc_locations[] = {
64#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
65#ifdef CONFIG_MTD_DOCPROBE_HIGH
Thomas Gleixnere5580fb2005-11-07 11:15:40 +000066 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
Thomas Gleixnere5580fb2005-11-07 11:15:40 +000068 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
69 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
71#else /* CONFIG_MTD_DOCPROBE_HIGH */
Thomas Gleixnere5580fb2005-11-07 11:15:40 +000072 0xc8000, 0xca000, 0xcc000, 0xce000,
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 0xd0000, 0xd2000, 0xd4000, 0xd6000,
Thomas Gleixnere5580fb2005-11-07 11:15:40 +000074 0xd8000, 0xda000, 0xdc000, 0xde000,
75 0xe0000, 0xe2000, 0xe4000, 0xe6000,
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 0xe8000, 0xea000, 0xec000, 0xee000,
77#endif /* CONFIG_MTD_DOCPROBE_HIGH */
David Woodhouse440fdb52007-08-01 11:23:57 +010078#else
Linus Torvalds1da177e2005-04-16 15:20:36 -070079#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
85static 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
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000102#ifndef DOC_PASSIVE_PROBE
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 /* 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
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000109 * found it.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 */
111 tmp2 = ReadDOC(window, DOCControl);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000112
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 /* Reset the DiskOnChip ASIC */
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000114 WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 window, DOCControl);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000116 WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 window, DOCControl);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 /* Enable the DiskOnChip ASIC */
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000120 WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 window, DOCControl);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000122 WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 window, DOCControl);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000124#endif /* !DOC_PASSIVE_PROBE */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
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);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000130
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 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;
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000140
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 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;
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000155
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 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);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 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);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000178#endif /* !DOC_PASSIVE_PROBE */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
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;
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000218}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
220static int docfound;
221
David Woodhouse396674e2006-05-08 17:10:11 +0100222extern void DoC2k_init(struct mtd_info *);
David Woodhouse396674e2006-05-08 17:10:11 +0100223extern void DoCMil_init(struct mtd_info *);
David Woodhouse396674e2006-05-08 17:10:11 +0100224extern void DoCMilPlus_init(struct mtd_info *);
David Woodhouse396674e2006-05-08 17:10:11 +0100225
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226static 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 void (*initroutine)(struct mtd_info *) = NULL;
235
236 docptr = ioremap(physadr, DOC_IOREMAP_LEN);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000237
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 if (!docptr)
239 return;
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 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 }
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000256
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 this = (struct DiskOnChip *)(&mtd[1]);
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 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";
David Woodhouseecde2632006-05-21 18:38:51 +0100271 initroutine = symbol_request(DoC2k_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 break;
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000273
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 case DOC_ChipID_Doc2k:
275 name="2000";
David Woodhouseecde2632006-05-21 18:38:51 +0100276 initroutine = symbol_request(DoC2k_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 break;
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000278
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 case DOC_ChipID_DocMil:
280 name="Millennium";
281#ifdef DOC_SINGLE_DRIVER
David Woodhouseecde2632006-05-21 18:38:51 +0100282 initroutine = symbol_request(DoC2k_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283#else
David Woodhouseecde2632006-05-21 18:38:51 +0100284 initroutine = symbol_request(DoCMil_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285#endif /* DOC_SINGLE_DRIVER */
286 break;
287
288 case DOC_ChipID_DocMilPlus16:
289 case DOC_ChipID_DocMilPlus32:
290 name="MillenniumPlus";
David Woodhouseecde2632006-05-21 18:38:51 +0100291 initroutine = symbol_request(DoCMilPlus_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 break;
293 }
294
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 if (initroutine) {
296 (*initroutine)(mtd);
David Woodhouse5e535422006-05-08 14:05:05 +0100297 symbol_put_addr(initroutine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 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
313static int __init init_doc(void)
314{
315 int i;
Thomas Gleixnere5580fb2005-11-07 11:15:40 +0000316
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 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
332module_init(init_doc);
333
334MODULE_LICENSE("GPL");
335MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
336MODULE_DESCRIPTION("Probe code for DiskOnChip 2000 and Millennium devices");
337