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