2 * linux/arch/i386/kernel/mca.c
3 * Written by Martin Kolinek, February 1996
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>
19 #include <linux/proc_fs.h>
20 #include <linux/mman.h>
21 #include <linux/config.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.
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 */
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
53 static struct MCA_info
* mca_info
= 0;
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 /*--------------------------------------------------------------------*/
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
= {
71 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
73 static struct inode_operations proc_mca_inode_operations
= {
75 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
76 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
80 /*--------------------------------------------------------------------*/
82 __initfunc(void mca_init(void))
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
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
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
154 * Didn't find it at 0xfd, try somewhere else...
156 outb_p(0xfd, MCA_MOTHERBOARD_SETUP_REG
);
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
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
194 /*--------------------------------------------------------------------*/
196 int mca_find_adapter( int id
, int start
)
199 unsigned char status
= 0;
201 if( mca_info
== 0 || id
== 0 || id
== 0xffff ) {
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
) ) {
218 if( id
== slot_id
) {
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;
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
);
256 /* make sure the stored values are consistent, while we're here */
257 mca_info
->slot
[slot
].pos
[reg
] = 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
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;
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
);
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
;
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. */
335 for( i
= 0; i
< 8; i
++ ) {
336 if( mca_info
->slot
[slot
].pos
[i
] != 0xff ) {
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);
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
);
360 /*--------------------------------------------------------------------*/
362 #ifdef CONFIG_PROC_FS
364 int get_mca_info(char *buf
)
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);
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: ");
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: ");
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
);
402 * Leave it empty if MCA not detected
403 * this should never happen
411 /*--------------------------------------------------------------------*/
412 __initfunc(void mca_do_proc_init( void ))
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
,
439 } else if( i
== MCA_INTEGVIDEO
) {
440 node
->low_ino
= PROC_MCA_VIDEO
;
441 node
->namelen
= sprintf( mca_info
->slot
[i
].procname
,
443 } else if( i
== MCA_INTEGSCSI
) {
444 node
->low_ino
= PROC_MCA_SCSI
;
445 node
->namelen
= sprintf( mca_info
->slot
[i
].procname
,
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
)
462 /* this really shouldn't happen... */
463 if( mca_info
== 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
);
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
]);
495 } /* mca_default_procfn() */
497 static int get_mca_machine_info( char* buf
)
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
);
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
)
522 case PROC_MCA_REGISTERS
:
523 return get_mca_info( page
);
524 case PROC_MCA_MACHINE
:
525 return get_mca_machine_info( page
);
527 slot
= MCA_INTEGVIDEO
;
530 slot
= MCA_INTEGSCSI
;
533 if( type
< PROC_MCA_SLOT
|| type
>= PROC_MCA_LAST
) {
536 slot
= type
- PROC_MCA_SLOT
;
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
);
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
)
567 unsigned int type
, pid
;
568 struct proc_dir_entry
*dp
;
569 struct inode
*inode
= file
->f_dentry
->d_inode
;
573 if (count
> PROC_BLOCK_SIZE
)
574 count
= PROC_BLOCK_SIZE
;
575 if (!(page
= __get_free_page(GFP_KERNEL
)))
581 dp
= (struct proc_dir_entry
*) inode
->u
.generic_ip
;
582 length
= mca_fill((char *) page
, pid
, type
,
583 &start
, ppos
, count
);
589 /* We have had block-adjusting processing! */
590 copy_to_user(buf
, start
, length
);
594 /* Static 4kB (or whatever) block capacity */
595 if (*ppos
>= length
) {
599 if (count
+ *ppos
> length
)
600 count
= length
- *ppos
;
602 copy_to_user(buf
, (char *) page
+ *ppos
, count
);
607 } /* proc_mca_read() */