2005-04-16 15:20:36 -07:00
/*
* Written by Martin Kolinek , February 1996
*
* Changes :
*
* Chris Beauregard July 28 th , 1996
* - Fixed up integrated SCSI detection
*
* Chris Beauregard August 3 rd , 1996
* - Made mca_info local
* - Made integrated registers accessible through standard function calls
* - Added name field
* - More sanity checking
*
* Chris Beauregard August 9 th , 1996
* - Rewrote / proc / mca
*
* Chris Beauregard January 7 th , 1997
* - Added basic NMI - processing
* - Added more information to mca_info structure
*
* David Weinehall October 12 th , 1998
* - Made a lot of cleaning up in the source
* - Added use of save_flags / restore_flags
* - Added the ' driver_loaded ' flag in MCA_adapter
* - Added an alternative implemention of ZP Gu ' s mca_find_unused_adapter
*
* David Weinehall March 24 th , 1999
* - Fixed the output of ' Driver Installed ' in / proc / mca / pos
* - Made the Integrated Video & SCSI show up even if they have id 0000
*
* Alexander Viro November 9 th , 1999
* - Switched to regular procfs methods
*
* Alfred Arnold & David Weinehall August 23 rd , 2000
* - Added support for Planar POS - registers
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/mca.h>
2006-09-26 10:52:36 +02:00
# include <linux/kprobes.h>
2005-04-16 15:20:36 -07:00
# include <asm/system.h>
# include <asm/io.h>
# include <linux/proc_fs.h>
# include <linux/mman.h>
# include <linux/mm.h>
# include <linux/pagemap.h>
# include <linux/ioport.h>
# include <asm/uaccess.h>
# include <linux/init.h>
# include <asm/arch_hooks.h>
2008-02-22 23:10:06 +01:00
static unsigned char which_scsi ;
2005-04-16 15:20:36 -07:00
2008-02-22 23:10:06 +01:00
int MCA_bus ;
2005-04-16 15:20:36 -07:00
EXPORT_SYMBOL ( MCA_bus ) ;
/*
* Motherboard register spinlock . Untested on SMP at the moment , but
* are there any MCA SMP boxes ?
*
* Yes - Alan
*/
static DEFINE_SPINLOCK ( mca_lock ) ;
/* Build the status info for the adapter */
2008-02-22 23:10:06 +01:00
static void mca_configure_adapter_status ( struct mca_device * mca_dev )
{
2005-04-16 15:20:36 -07:00
mca_dev - > status = MCA_ADAPTER_NONE ;
mca_dev - > pos_id = mca_dev - > pos [ 0 ]
+ ( mca_dev - > pos [ 1 ] < < 8 ) ;
2008-02-22 23:10:06 +01:00
if ( ! mca_dev - > pos_id & & mca_dev - > slot < MCA_MAX_SLOT_NR ) {
2005-04-16 15:20:36 -07:00
2008-02-22 23:10:06 +01:00
/*
* id = 0x0000 usually indicates hardware failure ,
2005-04-16 15:20:36 -07:00
* however , ZP Gu ( zpg @ castle . net > reports that his 9556
* has 0x0000 as id and everything still works . There
* also seem to be an adapter with id = 0x0000 ; the
* NCR Parallel Bus Memory Card . Until this is confirmed ,
* however , this code will stay .
*/
mca_dev - > status = MCA_ADAPTER_ERROR ;
return ;
2008-02-22 23:10:06 +01:00
} else if ( mca_dev - > pos_id ! = 0xffff ) {
2005-04-16 15:20:36 -07:00
2008-02-22 23:10:06 +01:00
/*
* 0xffff usually indicates that there ' s no adapter ,
2005-04-16 15:20:36 -07:00
* however , some integrated adapters may have 0xffff as
* their id and still be valid . Examples are on - board
* VGA of the 55 sx , the integrated SCSI of the 56 & 57 ,
* and possibly also the 95 ULTIMEDIA .
*/
mca_dev - > status = MCA_ADAPTER_NORMAL ;
}
2008-02-22 23:10:06 +01:00
if ( ( mca_dev - > pos_id = = 0xffff | |
2005-04-16 15:20:36 -07:00
mca_dev - > pos_id = = 0x0000 ) & & mca_dev - > slot > = MCA_MAX_SLOT_NR ) {
int j ;
2008-02-22 23:10:06 +01:00
for ( j = 2 ; j < 8 ; j + + ) {
if ( mca_dev - > pos [ j ] ! = 0xff ) {
2005-04-16 15:20:36 -07:00
mca_dev - > status = MCA_ADAPTER_NORMAL ;
break ;
}
}
}
2008-02-22 23:10:06 +01:00
if ( ! ( mca_dev - > pos [ 2 ] & MCA_ENABLED ) ) {
2005-04-16 15:20:36 -07:00
/* enabled bit is in POS 2 */
mca_dev - > status = MCA_ADAPTER_DISABLED ;
}
} /* mca_configure_adapter_status */
/*--------------------------------------------------------------------*/
static struct resource mca_standard_resources [ ] = {
{ . start = 0x60 , . end = 0x60 , . name = " system control port B (MCA) " } ,
{ . start = 0x90 , . end = 0x90 , . name = " arbitration (MCA) " } ,
{ . start = 0x91 , . end = 0x91 , . name = " card Select Feedback (MCA) " } ,
{ . start = 0x92 , . end = 0x92 , . name = " system Control port A (MCA) " } ,
{ . start = 0x94 , . end = 0x94 , . name = " system board setup (MCA) " } ,
{ . start = 0x96 , . end = 0x97 , . name = " POS (MCA) " } ,
{ . start = 0x100 , . end = 0x107 , . name = " POS (MCA) " }
} ;
2005-11-07 00:58:31 -08:00
# define MCA_STANDARD_RESOURCES ARRAY_SIZE(mca_standard_resources)
2005-04-16 15:20:36 -07:00
2008-02-22 23:10:06 +01:00
/*
2005-04-16 15:20:36 -07:00
* mca_read_and_store_pos - read the POS registers into a memory buffer
* @ pos : a char pointer to 8 bytes , contains the POS register value on
* successful return
*
* Returns 1 if a card actually exists ( i . e . the pos isn ' t
* all 0xff ) or 0 otherwise
*/
2008-02-22 23:10:06 +01:00
static int mca_read_and_store_pos ( unsigned char * pos )
{
2005-04-16 15:20:36 -07:00
int j ;
int found = 0 ;
2008-02-22 23:10:06 +01:00
for ( j = 0 ; j < 8 ; j + + ) {
pos [ j ] = inb_p ( MCA_POS_REG ( j ) ) ;
if ( pos [ j ] ! = 0xff ) {
2005-04-16 15:20:36 -07:00
/* 0xff all across means no device. 0x00 means
* something ' s broken , but a device is
* probably there . However , if you get 0x00
* from a motherboard register it won ' t matter
* what we find . For the record , on the
* 57 SLC , the integrated SCSI adapter has
* 0xffff for the adapter ID , but nonzero for
* other registers . */
found = 1 ;
}
}
return found ;
}
static unsigned char mca_pc_read_pos ( struct mca_device * mca_dev , int reg )
{
unsigned char byte ;
unsigned long flags ;
2008-02-22 23:10:06 +01:00
if ( reg < 0 | | reg > = 8 )
2005-04-16 15:20:36 -07:00
return 0 ;
spin_lock_irqsave ( & mca_lock , flags ) ;
2008-02-22 23:10:06 +01:00
if ( mca_dev - > pos_register ) {
2005-04-16 15:20:36 -07:00
/* Disable adapter setup, enable motherboard setup */
outb_p ( 0 , MCA_ADAPTER_SETUP_REG ) ;
outb_p ( mca_dev - > pos_register , MCA_MOTHERBOARD_SETUP_REG ) ;
byte = inb_p ( MCA_POS_REG ( reg ) ) ;
outb_p ( 0xff , MCA_MOTHERBOARD_SETUP_REG ) ;
} else {
/* Make sure motherboard setup is off */
outb_p ( 0xff , MCA_MOTHERBOARD_SETUP_REG ) ;
/* Read the appropriate register */
outb_p ( 0x8 | ( mca_dev - > slot & 0xf ) , MCA_ADAPTER_SETUP_REG ) ;
byte = inb_p ( MCA_POS_REG ( reg ) ) ;
outb_p ( 0 , MCA_ADAPTER_SETUP_REG ) ;
}
spin_unlock_irqrestore ( & mca_lock , flags ) ;
mca_dev - > pos [ reg ] = byte ;
return byte ;
}
static void mca_pc_write_pos ( struct mca_device * mca_dev , int reg ,
unsigned char byte )
{
unsigned long flags ;
2008-02-22 23:10:06 +01:00
if ( reg < 0 | | reg > = 8 )
2005-04-16 15:20:36 -07:00
return ;
spin_lock_irqsave ( & mca_lock , flags ) ;
/* Make sure motherboard setup is off */
outb_p ( 0xff , MCA_MOTHERBOARD_SETUP_REG ) ;
/* Read in the appropriate register */
outb_p ( 0x8 | ( mca_dev - > slot & 0xf ) , MCA_ADAPTER_SETUP_REG ) ;
outb_p ( byte , MCA_POS_REG ( reg ) ) ;
outb_p ( 0 , MCA_ADAPTER_SETUP_REG ) ;
spin_unlock_irqrestore ( & mca_lock , flags ) ;
/* Update the global register list, while we have the byte */
mca_dev - > pos [ reg ] = byte ;
}
/* for the primary MCA bus, we have identity transforms */
2008-02-22 23:10:06 +01:00
static int mca_dummy_transform_irq ( struct mca_device * mca_dev , int irq )
2005-04-16 15:20:36 -07:00
{
return irq ;
}
2008-02-22 23:10:06 +01:00
static int mca_dummy_transform_ioport ( struct mca_device * mca_dev , int port )
2005-04-16 15:20:36 -07:00
{
return port ;
}
2008-02-22 23:10:06 +01:00
static void * mca_dummy_transform_memory ( struct mca_device * mca_dev , void * mem )
2005-04-16 15:20:36 -07:00
{
return mem ;
}
static int __init mca_init ( void )
{
unsigned int i , j ;
struct mca_device * mca_dev ;
unsigned char pos [ 8 ] ;
short mca_builtin_scsi_ports [ ] = { 0xf7 , 0xfd , 0x00 } ;
struct mca_bus * bus ;
2008-02-22 23:10:06 +01:00
/*
* WARNING : Be careful when making changes here . Putting an adapter
2005-04-16 15:20:36 -07:00
* and the motherboard simultaneously into setup mode may result in
* damage to chips ( according to The Indispensible PC Hardware Book
* by Hans - Peter Messmer ) . Also , we disable system interrupts ( so
* that we are not disturbed in the middle of this ) .
*/
/* Make sure the MCA bus is present */
if ( mca_system_init ( ) ) {
printk ( KERN_ERR " MCA bus system initialisation failed \n " ) ;
return - ENODEV ;
}
if ( ! MCA_bus )
return - ENODEV ;
printk ( KERN_INFO " Micro Channel bus detected. \n " ) ;
/* All MCA systems have at least a primary bus */
bus = mca_attach_bus ( MCA_PRIMARY_BUS ) ;
if ( ! bus )
goto out_nomem ;
bus - > default_dma_mask = 0xffffffffLL ;
bus - > f . mca_write_pos = mca_pc_write_pos ;
bus - > f . mca_read_pos = mca_pc_read_pos ;
bus - > f . mca_transform_irq = mca_dummy_transform_irq ;
bus - > f . mca_transform_ioport = mca_dummy_transform_ioport ;
bus - > f . mca_transform_memory = mca_dummy_transform_memory ;
/* get the motherboard device */
2006-12-07 02:14:19 +01:00
mca_dev = kzalloc ( sizeof ( struct mca_device ) , GFP_KERNEL ) ;
2008-02-22 23:10:06 +01:00
if ( unlikely ( ! mca_dev ) )
2005-04-16 15:20:36 -07:00
goto out_nomem ;
/*
* We do not expect many MCA interrupts during initialization ,
* but let us be safe :
*/
spin_lock_irq ( & mca_lock ) ;
/* Make sure adapter setup is off */
outb_p ( 0 , MCA_ADAPTER_SETUP_REG ) ;
/* Read motherboard POS registers */
mca_dev - > pos_register = 0x7f ;
outb_p ( mca_dev - > pos_register , MCA_MOTHERBOARD_SETUP_REG ) ;
mca_dev - > name [ 0 ] = 0 ;
mca_read_and_store_pos ( mca_dev - > pos ) ;
mca_configure_adapter_status ( mca_dev ) ;
/* fake POS and slot for a motherboard */
mca_dev - > pos_id = MCA_MOTHERBOARD_POS ;
mca_dev - > slot = MCA_MOTHERBOARD ;
mca_register_device ( MCA_PRIMARY_BUS , mca_dev ) ;
2006-12-07 02:14:19 +01:00
mca_dev = kzalloc ( sizeof ( struct mca_device ) , GFP_ATOMIC ) ;
2008-02-22 23:10:06 +01:00
if ( unlikely ( ! mca_dev ) )
2005-04-16 15:20:36 -07:00
goto out_unlock_nomem ;
/* Put motherboard into video setup mode, read integrated video
* POS registers , and turn motherboard setup off .
*/
mca_dev - > pos_register = 0xdf ;
outb_p ( mca_dev - > pos_register , MCA_MOTHERBOARD_SETUP_REG ) ;
mca_dev - > name [ 0 ] = 0 ;
mca_read_and_store_pos ( mca_dev - > pos ) ;
mca_configure_adapter_status ( mca_dev ) ;
/* fake POS and slot for the integrated video */
mca_dev - > pos_id = MCA_INTEGVIDEO_POS ;
mca_dev - > slot = MCA_INTEGVIDEO ;
mca_register_device ( MCA_PRIMARY_BUS , mca_dev ) ;
2008-02-22 23:10:06 +01:00
/*
* Put motherboard into scsi setup mode , read integrated scsi
2005-04-16 15:20:36 -07:00
* POS registers , and turn motherboard setup off .
*
* It seems there are two possible SCSI registers . Martin says that
* for the 56 , 57 , 0xf7 is the one , but fails on the 76.
* Alfredo ( apena @ vnet . ibm . com ) says
* 0xfd works on his machine . We ' ll try both of them . I figure it ' s
* a good bet that only one could be valid at a time . This could
* screw up though if one is used for something else on the other
* machine .
*/
2008-02-22 23:10:06 +01:00
for ( i = 0 ; ( which_scsi = mca_builtin_scsi_ports [ i ] ) ! = 0 ; i + + ) {
2005-04-16 15:20:36 -07:00
outb_p ( which_scsi , MCA_MOTHERBOARD_SETUP_REG ) ;
2008-02-22 23:10:06 +01:00
if ( mca_read_and_store_pos ( pos ) )
2005-04-16 15:20:36 -07:00
break ;
}
2008-02-22 23:10:06 +01:00
if ( which_scsi ) {
2005-04-16 15:20:36 -07:00
/* found a scsi card */
2006-12-07 02:14:19 +01:00
mca_dev = kzalloc ( sizeof ( struct mca_device ) , GFP_ATOMIC ) ;
2008-02-22 23:10:06 +01:00
if ( unlikely ( ! mca_dev ) )
2005-04-16 15:20:36 -07:00
goto out_unlock_nomem ;
2008-02-22 23:10:06 +01:00
for ( j = 0 ; j < 8 ; j + + )
2005-04-16 15:20:36 -07:00
mca_dev - > pos [ j ] = pos [ j ] ;
mca_configure_adapter_status ( mca_dev ) ;
/* fake POS and slot for integrated SCSI controller */
mca_dev - > pos_id = MCA_INTEGSCSI_POS ;
mca_dev - > slot = MCA_INTEGSCSI ;
mca_dev - > pos_register = which_scsi ;
mca_register_device ( MCA_PRIMARY_BUS , mca_dev ) ;
}
/* Turn off motherboard setup */
outb_p ( 0xff , MCA_MOTHERBOARD_SETUP_REG ) ;
2008-02-22 23:10:06 +01:00
/*
* Now loop over MCA slots : put each adapter into setup mode , and
2005-04-16 15:20:36 -07:00
* read its POS registers . Then put adapter setup off .
*/
2008-02-22 23:10:06 +01:00
for ( i = 0 ; i < MCA_MAX_SLOT_NR ; i + + ) {
2005-04-16 15:20:36 -07:00
outb_p ( 0x8 | ( i & 0xf ) , MCA_ADAPTER_SETUP_REG ) ;
2008-02-22 23:10:06 +01:00
if ( ! mca_read_and_store_pos ( pos ) )
2005-04-16 15:20:36 -07:00
continue ;
2006-12-07 02:14:19 +01:00
mca_dev = kzalloc ( sizeof ( struct mca_device ) , GFP_ATOMIC ) ;
2008-02-22 23:10:06 +01:00
if ( unlikely ( ! mca_dev ) )
2005-04-16 15:20:36 -07:00
goto out_unlock_nomem ;
2008-02-22 23:10:06 +01:00
for ( j = 0 ; j < 8 ; j + + )
mca_dev - > pos [ j ] = pos [ j ] ;
2005-04-16 15:20:36 -07:00
mca_dev - > driver_loaded = 0 ;
mca_dev - > slot = i ;
mca_dev - > pos_register = 0 ;
mca_configure_adapter_status ( mca_dev ) ;
mca_register_device ( MCA_PRIMARY_BUS , mca_dev ) ;
}
outb_p ( 0 , MCA_ADAPTER_SETUP_REG ) ;
/* Enable interrupts and return memory start */
spin_unlock_irq ( & mca_lock ) ;
for ( i = 0 ; i < MCA_STANDARD_RESOURCES ; i + + )
request_resource ( & ioport_resource , mca_standard_resources + i ) ;
mca_do_proc_init ( ) ;
return 0 ;
out_unlock_nomem :
spin_unlock_irq ( & mca_lock ) ;
out_nomem :
printk ( KERN_EMERG " Failed memory allocation in MCA setup! \n " ) ;
return - ENOMEM ;
}
subsys_initcall ( mca_init ) ;
/*--------------------------------------------------------------------*/
2006-09-26 10:52:36 +02:00
static __kprobes void
mca_handle_nmi_device ( struct mca_device * mca_dev , int check_flag )
2005-04-16 15:20:36 -07:00
{
int slot = mca_dev - > slot ;
2008-02-22 23:10:06 +01:00
if ( slot = = MCA_INTEGSCSI ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_CRIT " NMI: caused by MCA integrated SCSI adapter (%s) \n " ,
mca_dev - > name ) ;
2008-02-22 23:10:06 +01:00
} else if ( slot = = MCA_INTEGVIDEO ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_CRIT " NMI: caused by MCA integrated video adapter (%s) \n " ,
mca_dev - > name ) ;
2008-02-22 23:10:06 +01:00
} else if ( slot = = MCA_MOTHERBOARD ) {
2005-04-16 15:20:36 -07:00
printk ( KERN_CRIT " NMI: caused by motherboard (%s) \n " ,
mca_dev - > name ) ;
}
/* More info available in POS 6 and 7? */
2008-02-22 23:10:06 +01:00
if ( check_flag ) {
2005-04-16 15:20:36 -07:00
unsigned char pos6 , pos7 ;
pos6 = mca_device_read_pos ( mca_dev , 6 ) ;
pos7 = mca_device_read_pos ( mca_dev , 7 ) ;
printk ( KERN_CRIT " NMI: POS 6 = 0x%x, POS 7 = 0x%x \n " , pos6 , pos7 ) ;
}
} /* mca_handle_nmi_slot */
/*--------------------------------------------------------------------*/
2006-09-26 10:52:36 +02:00
static int __kprobes mca_handle_nmi_callback ( struct device * dev , void * data )
2005-04-16 15:20:36 -07:00
{
struct mca_device * mca_dev = to_mca_device ( dev ) ;
unsigned char pos5 ;
pos5 = mca_device_read_pos ( mca_dev , 5 ) ;
2008-02-22 23:10:06 +01:00
if ( ! ( pos5 & 0x80 ) ) {
/*
* Bit 7 of POS 5 is reset when this adapter has a hardware
2005-04-16 15:20:36 -07:00
* error . Bit 7 it reset if there ' s error information
* available in POS 6 and 7.
*/
mca_handle_nmi_device ( mca_dev , ! ( pos5 & 0x40 ) ) ;
return 1 ;
}
return 0 ;
}
2006-09-26 10:52:36 +02:00
void __kprobes mca_handle_nmi ( void )
2005-04-16 15:20:36 -07:00
{
2008-02-22 23:10:06 +01:00
/*
* First try - scan the various adapters and see if a specific
2005-04-16 15:20:36 -07:00
* adapter was responsible for the error .
*/
bus_for_each_dev ( & mca_bus_type , NULL , NULL , mca_handle_nmi_callback ) ;
mca_nmi_hook ( ) ;
} /* mca_handle_nmi */