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 
mca_configure_adapter_status(int slot)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 
mca_init(void)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 
mca_handle_nmi_slot(int slot,int check_flag)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 
mca_handle_nmi(void)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 
mca_find_adapter(int id,int start)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 
mca_find_unused_adapter(int id,int start)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 
mca_read_stored_pos(int slot,int reg)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 
mca_read_pos(int slot,int reg)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 
mca_write_pos(int slot,int reg,unsigned char byte)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 
mca_set_adapter_name(int slot,char * name)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 
mca_set_adapter_procfn(int slot,MCA_ProcFn procfn,void * dev)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 
mca_is_adapter_used(int slot)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 
mca_mark_as_used(int slot)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 
mca_mark_as_unused(int slot)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 
mca_get_adapter_name(int slot)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 
mca_isadapter(int slot)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 
mca_isenabled(int slot)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 
get_mca_info(char * page,char ** start,off_t off,int count,int * eof,void * data)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 
mca_default_procfn(char * buf,struct MCA_adapter * p)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 
get_mca_machine_info(char * page,char ** start,off_t off,int count,int * eof,void * data)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 
mca_read_proc(char * page,char ** start,off_t off,int count,int * eof,void * data)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 
mca_do_proc_init(void)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
981