Import 2.3.12pre3
[davej-history.git] / arch / i386 / kernel / mca.c
blobe820b03537db880e1286a9b96ecd9c11d8050600
1 /*
2 * linux/arch/i386/kernel/mca.c
3 * Written by Martin Kolinek, February 1996
5 * Changes:
7 * Chris Beauregard July 28th, 1996
8 * - Fixed up integrated SCSI detection
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
16 * Chris Beauregard August 9th, 1996
17 * - Rewrote /proc/mca
19 * Chris Beauregard January 7th, 1997
20 * - Added basic NMI-processing
21 * - Added more information to mca_info structure
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
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
34 #include <linux/types.h>
35 #include <linux/errno.h>
36 #include <linux/kernel.h>
37 #include <linux/mca.h>
38 #include <asm/system.h>
39 #include <asm/io.h>
40 #include <linux/proc_fs.h>
41 #include <linux/mman.h>
42 #include <linux/config.h>
43 #include <linux/mm.h>
44 #include <linux/pagemap.h>
45 #include <linux/ioport.h>
46 #include <asm/uaccess.h>
47 #include <linux/init.h>
49 /* This structure holds MCA information. Each (plug-in) adapter has
50 * eight POS registers. Then the machine may have integrated video and
51 * SCSI subsystems, which also have eight POS registers.
52 * Other miscellaneous information follows.
55 typedef enum {
56 MCA_ADAPTER_NORMAL = 0,
57 MCA_ADAPTER_NONE = 1,
58 MCA_ADAPTER_DISABLED = 2,
59 MCA_ADAPTER_ERROR = 3
60 } MCA_AdapterStatus;
62 struct MCA_adapter {
63 MCA_AdapterStatus status; /* is there a valid adapter? */
64 int id; /* adapter id value */
65 unsigned char pos[8]; /* POS registers */
66 int driver_loaded; /* is there a driver installed? */
67 /* 0 - No, 1 - Yes */
68 char name[48]; /* adapter-name provided by driver */
69 char procname[8]; /* name of /proc/mca file */
70 MCA_ProcFn procfn; /* /proc info callback */
71 void* dev; /* device/context info for callback */
74 struct MCA_info {
75 /* one for each of the 8 possible slots, plus one for integrated SCSI
76 * and one for integrated video.
79 struct MCA_adapter slot[MCA_NUMADAPTERS];
81 /* two potential addresses for integrated SCSI adapter - this will
82 * track which one we think it is.
85 unsigned char which_scsi;
88 /* The mca_info structure pointer. If MCA bus is present, the function
89 * mca_probe() is invoked. The function puts motherboard, then all
90 * adapters into setup mode, allocates and fills an MCA_info structure,
91 * and points this pointer to the structure. Otherwise the pointer
92 * is set to zero.
95 static struct MCA_info* mca_info = NULL;
97 /* MCA registers */
99 #define MCA_MOTHERBOARD_SETUP_REG 0x94
100 #define MCA_ADAPTER_SETUP_REG 0x96
101 #define MCA_POS_REG(n) (0x100+(n))
103 #define MCA_ENABLED 0x01 /* POS 2, set if adapter enabled */
105 /*--------------------------------------------------------------------*/
107 #ifdef CONFIG_PROC_FS
109 static void mca_do_proc_init(void);
110 static int mca_default_procfn(char* buf, int slot);
112 static ssize_t proc_mca_read(struct file*, char*, size_t, loff_t *);
114 static struct file_operations proc_mca_operations = {
115 NULL, /* llseek */
116 proc_mca_read, /* read */
117 NULL, /* write */
118 NULL, /* readdir */
119 NULL, /* poll */
120 NULL, /* ioctl */
121 NULL, /* mmap */
122 NULL, /* open */
123 NULL, /* flush */
124 NULL, /* release */
125 NULL, /* fsync */
126 NULL, /* fascync */
127 NULL, /* check_media_change */
128 NULL, /* revalidate */
129 NULL /* lock */
132 static struct inode_operations proc_mca_inode_operations = {
133 &proc_mca_operations, /* default file-ops */
134 NULL, /* create */
135 NULL, /* lookup */
136 NULL, /* link */
137 NULL, /* unlink */
138 NULL, /* symlink */
139 NULL, /* mkdir */
140 NULL, /* rmdir */
141 NULL, /* mknod */
142 NULL, /* rename */
143 NULL, /* readlink */
144 NULL, /* follow_link */
145 NULL, /* get_block */
146 NULL, /* readpage */
147 NULL, /* writepage */
148 NULL, /* flushpage */
149 NULL, /* truncate */
150 NULL, /* permission */
151 NULL, /* smap */
152 NULL /* revalidate */
154 #endif
156 /*--------------------------------------------------------------------*/
158 /* Build the status info for the adapter */
160 static void mca_configure_adapter_status(int slot) {
161 mca_info->slot[slot].status = MCA_ADAPTER_NONE;
163 mca_info->slot[slot].id = mca_info->slot[slot].pos[0]
164 + (mca_info->slot[slot].pos[1] << 8);
166 if(!mca_info->slot[slot].id && slot < MCA_MAX_SLOT_NR) {
168 /* id = 0x0000 usually indicates hardware failure,
169 * however, ZP Gu (zpg@castle.net> reports that his 9556
170 * has 0x0000 as id and everything still works. There
171 * also seem to be an adapter with id = 0x0000; the
172 * NCR Parallel Bus Memory Card. Until this is confirmed,
173 * however, this code will stay.
176 mca_info->slot[slot].status = MCA_ADAPTER_ERROR;
178 return;
179 } else if(mca_info->slot[slot].id != 0xffff) {
181 /* 0xffff usually indicates that there's no adapter,
182 * however, some integrated adapters may have 0xffff as
183 * their id and still be valid. Examples are on-board
184 * VGA of the 55sx, the integrated SCSI of the 56 & 57,
185 * and possibly also the 95 ULTIMEDIA.
188 mca_info->slot[slot].status = MCA_ADAPTER_NORMAL;
191 if((mca_info->slot[slot].id == 0xffff ||
192 mca_info->slot[slot].id == 0x0000) && slot >= MCA_MAX_SLOT_NR) {
193 int j;
195 for(j = 2; j < 8; j++) {
196 if(mca_info->slot[slot].pos[j] != 0xff) {
197 mca_info->slot[slot].status = MCA_ADAPTER_NORMAL;
198 break;
203 if(!(mca_info->slot[slot].pos[2] & MCA_ENABLED)) {
205 /* enabled bit is in POS 2 */
207 mca_info->slot[slot].status = MCA_ADAPTER_DISABLED;
209 } /* mca_configure_adapter_status */
211 /*--------------------------------------------------------------------*/
213 struct resource mca_standard_resources[] = {
214 { "system control port B (MCA)", 0x60, 0x60 },
215 { "arbitration (MCA)", 0x90, 0x90 },
216 { "card Select Feedback (MCA)", 0x91, 0x91 },
217 { "system Control port A (MCA)", 0x92, 0x92 },
218 { "system board setup (MCA)", 0x94, 0x94 },
219 { "POS (MCA)", 0x96, 0x97 },
220 { "POS (MCA)", 0x100, 0x107 }
223 #define MCA_STANDARD_RESOURCES (sizeof(mca_standard_resources)/sizeof(struct resource))
225 __initfunc(void mca_init(void))
227 unsigned int i, j;
228 unsigned long flags;
230 /* WARNING: Be careful when making changes here. Putting an adapter
231 * and the motherboard simultaneously into setup mode may result in
232 * damage to chips (according to The Indispensible PC Hardware Book
233 * by Hans-Peter Messmer). Also, we disable system interrupts (so
234 * that we are not disturbed in the middle of this).
237 /* Make sure the MCA bus is present */
239 if(!MCA_bus)
240 return;
241 printk("Micro Channel bus detected.\n");
243 /* Allocate MCA_info structure (at address divisible by 8) */
245 mca_info = (struct MCA_info *)kmalloc(sizeof(struct MCA_info), GFP_KERNEL);
247 if(mca_info == NULL) {
248 printk("Failed to allocate memory for mca_info!");
249 return;
251 memset(mca_info, 0, sizeof(struct MCA_info));
253 save_flags(flags);
254 cli();
256 /* Make sure adapter setup is off */
258 outb_p(0, MCA_ADAPTER_SETUP_REG);
260 /* Put motherboard into video setup mode, read integrated video
261 * POS registers, and turn motherboard setup off.
264 outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG);
265 mca_info->slot[MCA_INTEGVIDEO].name[0] = 0;
266 for(j=0; j<8; j++) {
267 mca_info->slot[MCA_INTEGVIDEO].pos[j] = inb_p(MCA_POS_REG(j));
269 mca_configure_adapter_status(MCA_INTEGVIDEO);
271 /* Put motherboard into scsi setup mode, read integrated scsi
272 * POS registers, and turn motherboard setup off.
274 * It seems there are two possible SCSI registers. Martin says that
275 * for the 56,57, 0xf7 is the one, but fails on the 76.
276 * Alfredo (apena@vnet.ibm.com) says
277 * 0xfd works on his machine. We'll try both of them. I figure it's
278 * a good bet that only one could be valid at a time. This could
279 * screw up though if one is used for something else on the other
280 * machine.
283 outb_p(0xf7, MCA_MOTHERBOARD_SETUP_REG);
284 mca_info->slot[MCA_INTEGSCSI].name[0] = 0;
285 for(j=0; j<8; j++) {
286 if((mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j))) != 0xff)
288 /* 0xff all across means no device. 0x00 means
289 * something's broken, but a device is probably there.
290 * However, if you get 0x00 from a motherboard
291 * register it won't matter what we find. For the
292 * record, on the 57SLC, the integrated SCSI
293 * adapter has 0xffff for the adapter ID, but
294 * nonzero for other registers.
297 mca_info->which_scsi = 0xf7;
300 if(!mca_info->which_scsi) {
302 /* Didn't find it at 0xf7, try somewhere else... */
303 mca_info->which_scsi = 0xfd;
305 outb_p(0xfd, MCA_MOTHERBOARD_SETUP_REG);
306 for(j=0; j<8; j++)
307 mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j));
309 mca_configure_adapter_status(MCA_INTEGSCSI);
311 /* Turn off motherboard setup */
313 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
315 /* Now loop over MCA slots: put each adapter into setup mode, and
316 * read its POS registers. Then put adapter setup off.
319 for(i=0; i<MCA_MAX_SLOT_NR; i++) {
320 outb_p(0x8|(i&0xf), MCA_ADAPTER_SETUP_REG);
321 for(j=0; j<8; j++) {
322 mca_info->slot[i].pos[j]=inb_p(MCA_POS_REG(j));
324 mca_info->slot[i].name[0] = 0;
325 mca_info->slot[i].driver_loaded = 0;
326 mca_configure_adapter_status(i);
328 outb_p(0, MCA_ADAPTER_SETUP_REG);
330 /* Enable interrupts and return memory start */
332 restore_flags(flags);
334 for (i = 0; i < MCA_STANDARD_RESOURCES; i++)
335 request_resource(&ioport_resource, mca_standard_resources + i);
337 #ifdef CONFIG_PROC_FS
338 mca_do_proc_init();
339 #endif
342 /*--------------------------------------------------------------------*/
344 static void mca_handle_nmi_slot(int slot, int check_flag)
346 if(slot < MCA_MAX_SLOT_NR) {
347 printk("NMI: caused by MCA adapter in slot %d (%s)\n", slot+1,
348 mca_info->slot[slot].name);
349 } else if(slot == MCA_INTEGSCSI) {
350 printk("NMI: caused by MCA integrated SCSI adapter (%s)\n",
351 mca_info->slot[slot].name);
352 } else if(slot == MCA_INTEGVIDEO) {
353 printk("NMI: caused by MCA integrated video adapter (%s)\n",
354 mca_info->slot[slot].name);
357 /* More info available in POS 6 and 7? */
359 if(check_flag) {
360 unsigned char pos6, pos7;
362 pos6 = mca_read_pos(slot, 6);
363 pos7 = mca_read_pos(slot, 7);
365 printk("NMI: POS 6 = 0x%x, POS 7 = 0x%x\n", pos6, pos7);
368 } /* mca_handle_nmi_slot */
370 /*--------------------------------------------------------------------*/
372 void mca_handle_nmi(void)
375 int i;
376 unsigned char pos5;
378 /* First try - scan the various adapters and see if a specific
379 * adapter was responsible for the error.
382 for(i = 0; i < MCA_NUMADAPTERS; i++) {
384 /* Bit 7 of POS 5 is reset when this adapter has a hardware
385 * error. Bit 7 it reset if there's error information
386 * available in POS 6 and 7.
389 pos5 = mca_read_pos(i, 5);
391 if(!(pos5 & 0x80)) {
392 mca_handle_nmi_slot(i, !(pos5 & 0x40));
393 return;
397 /* If I recall correctly, there's a whole bunch of other things that
398 * we can do to check for NMI problems, but that's all I know about
399 * at the moment.
402 printk("NMI generated from unknown source!\n");
403 } /* mca_handle_nmi */
405 /*--------------------------------------------------------------------*/
407 int mca_find_adapter(int id, int start)
409 if(mca_info == NULL || id == 0xffff) {
410 return MCA_NOTFOUND;
413 for(; start >= 0 && start < MCA_NUMADAPTERS; start++) {
415 /* Not sure about this. There's no point in returning
416 * adapters that aren't enabled, since they can't actually
417 * be used. However, they might be needed for statistical
418 * purposes or something... But if that is the case, the
419 * user is free to write a routine that manually iterates
420 * through the adapters.
423 if(mca_info->slot[start].status == MCA_ADAPTER_DISABLED) {
424 continue;
427 if(id == mca_info->slot[start].id) {
428 return start;
432 return MCA_NOTFOUND;
433 } /* mca_find_adapter() */
435 /*--------------------------------------------------------------------*/
437 int mca_find_unused_adapter(int id, int start)
439 if(mca_info == NULL || id == 0xffff) {
440 return MCA_NOTFOUND;
443 for(; start >= 0 && start < MCA_NUMADAPTERS; start++) {
445 /* not sure about this. There's no point in returning
446 * adapters that aren't enabled, since they can't actually
447 * be used. However, they might be needed for statistical
448 * purposes or something... But if that is the case, the
449 * user is free to write a routine that manually iterates
450 * through the adapters.
453 if(mca_info->slot[start].status == MCA_ADAPTER_DISABLED ||
454 mca_info->slot[start].driver_loaded) {
455 continue;
458 if(id == mca_info->slot[start].id) {
459 return start;
463 return MCA_NOTFOUND;
464 } /* mca_find_unused_adapter() */
466 /*--------------------------------------------------------------------*/
468 unsigned char mca_read_stored_pos(int slot, int reg)
470 if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0;
471 if(reg < 0 || reg >= 8) return 0;
472 return mca_info->slot[slot].pos[reg];
473 } /* mca_read_stored_pos() */
475 /*--------------------------------------------------------------------*/
477 unsigned char mca_read_pos(int slot, int reg)
479 unsigned int byte = 0;
480 unsigned long flags;
482 if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0;
483 if(reg < 0 || reg >= 8) return 0;
485 save_flags(flags);
486 cli();
488 /* Make sure motherboard setup is off */
490 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
492 /* Read in the appropriate register */
494 if(slot == MCA_INTEGSCSI && mca_info->which_scsi) {
496 /* Disable adapter setup, enable motherboard setup */
498 outb_p(0, MCA_ADAPTER_SETUP_REG);
499 outb_p(mca_info->which_scsi, MCA_MOTHERBOARD_SETUP_REG);
501 byte = inb_p(MCA_POS_REG(reg));
502 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
503 } else if(slot == MCA_INTEGVIDEO) {
505 /* Disable adapter setup, enable motherboard setup */
507 outb_p(0, MCA_ADAPTER_SETUP_REG);
508 outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG);
510 byte = inb_p(MCA_POS_REG(reg));
511 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
512 } else if(slot < MCA_MAX_SLOT_NR) {
514 /* Make sure motherboard setup is off */
516 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
518 /* Read the appropriate register */
520 outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
521 byte = inb_p(MCA_POS_REG(reg));
522 outb_p(0, MCA_ADAPTER_SETUP_REG);
525 /* Make sure the stored values are consistent, while we're here */
527 mca_info->slot[slot].pos[reg] = byte;
529 restore_flags(flags);
531 return byte;
532 } /* mca_read_pos() */
534 /*--------------------------------------------------------------------*/
536 /* Note that this a technically a Bad Thing, as IBM tech stuff says
537 * you should only set POS values through their utilities.
538 * However, some devices such as the 3c523 recommend that you write
539 * back some data to make sure the configuration is consistent.
540 * I'd say that IBM is right, but I like my drivers to work.
541 * This function can't do checks to see if multiple devices end up
542 * with the same resources, so you might see magic smoke if someone
543 * screws up.
546 void mca_write_pos(int slot, int reg, unsigned char byte)
548 unsigned long flags;
550 if(slot < 0 || slot >= MCA_MAX_SLOT_NR)
551 return;
552 if(reg < 0 || reg >= 8)
553 return;
554 if(mca_info == NULL)
555 return;
557 save_flags(flags);
558 cli();
560 /* Make sure motherboard setup is off */
562 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
564 /* Read in the appropriate register */
566 outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
567 outb_p(byte, MCA_POS_REG(reg));
568 outb_p(0, MCA_ADAPTER_SETUP_REG);
570 restore_flags(flags);
572 /* Update the global register list, while we have the byte */
574 mca_info->slot[slot].pos[reg] = byte;
575 } /* mca_write_pos() */
577 /*--------------------------------------------------------------------*/
579 void mca_set_adapter_name(int slot, char* name)
581 if(mca_info == NULL) return;
583 if(slot >= 0 && slot < MCA_NUMADAPTERS) {
584 if(name != NULL) {
585 strncpy(mca_info->slot[slot].name, name,
586 sizeof(mca_info->slot[slot].name)-1);
587 mca_info->slot[slot].name[
588 sizeof(mca_info->slot[slot].name)-1] = 0;
589 } else {
590 mca_info->slot[slot].name[0] = 0;
595 void mca_set_adapter_procfn(int slot, MCA_ProcFn procfn, void* dev)
597 if(mca_info == NULL) return;
599 if(slot >= 0 && slot < MCA_NUMADAPTERS) {
600 mca_info->slot[slot].procfn = procfn;
601 mca_info->slot[slot].dev = dev;
605 int mca_is_adapter_used(int slot)
607 return mca_info->slot[slot].driver_loaded;
610 int mca_mark_as_used(int slot)
612 if(mca_info->slot[slot].driver_loaded) return 1;
613 mca_info->slot[slot].driver_loaded = 1;
614 return 0;
617 void mca_mark_as_unused(int slot)
619 mca_info->slot[slot].driver_loaded = 0;
622 char *mca_get_adapter_name(int slot)
624 if(mca_info == NULL) return 0;
626 if(slot >= 0 && slot < MCA_NUMADAPTERS) {
627 return mca_info->slot[slot].name;
630 return 0;
633 int mca_isadapter(int slot)
635 if(mca_info == NULL) return 0;
637 if(slot >= 0 && slot < MCA_NUMADAPTERS) {
638 return ((mca_info->slot[slot].status == MCA_ADAPTER_NORMAL)
639 || (mca_info->slot[slot].status == MCA_ADAPTER_DISABLED));
642 return 0;
645 int mca_isenabled(int slot)
647 if(mca_info == NULL) return 0;
649 if(slot >= 0 && slot < MCA_NUMADAPTERS) {
650 return (mca_info->slot[slot].status == MCA_ADAPTER_NORMAL);
653 return 0;
656 /*--------------------------------------------------------------------*/
658 #ifdef CONFIG_PROC_FS
660 int get_mca_info(char *buf)
662 int i, j, len = 0;
664 if(MCA_bus && mca_info != NULL)
666 /* Format POS registers of eight MCA slots */
668 for(i=0; i<MCA_MAX_SLOT_NR; i++)
670 len += sprintf(buf+len, "Slot %d: ", i+1);
671 for(j=0; j<8; j++)
672 len += sprintf(buf+len, "%02x ", mca_info->slot[i].pos[j]);
673 len += sprintf(buf+len, " %s\n", mca_info->slot[i].name);
676 /* Format POS registers of integrated video subsystem */
678 len += sprintf(buf+len, "Video : ");
679 for(j=0; j<8; j++)
680 len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGVIDEO].pos[j]);
681 len += sprintf(buf+len, " %s\n", mca_info->slot[MCA_INTEGVIDEO].name);
683 /* Format POS registers of integrated SCSI subsystem */
685 len += sprintf(buf+len, "SCSI : ");
686 for(j=0; j<8; j++)
687 len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGSCSI].pos[j]);
688 len += sprintf(buf+len, " %s\n", mca_info->slot[MCA_INTEGSCSI].name);
689 } else {
690 /* Leave it empty if MCA not detected - this should *never*
691 * happen!
695 return len;
699 /*--------------------------------------------------------------------*/
701 __initfunc(void mca_do_proc_init(void))
703 int i;
704 struct proc_dir_entry* node = NULL;
706 if(mca_info == NULL) return; /* Should never happen */
708 proc_register(&proc_mca, &(struct proc_dir_entry) {
709 PROC_MCA_REGISTERS, 3, "pos", S_IFREG|S_IRUGO,
710 1, 0, 0, 0, &proc_mca_inode_operations,});
712 proc_register(&proc_mca, &(struct proc_dir_entry) {
713 PROC_MCA_MACHINE, 7, "machine", S_IFREG|S_IRUGO,
714 1, 0, 0, 0, &proc_mca_inode_operations,});
716 /* Initialize /proc/mca entries for existing adapters */
718 for(i = 0; i < MCA_NUMADAPTERS; i++) {
719 mca_info->slot[i].procfn = 0;
720 mca_info->slot[i].dev = 0;
722 if(!mca_isadapter(i)) continue;
724 node = (struct proc_dir_entry *)kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
726 if(node == NULL) {
727 printk("Failed to allocate memory for MCA proc-entries!");
728 return;
730 memset(node, 0, sizeof(struct proc_dir_entry));
732 if(i < MCA_MAX_SLOT_NR) {
733 node->low_ino = PROC_MCA_SLOT + i;
734 node->namelen = sprintf(mca_info->slot[i].procname,
735 "slot%d", i+1);
736 } else if(i == MCA_INTEGVIDEO) {
737 node->low_ino = PROC_MCA_VIDEO;
738 node->namelen = sprintf(mca_info->slot[i].procname,
739 "video");
740 } else if(i == MCA_INTEGSCSI) {
741 node->low_ino = PROC_MCA_SCSI;
742 node->namelen = sprintf(mca_info->slot[i].procname,
743 "scsi");
745 node->name = mca_info->slot[i].procname;
746 node->mode = S_IFREG | S_IRUGO;
747 node->ops = &proc_mca_inode_operations;
748 proc_register(&proc_mca, node);
751 } /* mca_do_proc_init() */
753 /*--------------------------------------------------------------------*/
755 int mca_default_procfn(char* buf, int slot)
757 int len = 0, i;
759 /* This really shouldn't happen... */
761 if(mca_info == NULL) {
762 *buf = 0;
763 return 0;
766 /* Print out the basic information */
768 if(slot < MCA_MAX_SLOT_NR) {
769 len += sprintf(buf+len, "Slot: %d\n", slot+1);
770 } else if(slot == MCA_INTEGSCSI) {
771 len += sprintf(buf+len, "Integrated SCSI Adapter\n");
772 } else if(slot == MCA_INTEGVIDEO) {
773 len += sprintf(buf+len, "Integrated Video Adapter\n");
775 if(mca_info->slot[slot].name[0]) {
777 /* Drivers might register a name without /proc handler... */
779 len += sprintf(buf+len, "Adapter Name: %s\n",
780 mca_info->slot[slot].name);
781 } else {
782 len += sprintf(buf+len, "Adapter Name: Unknown\n");
784 len += sprintf(buf+len, "Id: %02x%02x\n",
785 mca_info->slot[slot].pos[1], mca_info->slot[slot].pos[0]);
786 len += sprintf(buf+len, "Enabled: %s\nPOS: ",
787 mca_isenabled(slot) ? "Yes" : "No");
788 for(i=0; i<8; i++) {
789 len += sprintf(buf+len, "%02x ", mca_info->slot[slot].pos[i]);
791 len += sprintf(buf+len, "\nDriver Installed: %s",
792 mca_is_adapter_used(slot) ? "Yes" : "No");
793 buf[len++] = '\n';
794 buf[len] = 0;
796 return len;
797 } /* mca_default_procfn() */
799 static int get_mca_machine_info(char* buf)
801 int len = 0;
803 len += sprintf(buf+len, "Model Id: 0x%x\n", machine_id);
804 len += sprintf(buf+len, "Submodel Id: 0x%x\n", machine_submodel_id);
805 len += sprintf(buf+len, "BIOS Revision: 0x%x\n", BIOS_revision);
807 return len;
810 static int mca_fill(char* page, int pid, int type, char** start,
811 loff_t *offset, int length)
813 int len = 0;
814 int slot = 0;
816 switch(type) {
817 case PROC_MCA_REGISTERS:
818 return get_mca_info(page);
819 case PROC_MCA_MACHINE:
820 return get_mca_machine_info(page);
821 case PROC_MCA_VIDEO:
822 slot = MCA_INTEGVIDEO;
823 break;
824 case PROC_MCA_SCSI:
825 slot = MCA_INTEGSCSI;
826 break;
827 default:
828 if(type < PROC_MCA_SLOT || type >= PROC_MCA_LAST) {
829 return -EBADF;
831 slot = type - PROC_MCA_SLOT;
832 break;
835 /* If we made it here, we better have a valid slot */
837 /* Get the standard info */
839 len = mca_default_procfn(page, slot);
841 /* Do any device-specific processing, if there is any */
843 if(mca_info->slot[slot].procfn) {
844 len += mca_info->slot[slot].procfn(page+len, slot,
845 mca_info->slot[slot].dev);
848 return len;
849 } /* mca_fill() */
851 /* Blatantly stolen from fs/proc/array.c, and thus is probably overkill */
853 #define PROC_BLOCK_SIZE (3*1024)
855 static ssize_t proc_mca_read(struct file* file,
856 char* buf, size_t count, loff_t *ppos)
858 unsigned long page;
859 char *start;
860 int length;
861 int end;
862 unsigned int type, pid;
863 struct proc_dir_entry *dp;
864 struct inode *inode = file->f_dentry->d_inode;
866 if(count < 0)
867 return -EINVAL;
868 if(count > PROC_BLOCK_SIZE)
869 count = PROC_BLOCK_SIZE;
870 if(!(page = __get_free_page(GFP_KERNEL)))
871 return -ENOMEM;
872 type = inode->i_ino;
873 pid = type >> 16;
874 type &= 0x0000ffff;
875 start = NULL;
876 dp = (struct proc_dir_entry *) inode->u.generic_ip;
877 length = mca_fill((char *) page, pid, type,
878 &start, ppos, count);
879 if(length < 0) {
880 free_page(page);
881 return length;
883 if(start != NULL) {
884 /* We have had block-adjusting processing! */
886 copy_to_user(buf, start, length);
887 *ppos += length;
888 count = length;
889 } else {
890 /* Static 4kB (or whatever) block capacity */
892 if(*ppos >= length) {
893 free_page(page);
894 return 0;
896 if(count + *ppos > length)
897 count = length - *ppos;
898 end = count + *ppos;
899 copy_to_user(buf, (char *) page + *ppos, count);
900 *ppos = end;
902 free_page(page);
903 return count;
904 } /* proc_mca_read() */
906 #endif