Import 2.1.118
[davej-history.git] / arch / i386 / kernel / mca.c
blobde6efd1e2ac550f447317c517a4ff731af3f0e3e
1 /*
2 * linux/arch/i386/kernel/mca.c
3 * Written by Martin Kolinek, February 1996
5 * Changes:
6 * July 28, 1996: fixed up integrated SCSI detection. Chris Beauregard
7 * August 3rd, 1996: made mca_info local, made integrated registers
8 * accessible through standard function calls, added name field,
9 * more sanity checking. Chris Beauregard
10 * August 9, 1996: Rewrote /proc/mca. cpbeaure
13 #include <linux/types.h>
14 #include <linux/errno.h>
15 #include <linux/kernel.h>
16 #include <linux/mca.h>
17 #include <asm/system.h>
18 #include <asm/io.h>
19 #include <linux/proc_fs.h>
20 #include <linux/mman.h>
21 #include <linux/config.h>
22 #include <linux/mm.h>
23 #include <linux/pagemap.h>
24 #include <linux/ioport.h>
25 #include <asm/uaccess.h>
26 #include <linux/init.h>
28 /* This structure holds MCA information. Each (plug-in) adapter has
29 * eight POS registers. Then the machine may have integrated video and
30 * SCSI subsystems, which also have eight POS registers.
31 * Other miscellaneous information follows.
33 struct MCA_adapter {
34 unsigned char pos[8]; /* POS registers */
35 char name[32]; /* name of the device - provided by driver */
36 char procname[8]; /* name of /proc/mca file */
37 MCA_ProcFn procfn; /* /proc info callback */
38 void* dev; /* device/context info for callback */
41 struct MCA_info {
42 /* one for each of the 8 possible slots, plus one for integrated SCSI
43 and one for integrated video. */
44 struct MCA_adapter slot[MCA_NUMADAPTERS];
47 /* The mca_info structure pointer. If MCA bus is present, the function
48 * mca_probe() is invoked. The function puts motherboard, then all
49 * adapters into setup mode, allocates and fills an MCA_info structure,
50 * and points this pointer to the structure. Otherwise the pointer
51 * is set to zero.
53 static struct MCA_info* mca_info = 0;
55 /*MCA registers*/
56 #define MCA_MOTHERBOARD_SETUP_REG 0x94
57 #define MCA_ADAPTER_SETUP_REG 0x96
58 #define MCA_POS_REG(n) (0x100+(n))
60 #define MCA_ENABLED 0x01 /* POS 2, set if adapter enabled */
62 /*--------------------------------------------------------------------*/
64 #ifdef CONFIG_PROC_FS
65 static void mca_do_proc_init( void );
66 static int mca_default_procfn( char* buf, int slot );
68 static ssize_t proc_mca_read( struct file*, char*, size_t, loff_t *);
69 static struct file_operations proc_mca_operations = {
70 NULL, proc_mca_read,
71 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
73 static struct inode_operations proc_mca_inode_operations = {
74 &proc_mca_operations,
75 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
76 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
78 #endif
80 /*--------------------------------------------------------------------*/
82 __initfunc(void mca_init(void))
84 unsigned int i, j;
85 int foundscsi = 0;
87 /* WARNING: Be careful when making changes here. Putting an adapter
88 * and the motherboard simultaneously into setup mode may result in
89 * damage to chips (according to The Indispensible PC Hardware Book
90 * by Hans-Peter Messmer). Also, we disable system interrupts (so
91 * that we are not disturbed in the middle of this).
95 * Make sure the MCA bus is present
98 if (!MCA_bus)
99 return;
100 cli();
103 * Allocate MCA_info structure (at address divisible by 8)
106 mca_info = kmalloc(sizeof(struct MCA_info), GFP_ATOMIC);
109 * Make sure adapter setup is off
112 outb_p(0, MCA_ADAPTER_SETUP_REG);
115 * Put motherboard into video setup mode, read integrated video
116 * pos registers, and turn motherboard setup off.
119 outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG);
120 mca_info->slot[MCA_INTEGVIDEO].name[0] = 0;
121 for (j=0; j<8; j++) {
122 mca_info->slot[MCA_INTEGVIDEO].pos[j] = inb_p(MCA_POS_REG(j));
125 /* Put motherboard into scsi setup mode, read integrated scsi
126 * pos registers, and turn motherboard setup off.
128 * It seems there are two possible SCSI registers. Martin says that
129 * for the 56,57, 0xf7 is the one, but fails on the 76.
130 * Alfredo (apena@vnet.ibm.com) says
131 * 0xfd works on his machine. We'll try both of them. I figure it's
132 * a good bet that only one could be valid at a time. This could
133 * screw up though if one is used for something else on the other
134 * machine.
137 outb_p(0xf7, MCA_MOTHERBOARD_SETUP_REG);
138 mca_info->slot[MCA_INTEGSCSI].name[0] = 0;
139 for (j=0; j<8; j++) {
140 if( (mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j))) != 0xff )
142 /* 0xff all across means no device. 0x00 means something's
143 broken, but a device is probably there. However, if you get
144 0x00 from a motherboard register it won't matter what we
145 find. For the record, on the 57SLC, the integrated SCSI
146 adapter has 0xffff for the adapter ID, but nonzero for
147 other registers. */
148 foundscsi = 1;
151 if( !foundscsi )
154 * Didn't find it at 0xfd, try somewhere else...
156 outb_p(0xfd, MCA_MOTHERBOARD_SETUP_REG);
157 for (j=0; j<8; j++)
158 mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j));
161 /* turn off motherboard setup */
162 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
165 * Now loop over MCA slots: put each adapter into setup mode, and
166 * read its pos registers. Then put adapter setup off.
169 for (i=0; i<MCA_MAX_SLOT_NR; i++) {
170 outb_p(0x8|(i&0xf), MCA_ADAPTER_SETUP_REG);
171 for (j=0; j<8; j++) mca_info->slot[i].pos[j]=inb_p(MCA_POS_REG(j));
172 mca_info->slot[i].name[0] = 0;
174 outb_p(0, MCA_ADAPTER_SETUP_REG);
177 * Enable interrupts and return memory start
179 sti();
181 request_region(0x60,0x01,"system control port B (MCA)");
182 request_region(0x90,0x01,"arbitration (MCA)");
183 request_region(0x91,0x01,"card Select Feedback (MCA)");
184 request_region(0x92,0x01,"system Control port A (MCA)");
185 request_region(0x94,0x01,"system board setup (MCA)");
186 request_region(0x96,0x02,"POS (MCA)");
187 request_region(0x100,0x08,"POS (MCA)");
189 #ifdef CONFIG_PROC_FS
190 mca_do_proc_init();
191 #endif
194 /*--------------------------------------------------------------------*/
196 int mca_find_adapter( int id, int start )
198 int slot_id = 0;
199 unsigned char status = 0;
201 if( mca_info == 0 || id == 0 || id == 0xffff ) {
202 return MCA_NOTFOUND;
205 for( ; start >= 0 && start < MCA_NUMADAPTERS; start += 1 ) {
206 slot_id = (mca_info->slot[start].pos[1] << 8)
207 + mca_info->slot[start].pos[0];
208 status = mca_info->slot[start].pos[2];
210 /* not sure about this. There's no point in returning
211 adapters that aren't enabled, since they can't actually
212 be used. However, they might be needed for statistical
213 purposes or something... */
214 if( !(status & MCA_ENABLED) ) {
215 continue;
218 if( id == slot_id ) {
219 return start;
223 return MCA_NOTFOUND;
224 } /* mca_find_adapter() */
226 /*--------------------------------------------------------------------*/
228 unsigned char mca_read_stored_pos( int slot, int reg )
230 if( slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == 0 ) return 0;
231 if( reg < 0 || reg >= 8 ) return 0;
232 return mca_info->slot[slot].pos[reg];
233 } /* mca_read_stored_pos() */
235 /*--------------------------------------------------------------------*/
237 unsigned char mca_read_pos( int slot, int reg )
239 unsigned int byte = 0;
241 if( slot < 0 || slot >= MCA_MAX_SLOT_NR || mca_info == 0 ) return 0;
242 if( reg < 0 || reg >= 8 ) return 0;
244 cli();
246 /*make sure motherboard setup is off*/
247 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
249 /* read in the appropriate register */
250 outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
251 byte = inb_p(MCA_POS_REG(reg));
252 outb_p(0, MCA_ADAPTER_SETUP_REG);
254 sti();
256 /* make sure the stored values are consistent, while we're here */
257 mca_info->slot[slot].pos[reg] = byte;
259 return byte;
260 } /* mca_read_pos() */
262 /*--------------------------------------------------------------------*/
263 /* Note that this a technically a Bad Thing, as IBM tech stuff says
264 you should only set POS values through their utilities.
265 However, some devices such as the 3c523 recommend that you write
266 back some data to make sure the configuration is consistent.
267 I'd say that IBM is right, but I like my drivers to work.
268 This function can't do checks to see if multiple devices end up
269 with the same resources, so you might see magic smoke if someone
270 screws up. */
272 void mca_write_pos( int slot, int reg, unsigned char byte )
274 if( slot < 0 || slot >= MCA_MAX_SLOT_NR ) return;
275 if( reg < 0 || reg >= 8 ) return;
276 if (mca_info == 0 ) return;
278 cli();
280 /*make sure motherboard setup is off*/
281 outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
283 /* read in the appropriate register */
284 outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
285 outb_p( byte, MCA_POS_REG(reg) );
286 outb_p(0, MCA_ADAPTER_SETUP_REG);
288 sti();
290 /* update the global register list, while we have the byte */
291 mca_info->slot[slot].pos[reg] = byte;
292 } /* mca_write_pos() */
294 /*--------------------------------------------------------------------*/
296 void mca_set_adapter_name( int slot, char* name )
298 if( mca_info == 0 ) return;
300 if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
301 strncpy( mca_info->slot[slot].name, name,
302 sizeof(mca_info->slot[slot].name) );
306 void mca_set_adapter_procfn( int slot, MCA_ProcFn procfn, void* dev)
308 if( mca_info == 0 ) return;
310 if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
311 mca_info->slot[slot].procfn = procfn;
312 mca_info->slot[slot].dev = dev;
316 char *mca_get_adapter_name( int slot )
318 if( mca_info == 0 ) return 0;
320 if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
321 return mca_info->slot[slot].name;
324 return 0;
327 int mca_isadapter( int slot )
329 if( mca_info == 0 ) return 0;
331 if( slot >= MCA_MAX_SLOT_NR ) {
332 /* some integrated adapters have 0xffff for an ID, but
333 are still there. VGA, for example. */
334 int i;
335 for( i = 0; i < 8; i ++ ) {
336 if( mca_info->slot[slot].pos[i] != 0xff ) {
337 return 1;
340 return 0;
341 } else if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
342 return (mca_info->slot[slot].pos[0] != 0xff ||
343 mca_info->slot[slot].pos[1] != 0xff);
346 return 0;
349 int mca_isenabled( int slot )
351 if( mca_info == 0 ) return 0;
353 if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
354 return (mca_info->slot[slot].pos[2] & MCA_ENABLED);
357 return 0;
360 /*--------------------------------------------------------------------*/
362 #ifdef CONFIG_PROC_FS
364 int get_mca_info(char *buf)
366 int i, j, len = 0;
368 if( MCA_bus && mca_info != 0 )
371 * Format pos registers of eight MCA slots
373 for (i=0; i<MCA_MAX_SLOT_NR; i++)
375 len += sprintf(buf+len, "Slot %d: ", i+1);
376 for (j=0; j<8; j++)
377 len += sprintf(buf+len, "%02x ", mca_info->slot[i].pos[j]);
378 len += sprintf( buf+len, " %s\n", mca_info->slot[i].name );
382 * Format pos registers of integrated video subsystem
385 len += sprintf(buf+len, "Video: ");
386 for (j=0; j<8; j++)
387 len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGVIDEO].pos[j]);
388 len += sprintf( buf+len, " %s\n", mca_info->slot[MCA_INTEGVIDEO].name );
391 * Format pos registers of integrated SCSI subsystem
394 len += sprintf(buf+len, "SCSI: ");
395 for (j=0; j<8; j++)
396 len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGSCSI].pos[j]);
397 len += sprintf( buf+len, " %s\n", mca_info->slot[MCA_INTEGSCSI].name );
399 else
402 * Leave it empty if MCA not detected
403 * this should never happen
407 return len;
411 /*--------------------------------------------------------------------*/
412 __initfunc(void mca_do_proc_init( void ))
414 int i = 0;
415 struct proc_dir_entry* node = 0;
417 if( mca_info == 0 ) return; /* never happens */
419 proc_register( &proc_mca, &(struct proc_dir_entry) {
420 PROC_MCA_REGISTERS, 3, "pos", S_IFREG|S_IRUGO,
421 1, 0, 0, 0, &proc_mca_inode_operations,} );
423 proc_register( &proc_mca, &(struct proc_dir_entry) {
424 PROC_MCA_MACHINE, 7, "machine", S_IFREG|S_IRUGO,
425 1, 0, 0, 0, &proc_mca_inode_operations,} );
427 /* initialize /proc entries for existing adapters */
428 for( i = 0; i < MCA_NUMADAPTERS; i += 1 ) {
429 mca_info->slot[i].procfn = 0;
430 mca_info->slot[i].dev = 0;
432 if( ! mca_isadapter( i ) ) continue;
433 node = kmalloc(sizeof(struct proc_dir_entry), GFP_ATOMIC);
435 if( i < MCA_MAX_SLOT_NR ) {
436 node->low_ino = PROC_MCA_SLOT + i;
437 node->namelen = sprintf( mca_info->slot[i].procname,
438 "slot%d", i+1 );
439 } else if( i == MCA_INTEGVIDEO ) {
440 node->low_ino = PROC_MCA_VIDEO;
441 node->namelen = sprintf( mca_info->slot[i].procname,
442 "video" );
443 } else if( i == MCA_INTEGSCSI ) {
444 node->low_ino = PROC_MCA_SCSI;
445 node->namelen = sprintf( mca_info->slot[i].procname,
446 "scsi" );
448 node->name = mca_info->slot[i].procname;
449 node->mode = S_IFREG | S_IRUGO;
450 node->ops = &proc_mca_inode_operations;
451 proc_register( &proc_mca, node );
454 } /* mca_do_proc_init() */
456 /*--------------------------------------------------------------------*/
458 int mca_default_procfn( char* buf, int slot )
460 int len = 0, i;
462 /* this really shouldn't happen... */
463 if( mca_info == 0 ) {
464 *buf = 0;
465 return 0;
468 /* print out the basic information */
470 if( slot < MCA_MAX_SLOT_NR ) {
471 len += sprintf( buf+len, "Slot: %d\n", slot+1 );
472 } else if( slot == MCA_INTEGSCSI ) {
473 len += sprintf( buf+len, "Integrated SCSI Adapter\n" );
474 } else if( slot == MCA_INTEGVIDEO ) {
475 len += sprintf( buf+len, "Integrated Video Adapter\n" );
477 if( mca_info->slot[slot].name[0] ) {
478 /* drivers might register a name without /proc handler... */
479 len += sprintf( buf+len, "Adapter Name: %s\n",
480 mca_info->slot[slot].name );
481 } else {
482 len += sprintf( buf+len, "Adapter Name: Unknown\n" );
484 len += sprintf( buf+len, "Id: %02x%02x\n",
485 mca_info->slot[slot].pos[1], mca_info->slot[slot].pos[0] );
486 len += sprintf( buf+len, "Enabled: %s\nPOS: ",
487 mca_isenabled(slot) ? "Yes" : "No" );
488 for (i=0; i<8; i++) {
489 len += sprintf(buf+len, "%02x ", mca_info->slot[slot].pos[i]);
491 buf[len++] = '\n';
492 buf[len] = 0;
494 return len;
495 } /* mca_default_procfn() */
497 static int get_mca_machine_info( char* buf )
499 int len = 0;
501 len += sprintf( buf+len, "Model Id: 0x%x\n", machine_id );
502 len += sprintf( buf+len, "Submodel Id: 0x%x\n", machine_submodel_id );
503 len += sprintf( buf+len, "BIOS Revision: 0x%x\n", BIOS_revision );
505 return len;
509 static int mca_not_implemented( char* buf )
511 return sprintf( buf, "Sorry, not implemented yet...\n" );
515 static int mca_fill( char* page, int pid, int type, char** start,
516 loff_t *offset, int length)
518 int len = 0;
519 int slot = 0;
521 switch( type ) {
522 case PROC_MCA_REGISTERS:
523 return get_mca_info( page );
524 case PROC_MCA_MACHINE:
525 return get_mca_machine_info( page );
526 case PROC_MCA_VIDEO:
527 slot = MCA_INTEGVIDEO;
528 break;
529 case PROC_MCA_SCSI:
530 slot = MCA_INTEGSCSI;
531 break;
532 default:
533 if( type < PROC_MCA_SLOT || type >= PROC_MCA_LAST ) {
534 return -EBADF;
536 slot = type - PROC_MCA_SLOT;
537 break;
540 /* if we made it here, we better have a valid slot */
542 /* get the standard info */
543 len = mca_default_procfn( page, slot );
545 /* do any device-specific processing, if there is any */
546 if( mca_info->slot[slot].procfn ) {
547 len += mca_info->slot[slot].procfn( page+len, slot,
548 mca_info->slot[slot].dev );
551 return len;
552 } /* mca_fill() */
555 * Blatantly stolen from fs/proc/array.c, and thus is probably overkill
558 #define PROC_BLOCK_SIZE (3*1024)
560 static ssize_t proc_mca_read( struct file* file,
561 char* buf, size_t count, loff_t *ppos)
563 unsigned long page;
564 char *start;
565 int length;
566 int end;
567 unsigned int type, pid;
568 struct proc_dir_entry *dp;
569 struct inode *inode = file->f_dentry->d_inode;
571 if (count < 0)
572 return -EINVAL;
573 if (count > PROC_BLOCK_SIZE)
574 count = PROC_BLOCK_SIZE;
575 if (!(page = __get_free_page(GFP_KERNEL)))
576 return -ENOMEM;
577 type = inode->i_ino;
578 pid = type >> 16;
579 type &= 0x0000ffff;
580 start = 0;
581 dp = (struct proc_dir_entry *) inode->u.generic_ip;
582 length = mca_fill((char *) page, pid, type,
583 &start, ppos, count);
584 if (length < 0) {
585 free_page(page);
586 return length;
588 if (start != 0) {
589 /* We have had block-adjusting processing! */
590 copy_to_user(buf, start, length);
591 *ppos += length;
592 count = length;
593 } else {
594 /* Static 4kB (or whatever) block capacity */
595 if (*ppos >= length) {
596 free_page(page);
597 return 0;
599 if (count + *ppos > length)
600 count = length - *ppos;
601 end = count + *ppos;
602 copy_to_user(buf, (char *) page + *ppos, count);
603 *ppos = end;
605 free_page(page);
606 return count;
607 } /* proc_mca_read() */
609 #endif