more debug output
[linux-2.4.git] / arch / i386 / kernel / mca.c
1 /*
2  *  linux/arch/i386/kernel/mca.c
3  *  Written by Martin Kolinek, February 1996
4  *
5  * Changes:
6  *
7  *      Chris Beauregard July 28th, 1996
8  *      - Fixed up integrated SCSI detection
9  *
10  *      Chris Beauregard August 3rd, 1996
11  *      - Made mca_info local
12  *      - Made integrated registers accessible through standard function calls
13  *      - Added name field
14  *      - More sanity checking
15  *
16  *      Chris Beauregard August 9th, 1996
17  *      - Rewrote /proc/mca
18  *
19  *      Chris Beauregard January 7th, 1997
20  *      - Added basic NMI-processing
21  *      - Added more information to mca_info structure
22  *
23  *      David Weinehall October 12th, 1998
24  *      - Made a lot of cleaning up in the source
25  *      - Added use of save_flags / restore_flags
26  *      - Added the 'driver_loaded' flag in MCA_adapter
27  *      - Added an alternative implemention of ZP Gu's mca_find_unused_adapter
28  *
29  *      David Weinehall March 24th, 1999
30  *      - Fixed the output of 'Driver Installed' in /proc/mca/pos
31  *      - Made the Integrated Video & SCSI show up even if they have id 0000
32  *
33  *      Alexander Viro November 9th, 1999
34  *      - Switched to regular procfs methods
35  *
36  *      Alfred Arnold & David Weinehall August 23rd, 2000
37  *      - Added support for Planar POS-registers
38  */
39
40 #include <linux/module.h>
41 #include <linux/types.h>
42 #include <linux/errno.h>
43 #include <linux/kernel.h>
44 #include <linux/mca.h>
45 #include <asm/system.h>
46 #include <asm/io.h>
47 #include <linux/proc_fs.h>
48 #include <linux/mman.h>
49 #include <linux/config.h>
50 #include <linux/mm.h>
51 #include <linux/pagemap.h>
52 #include <linux/ioport.h>
53 #include <asm/uaccess.h>
54 #include <linux/init.h>
55
56 /* This structure holds MCA information. Each (plug-in) adapter has
57  * eight POS registers. Then the machine may have integrated video and
58  * SCSI subsystems, which also have eight POS registers.
59  * Finally, the motherboard (planar) has got POS-registers.
60  * Other miscellaneous information follows.
61  */
62
63 typedef enum {
64         MCA_ADAPTER_NORMAL = 0,
65         MCA_ADAPTER_NONE = 1,
66         MCA_ADAPTER_DISABLED = 2,
67         MCA_ADAPTER_ERROR = 3
68 } MCA_AdapterStatus;
69
70 struct MCA_adapter {
71         MCA_AdapterStatus status;       /* is there a valid adapter? */
72         int id;                         /* adapter id value */
73         unsigned char pos[8];           /* POS registers */
74         int driver_loaded;              /* is there a driver installed? */
75                                         /* 0 - No, 1 - Yes */
76         char name[48];                  /* adapter-name provided by driver */
77         char procname[8];               /* name of /proc/mca file */
78         MCA_ProcFn procfn;              /* /proc info callback */
79         void* dev;                      /* device/context info for callback */
80 };
81
82 struct MCA_info {
83         /* one for each of the 8 possible slots, plus one for integrated SCSI
84          * and one for integrated video.
85          */
86
87         struct MCA_adapter slot[MCA_NUMADAPTERS];
88
89         /* two potential addresses for integrated SCSI adapter - this will
90          * track which one we think it is.
91          */
92
93         unsigned char which_scsi;
94 };
95
96 /* The mca_info structure pointer. If MCA bus is present, the function
97  * mca_probe() is invoked. The function puts motherboard, then all
98  * adapters into setup mode, allocates and fills an MCA_info structure,
99  * and points this pointer to the structure. Otherwise the pointer
100  * is set to zero.
101  */
102
103 static struct MCA_info* mca_info = NULL;
104
105 /* MCA registers */
106
107 #define MCA_MOTHERBOARD_SETUP_REG       0x94
108 #define MCA_ADAPTER_SETUP_REG           0x96
109 #define MCA_POS_REG(n)                  (0x100+(n))
110
111 #define MCA_ENABLED     0x01    /* POS 2, set if adapter enabled */
112
113 /*--------------------------------------------------------------------*/
114
115 #ifdef CONFIG_PROC_FS
116 static void mca_do_proc_init(void);
117 #endif
118
119 /*--------------------------------------------------------------------*/
120
121 /* Build the status info for the adapter */
122
123 static void mca_configure_adapter_status(int slot) {
124         mca_info->slot[slot].status = MCA_ADAPTER_NONE;
125
126         mca_info->slot[slot].id = mca_info->slot[slot].pos[0]
127                 + (mca_info->slot[slot].pos[1] << 8);
128
129         if(!mca_info->slot[slot].id && slot < MCA_MAX_SLOT_NR) {
130
131                 /* id = 0x0000 usually indicates hardware failure,
132                  * however, ZP Gu (zpg@castle.net> reports that his 9556
133                  * has 0x0000 as id and everything still works. There
134                  * also seem to be an adapter with id = 0x0000; the
135                  * NCR Parallel Bus Memory Card. Until this is confirmed,
136                  * however, this code will stay.
137                  */
138
139                 mca_info->slot[slot].status = MCA_ADAPTER_ERROR;
140
141                 return;
142         } else if(mca_info->slot[slot].id != 0xffff) {
143
144                 /* 0xffff usually indicates that there's no adapter,
145                  * however, some integrated adapters may have 0xffff as
146                  * their id and still be valid. Examples are on-board
147                  * VGA of the 55sx, the integrated SCSI of the 56 & 57,
148                  * and possibly also the 95 ULTIMEDIA.
149                  */
150
151                 mca_info->slot[slot].status = MCA_ADAPTER_NORMAL;
152         }
153
154         if((mca_info->slot[slot].id == 0xffff ||
155            mca_info->slot[slot].id == 0x0000) && slot >= MCA_MAX_SLOT_NR) {
156                 int j;
157
158                 for(j = 2; j < 8; j++) {
159                         if(mca_info->slot[slot].pos[j] != 0xff) {
160                                 mca_info->slot[slot].status = MCA_ADAPTER_NORMAL;
161                                 break;
162                         }
163                 }
164         }
165
166         if(!(mca_info->slot[slot].pos[2] & MCA_ENABLED)) {
167
168                 /* enabled bit is in POS 2 */
169
170                 mca_info->slot[slot].status = MCA_ADAPTER_DISABLED;
171         }
172 } /* mca_configure_adapter_status */
173
174 /*--------------------------------------------------------------------*/
175
176 struct resource mca_standard_resources[] = {
177         { "system control port B (MCA)", 0x60, 0x60 },
178         { "arbitration (MCA)", 0x90, 0x90 },
179         { "card Select Feedback (MCA)", 0x91, 0x91 },
180         { "system Control port A (MCA)", 0x92, 0x92 },
181         { "system board setup (MCA)", 0x94, 0x94 },
182         { "POS (MCA)", 0x96, 0x97 },
183         { "POS (MCA)", 0x100, 0x107 }
184 };
185
186 #define MCA_STANDARD_RESOURCES  (sizeof(mca_standard_resources)/sizeof(struct resource))
187
188 void __init mca_init(void)
189 {
190         unsigned int i, j;
191         unsigned long flags;
192
193         /* WARNING: Be careful when making changes here. Putting an adapter
194          * and the motherboard simultaneously into setup mode may result in
195          * damage to chips (according to The Indispensible PC Hardware Book
196          * by Hans-Peter Messmer). Also, we disable system interrupts (so
197          * that we are not disturbed in the middle of this).
198          */
199
200         /* Make sure the MCA bus is present */
201
202         if(!MCA_bus)
203                 return;
204         printk("Micro Channel bus detected.\n");
205
206         /* Allocate MCA_info structure (at address divisible by 8) */
207
208         mca_info = (struct MCA_info *)kmalloc(sizeof(struct MCA_info), GFP_KERNEL);
209
210         if(mca_info == NULL) {
211                 printk("Failed to allocate memory for mca_info!");
212                 return;
213         }
214         memset(mca_info, 0, sizeof(struct MCA_info));
215
216         save_flags(flags);
217         cli();
218
219         /* Make sure adapter setup is off */
220
221         outb_p(0, MCA_ADAPTER_SETUP_REG);
222
223         /* Read motherboard POS registers */
224
225         outb_p(0x7f, MCA_MOTHERBOARD_SETUP_REG);
226         mca_info->slot[MCA_MOTHERBOARD].name[0] = 0;
227         for(j=0; j<8; j++) {
228                 mca_info->slot[MCA_MOTHERBOARD].pos[j] = inb_p(MCA_POS_REG(j));
229         }
230         mca_configure_adapter_status(MCA_MOTHERBOARD);
231
232         /* Put motherboard into video setup mode, read integrated video
233          * POS registers, and turn motherboard setup off.
234          */
235
236         outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG);
237         mca_info->slot[MCA_INTEGVIDEO].name[0] = 0;
238         for(j=0; j<8; j++) {
239                 mca_info->slot[MCA_INTEGVIDEO].pos[j] = inb_p(MCA_POS_REG(j));
240         }
241         mca_configure_adapter_status(MCA_INTEGVIDEO);
242
243         /* Put motherboard into scsi setup mode, read integrated scsi
244          * POS registers, and turn motherboard setup off.
245          *
246          * It seems there are two possible SCSI registers. Martin says that
247          * for the 56,57, 0xf7 is the one, but fails on the 76.
248          * Alfredo (apena@vnet.ibm.com) says
249          * 0xfd works on his machine. We'll try both of them. I figure it's
250          * a good bet that only one could be valid at a time. This could
251          * screw up though if one is used for something else on the other
252          * machine.
253          */
254
255         outb_p(0xf7, MCA_MOTHERBOARD_SETUP_REG);
256         mca_info->slot[MCA_INTEGSCSI].name[0] = 0;
257         for(j=0; j<8; j++) {
258                 if((mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j))) != 0xff)
259                 {
260                         /* 0xff all across means no device. 0x00 means
261                          * something's broken, but a device is probably there.
262                          * However, if you get 0x00 from a motherboard
263                          * register it won't matter what we find.  For the
264                          * record, on the 57SLC, the integrated SCSI
265                          * adapter has 0xffff for the adapter ID, but
266                          * nonzero for other registers.
267                          */
268
269                         mca_info->which_scsi = 0xf7;
270                 }
271         }
272         if(!mca_info->which_scsi) {
273
274                 /* Didn't find it at 0xf7, try somewhere else... */
275                 mca_info->which_scsi = 0xfd;
276
277                 outb_p(0xfd, MCA_MOTHERBOARD_SETUP_REG);
278                 for(j=0; j<8; j++)
279                         mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j));
280         }
281         mca_configure_adapter_status(MCA_INTEGSCSI);
282
283         /* Turn off motherboard setup */
284
285         outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
286
287         /* Now loop over MCA slots: put each adapter into setup mode, and
288          * read its POS registers. Then put adapter setup off.
289          */
290
291         for(i=0; i<MCA_MAX_SLOT_NR; i++) {
292                 outb_p(0x8|(i&0xf), MCA_ADAPTER_SETUP_REG);
293                 for(j=0; j<8; j++) {
294                         mca_info->slot[i].pos[j]=inb_p(MCA_POS_REG(j));
295                 }
296                 mca_info->slot[i].name[0] = 0;
297                 mca_info->slot[i].driver_loaded = 0;
298                 mca_configure_adapter_status(i);
299         }
300         outb_p(0, MCA_ADAPTER_SETUP_REG);
301
302         /* Enable interrupts and return memory start */
303
304         restore_flags(flags);
305
306         for (i = 0; i < MCA_STANDARD_RESOURCES; i++)
307                 request_resource(&ioport_resource, mca_standard_resources + i);
308
309 #ifdef CONFIG_PROC_FS
310         mca_do_proc_init();
311 #endif
312 }
313
314 /*--------------------------------------------------------------------*/
315
316 static void mca_handle_nmi_slot(int slot, int check_flag)
317 {
318         if(slot < MCA_MAX_SLOT_NR) {
319                 printk("NMI: caused by MCA adapter in slot %d (%s)\n", slot+1,
320                         mca_info->slot[slot].name);
321         } else if(slot == MCA_INTEGSCSI) {
322                 printk("NMI: caused by MCA integrated SCSI adapter (%s)\n",
323                         mca_info->slot[slot].name);
324         } else if(slot == MCA_INTEGVIDEO) {
325                 printk("NMI: caused by MCA integrated video adapter (%s)\n",
326                         mca_info->slot[slot].name);
327         } else if(slot == MCA_MOTHERBOARD) {
328                 printk("NMI: caused by motherboard (%s)\n",
329                         mca_info->slot[slot].name);
330         }
331
332         /* More info available in POS 6 and 7? */
333
334         if(check_flag) {
335                 unsigned char pos6, pos7;
336
337                 pos6 = mca_read_pos(slot, 6);
338                 pos7 = mca_read_pos(slot, 7);
339
340                 printk("NMI: POS 6 = 0x%x, POS 7 = 0x%x\n", pos6, pos7);
341         }
342
343 } /* mca_handle_nmi_slot */
344
345 /*--------------------------------------------------------------------*/
346
347 void mca_handle_nmi(void)
348 {
349
350         int i;
351         unsigned char pos5;
352
353         /* First try - scan the various adapters and see if a specific
354          * adapter was responsible for the error.
355          */
356
357         for(i = 0; i < MCA_NUMADAPTERS; i++) {
358
359         /* Bit 7 of POS 5 is reset when this adapter has a hardware
360          * error. Bit 7 it reset if there's error information
361          * available in POS 6 and 7.
362          */
363
364         pos5 = mca_read_pos(i, 5);
365
366         if(!(pos5 & 0x80)) {
367                         mca_handle_nmi_slot(i, !(pos5 & 0x40));
368                         return;
369                 }
370         }
371
372         /* If I recall correctly, there's a whole bunch of other things that
373          * we can do to check for NMI problems, but that's all I know about
374          * at the moment.
375          */
376
377         printk("NMI generated from unknown source!\n");
378 } /* mca_handle_nmi */
379
380 /*--------------------------------------------------------------------*/
381
382 /**
383  *      mca_find_adapter - scan for adapters
384  *      @id:    MCA identification to search for
385  *      @start: starting slot
386  *
387  *      Search the MCA configuration for adapters matching the 16bit
388  *      ID given. The first time it should be called with start as zero
389  *      and then further calls made passing the return value of the
390  *      previous call until %MCA_NOTFOUND is returned.
391  *
392  *      Disabled adapters are not reported.
393  */
394
395 int mca_find_adapter(int id, int start)
396 {
397         if(mca_info == NULL || id == 0xffff) {
398                 return MCA_NOTFOUND;
399         }
400
401         for(; start >= 0 && start < MCA_NUMADAPTERS; start++) {
402
403                 /* Not sure about this. There's no point in returning
404                  * adapters that aren't enabled, since they can't actually
405                  * be used. However, they might be needed for statistical
406                  * purposes or something... But if that is the case, the
407                  * user is free to write a routine that manually iterates
408                  * through the adapters.
409                  */
410
411                 if(mca_info->slot[start].status == MCA_ADAPTER_DISABLED) {
412                         continue;
413                 }
414
415                 if(id == mca_info->slot[start].id) {
416                         return start;
417                 }
418         }
419
420         return MCA_NOTFOUND;
421 } /* mca_find_adapter() */
422
423 EXPORT_SYMBOL(mca_find_adapter);
424
425 /*--------------------------------------------------------------------*/
426
427 /**
428  *      mca_find_unused_adapter - scan for unused adapters
429  *      @id:    MCA identification to search for
430  *      @start: starting slot
431  *
432  *      Search the MCA configuration for adapters matching the 16bit
433  *      ID given. The first time it should be called with start as zero
434  *      and then further calls made passing the return value of the
435  *      previous call until %MCA_NOTFOUND is returned.
436  *
437  *      Adapters that have been claimed by drivers and those that
438  *      are disabled are not reported. This function thus allows a driver
439  *      to scan for further cards when some may already be driven.
440  */
441
442 int mca_find_unused_adapter(int id, int start)
443 {
444         if(mca_info == NULL || id == 0xffff) {
445                 return MCA_NOTFOUND;
446         }
447
448         for(; start >= 0 && start < MCA_NUMADAPTERS; start++) {
449
450                 /* not sure about this. There's no point in returning
451                  * adapters that aren't enabled, since they can't actually
452                  * be used. However, they might be needed for statistical
453                  * purposes or something... But if that is the case, the
454                  * user is free to write a routine that manually iterates
455                  * through the adapters.
456                  */
457
458                 if(mca_info->slot[start].status == MCA_ADAPTER_DISABLED ||
459                    mca_info->slot[start].driver_loaded) {
460                         continue;
461                 }
462
463                 if(id == mca_info->slot[start].id) {
464                         return start;
465                 }
466         }
467
468         return MCA_NOTFOUND;
469 } /* mca_find_unused_adapter() */
470
471 EXPORT_SYMBOL(mca_find_unused_adapter);
472
473 /*--------------------------------------------------------------------*/
474
475 /**
476  *      mca_read_stored_pos - read POS register from boot data
477  *      @slot: slot number to read from
478  *      @reg:  register to read from
479  *
480  *      Fetch a POS value that was stored at boot time by the kernel
481  *      when it scanned the MCA space. The register value is returned.
482  *      Missing or invalid registers report 0.
483  */
484
485 unsigned char mca_read_stored_pos(int slot, int reg)
486 {
487         if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0;
488         if(reg < 0 || reg >= 8) return 0;
489         return mca_info->slot[slot].pos[reg];
490 } /* mca_read_stored_pos() */
491
492 EXPORT_SYMBOL(mca_read_stored_pos);
493
494 /*--------------------------------------------------------------------*/
495
496 /**
497  *      mca_read_pos - read POS register from card
498  *      @slot: slot number to read from
499  *      @reg:  register to read from
500  *
501  *      Fetch a POS value directly from the hardware to obtain the
502  *      current value. This is much slower than mca_read_stored_pos and
503  *      may not be invoked from interrupt context. It handles the
504  *      deep magic required for onboard devices transparently.
505  */
506
507 unsigned char mca_read_pos(int slot, int reg)
508 {
509         unsigned int byte = 0;
510         unsigned long flags;
511
512         if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0;
513         if(reg < 0 || reg >= 8) return 0;
514
515         save_flags(flags);
516         cli();
517
518         /* Make sure motherboard setup is off */
519
520         outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
521
522         /* Read in the appropriate register */
523
524         if(slot == MCA_INTEGSCSI && mca_info->which_scsi) {
525
526                 /* Disable adapter setup, enable motherboard setup */
527
528                 outb_p(0, MCA_ADAPTER_SETUP_REG);
529                 outb_p(mca_info->which_scsi, MCA_MOTHERBOARD_SETUP_REG);
530
531                 byte = inb_p(MCA_POS_REG(reg));
532                 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
533         } else if(slot == MCA_INTEGVIDEO) {
534
535                 /* Disable adapter setup, enable motherboard setup */
536
537                 outb_p(0, MCA_ADAPTER_SETUP_REG);
538                 outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG);
539
540                 byte = inb_p(MCA_POS_REG(reg));
541                 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
542         } else if(slot == MCA_MOTHERBOARD) {
543
544                 /* Disable adapter setup, enable motherboard setup */
545                 outb_p(0, MCA_ADAPTER_SETUP_REG);
546                 outb_p(0x7f, MCA_MOTHERBOARD_SETUP_REG);
547
548                 byte = inb_p(MCA_POS_REG(reg));
549                 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
550         } else if(slot < MCA_MAX_SLOT_NR) {
551
552                 /* Make sure motherboard setup is off */
553
554                 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
555
556                 /* Read the appropriate register */
557
558                 outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
559                 byte = inb_p(MCA_POS_REG(reg));
560                 outb_p(0, MCA_ADAPTER_SETUP_REG);
561         }
562
563         /* Make sure the stored values are consistent, while we're here */
564
565         mca_info->slot[slot].pos[reg] = byte;
566
567         restore_flags(flags);
568
569         return byte;
570 } /* mca_read_pos() */
571
572 EXPORT_SYMBOL(mca_read_pos);
573
574 /*--------------------------------------------------------------------*/
575
576 /**
577  *      mca_write_pos - read POS register from card
578  *      @slot: slot number to read from
579  *      @reg:  register to read from
580  *      @byte: byte to write to the POS registers
581  *
582  *      Store a POS value directly from the hardware. You should not
583  *      normally need to use this function and should have a very good
584  *      knowledge of MCA bus before you do so. Doing this wrongly can
585  *      damage the hardware.
586  *
587  *      This function may not be used from interrupt context.
588  *
589  *      Note that this a technically a Bad Thing, as IBM tech stuff says
590  *      you should only set POS values through their utilities.
591  *      However, some devices such as the 3c523 recommend that you write
592  *      back some data to make sure the configuration is consistent.
593  *      I'd say that IBM is right, but I like my drivers to work.
594  *
595  *      This function can't do checks to see if multiple devices end up
596  *      with the same resources, so you might see magic smoke if someone
597  *      screws up.
598  */
599
600 void mca_write_pos(int slot, int reg, unsigned char byte)
601 {
602         unsigned long flags;
603
604         if(slot < 0 || slot >= MCA_MAX_SLOT_NR)
605                 return;
606         if(reg < 0 || reg >= 8)
607                 return;
608         if(mca_info == NULL)
609                 return;
610
611         save_flags(flags);
612         cli();
613
614         /* Make sure motherboard setup is off */
615
616         outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
617
618         /* Read in the appropriate register */
619
620         outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
621         outb_p(byte, MCA_POS_REG(reg));
622         outb_p(0, MCA_ADAPTER_SETUP_REG);
623
624         restore_flags(flags);
625
626         /* Update the global register list, while we have the byte */
627
628         mca_info->slot[slot].pos[reg] = byte;
629 } /* mca_write_pos() */
630
631 EXPORT_SYMBOL(mca_write_pos);
632
633 /*--------------------------------------------------------------------*/
634
635 /**
636  *      mca_set_adapter_name - Set the description of the card
637  *      @slot: slot to name
638  *      @name: text string for the namen
639  *
640  *      This function sets the name reported via /proc for this
641  *      adapter slot. This is for user information only. Setting a
642  *      name deletes any previous name.
643  */
644
645 void mca_set_adapter_name(int slot, char* name)
646 {
647         if(mca_info == NULL) return;
648
649         if(slot >= 0 && slot < MCA_NUMADAPTERS) {
650                 if(name != NULL) {
651                         strncpy(mca_info->slot[slot].name, name,
652                                 sizeof(mca_info->slot[slot].name)-1);
653                         mca_info->slot[slot].name[
654                                 sizeof(mca_info->slot[slot].name)-1] = 0;
655                 } else {
656                         mca_info->slot[slot].name[0] = 0;
657                 }
658         }
659 }
660
661 EXPORT_SYMBOL(mca_set_adapter_name);
662
663 /**
664  *      mca_set_adapter_procfn - Set the /proc callback
665  *      @slot: slot to configure
666  *      @procfn: callback function to call for /proc
667  *      @dev: device information passed to the callback
668  *
669  *      This sets up an information callback for /proc/mca/slot?.  The
670  *      function is called with the buffer, slot, and device pointer (or
671  *      some equally informative context information, or nothing, if you
672  *      prefer), and is expected to put useful information into the
673  *      buffer.  The adapter name, ID, and POS registers get printed
674  *      before this is called though, so don't do it again.
675  *
676  *      This should be called with a %NULL @procfn when a module
677  *      unregisters, thus preventing kernel crashes and other such
678  *      nastiness.
679  */
680
681 void mca_set_adapter_procfn(int slot, MCA_ProcFn procfn, void* dev)
682 {
683         if(mca_info == NULL) return;
684
685         if(slot >= 0 && slot < MCA_NUMADAPTERS) {
686                 mca_info->slot[slot].procfn = procfn;
687                 mca_info->slot[slot].dev = dev;
688         }
689 }
690
691 EXPORT_SYMBOL(mca_set_adapter_procfn);
692
693 /**
694  *      mca_is_adapter_used - check if claimed by driver
695  *      @slot:  slot to check
696  *
697  *      Returns 1 if the slot has been claimed by a driver
698  */
699
700 int mca_is_adapter_used(int slot)
701 {
702         return mca_info->slot[slot].driver_loaded;
703 }
704
705 EXPORT_SYMBOL(mca_is_adapter_used);
706
707 /**
708  *      mca_mark_as_used - claim an MCA device
709  *      @slot:  slot to claim
710  *      FIXME:  should we make this threadsafe
711  *
712  *      Claim an MCA slot for a device driver. If the
713  *      slot is already taken the function returns 1,
714  *      if it is not taken it is claimed and 0 is
715  *      returned.
716  */
717
718 int mca_mark_as_used(int slot)
719 {
720         if(mca_info->slot[slot].driver_loaded) return 1;
721         mca_info->slot[slot].driver_loaded = 1;
722         return 0;
723 }
724
725 EXPORT_SYMBOL(mca_mark_as_used);
726
727 /**
728  *      mca_mark_as_unused - release an MCA device
729  *      @slot:  slot to claim
730  *
731  *      Release the slot for other drives to use.
732  */
733
734 void mca_mark_as_unused(int slot)
735 {
736         mca_info->slot[slot].driver_loaded = 0;
737 }
738
739 EXPORT_SYMBOL(mca_mark_as_unused);
740
741 /**
742  *      mca_get_adapter_name - get the adapter description
743  *      @slot:  slot to query
744  *
745  *      Return the adapter description if set. If it has not been
746  *      set or the slot is out range then return NULL.
747  */
748
749 char *mca_get_adapter_name(int slot)
750 {
751         if(mca_info == NULL) return 0;
752
753         if(slot >= 0 && slot < MCA_NUMADAPTERS) {
754                 return mca_info->slot[slot].name;
755         }
756
757         return 0;
758 }
759
760 EXPORT_SYMBOL(mca_get_adapter_name);
761
762 /**
763  *      mca_isadapter - check if the slot holds an adapter
764  *      @slot:  slot to query
765  *
766  *      Returns zero if the slot does not hold an adapter, non zero if
767  *      it does.
768  */
769
770 int mca_isadapter(int slot)
771 {
772         if(mca_info == NULL) return 0;
773
774         if(slot >= 0 && slot < MCA_NUMADAPTERS) {
775                 return ((mca_info->slot[slot].status == MCA_ADAPTER_NORMAL)
776                         || (mca_info->slot[slot].status == MCA_ADAPTER_DISABLED));
777         }
778
779         return 0;
780 }
781
782 EXPORT_SYMBOL(mca_isadapter);
783
784
785 /**
786  *      mca_isadapter - check if the slot holds an adapter
787  *      @slot:  slot to query
788  *
789  *      Returns a non zero value if the slot holds an enabled adapter
790  *      and zero for any other case.
791  */
792
793 int mca_isenabled(int slot)
794 {
795         if(mca_info == NULL) return 0;
796
797         if(slot >= 0 && slot < MCA_NUMADAPTERS) {
798                 return (mca_info->slot[slot].status == MCA_ADAPTER_NORMAL);
799         }
800
801         return 0;
802 }
803
804 EXPORT_SYMBOL(mca_isenabled);
805
806 /*--------------------------------------------------------------------*/
807
808 #ifdef CONFIG_PROC_FS
809
810 int get_mca_info(char *page, char **start, off_t off,
811                                  int count, int *eof, void *data)
812 {
813         int i, j, len = 0;
814
815         if(MCA_bus && mca_info != NULL) {
816                 /* Format POS registers of eight MCA slots */
817
818                 for(i=0; i<MCA_MAX_SLOT_NR; i++) {
819                         len += sprintf(page+len, "Slot %d: ", i+1);
820                         for(j=0; j<8; j++)
821                                 len += sprintf(page+len, "%02x ", mca_info->slot[i].pos[j]);
822                         len += sprintf(page+len, " %s\n", mca_info->slot[i].name);
823                 }
824
825                 /* Format POS registers of integrated video subsystem */
826
827                 len += sprintf(page+len, "Video : ");
828                 for(j=0; j<8; j++)
829                         len += sprintf(page+len, "%02x ", mca_info->slot[MCA_INTEGVIDEO].pos[j]);
830                 len += sprintf(page+len, " %s\n", mca_info->slot[MCA_INTEGVIDEO].name);
831
832                 /* Format POS registers of integrated SCSI subsystem */
833
834                 len += sprintf(page+len, "SCSI  : ");
835                 for(j=0; j<8; j++)
836                         len += sprintf(page+len, "%02x ", mca_info->slot[MCA_INTEGSCSI].pos[j]);
837                 len += sprintf(page+len, " %s\n", mca_info->slot[MCA_INTEGSCSI].name);
838
839                 /* Format POS registers of motherboard */
840
841                 len += sprintf(page+len, "Planar: ");
842                 for(j=0; j<8; j++)
843                         len += sprintf(page+len, "%02x ", mca_info->slot[MCA_MOTHERBOARD].pos[j]);
844                 len += sprintf(page+len, " %s\n", mca_info->slot[MCA_MOTHERBOARD].name);
845         } else {
846                 /* Leave it empty if MCA not detected - this should *never*
847                  * happen!
848                  */
849         }
850
851         if (len <= off+count) *eof = 1;
852         *start = page + off;
853         len -= off;
854         if (len>count) len = count;
855         if (len<0) len = 0;
856         return len;
857 }
858
859 /*--------------------------------------------------------------------*/
860
861 static int mca_default_procfn(char* buf, struct MCA_adapter *p)
862 {
863         int len = 0, i;
864         int slot = p - mca_info->slot;
865
866         /* Print out the basic information */
867
868         if(slot < MCA_MAX_SLOT_NR) {
869                 len += sprintf(buf+len, "Slot: %d\n", slot+1);
870         } else if(slot == MCA_INTEGSCSI) {
871                 len += sprintf(buf+len, "Integrated SCSI Adapter\n");
872         } else if(slot == MCA_INTEGVIDEO) {
873                 len += sprintf(buf+len, "Integrated Video Adapter\n");
874         } else if(slot == MCA_MOTHERBOARD) {
875                 len += sprintf(buf+len, "Motherboard\n");
876         }
877         if(p->name[0]) {
878
879                 /* Drivers might register a name without /proc handler... */
880
881                 len += sprintf(buf+len, "Adapter Name: %s\n",
882                         p->name);
883         } else {
884                 len += sprintf(buf+len, "Adapter Name: Unknown\n");
885         }
886         len += sprintf(buf+len, "Id: %02x%02x\n",
887                 p->pos[1], p->pos[0]);
888         len += sprintf(buf+len, "Enabled: %s\nPOS: ",
889                 mca_isenabled(slot) ? "Yes" : "No");
890         for(i=0; i<8; i++) {
891                 len += sprintf(buf+len, "%02x ", p->pos[i]);
892         }
893         len += sprintf(buf+len, "\nDriver Installed: %s",
894                 mca_is_adapter_used(slot) ? "Yes" : "No");
895         buf[len++] = '\n';
896         buf[len] = 0;
897
898         return len;
899 } /* mca_default_procfn() */
900
901 static int get_mca_machine_info(char* page, char **start, off_t off,
902                                  int count, int *eof, void *data)
903 {
904         int len = 0;
905
906         len += sprintf(page+len, "Model Id: 0x%x\n", machine_id);
907         len += sprintf(page+len, "Submodel Id: 0x%x\n", machine_submodel_id);
908         len += sprintf(page+len, "BIOS Revision: 0x%x\n", BIOS_revision);
909
910         if (len <= off+count) *eof = 1;
911         *start = page + off;
912         len -= off;
913         if (len>count) len = count;
914         if (len<0) len = 0;
915         return len;
916 }
917
918 static int mca_read_proc(char *page, char **start, off_t off,
919                                  int count, int *eof, void *data)
920 {
921         struct MCA_adapter *p = (struct MCA_adapter *)data;
922         int len = 0;
923
924         /* Get the standard info */
925
926         len = mca_default_procfn(page, p);
927
928         /* Do any device-specific processing, if there is any */
929
930         if(p->procfn) {
931                 len += p->procfn(page+len, p-mca_info->slot, p->dev);
932         }
933         if (len <= off+count) *eof = 1;
934         *start = page + off;
935         len -= off;
936         if (len>count) len = count;
937         if (len<0) len = 0;
938         return len;
939 } /* mca_read_proc() */
940
941 /*--------------------------------------------------------------------*/
942
943 void __init mca_do_proc_init(void)
944 {
945         int i;
946         struct proc_dir_entry *proc_mca;
947         struct proc_dir_entry* node = NULL;
948         struct MCA_adapter *p;
949
950         if(mca_info == NULL) return;    /* Should never happen */
951
952         proc_mca = proc_mkdir("mca", &proc_root);
953         create_proc_read_entry("pos",0,proc_mca,get_mca_info,NULL);
954         create_proc_read_entry("machine",0,proc_mca,get_mca_machine_info,NULL);
955
956         /* Initialize /proc/mca entries for existing adapters */
957
958         for(i = 0; i < MCA_NUMADAPTERS; i++) {
959                 p = &mca_info->slot[i];
960                 p->procfn = 0;
961
962                 if(i < MCA_MAX_SLOT_NR) sprintf(p->procname,"slot%d", i+1);
963                 else if(i == MCA_INTEGVIDEO) sprintf(p->procname,"video");
964                 else if(i == MCA_INTEGSCSI) sprintf(p->procname,"scsi");
965                 else if(i == MCA_MOTHERBOARD) sprintf(p->procname,"planar");
966
967                 if(!mca_isadapter(i)) continue;
968
969                 node = create_proc_read_entry(p->procname, 0, proc_mca,
970                                                 mca_read_proc, (void *)p);
971
972                 if(node == NULL) {
973                         printk("Failed to allocate memory for MCA proc-entries!");
974                         return;
975                 }
976         }
977
978 } /* mca_do_proc_init() */
979
980 #endif