Commit f347cc1b authored by Linus Torvalds's avatar Linus Torvalds

Merge http://linux-voyager.bkbits.net/mca-sysfs-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents e482449c c845933e
......@@ -1108,6 +1108,8 @@ config MCA
<file:Documentation/mca.txt> (and especially the web page given
there) before attempting to build an MCA bus kernel.
source "drivers/mca/Kconfig"
config HOTPLUG
bool "Support for hot-pluggable devices"
---help---
......
......@@ -54,54 +54,7 @@
#include <linux/init.h>
#include <asm/arch_hooks.h>
/* This structure holds MCA information. Each (plug-in) adapter has
* eight POS registers. Then the machine may have integrated video and
* SCSI subsystems, which also have eight POS registers.
* Finally, the motherboard (planar) has got POS-registers.
* Other miscellaneous information follows.
*/
typedef enum {
MCA_ADAPTER_NORMAL = 0,
MCA_ADAPTER_NONE = 1,
MCA_ADAPTER_DISABLED = 2,
MCA_ADAPTER_ERROR = 3
} MCA_AdapterStatus;
struct MCA_adapter {
MCA_AdapterStatus status; /* is there a valid adapter? */
int id; /* adapter id value */
unsigned char pos[8]; /* POS registers */
int driver_loaded; /* is there a driver installed? */
/* 0 - No, 1 - Yes */
char name[48]; /* adapter-name provided by driver */
char procname[8]; /* name of /proc/mca file */
MCA_ProcFn procfn; /* /proc info callback */
void* dev; /* device/context info for callback */
};
struct MCA_info {
/* one for each of the 8 possible slots, plus one for integrated SCSI
* and one for integrated video.
*/
struct MCA_adapter slot[MCA_NUMADAPTERS];
/* two potential addresses for integrated SCSI adapter - this will
* track which one we think it is.
*/
unsigned char which_scsi;
};
/* The mca_info structure pointer. If MCA bus is present, the function
* mca_probe() is invoked. The function puts motherboard, then all
* adapters into setup mode, allocates and fills an MCA_info structure,
* and points this pointer to the structure. Otherwise the pointer
* is set to zero.
*/
static struct MCA_info* mca_info = NULL;
static unsigned char which_scsi = 0;
/*
* Motherboard register spinlock. Untested on SMP at the moment, but
......@@ -109,33 +62,17 @@ static struct MCA_info* mca_info = NULL;
*
* Yes - Alan
*/
static spinlock_t mca_lock = SPIN_LOCK_UNLOCKED;
/* MCA registers */
#define MCA_MOTHERBOARD_SETUP_REG 0x94
#define MCA_ADAPTER_SETUP_REG 0x96
#define MCA_POS_REG(n) (0x100+(n))
#define MCA_ENABLED 0x01 /* POS 2, set if adapter enabled */
/*--------------------------------------------------------------------*/
#ifdef CONFIG_PROC_FS
static void mca_do_proc_init(void);
#endif
/*--------------------------------------------------------------------*/
spinlock_t mca_lock = SPIN_LOCK_UNLOCKED;
/* Build the status info for the adapter */
static void mca_configure_adapter_status(int slot) {
mca_info->slot[slot].status = MCA_ADAPTER_NONE;
static void mca_configure_adapter_status(struct mca_device *mca_dev) {
mca_dev->status = MCA_ADAPTER_NONE;
mca_info->slot[slot].id = mca_info->slot[slot].pos[0]
+ (mca_info->slot[slot].pos[1] << 8);
mca_dev->pos_id = mca_dev->pos[0]
+ (mca_dev->pos[1] << 8);
if(!mca_info->slot[slot].id && slot < MCA_MAX_SLOT_NR) {
if(!mca_dev->pos_id && mca_dev->slot < MCA_MAX_SLOT_NR) {
/* id = 0x0000 usually indicates hardware failure,
* however, ZP Gu (zpg@castle.net> reports that his 9556
......@@ -145,10 +82,10 @@ static void mca_configure_adapter_status(int slot) {
* however, this code will stay.
*/
mca_info->slot[slot].status = MCA_ADAPTER_ERROR;
mca_dev->status = MCA_ADAPTER_ERROR;
return;
} else if(mca_info->slot[slot].id != 0xffff) {
} else if(mca_dev->pos_id != 0xffff) {
/* 0xffff usually indicates that there's no adapter,
* however, some integrated adapters may have 0xffff as
......@@ -157,26 +94,26 @@ static void mca_configure_adapter_status(int slot) {
* and possibly also the 95 ULTIMEDIA.
*/
mca_info->slot[slot].status = MCA_ADAPTER_NORMAL;
mca_dev->status = MCA_ADAPTER_NORMAL;
}
if((mca_info->slot[slot].id == 0xffff ||
mca_info->slot[slot].id == 0x0000) && slot >= MCA_MAX_SLOT_NR) {
if((mca_dev->pos_id == 0xffff ||
mca_dev->pos_id == 0x0000) && mca_dev->slot >= MCA_MAX_SLOT_NR) {
int j;
for(j = 2; j < 8; j++) {
if(mca_info->slot[slot].pos[j] != 0xff) {
mca_info->slot[slot].status = MCA_ADAPTER_NORMAL;
if(mca_dev->pos[j] != 0xff) {
mca_dev->status = MCA_ADAPTER_NORMAL;
break;
}
}
}
if(!(mca_info->slot[slot].pos[2] & MCA_ENABLED)) {
if(!(mca_dev->pos[2] & MCA_ENABLED)) {
/* enabled bit is in POS 2 */
mca_info->slot[slot].status = MCA_ADAPTER_DISABLED;
mca_dev->status = MCA_ADAPTER_DISABLED;
}
} /* mca_configure_adapter_status */
......@@ -194,366 +131,51 @@ struct resource mca_standard_resources[] = {
#define MCA_STANDARD_RESOURCES (sizeof(mca_standard_resources)/sizeof(struct resource))
int __init mca_init(void)
{
unsigned int i, j;
/* WARNING: Be careful when making changes here. Putting an adapter
* 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_bus)
return -ENODEV;
printk(KERN_INFO "Micro Channel bus detected.\n");
/* Allocate MCA_info structure (at address divisible by 8) */
mca_info = (struct MCA_info *)kmalloc(sizeof(struct MCA_info), GFP_KERNEL);
if(mca_info == NULL) {
printk(KERN_ERR "Failed to allocate memory for mca_info!");
return -ENOMEM;
}
memset(mca_info, 0, sizeof(struct MCA_info));
/*
* 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 */
outb_p(0x7f, MCA_MOTHERBOARD_SETUP_REG);
mca_info->slot[MCA_MOTHERBOARD].name[0] = 0;
for(j=0; j<8; j++) {
mca_info->slot[MCA_MOTHERBOARD].pos[j] = inb_p(MCA_POS_REG(j));
}
mca_configure_adapter_status(MCA_MOTHERBOARD);
/* Put motherboard into video setup mode, read integrated video
* POS registers, and turn motherboard setup off.
*/
outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG);
mca_info->slot[MCA_INTEGVIDEO].name[0] = 0;
for(j=0; j<8; j++) {
mca_info->slot[MCA_INTEGVIDEO].pos[j] = inb_p(MCA_POS_REG(j));
}
mca_configure_adapter_status(MCA_INTEGVIDEO);
/* Put motherboard into scsi setup mode, read integrated scsi
* 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.
*/
outb_p(0xf7, MCA_MOTHERBOARD_SETUP_REG);
mca_info->slot[MCA_INTEGSCSI].name[0] = 0;
for(j=0; j<8; j++) {
if((mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j))) != 0xff)
{
/* 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 57SLC, the integrated SCSI
* adapter has 0xffff for the adapter ID, but
* nonzero for other registers.
*/
mca_info->which_scsi = 0xf7;
}
}
if(!mca_info->which_scsi) {
/* Didn't find it at 0xf7, try somewhere else... */
mca_info->which_scsi = 0xfd;
outb_p(0xfd, MCA_MOTHERBOARD_SETUP_REG);
for(j=0; j<8; j++)
mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j));
}
mca_configure_adapter_status(MCA_INTEGSCSI);
/* Turn off motherboard setup */
outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
/* Now loop over MCA slots: put each adapter into setup mode, and
* read its POS registers. Then put adapter setup off.
*/
for(i=0; i<MCA_MAX_SLOT_NR; i++) {
outb_p(0x8|(i&0xf), MCA_ADAPTER_SETUP_REG);
for(j=0; j<8; j++) {
mca_info->slot[i].pos[j]=inb_p(MCA_POS_REG(j));
}
mca_info->slot[i].name[0] = 0;
mca_info->slot[i].driver_loaded = 0;
mca_configure_adapter_status(i);
}
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);
#ifdef CONFIG_PROC_FS
mca_do_proc_init();
#endif
return 0;
}
subsys_initcall(mca_init);
/*--------------------------------------------------------------------*/
static void mca_handle_nmi_slot(int slot, int check_flag)
{
if(slot < MCA_MAX_SLOT_NR) {
printk(KERN_CRIT "NMI: caused by MCA adapter in slot %d (%s)\n", slot+1,
mca_info->slot[slot].name);
} else if(slot == MCA_INTEGSCSI) {
printk(KERN_CRIT "NMI: caused by MCA integrated SCSI adapter (%s)\n",
mca_info->slot[slot].name);
} else if(slot == MCA_INTEGVIDEO) {
printk(KERN_CRIT "NMI: caused by MCA integrated video adapter (%s)\n",
mca_info->slot[slot].name);
} else if(slot == MCA_MOTHERBOARD) {
printk(KERN_CRIT "NMI: caused by motherboard (%s)\n",
mca_info->slot[slot].name);
}
/* More info available in POS 6 and 7? */
if(check_flag) {
unsigned char pos6, pos7;
pos6 = mca_read_pos(slot, 6);
pos7 = mca_read_pos(slot, 7);
printk(KERN_CRIT "NMI: POS 6 = 0x%x, POS 7 = 0x%x\n", pos6, pos7);
}
} /* mca_handle_nmi_slot */
/*--------------------------------------------------------------------*/
void mca_handle_nmi(void)
{
int i;
unsigned char pos5;
/* First try - scan the various adapters and see if a specific
* adapter was responsible for the error.
*/
for(i = 0; i < MCA_NUMADAPTERS; i++)
{
/* Bit 7 of POS 5 is reset when this adapter has a hardware
* error. Bit 7 it reset if there's error information
* available in POS 6 and 7.
*/
pos5 = mca_read_pos(i, 5);
if(!(pos5 & 0x80)) {
mca_handle_nmi_slot(i, !(pos5 & 0x40));
return;
}
}
mca_nmi_hook();
} /* mca_handle_nmi */
/*--------------------------------------------------------------------*/
/**
* mca_find_adapter - scan for adapters
* @id: MCA identification to search for
* @start: starting slot
* mca_read_pos - read the POS registers into a memory buffer
*
* Search the MCA configuration for adapters matching the 16bit
* ID given. The first time it should be called with start as zero
* and then further calls made passing the return value of the
* previous call until %MCA_NOTFOUND is returned.
*
* Disabled adapters are not reported.
*/
int mca_find_adapter(int id, int start)
{
if(mca_info == NULL || id == 0xffff) {
return MCA_NOTFOUND;
}
for(; start >= 0 && start < MCA_NUMADAPTERS; start++) {
/* Not sure about this. There's no point in returning
* adapters that aren't enabled, since they can't actually
* be used. However, they might be needed for statistical
* purposes or something... But if that is the case, the
* user is free to write a routine that manually iterates
* through the adapters.
*/
if(mca_info->slot[start].status == MCA_ADAPTER_DISABLED) {
continue;
}
if(id == mca_info->slot[start].id) {
return start;
}
}
return MCA_NOTFOUND;
} /* mca_find_adapter() */
EXPORT_SYMBOL(mca_find_adapter);
/*--------------------------------------------------------------------*/
/**
* mca_find_unused_adapter - scan for unused adapters
* @id: MCA identification to search for
* @start: starting slot
*
* Search the MCA configuration for adapters matching the 16bit
* ID given. The first time it should be called with start as zero
* and then further calls made passing the return value of the
* previous call until %MCA_NOTFOUND is returned.
*
* Adapters that have been claimed by drivers and those that
* are disabled are not reported. This function thus allows a driver
* to scan for further cards when some may already be driven.
* Returns 1 if a card actually exists (i.e. the pos isn't
* all 0xff) or 0 otherwise
*/
static int mca_read_and_store_pos(unsigned char *pos) {
int j;
int found = 0;
int mca_find_unused_adapter(int id, int start)
{
if(mca_info == NULL || id == 0xffff) {
return MCA_NOTFOUND;
}
for(; start >= 0 && start < MCA_NUMADAPTERS; start++) {
/* not sure about this. There's no point in returning
* adapters that aren't enabled, since they can't actually
* be used. However, they might be needed for statistical
* purposes or something... But if that is the case, the
* user is free to write a routine that manually iterates
* through the adapters.
*/
if(mca_info->slot[start].status == MCA_ADAPTER_DISABLED ||
mca_info->slot[start].driver_loaded) {
continue;
}
if(id == mca_info->slot[start].id) {
return start;
for(j=0; j<8; j++) {
if((pos[j] = inb_p(MCA_POS_REG(j))) != 0xff) {
/* 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
* 57SLC, the integrated SCSI adapter has
* 0xffff for the adapter ID, but nonzero for
* other registers. */
found = 1;
}
}
return found;
}
return MCA_NOTFOUND;
} /* mca_find_unused_adapter() */
EXPORT_SYMBOL(mca_find_unused_adapter);
/*--------------------------------------------------------------------*/
/**
* mca_read_stored_pos - read POS register from boot data
* @slot: slot number to read from
* @reg: register to read from
*
* Fetch a POS value that was stored at boot time by the kernel
* when it scanned the MCA space. The register value is returned.
* Missing or invalid registers report 0.
*/
unsigned char mca_read_stored_pos(int slot, int reg)
{
if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0;
if(reg < 0 || reg >= 8) return 0;
return mca_info->slot[slot].pos[reg];
} /* mca_read_stored_pos() */
EXPORT_SYMBOL(mca_read_stored_pos);
/*--------------------------------------------------------------------*/
/**
* mca_read_pos - read POS register from card
* @slot: slot number to read from
* @reg: register to read from
*
* Fetch a POS value directly from the hardware to obtain the
* current value. This is much slower than mca_read_stored_pos and
* may not be invoked from interrupt context. It handles the
* deep magic required for onboard devices transparently.
*/
unsigned char mca_read_pos(int slot, int reg)
static unsigned char mca_pc_read_pos(struct mca_device *mca_dev, int reg)
{
unsigned int byte = 0;
unsigned char byte;
unsigned long flags;
if(slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == NULL) return 0;
if(reg < 0 || reg >= 8) return 0;
if(reg < 0 || reg >= 8)
return 0;
spin_lock_irqsave(&mca_lock, flags);
/* Make sure motherboard setup is off */
outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
/* Read in the appropriate register */
if(slot == MCA_INTEGSCSI && mca_info->which_scsi) {
/* Disable adapter setup, enable motherboard setup */
outb_p(0, MCA_ADAPTER_SETUP_REG);
outb_p(mca_info->which_scsi, MCA_MOTHERBOARD_SETUP_REG);
byte = inb_p(MCA_POS_REG(reg));
outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
} else if(slot == MCA_INTEGVIDEO) {
if(mca_dev->pos_register) {
/* Disable adapter setup, enable motherboard setup */
outb_p(0, MCA_ADAPTER_SETUP_REG);
outb_p(0xdf, MCA_MOTHERBOARD_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 if(slot == MCA_MOTHERBOARD) {
/* Disable adapter setup, enable motherboard setup */
outb_p(0, MCA_ADAPTER_SETUP_REG);
outb_p(0x7f, MCA_MOTHERBOARD_SETUP_REG);
byte = inb_p(MCA_POS_REG(reg));
outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
} else if(slot < MCA_MAX_SLOT_NR) {
} else {
/* Make sure motherboard setup is off */
......@@ -561,58 +183,24 @@ unsigned char mca_read_pos(int slot, int reg)
/* Read the appropriate register */
outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
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);
}
/* Make sure the stored values are consistent, while we're here */
mca_info->slot[slot].pos[reg] = byte;
spin_unlock_irqrestore(&mca_lock, flags);
return byte;
} /* mca_read_pos() */
EXPORT_SYMBOL(mca_read_pos);
mca_dev->pos[reg] = byte;
/*--------------------------------------------------------------------*/
/**
* mca_write_pos - read POS register from card
* @slot: slot number to read from
* @reg: register to read from
* @byte: byte to write to the POS registers
*
* Store a POS value directly from the hardware. You should not
* normally need to use this function and should have a very good
* knowledge of MCA bus before you do so. Doing this wrongly can
* damage the hardware.
*
* This function may not be used from interrupt context.
*
* Note that this a technically a Bad Thing, as IBM tech stuff says
* you should only set POS values through their utilities.
* However, some devices such as the 3c523 recommend that you write
* back some data to make sure the configuration is consistent.
* I'd say that IBM is right, but I like my drivers to work.
*
* This function can't do checks to see if multiple devices end up
* with the same resources, so you might see magic smoke if someone
* screws up.
*/
return byte;
}
void mca_write_pos(int slot, int reg, unsigned char byte)
static void mca_pc_write_pos(struct mca_device *mca_dev, int reg,
unsigned char byte)
{
unsigned long flags;
if(slot < 0 || slot >= MCA_MAX_SLOT_NR)
return;
if(reg < 0 || reg >= 8)
return;
if(mca_info == NULL)
return;
spin_lock_irqsave(&mca_lock, flags);
......@@ -622,7 +210,7 @@ void mca_write_pos(int slot, int reg, unsigned char byte)
/* Read in the appropriate register */
outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
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);
......@@ -630,356 +218,250 @@ void mca_write_pos(int slot, int reg, unsigned char byte)
/* Update the global register list, while we have the byte */
mca_info->slot[slot].pos[reg] = byte;
} /* mca_write_pos() */
EXPORT_SYMBOL(mca_write_pos);
mca_dev->pos[reg] = byte;
/*--------------------------------------------------------------------*/
/**
* mca_set_adapter_name - Set the description of the card
* @slot: slot to name
* @name: text string for the namen
*
* This function sets the name reported via /proc for this
* adapter slot. This is for user information only. Setting a
* name deletes any previous name.
*/
void mca_set_adapter_name(int slot, char* name)
{
if(mca_info == NULL) return;
if(slot >= 0 && slot < MCA_NUMADAPTERS) {
if(name != NULL) {
strncpy(mca_info->slot[slot].name, name,
sizeof(mca_info->slot[slot].name)-1);
mca_info->slot[slot].name[
sizeof(mca_info->slot[slot].name)-1] = 0;
} else {
mca_info->slot[slot].name[0] = 0;
}
}
}
EXPORT_SYMBOL(mca_set_adapter_name);
/**
* mca_set_adapter_procfn - Set the /proc callback
* @slot: slot to configure
* @procfn: callback function to call for /proc
* @dev: device information passed to the callback
*
* This sets up an information callback for /proc/mca/slot?. The
* function is called with the buffer, slot, and device pointer (or
* some equally informative context information, or nothing, if you
* prefer), and is expected to put useful information into the
* buffer. The adapter name, ID, and POS registers get printed
* before this is called though, so don't do it again.
*
* This should be called with a %NULL @procfn when a module
* unregisters, thus preventing kernel crashes and other such
* nastiness.
*/
void mca_set_adapter_procfn(int slot, MCA_ProcFn procfn, void* dev)
/* for the primary MCA bus, we have identity transforms */
static int mca_dummy_transform_irq(struct mca_device * mca_dev, int irq)
{
if(mca_info == NULL) return;
if(slot >= 0 && slot < MCA_NUMADAPTERS) {
mca_info->slot[slot].procfn = procfn;
mca_info->slot[slot].dev = dev;
}
return irq;
}
EXPORT_SYMBOL(mca_set_adapter_procfn);
/**
* mca_is_adapter_used - check if claimed by driver
* @slot: slot to check
*
* Returns 1 if the slot has been claimed by a driver
*/
int mca_is_adapter_used(int slot)
static int mca_dummy_transform_ioport(struct mca_device * mca_dev, int port)
{
return mca_info->slot[slot].driver_loaded;
return port;
}
EXPORT_SYMBOL(mca_is_adapter_used);
/**
* mca_mark_as_used - claim an MCA device
* @slot: slot to claim
* FIXME: should we make this threadsafe
*
* Claim an MCA slot for a device driver. If the
* slot is already taken the function returns 1,
* if it is not taken it is claimed and 0 is
* returned.
*/
int mca_mark_as_used(int slot)
static void *mca_dummy_transform_memory(struct mca_device * mca_dev, void *mem)
{
if(mca_info->slot[slot].driver_loaded) return 1;
mca_info->slot[slot].driver_loaded = 1;
return 0;
return mem;
}
EXPORT_SYMBOL(mca_mark_as_used);
/**
* mca_mark_as_unused - release an MCA device
* @slot: slot to claim
*
* Release the slot for other drives to use.
*/
void mca_mark_as_unused(int slot)
static int __init mca_init(void)
{
mca_info->slot[slot].driver_loaded = 0;
}
EXPORT_SYMBOL(mca_mark_as_unused);
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;
/**
* mca_get_adapter_name - get the adapter description
* @slot: slot to query
*
* Return the adapter description if set. If it has not been
* set or the slot is out range then return NULL.
*/
/* WARNING: Be careful when making changes here. Putting an adapter
* 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).
*/
char *mca_get_adapter_name(int slot)
{
if(mca_info == NULL) return 0;
/* Make sure the MCA bus is present */
if(slot >= 0 && slot < MCA_NUMADAPTERS) {
return mca_info->slot[slot].name;
}
if(!MCA_bus)
return -ENODEV;
return 0;
}
printk(KERN_INFO "Micro Channel bus detected.\n");
EXPORT_SYMBOL(mca_get_adapter_name);
if(mca_system_init()) {
printk(KERN_ERR "MCA bus system initialisation failed\n");
return -ENODEV;
}
/**
* mca_isadapter - check if the slot holds an adapter
* @slot: slot to query
*
* Returns zero if the slot does not hold an adapter, non zero if
* it does.
*/
/* All MCA systems have at least a primary bus */
bus = mca_attach_bus(MCA_PRIMARY_BUS);
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 */
mca_dev = kmalloc(sizeof(struct mca_device), GFP_KERNEL);
if(unlikely(!mca_dev))
goto out_nomem;
memset(mca_dev, 0, sizeof(struct mca_device));
int mca_isadapter(int slot)
{
if(mca_info == NULL) return 0;
/*
* We do not expect many MCA interrupts during initialization,
* but let us be safe:
*/
spin_lock_irq(&mca_lock);
if(slot >= 0 && slot < MCA_NUMADAPTERS) {
return ((mca_info->slot[slot].status == MCA_ADAPTER_NORMAL)
|| (mca_info->slot[slot].status == MCA_ADAPTER_DISABLED));
}
/* Make sure adapter setup is off */
return 0;
}
outb_p(0, MCA_ADAPTER_SETUP_REG);
EXPORT_SYMBOL(mca_isadapter);
/* Read motherboard POS registers */
mca_dev->pos_register = 0x7f;
outb_p(mca_dev->pos_register, MCA_MOTHERBOARD_SETUP_REG);
mca_dev->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);
/**
* mca_isadapter - check if the slot holds an adapter
* @slot: slot to query
*
* Returns a non zero value if the slot holds an enabled adapter
* and zero for any other case.
*/
mca_dev = kmalloc(sizeof(struct mca_device), GFP_ATOMIC);
if(unlikely(!mca_dev))
goto out_unlock_nomem;
memset(mca_dev, 0, sizeof(struct mca_device));
int mca_isenabled(int slot)
{
if(mca_info == NULL) return 0;
if(slot >= 0 && slot < MCA_NUMADAPTERS) {
return (mca_info->slot[slot].status == MCA_ADAPTER_NORMAL);
}
/* Put motherboard into video setup mode, read integrated video
* POS registers, and turn motherboard setup off.
*/
return 0;
}
mca_dev->pos_register = 0xdf;
outb_p(mca_dev->pos_register, MCA_MOTHERBOARD_SETUP_REG);
mca_dev->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);
EXPORT_SYMBOL(mca_isenabled);
/* Put motherboard into scsi setup mode, read integrated scsi
* 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.
*/
/*--------------------------------------------------------------------*/
for(i = 0; (which_scsi = mca_builtin_scsi_ports[i]) != 0; i++) {
outb_p(which_scsi, MCA_MOTHERBOARD_SETUP_REG);
if(mca_read_and_store_pos(pos))
break;
}
if(which_scsi) {
/* found a scsi card */
mca_dev = kmalloc(sizeof(struct mca_device), GFP_ATOMIC);
if(unlikely(!mca_dev))
goto out_unlock_nomem;
memset(mca_dev, 0, sizeof(struct mca_device));
for(j = 0; j < 8; j++)
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);
}
#ifdef CONFIG_PROC_FS
/* Turn off motherboard setup */
int get_mca_info(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int i, j, len = 0;
outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
if(MCA_bus && mca_info != NULL) {
/* Format POS registers of eight MCA slots */
/* Now loop over MCA slots: put each adapter into setup mode, and
* read its POS registers. Then put adapter setup off.
*/
for(i=0; i<MCA_MAX_SLOT_NR; i++) {
len += sprintf(page+len, "Slot %d: ", i+1);
for(j=0; j<8; j++)
len += sprintf(page+len, "%02x ", mca_info->slot[i].pos[j]);
len += sprintf(page+len, " %s\n", mca_info->slot[i].name);
}
for(i=0; i<MCA_MAX_SLOT_NR; i++) {
outb_p(0x8|(i&0xf), MCA_ADAPTER_SETUP_REG);
if(!mca_read_and_store_pos(pos))
continue;
/* Format POS registers of integrated video subsystem */
mca_dev = kmalloc(sizeof(struct mca_device), GFP_ATOMIC);
if(unlikely(!mca_dev))
goto out_unlock_nomem;
memset(mca_dev, 0, sizeof(struct mca_device));
len += sprintf(page+len, "Video : ");
for(j=0; j<8; j++)
len += sprintf(page+len, "%02x ", mca_info->slot[MCA_INTEGVIDEO].pos[j]);
len += sprintf(page+len, " %s\n", mca_info->slot[MCA_INTEGVIDEO].name);
mca_dev->pos[j]=pos[j];
/* Format POS registers of integrated SCSI subsystem */
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);
len += sprintf(page+len, "SCSI : ");
for(j=0; j<8; j++)
len += sprintf(page+len, "%02x ", mca_info->slot[MCA_INTEGSCSI].pos[j]);
len += sprintf(page+len, " %s\n", mca_info->slot[MCA_INTEGSCSI].name);
/* Enable interrupts and return memory start */
spin_unlock_irq(&mca_lock);
/* Format POS registers of motherboard */
for (i = 0; i < MCA_STANDARD_RESOURCES; i++)
request_resource(&ioport_resource, mca_standard_resources + i);
len += sprintf(page+len, "Planar: ");
for(j=0; j<8; j++)
len += sprintf(page+len, "%02x ", mca_info->slot[MCA_MOTHERBOARD].pos[j]);
len += sprintf(page+len, " %s\n", mca_info->slot[MCA_MOTHERBOARD].name);
} else {
/* Leave it empty if MCA not detected - this should *never*
* happen!
*/
}
mca_do_proc_init();
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
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);
/*--------------------------------------------------------------------*/
static int mca_default_procfn(char* buf, struct MCA_adapter *p)
static void mca_handle_nmi_device(struct mca_device *mca_dev, int check_flag)
{
int len = 0, i;
int slot = p - mca_info->slot;
/* Print out the basic information */
int slot = mca_dev->slot;
if(slot < MCA_MAX_SLOT_NR) {
len += sprintf(buf+len, "Slot: %d\n", slot+1);
} else if(slot == MCA_INTEGSCSI) {
len += sprintf(buf+len, "Integrated SCSI Adapter\n");
if(slot == MCA_INTEGSCSI) {
printk(KERN_CRIT "NMI: caused by MCA integrated SCSI adapter (%s)\n",
mca_dev->dev.name);
} else if(slot == MCA_INTEGVIDEO) {
len += sprintf(buf+len, "Integrated Video Adapter\n");
printk(KERN_CRIT "NMI: caused by MCA integrated video adapter (%s)\n",
mca_dev->dev.name);
} else if(slot == MCA_MOTHERBOARD) {
len += sprintf(buf+len, "Motherboard\n");
}
if(p->name[0]) {
/* Drivers might register a name without /proc handler... */
len += sprintf(buf+len, "Adapter Name: %s\n",
p->name);
} else {
len += sprintf(buf+len, "Adapter Name: Unknown\n");
}
len += sprintf(buf+len, "Id: %02x%02x\n",
p->pos[1], p->pos[0]);
len += sprintf(buf+len, "Enabled: %s\nPOS: ",
mca_isenabled(slot) ? "Yes" : "No");
for(i=0; i<8; i++) {
len += sprintf(buf+len, "%02x ", p->pos[i]);
printk(KERN_CRIT "NMI: caused by motherboard (%s)\n",
mca_dev->dev.name);
}
len += sprintf(buf+len, "\nDriver Installed: %s",
mca_is_adapter_used(slot) ? "Yes" : "No");
buf[len++] = '\n';
buf[len] = 0;
return len;
} /* mca_default_procfn() */
static int get_mca_machine_info(char* page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
len += sprintf(page+len, "Model Id: 0x%x\n", machine_id);
len += sprintf(page+len, "Submodel Id: 0x%x\n", machine_submodel_id);
len += sprintf(page+len, "BIOS Revision: 0x%x\n", BIOS_revision);
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
}
static int mca_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
struct MCA_adapter *p = (struct MCA_adapter *)data;
int len = 0;
/* Get the standard info */
/* More info available in POS 6 and 7? */
len = mca_default_procfn(page, p);
if(check_flag) {
unsigned char pos6, pos7;
/* Do any device-specific processing, if there is any */
pos6 = mca_device_read_pos(mca_dev, 6);
pos7 = mca_device_read_pos(mca_dev, 7);
if(p->procfn) {
len += p->procfn(page+len, p-mca_info->slot, p->dev);
printk(KERN_CRIT "NMI: POS 6 = 0x%x, POS 7 = 0x%x\n", pos6, pos7);
}
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
} /* mca_read_proc() */
} /* mca_handle_nmi_slot */
/*--------------------------------------------------------------------*/
void __init mca_do_proc_init(void)
static int mca_handle_nmi_callback(struct device *dev, void *data)
{
int i;
struct proc_dir_entry *proc_mca;
struct proc_dir_entry* node = NULL;
struct MCA_adapter *p;
if(mca_info == NULL) return; /* Should never happen */
proc_mca = proc_mkdir("mca", &proc_root);
create_proc_read_entry("pos",0,proc_mca,get_mca_info,NULL);
create_proc_read_entry("machine",0,proc_mca,get_mca_machine_info,NULL);
/* Initialize /proc/mca entries for existing adapters */
for(i = 0; i < MCA_NUMADAPTERS; i++) {
p = &mca_info->slot[i];
p->procfn = 0;
if(i < MCA_MAX_SLOT_NR) sprintf(p->procname,"slot%d", i+1);
else if(i == MCA_INTEGVIDEO) sprintf(p->procname,"video");
else if(i == MCA_INTEGSCSI) sprintf(p->procname,"scsi");
else if(i == MCA_MOTHERBOARD) sprintf(p->procname,"planar");
if(!mca_isadapter(i)) continue;
struct mca_device *mca_dev = to_mca_device(dev);
unsigned char pos5;
node = create_proc_read_entry(p->procname, 0, proc_mca,
mca_read_proc, (void *)p);
pos5 = mca_device_read_pos(mca_dev, 5);
if(node == NULL) {
printk("Failed to allocate memory for MCA proc-entries!");
return;
}
if(!(pos5 & 0x80)) {
/* Bit 7 of POS 5 is reset when this adapter has a hardware
* 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;
}
} /* mca_do_proc_init() */
void mca_handle_nmi(void)
{
/* First try - scan the various adapters and see if a specific
* adapter was responsible for the error.
*/
bus_for_each_dev(&mca_bus_type, NULL, NULL, mca_handle_nmi_callback);
#endif
mca_nmi_hook();
} /* mca_handle_nmi */
......@@ -44,5 +44,6 @@ obj-$(CONFIG_MD) += md/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
obj-$(CONFIG_ISDN_BOOL) += isdn/
obj-$(CONFIG_MCA) += mca/
include $(TOPDIR)/Rules.make
comment "Micro Channel Architecture Bus support"
depends on MCA
config MCA_LEGACY
bool "Legacy MCA API Support"
depends on MCA
help
This compiles in support for the old slot based MCA API. If you
have an unconverted MCA driver, you will need to say Y here. It
is safe to say Y anyway.
config MCA_PROC_FS
bool "Support for the mca entry in /proc"
depends on MCA_LEGACY && PROC_FS
help
If you want the old style /proc/mca directory in addition to the
new style sysfs say Y here.
# Makefile for the Linux MCA bus support
obj-y := mca-bus.o mca-device.o mca-driver.o
obj-$(CONFIG_MCA_PROC_FS) += mca-proc.o
obj-$(CONFIG_MCA_LEGACY) += mca-legacy.o
export-objs := mca-bus.o mca-legacy.o mca-proc.o mca-driver.o
include $(TOPDIR)/Rules.make
/* -*- mode: c; c-basic-offset: 8 -*- */
/*
* MCA bus support functions for sysfs.
*
* (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
*
**-----------------------------------------------------------------------------
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/mca.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
/* Very few machines have more than one MCA bus. However, there are
* those that do (Voyager 35xx/5xxx), so we do it this way for future
* expansion. None that I know have more than 2 */
struct mca_bus *mca_root_busses[MAX_MCA_BUSSES];
#define MCA_DEVINFO(i,s) { .pos = i, .name = s }
struct mca_device_info {
short pos_id; /* the 2 byte pos id for this card */
char name[DEVICE_NAME_SIZE];
};
static int mca_bus_match (struct device *dev, struct device_driver *drv)
{
struct mca_device *mca_dev = to_mca_device (dev);
struct mca_driver *mca_drv = to_mca_driver (drv);
const short *mca_ids = mca_drv->id_table;
int i;
if (!mca_ids)
return 0;
for(i = 0; mca_ids[i]; i++) {
if (mca_ids[i] == mca_dev->pos_id) {
mca_dev->index = i;
return 1;
}
}
return 0;
}
struct bus_type mca_bus_type = {
.name = "MCA",
.match = mca_bus_match,
};
EXPORT_SYMBOL (mca_bus_type);
static ssize_t mca_show_pos_id(struct device *dev, char *buf, size_t count,
loff_t off)
{
/* four digits, \n and trailing \0 */
char mybuf[6];
struct mca_device *mca_dev = to_mca_device(dev);
int len;
if(mca_dev->pos_id < MCA_DUMMY_POS_START)
len = sprintf(mybuf, "%04x\n", mca_dev->pos_id);
else
len = sprintf(mybuf, "none\n");
len++;
if(len > off) {
len = min((size_t)(len - off), count);
memcpy(buf + off, mybuf + off, len);
} else {
len = 0;
}
return len;
}
static ssize_t mca_show_pos(struct device *dev, char *buf, size_t count,
loff_t off)
{
/* enough for 8 two byte hex chars plus space and new line */
char mybuf[26];
int j, len=0;
struct mca_device *mca_dev = to_mca_device(dev);
for(j=0; j<8; j++)
len += sprintf(mybuf+len, "%02x ", mca_dev->pos[j]);
/* change last trailing space to new line */
mybuf[len-1] = '\n';
len++;
if(len > off) {
len = min((size_t)(len - off), count);
memcpy(buf + off, mybuf + off, len);
} else {
len = 0;
}
return len;
}
static DEVICE_ATTR(id, S_IRUGO, mca_show_pos_id, NULL);
static DEVICE_ATTR(pos, S_IRUGO, mca_show_pos, NULL);
int __init mca_register_device(int bus, struct mca_device *mca_dev)
{
struct mca_bus *mca_bus = mca_root_busses[bus];
mca_dev->dev.parent = &mca_bus->dev;
mca_dev->dev.bus = &mca_bus_type;
sprintf (mca_dev->dev.bus_id, "%02d:%02X", bus, mca_dev->slot);
mca_dev->dma_mask = mca_bus->default_dma_mask;
mca_dev->dev.dma_mask = &mca_dev->dma_mask;
if (device_register(&mca_dev->dev))
return 0;
device_create_file(&mca_dev->dev, &dev_attr_id);
device_create_file(&mca_dev->dev, &dev_attr_pos);
return 1;
}
/* */
struct mca_bus * __devinit mca_attach_bus(int bus)
{
struct mca_bus *mca_bus;
if (unlikely(mca_root_busses[bus] != NULL)) {
/* This should never happen, but just in case */
printk(KERN_EMERG "MCA tried to add already existing bus %d\n",
bus);
dump_stack();
return NULL;
}
mca_bus = kmalloc(sizeof(struct mca_bus), GFP_KERNEL);
if (!mca_bus)
return NULL;
memset(mca_bus, 0, sizeof(struct mca_bus));
sprintf(mca_bus->dev.bus_id,"mca%d",bus);
sprintf(mca_bus->dev.name,"Host %s MCA Bridge", bus ? "Secondary" : "Primary");
device_register(&mca_bus->dev);
mca_root_busses[bus] = mca_bus;
return mca_bus;
}
int __init mca_system_init (void)
{
return bus_register(&mca_bus_type);
}
/* -*- mode: c; c-basic-offset: 8 -*- */
/*
* MCA device support functions
*
* These functions support the ongoing device access API.
*
* (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
*
**-----------------------------------------------------------------------------
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mca.h>
/**
* mca_device_read_stored_pos - read POS register from stored data
* @mca_dev: device to read from
* @reg: register to read from
*
* Fetch a POS value that was stored at boot time by the kernel
* when it scanned the MCA space. The register value is returned.
* Missing or invalid registers report 0.
*/
unsigned char mca_device_read_stored_pos(struct mca_device *mca_dev, int reg)
{
if(reg < 0 || reg >= 8)
return 0;
return mca_dev->pos[reg];
}
EXPORT_SYMBOL(mca_device_read_stored_pos);
/**
* mca_device_read_pos - read POS register from card
* @mca_dev: device to read from
* @reg: register to read from
*
* Fetch a POS value directly from the hardware to obtain the
* current value. This is much slower than
* mca_device_read_stored_pos and may not be invoked from
* interrupt context. It handles the deep magic required for
* onboard devices transparently.
*/
unsigned char mca_device_read_pos(struct mca_device *mca_dev, int reg)
{
struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
return mca_bus->f.mca_read_pos(mca_dev, reg);
return mca_dev->pos[reg];
}
EXPORT_SYMBOL(mca_device_read_pos);
/**
* mca_device_write_pos - read POS register from card
* @mca_dev: device to write pos register to
* @reg: register to write to
* @byte: byte to write to the POS registers
*
* Store a POS value directly to the hardware. You should not
* normally need to use this function and should have a very good
* knowledge of MCA bus before you do so. Doing this wrongly can
* damage the hardware.
*
* This function may not be used from interrupt context.
*
*/
void mca_device_write_pos(struct mca_device *mca_dev, int reg,
unsigned char byte)
{
struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
mca_bus->f.mca_write_pos(mca_dev, reg, byte);
}
EXPORT_SYMBOL(mca_device_write_pos);
/**
* mca_device_transform_irq - transform the ADF obtained IRQ
* @mca_device: device whose irq needs transforming
* @irq: input irq from ADF
*
* MCA Adapter Definition Files (ADF) contain irq, ioport, memory
* etc. definitions. In systems with more than one bus, these need
* to be transformed through bus mapping functions to get the real
* system global quantities.
*
* This function transforms the interrupt number and returns the
* transformed system global interrupt
*/
int mca_device_transform_irq(struct mca_device *mca_dev, int irq)
{
struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
return mca_bus->f.mca_transform_irq(mca_dev, irq);
}
EXPORT_SYMBOL(mca_device_transform_irq);
/**
* mca_device_transform_ioport - transform the ADF obtained I/O port
* @mca_device: device whose port needs transforming
* @ioport: input I/O port from ADF
*
* MCA Adapter Definition Files (ADF) contain irq, ioport, memory
* etc. definitions. In systems with more than one bus, these need
* to be transformed through bus mapping functions to get the real
* system global quantities.
*
* This function transforms the I/O port number and returns the
* transformed system global port number.
*
* This transformation can be assumed to be linear for port ranges.
*/
int mca_device_transform_ioport(struct mca_device *mca_dev, int port)
{
struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
return mca_bus->f.mca_transform_ioport(mca_dev, port);
}
EXPORT_SYMBOL(mca_device_transform_ioport);
/**
* mca_device_transform_memory - transform the ADF obtained memory
* @mca_device: device whose memory region needs transforming
* @mem: memory region start from ADF
*
* MCA Adapter Definition Files (ADF) contain irq, ioport, memory
* etc. definitions. In systems with more than one bus, these need
* to be transformed through bus mapping functions to get the real
* system global quantities.
*
* This function transforms the memory region start and returns the
* transformed system global memory region (physical).
*
* This transformation can be assumed to be linear for region ranges.
*/
void *mca_device_transform_memory(struct mca_device *mca_dev, void *mem)
{
struct mca_bus *mca_bus = to_mca_bus(mca_dev->dev.parent);
return mca_bus->f.mca_transform_memory(mca_dev, mem);
}
EXPORT_SYMBOL(mca_device_transform_memory);
/**
* mca_device_claimed - check if claimed by driver
* @mca_dev: device to check
*
* Returns 1 if the slot has been claimed by a driver
*/
int mca_device_claimed(struct mca_device *mca_dev)
{
return mca_dev->driver_loaded;
}
EXPORT_SYMBOL(mca_device_claimed);
/**
* mca_device_set_claim - set the claim value of the driver
* @mca_dev: device to set value for
* @val: claim value to set (1 claimed, 0 unclaimed)
*/
void mca_device_set_claim(struct mca_device *mca_dev, int val)
{
mca_dev->driver_loaded = val;
}
EXPORT_SYMBOL(mca_device_set_claim);
/**
* mca_device_status - get the status of the device
* @mca_device: device to get
*
* returns an enumeration of the device status:
*
* MCA_ADAPTER_NORMAL adapter is OK.
* MCA_ADAPTER_NONE no adapter at device (should never happen).
* MCA_ADAPTER_DISABLED adapter is disabled.
* MCA_ADAPTER_ERROR adapter cannot be initialised.
*/
enum MCA_AdapterStatus mca_device_status(struct mca_device *mca_dev)
{
return mca_dev->status;
}
/* -*- mode: c; c-basic-offset: 8 -*- */
/*
* MCA driver support functions for sysfs.
*
* (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
*
**-----------------------------------------------------------------------------
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
*/
#include <linux/device.h>
#include <linux/mca.h>
#include <linux/module.h>
int mca_register_driver(struct mca_driver *mca_drv)
{
int r;
mca_drv->driver.bus = &mca_bus_type;
if ((r = driver_register(&mca_drv->driver)) < 0)
return r;
return 0;
}
EXPORT_SYMBOL(mca_register_driver);
void mca_unregister_driver(struct mca_driver *mca_drv)
{
driver_unregister(&mca_drv->driver);
}
EXPORT_SYMBOL(mca_unregister_driver);
/* -*- mode: c; c-basic-offset: 8 -*- */
/*
* MCA bus support functions for legacy (2.4) API.
*
* Legacy API means the API that operates in terms of MCA slot number
*
* (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
*
**-----------------------------------------------------------------------------
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mca.h>
#include <asm/io.h>
/* NOTE: This structure is stack allocated */
struct mca_find_adapter_info {
int id;
int slot;
struct mca_device *mca_dev;
};
/* The purpose of this iterator is to loop over all the devices and
* find the one with the smallest slot number that's just greater than
* or equal to the required slot with a matching id */
static int mca_find_adapter_callback(struct device *dev, void *data)
{
struct mca_find_adapter_info *info = data;
struct mca_device *mca_dev = to_mca_device(dev);
if(mca_dev->pos_id != info->id)
return 0;
if(mca_dev->slot < info->slot)
return 0;
if(!info->mca_dev || info->mca_dev->slot >= mca_dev->slot)
info->mca_dev = mca_dev;
return 0;
}
/**
* mca_find_adapter - scan for adapters
* @id: MCA identification to search for
* @start: starting slot
*
* Search the MCA configuration for adapters matching the 16bit
* ID given. The first time it should be called with start as zero
* and then further calls made passing the return value of the
* previous call until %MCA_NOTFOUND is returned.
*
* Disabled adapters are not reported.
*/
int mca_find_adapter(int id, int start)
{
struct mca_find_adapter_info info;
if(id == 0xffff)
return MCA_NOTFOUND;
info.slot = start;
info.id = id;
info.mca_dev = NULL;
for(;;) {
bus_for_each_dev(&mca_bus_type, NULL, &info, mca_find_adapter_callback);
if(info.mca_dev == NULL)
return MCA_NOTFOUND;
if(info.mca_dev->status != MCA_ADAPTER_DISABLED)
break;
/* OK, found adapter but it was disabled. Go around
* again, excluding the slot we just found */
info.slot = info.mca_dev->slot + 1;
info.mca_dev = NULL;
}
return info.mca_dev->slot;
}
EXPORT_SYMBOL(mca_find_adapter);
/*--------------------------------------------------------------------*/
/**
* mca_find_unused_adapter - scan for unused adapters
* @id: MCA identification to search for
* @start: starting slot
*
* Search the MCA configuration for adapters matching the 16bit
* ID given. The first time it should be called with start as zero
* and then further calls made passing the return value of the
* previous call until %MCA_NOTFOUND is returned.
*
* Adapters that have been claimed by drivers and those that
* are disabled are not reported. This function thus allows a driver
* to scan for further cards when some may already be driven.
*/
int mca_find_unused_adapter(int id, int start)
{
struct mca_find_adapter_info info = { 0 };
if(id == 0xffff)
return MCA_NOTFOUND;
info.slot = start;
info.id = id;
info.mca_dev = NULL;
for(;;) {
bus_for_each_dev(&mca_bus_type, NULL, &info, mca_find_adapter_callback);
if(info.mca_dev == NULL)
return MCA_NOTFOUND;
if(info.mca_dev->status != MCA_ADAPTER_DISABLED
&& !info.mca_dev->driver_loaded)
break;
/* OK, found adapter but it was disabled or already in
* use. Go around again, excluding the slot we just
* found */
info.slot = info.mca_dev->slot + 1;
info.mca_dev = NULL;
}
return info.mca_dev->slot;
}
EXPORT_SYMBOL(mca_find_unused_adapter);
/* NOTE: stack allocated structure */
struct mca_find_device_by_slot_info {
int slot;
struct mca_device *mca_dev;
};
static int mca_find_device_by_slot_callback(struct device *dev, void *data)
{
struct mca_find_device_by_slot_info *info = data;
struct mca_device *mca_dev = to_mca_device(dev);
if(mca_dev->slot == info->slot)
info->mca_dev = mca_dev;
return 0;
}
struct mca_device *mca_find_device_by_slot(int slot)
{
struct mca_find_device_by_slot_info info;
info.slot = slot;
info.mca_dev = NULL;
bus_for_each_dev(&mca_bus_type, NULL, &info, mca_find_device_by_slot_callback);
return info.mca_dev;
}
EXPORT_SYMBOL(mca_find_device_by_slot);
/**
* mca_read_stored_pos - read POS register from boot data
* @slot: slot number to read from
* @reg: register to read from
*
* Fetch a POS value that was stored at boot time by the kernel
* when it scanned the MCA space. The register value is returned.
* Missing or invalid registers report 0.
*/
unsigned char mca_read_stored_pos(int slot, int reg)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
return 0;
return mca_device_read_stored_pos(mca_dev, reg);
}
EXPORT_SYMBOL(mca_read_stored_pos);
/**
* mca_read_pos - read POS register from card
* @slot: slot number to read from
* @reg: register to read from
*
* Fetch a POS value directly from the hardware to obtain the
* current value. This is much slower than mca_read_stored_pos and
* may not be invoked from interrupt context. It handles the
* deep magic required for onboard devices transparently.
*/
unsigned char mca_read_pos(int slot, int reg)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
return 0;
return mca_device_read_pos(mca_dev, reg);
}
EXPORT_SYMBOL(mca_read_pos);
/**
* mca_write_pos - read POS register from card
* @slot: slot number to read from
* @reg: register to read from
* @byte: byte to write to the POS registers
*
* Store a POS value directly from the hardware. You should not
* normally need to use this function and should have a very good
* knowledge of MCA bus before you do so. Doing this wrongly can
* damage the hardware.
*
* This function may not be used from interrupt context.
*
* Note that this a technically a Bad Thing, as IBM tech stuff says
* you should only set POS values through their utilities.
* However, some devices such as the 3c523 recommend that you write
* back some data to make sure the configuration is consistent.
* I'd say that IBM is right, but I like my drivers to work.
*
* This function can't do checks to see if multiple devices end up
* with the same resources, so you might see magic smoke if someone
* screws up.
*/
void mca_write_pos(int slot, int reg, unsigned char byte)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
return;
mca_device_write_pos(mca_dev, reg, byte);
}
EXPORT_SYMBOL(mca_write_pos);
/**
* mca_set_adapter_name - Set the description of the card
* @slot: slot to name
* @name: text string for the namen
*
* This function sets the name reported via /proc for this
* adapter slot. This is for user information only. Setting a
* name deletes any previous name.
*/
void mca_set_adapter_name(int slot, char* name)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
return;
strncpy(mca_dev->dev.name, name, sizeof(mca_dev->dev.name));
mca_dev->dev.name[sizeof(mca_dev->dev.name) - 1] = '\0';
}
EXPORT_SYMBOL(mca_set_adapter_name);
/**
* mca_get_adapter_name - get the adapter description
* @slot: slot to query
*
* Return the adapter description if set. If it has not been
* set or the slot is out range then return NULL.
*/
char *mca_get_adapter_name(int slot)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
return NULL;
return mca_dev->dev.name;
}
EXPORT_SYMBOL(mca_get_adapter_name);
/**
* mca_is_adapter_used - check if claimed by driver
* @slot: slot to check
*
* Returns 1 if the slot has been claimed by a driver
*/
int mca_is_adapter_used(int slot)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
return 0;
return mca_device_claimed(mca_dev);
}
EXPORT_SYMBOL(mca_is_adapter_used);
/**
* mca_mark_as_used - claim an MCA device
* @slot: slot to claim
* FIXME: should we make this threadsafe
*
* Claim an MCA slot for a device driver. If the
* slot is already taken the function returns 1,
* if it is not taken it is claimed and 0 is
* returned.
*/
int mca_mark_as_used(int slot)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
/* FIXME: this is actually a severe error */
return 1;
if(mca_device_claimed(mca_dev))
return 1;
mca_device_set_claim(mca_dev, 1);
return 0;
}
EXPORT_SYMBOL(mca_mark_as_used);
/**
* mca_mark_as_unused - release an MCA device
* @slot: slot to claim
*
* Release the slot for other drives to use.
*/
void mca_mark_as_unused(int slot)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
return;
mca_device_set_claim(mca_dev, 0);
}
EXPORT_SYMBOL(mca_mark_as_unused);
/**
* mca_isadapter - check if the slot holds an adapter
* @slot: slot to query
*
* Returns zero if the slot does not hold an adapter, non zero if
* it does.
*/
int mca_isadapter(int slot)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
enum MCA_AdapterStatus status;
if(!mca_dev)
return 0;
status = mca_device_status(mca_dev);
return status == MCA_ADAPTER_NORMAL
|| status == MCA_ADAPTER_DISABLED;
}
EXPORT_SYMBOL(mca_isadapter);
/**
* mca_isenabled - check if the slot holds an enabled adapter
* @slot: slot to query
*
* Returns a non zero value if the slot holds an enabled adapter
* and zero for any other case.
*/
int mca_isenabled(int slot)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
return 0;
return mca_device_status(mca_dev) == MCA_ADAPTER_NORMAL;
}
/* -*- mode: c; c-basic-offset: 8 -*- */
/*
* MCA bus support functions for the proc fs.
*
* NOTE: this code *requires* the legacy MCA api.
*
* Legacy API means the API that operates in terms of MCA slot number
*
* (C) 2002 James Bottomley <James.Bottomley@HansenPartnership.com>
*
**-----------------------------------------------------------------------------
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
**-----------------------------------------------------------------------------
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/mca.h>
static int get_mca_info_helper(struct mca_device *mca_dev, char *page, int len)
{
int j;
for(j=0; j<8; j++)
len += sprintf(page+len, "%02x ",
mca_dev ? mca_dev->pos[j] : 0xff);
len += sprintf(page+len, " %s\n", mca_dev ? mca_dev->dev.name : "");
return len;
}
int get_mca_info(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int i, len = 0;
if(MCA_bus) {
struct mca_device *mca_dev;
/* Format POS registers of eight MCA slots */
for(i=0; i<MCA_MAX_SLOT_NR; i++) {
mca_dev = mca_find_device_by_slot(i);
len += sprintf(page+len, "Slot %d: ", i+1);
len = get_mca_info_helper(mca_dev, page, len);
}
/* Format POS registers of integrated video subsystem */
mca_dev = mca_find_device_by_slot(MCA_INTEGVIDEO);
len += sprintf(page+len, "Video : ");
len = get_mca_info_helper(mca_dev, page, len);
/* Format POS registers of integrated SCSI subsystem */
mca_dev = mca_find_device_by_slot(MCA_INTEGSCSI);
len += sprintf(page+len, "SCSI : ");
len = get_mca_info_helper(mca_dev, page, len);
/* Format POS registers of motherboard */
mca_dev = mca_find_device_by_slot(MCA_MOTHERBOARD);
len += sprintf(page+len, "Planar: ");
len = get_mca_info_helper(mca_dev, page, len);
} else {
/* Leave it empty if MCA not detected - this should *never*
* happen!
*/
}
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
}
/*--------------------------------------------------------------------*/
static int mca_default_procfn(char* buf, struct mca_device *mca_dev)
{
int len = 0, i;
int slot = mca_dev->slot;
/* Print out the basic information */
if(slot < MCA_MAX_SLOT_NR) {
len += sprintf(buf+len, "Slot: %d\n", slot+1);
} else if(slot == MCA_INTEGSCSI) {
len += sprintf(buf+len, "Integrated SCSI Adapter\n");
} else if(slot == MCA_INTEGVIDEO) {
len += sprintf(buf+len, "Integrated Video Adapter\n");
} else if(slot == MCA_MOTHERBOARD) {
len += sprintf(buf+len, "Motherboard\n");
}
if(mca_dev->dev.name[0]) {
/* Drivers might register a name without /proc handler... */
len += sprintf(buf+len, "Adapter Name: %s\n",
mca_dev->dev.name);
} else {
len += sprintf(buf+len, "Adapter Name: Unknown\n");
}
len += sprintf(buf+len, "Id: %02x%02x\n",
mca_dev->pos[1], mca_dev->pos[0]);
len += sprintf(buf+len, "Enabled: %s\nPOS: ",
mca_isenabled(slot) ? "Yes" : "No");
for(i=0; i<8; i++) {
len += sprintf(buf+len, "%02x ", mca_dev->pos[i]);
}
len += sprintf(buf+len, "\nDriver Installed: %s",
mca_is_adapter_used(slot) ? "Yes" : "No");
buf[len++] = '\n';
buf[len] = 0;
return len;
} /* mca_default_procfn() */
static int get_mca_machine_info(char* page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = 0;
len += sprintf(page+len, "Model Id: 0x%x\n", machine_id);
len += sprintf(page+len, "Submodel Id: 0x%x\n", machine_submodel_id);
len += sprintf(page+len, "BIOS Revision: 0x%x\n", BIOS_revision);
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
}
static int mca_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
struct mca_device *mca_dev = (struct mca_device *)data;
int len = 0;
/* Get the standard info */
len = mca_default_procfn(page, mca_dev);
/* Do any device-specific processing, if there is any */
if(mca_dev->procfn) {
len += mca_dev->procfn(page+len, mca_dev->slot,
mca_dev->proc_dev);
}
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
} /* mca_read_proc() */
/*--------------------------------------------------------------------*/
void __init mca_do_proc_init(void)
{
int i;
struct proc_dir_entry *proc_mca;
struct proc_dir_entry* node = NULL;
struct mca_device *mca_dev;
proc_mca = proc_mkdir("mca", &proc_root);
create_proc_read_entry("pos",0,proc_mca,get_mca_info,NULL);
create_proc_read_entry("machine",0,proc_mca,get_mca_machine_info,NULL);
/* Initialize /proc/mca entries for existing adapters */
for(i = 0; i < MCA_NUMADAPTERS; i++) {
mca_dev = mca_find_device_by_slot(i);
if(!mca_dev)
continue;
mca_dev->procfn = NULL;
if(i < MCA_MAX_SLOT_NR) sprintf(mca_dev->procname,"slot%d", i+1);
else if(i == MCA_INTEGVIDEO) sprintf(mca_dev->procname,"video");
else if(i == MCA_INTEGSCSI) sprintf(mca_dev->procname,"scsi");
else if(i == MCA_MOTHERBOARD) sprintf(mca_dev->procname,"planar");
if(!mca_isadapter(i)) continue;
node = create_proc_read_entry(mca_dev->procname, 0, proc_mca,
mca_read_proc, (void *)mca_dev);
if(node == NULL) {
printk("Failed to allocate memory for MCA proc-entries!");
return;
}
}
} /* mca_do_proc_init() */
/**
* mca_set_adapter_procfn - Set the /proc callback
* @slot: slot to configure
* @procfn: callback function to call for /proc
* @dev: device information passed to the callback
*
* This sets up an information callback for /proc/mca/slot?. The
* function is called with the buffer, slot, and device pointer (or
* some equally informative context information, or nothing, if you
* prefer), and is expected to put useful information into the
* buffer. The adapter name, ID, and POS registers get printed
* before this is called though, so don't do it again.
*
* This should be called with a %NULL @procfn when a module
* unregisters, thus preventing kernel crashes and other such
* nastiness.
*/
void mca_set_adapter_procfn(int slot, MCA_ProcFn procfn, void* proc_dev)
{
struct mca_device *mca_dev = mca_find_device_by_slot(slot);
if(!mca_dev)
return;
mca_dev->procfn = procfn;
mca_dev->proc_dev = proc_dev;
}
EXPORT_SYMBOL(mca_set_adapter_procfn);
......@@ -48,7 +48,6 @@ extern int ne2_probe(struct net_device *dev);
extern int hp100_probe(struct net_device *dev);
extern int ultra_probe(struct net_device *dev);
extern int ultra32_probe(struct net_device *dev);
extern int ultramca_probe(struct net_device *dev);
extern int wd_probe(struct net_device *dev);
extern int el2_probe(struct net_device *dev);
extern int ne_probe(struct net_device *dev);
......@@ -191,9 +190,6 @@ static struct devprobe eisa_probes[] __initdata = {
static struct devprobe mca_probes[] __initdata = {
#ifdef CONFIG_ULTRAMCA
{ultramca_probe, 0},
#endif
#ifdef CONFIG_NE2_MCA
{ne2_probe, 0},
#endif
......
......@@ -51,8 +51,6 @@
#include "8390.h"
#include "smc-mca.h"
int ultramca_probe(struct net_device *dev);
static int ultramca_open(struct net_device *dev);
static void ultramca_reset_8390(struct net_device *dev);
static void ultramca_get_8390_hdr(struct net_device *dev,
......@@ -89,37 +87,57 @@ struct smc_mca_adapters_t {
char *name;
};
static struct smc_mca_adapters_t smc_mca_adapters[] __initdata = {
{ 0x61c8, "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)" },
{ 0x61c9, "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)" },
{ 0x6fc0, "WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)" },
{ 0x6fc1, "WD Starcard PLUS/A (WD8003ST/A)" },
{ 0x6fc2, "WD Ethercard PLUS 10T/A (WD8003W/A)" },
{ 0xefd4, "IBM PS/2 Adapter/A for Ethernet UTP/AUI (WD8013WP/A)" },
{ 0xefd5, "IBM PS/2 Adapter/A for Ethernet BNC/AUI (WD8013EP/A)" },
{ 0xefe5, "IBM PS/2 Adapter/A for Ethernet" },
{ 0x0000, NULL }
#define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */
static int ultra_io[MAX_ULTRAMCA_CARDS];
static int ultra_irq[MAX_ULTRAMCA_CARDS];
MODULE_LICENSE("GPL");
MODULE_PARM(ultra_io, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
MODULE_PARM(ultra_irq, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
MODULE_PARM_DESC(ultra_io, "SMC Ultra/EtherEZ MCA I/O base address(es)");
MODULE_PARM_DESC(ultra_irq, "SMC Ultra/EtherEZ MCA IRQ number(s)");
static short smc_mca_adapter_ids[] __initdata = {
0x61c8,
0x61c9,
0x6fc0,
0x6fc1,
0x6fc2,
0xefd4,
0xefd5,
0xefe5,
0x0000
};
static char *smc_mca_adapter_names[] __initdata = {
"SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)",
"SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)",
"WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)",
"WD Starcard PLUS/A (WD8003ST/A)",
"WD Ethercard PLUS 10T/A (WD8003W/A)",
"IBM PS/2 Adapter/A for Ethernet UTP/AUI (WD8013WP/A)",
"IBM PS/2 Adapter/A for Ethernet BNC/AUI (WD8013EP/A)",
"IBM PS/2 Adapter/A for Ethernet",
NULL
};
int __init ultramca_probe(struct net_device *dev)
static int ultra_found = 0;
int __init ultramca_probe(struct device *gen_dev)
{
unsigned short ioaddr;
struct net_device *dev;
unsigned char reg4, num_pages;
char slot = -1;
struct mca_device *mca_dev = to_mca_device(gen_dev);
char slot = mca_dev->slot;
unsigned char pos2 = 0xff, pos3 = 0xff, pos4 = 0xff, pos5 = 0xff;
int i, j;
int adapter_found = 0;
int adapter = 0;
int i;
int adapter = mca_dev->index;
int tbase = 0;
int tirq = 0;
int base_addr = dev->base_addr;
int irq = dev->irq;
if (!MCA_bus) {
return -ENODEV;
}
SET_MODULE_OWNER(dev);
int base_addr = ultra_io[ultra_found];
int irq = ultra_irq[ultra_found];
if (base_addr || irq) {
printk(KERN_INFO "Probing for SMC MCA adapter");
......@@ -132,82 +150,76 @@ int __init ultramca_probe(struct net_device *dev)
}
}
/* proper multicard detection by ZP Gu (zpg@castle.net) */
for (j = 0; (smc_mca_adapters[j].name != NULL) && !adapter_found; j++) {
slot = mca_find_unused_adapter(smc_mca_adapters[j].id, 0);
while((slot != MCA_NOTFOUND) && !adapter_found) {
tirq = 0;
tbase = 0;
/* If we're trying to match a specificied irq or
* io address, we'll reject the adapter
* found unless it's the one we're looking for
*/
pos2 = mca_read_stored_pos(slot, 2); /* io_addr */
pos3 = mca_read_stored_pos(slot, 3); /* shared mem */
pos4 = mca_read_stored_pos(slot, 4); /* ROM bios addr
* range */
pos5 = mca_read_stored_pos(slot, 5); /* irq, media
* and RIPL */
/* Test the following conditions:
* - If an irq parameter is supplied, compare it
* with the irq of the adapter we found
* - If a base_addr paramater is given, compare it
* with the base_addr of the adapter we found
* - Check that the irq and the base_addr of the
* adapter we found is not already in use by
* this driver
*/
switch (j) { /* j = card-idx (card array above) [hs] */
case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
{
tbase = addr_table[(pos2 & 0xf0) >> 4].base_addr;
tirq = irq_table[(pos5 & 0xc) >> 2].new_irq;
break;
}
case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
{
tbase = ((pos2 & 0x0fe) * 0x10);
tirq = irq_table[(pos5 & 3)].old_irq;
break;
}
}
tirq = 0;
tbase = 0;
/* If we're trying to match a specificied irq or io address,
* we'll reject the adapter found unless it's the one we're
* looking for */
pos2 = mca_device_read_stored_pos(mca_dev, 2); /* io_addr */
pos3 = mca_device_read_stored_pos(mca_dev, 3); /* shared mem */
pos4 = mca_device_read_stored_pos(mca_dev, 4); /* ROM bios addr range */
pos5 = mca_device_read_stored_pos(mca_dev, 5); /* irq, media and RIPL */
/* Test the following conditions:
* - If an irq parameter is supplied, compare it
* with the irq of the adapter we found
* - If a base_addr paramater is given, compare it
* with the base_addr of the adapter we found
* - Check that the irq and the base_addr of the
* adapter we found is not already in use by
* this driver
*/
if(!tirq || !tbase || (irq && irq != tirq) || (base_addr && tbase != base_addr)) {
slot = mca_find_unused_adapter(smc_mca_adapters[j].id, slot + 1);
} else {
adapter_found = 1;
adapter = j;
}
switch (mca_dev->index) {
case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A:
case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A:
{
tbase = addr_table[(pos2 & 0xf0) >> 4].base_addr;
tirq = irq_table[(pos5 & 0xc) >> 2].new_irq;
break;
}
case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A:
case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A:
case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
{
tbase = ((pos2 & 0x0fe) * 0x10);
tirq = irq_table[(pos5 & 3)].old_irq;
break;
}
}
if(!adapter_found) {
return ((base_addr || irq) ? -ENXIO : -ENODEV);
}
if(!tirq || !tbase
|| (irq && irq != tirq)
|| (base_addr && tbase != base_addr))
/* FIXME: we're trying to force the ordering of the
* devices here, there should be a way of getting this
* to happen */
return -ENXIO;
/* Adapter found. */
dev = alloc_etherdev(0);
if(!dev)
return -ENODEV;
printk(KERN_INFO "%s: %s found in slot %d\n",
dev->name, smc_mca_adapters[adapter].name, slot + 1);
SET_MODULE_OWNER(dev);
if((i = register_netdev(dev)) != 0)
return i;
mca_set_adapter_name(slot, smc_mca_adapters[adapter].name);
mca_mark_as_used(slot);
printk(KERN_INFO "%s: %s found in slot %d\n",
dev->name, smc_mca_adapter_names[adapter], slot + 1);
strncpy(gen_dev->name, smc_mca_adapter_names[adapter], sizeof(gen_dev->name));
mca_device_set_claim(mca_dev, 1);
ultra_found++;
dev->base_addr = ioaddr = tbase;
dev->irq = tirq;
dev->base_addr = ioaddr = mca_device_transform_ioport(mca_dev, tbase);
dev->irq = mca_device_transform_irq(mca_dev, tirq);
dev->mem_start = 0;
num_pages = 40;
......@@ -218,7 +230,8 @@ int __init ultramca_probe(struct net_device *dev)
for (i = 0; i < 16; i++) { /* taking 16 counts
* up to 15 [hs] */
if (mem_table[i].mem_index == (pos3 & ~MEM_MASK)) {
dev->mem_start = mem_table[i].mem_start;
dev->mem_start = (unsigned long)
mca_device_transform_memory(mca_dev, (void *)mem_table[i].mem_start);
num_pages = mem_table[i].num_pages;
}
}
......@@ -229,7 +242,8 @@ int __init ultramca_probe(struct net_device *dev)
case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A:
case _efe5_IBM_PS2_Adapter_A_for_Ethernet:
{
dev->mem_start = ((pos3 & 0xfc) * 0x1000);
dev->mem_start = (unsigned long)
mca_device_transform_memory(mca_dev, (void *)((pos3 & 0xfc) * 0x1000));
num_pages = 0x40;
break;
}
......@@ -240,7 +254,8 @@ int __init ultramca_probe(struct net_device *dev)
* the index of the 0x2000 step.
* beware different number of pages [hs]
*/
dev->mem_start = 0xc0000 + (0x2000 * (pos3 & 0xf));
dev->mem_start = (unsigned long)
mca_device_transform_memory(mca_dev, (void *)(0xc0000 + (0x2000 * (pos3 & 0xf))));
num_pages = 0x20 + (2 * (pos3 & 0x10));
break;
}
......@@ -258,7 +273,7 @@ int __init ultramca_probe(struct net_device *dev)
printk(KERN_INFO "%s: Parameters: %#3x,", dev->name, ioaddr);
for (i = 0; i < 6; i++)
printk(KERN_INFO " %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
/* Switch from the station address to the alternate register set
* and read the useful registers there.
......@@ -281,10 +296,11 @@ int __init ultramca_probe(struct net_device *dev)
*/
if (ethdev_init(dev)) {
printk (KERN_INFO ", no memory for dev->priv.\n");
printk (", no memory for dev->priv.\n");
release_region(ioaddr, ULTRA_IO_EXTENT);
return -ENOMEM;
}
gen_dev->driver_data = dev;
/* The 8390 isn't at the base address, so fake the offset
*/
......@@ -301,7 +317,7 @@ int __init ultramca_probe(struct net_device *dev)
dev->mem_end = ei_status.rmem_end =
dev->mem_start + (ei_status.stop_page - START_PG) * 256;
printk(KERN_INFO ", IRQ %d memory %#lx-%#lx.\n",
printk(", IRQ %d memory %#lx-%#lx.\n",
dev->irq, dev->mem_start, dev->mem_end - 1);
ei_status.reset_8390 = &ultramca_reset_8390;
......@@ -313,6 +329,7 @@ int __init ultramca_probe(struct net_device *dev)
dev->open = &ultramca_open;
dev->stop = &ultramca_close_card;
NS8390_init(dev, 0);
return 0;
......@@ -426,69 +443,48 @@ static int ultramca_close_card(struct net_device *dev)
return 0;
}
static int ultramca_remove(struct device *gen_dev)
{
struct mca_device *mca_dev = to_mca_device(gen_dev);
struct net_device *dev = (struct net_device *)gen_dev->driver_data;
#ifdef MODULE
#undef MODULE /* don't want to bother now! */
if(dev && dev->priv) {
/* NB: ultra_close_card() does free_irq */
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
#define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */
mca_device_set_claim(mca_dev, 0);
release_region(ioaddr, ULTRA_IO_EXTENT);
unregister_netdev(dev);
kfree(dev->priv);
}
return 0;
}
static struct net_device dev_ultra[MAX_ULTRAMCA_CARDS];
static int io[MAX_ULTRAMCA_CARDS];
static int irq[MAX_ULTRAMCA_CARDS];
MODULE_LICENSE("GPL");
MODULE_PARM(io, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS) "i");
MODULE_PARM_DESC(io, "SMC Ultra/EtherEZ MCA I/O base address(es)");
MODULE_PARM_DESC(irq, "SMC Ultra/EtherEZ MCA IRQ number(s)");
static struct mca_driver ultra_driver = {
.id_table = smc_mca_adapter_ids,
.driver = {
.name = "smc-mca",
.bus = &mca_bus_type,
.probe = ultramca_probe,
.remove = ultramca_remove,
}
};
int init_module(void)
static int __init ultramca_init_module(void)
{
int this_dev, found = 0;
if(!MCA_bus)
return -ENXIO;
for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) {
struct net_device *dev = &dev_ultra[this_dev];
dev->irq = irq[this_dev];
dev->base_addr = io[this_dev];
dev->init = ultramca_probe;
mca_register_driver(&ultra_driver);
if (register_netdev(dev) != 0) {
if (found != 0) { /* Got at least one. */
return 0;
}
printk(KERN_NOTICE "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
return -ENXIO;
}
found++;
}
return 0;
return ultra_found ? 0 : -ENXIO;
}
void cleanup_module(void)
static void __exit ultramca_cleanup_module(void)
{
int this_dev;
for (this_dev = 0; this_dev < MAX_ULTRAMCA_CARDS; this_dev++) {
struct net_device *dev = &dev_ultra[this_dev];
if (dev->priv != NULL) {
void *priv = dev->priv;
/* NB: ultra_close_card() does free_irq */
int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
mca_mark_as_unused(ei_status.priv);
release_region(ioaddr, ULTRA_IO_EXTENT);
unregister_netdev(dev);
kfree(priv);
}
}
mca_unregister_driver(&ultra_driver);
}
#endif /* MODULE */
module_init(ultramca_init_module);
module_exit(ultramca_cleanup_module);
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -Wall -O6 -I/usr/src/linux/net/inet -c smc-mca.c"
* version-control: t
* kept-new-versions: 5
* c-indent-level: 8
* tab-width: 8
* End:
*/
......@@ -105,6 +105,7 @@
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mca.h>
#include <asm/dma.h>
......@@ -182,131 +183,140 @@ param_setup(char *string)
__setup("NCR_D700=", param_setup);
#endif
/* private stack allocated structure for passing device information from
* detect to probe */
struct NCR_700_info {
Scsi_Host_Template *tpnt;
int found;
};
/* Detect a D700 card. Note, because of the set up---the chips are
* essentially connectecd to the MCA bus independently, it is easier
* to set them up as two separate host adapters, rather than one
* adapter with two channels */
STATIC int __init
D700_detect(Scsi_Host_Template *tpnt)
static int
NCR_D700_probe(struct device *dev)
{
int slot = 0;
int found = 0;
int differential;
int banner = 1;
if(!MCA_bus)
return 0;
#ifdef MODULE
if(NCR_D700)
param_setup(NCR_D700);
#endif
static int banner = 1;
struct mca_device *mca_dev = to_mca_device(dev);
int slot = mca_dev->slot;
struct NCR_700_info *info = to_mca_driver(dev->driver)->driver_data;
int found = 0;
int irq, i;
int pos3j, pos3k, pos3a, pos3b, pos4;
__u32 base_addr, offset_addr;
struct Scsi_Host *host = NULL;
/* enable board interrupt */
pos4 = mca_device_read_pos(mca_dev, 4);
pos4 |= 0x4;
mca_device_write_pos(mca_dev, 4, pos4);
mca_device_write_pos(mca_dev, 6, 9);
pos3j = mca_device_read_pos(mca_dev, 3);
mca_device_write_pos(mca_dev, 6, 10);
pos3k = mca_device_read_pos(mca_dev, 3);
mca_device_write_pos(mca_dev, 6, 0);
pos3a = mca_device_read_pos(mca_dev, 3);
mca_device_write_pos(mca_dev, 6, 1);
pos3b = mca_device_read_pos(mca_dev, 3);
base_addr = ((pos3j << 8) | pos3k) & 0xfffffff0;
offset_addr = ((pos3a << 8) | pos3b) & 0xffffff70;
irq = (pos4 & 0x3) + 11;
if(irq >= 13)
irq++;
if(banner) {
printk(KERN_NOTICE "NCR D700: Driver Version " NCR_D700_VERSION "\n"
"NCR D700: Copyright (c) 2001 by James.Bottomley@HansenPartnership.com\n"
"NCR D700:\n");
banner = 0;
}
/* now do the bus related transforms */
irq = mca_device_transform_irq(mca_dev, irq);
base_addr = mca_device_transform_ioport(mca_dev, base_addr);
offset_addr = mca_device_transform_ioport(mca_dev, offset_addr);
printk(KERN_NOTICE "NCR D700: found in slot %d irq = %d I/O base = 0x%x\n", slot, irq, offset_addr);
info->tpnt->proc_name = "NCR_D700";
/*outb(BOARD_RESET, base_addr);*/
/* clear any pending interrupts */
(void)inb(base_addr + 0x08);
/* get modctl, used later for setting diff bits */
switch(differential = (inb(base_addr + 0x08) >> 6)) {
case 0x00:
/* only SIOP1 differential */
differential = 0x02;
break;
case 0x01:
/* Both SIOPs differential */
differential = 0x03;
break;
case 0x03:
/* No SIOPs differential */
differential = 0x00;
break;
default:
printk(KERN_ERR "D700: UNEXPECTED DIFFERENTIAL RESULT 0x%02x\n",
differential);
differential = 0x00;
break;
}
for(slot = 0; (slot = mca_find_adapter(NCR_D700_MCA_ID, slot)) != MCA_NOTFOUND; slot++) {
int irq, i;
int pos3j, pos3k, pos3a, pos3b, pos4;
__u32 base_addr, offset_addr;
struct Scsi_Host *host = NULL;
/* enable board interrupt */
pos4 = mca_read_pos(slot, 4);
pos4 |= 0x4;
mca_write_pos(slot, 4, pos4);
mca_write_pos(slot, 6, 9);
pos3j = mca_read_pos(slot, 3);
mca_write_pos(slot, 6, 10);
pos3k = mca_read_pos(slot, 3);
mca_write_pos(slot, 6, 0);
pos3a = mca_read_pos(slot, 3);
mca_write_pos(slot, 6, 1);
pos3b = mca_read_pos(slot, 3);
base_addr = ((pos3j << 8) | pos3k) & 0xfffffff0;
offset_addr = ((pos3a << 8) | pos3b) & 0xffffff70;
irq = (pos4 & 0x3) + 11;
if(irq >= 13)
irq++;
if(banner) {
printk(KERN_NOTICE "NCR D700: Driver Version " NCR_D700_VERSION "\n"
"NCR D700: Copyright (c) 2001 by James.Bottomley@HansenPartnership.com\n"
"NCR D700:\n");
banner = 0;
/* plumb in both 700 chips */
for(i=0; i<2; i++) {
__u32 region = offset_addr | (0x80 * i);
struct NCR_700_Host_Parameters *hostdata =
kmalloc(sizeof(struct NCR_700_Host_Parameters),
GFP_KERNEL);
if(hostdata == NULL) {
printk(KERN_ERR "NCR D700: Failed to allocate host data for channel %d, detatching\n", i);
continue;
}
printk(KERN_NOTICE "NCR D700: found in slot %d irq = %d I/O base = 0x%x\n", slot, irq, offset_addr);
tpnt->proc_name = "NCR_D700";
/*outb(BOARD_RESET, base_addr);*/
/* clear any pending interrupts */
(void)inb(base_addr + 0x08);
/* get modctl, used later for setting diff bits */
switch(differential = (inb(base_addr + 0x08) >> 6)) {
case 0x00:
/* only SIOP1 differential */
differential = 0x02;
break;
case 0x01:
/* Both SIOPs differential */
differential = 0x03;
break;
case 0x03:
/* No SIOPs differential */
differential = 0x00;
break;
default:
printk(KERN_ERR "D700: UNEXPECTED DIFFERENTIAL RESULT 0x%02x\n",
differential);
differential = 0x00;
break;
memset(hostdata, 0, sizeof(struct NCR_700_Host_Parameters));
if(request_region(region, 64, "NCR_D700") == NULL) {
printk(KERN_ERR "NCR D700: Failed to reserve IO region 0x%x\n", region);
kfree(hostdata);
continue;
}
/* plumb in both 700 chips */
for(i=0; i<2; i++) {
__u32 region = offset_addr | (0x80 * i);
struct NCR_700_Host_Parameters *hostdata =
kmalloc(sizeof(struct NCR_700_Host_Parameters),
GFP_KERNEL);
if(hostdata == NULL) {
printk(KERN_ERR "NCR D700: Failed to allocate host data for channel %d, detatching\n", i);
continue;
}
memset(hostdata, 0, sizeof(struct NCR_700_Host_Parameters));
if(request_region(region, 64, "NCR_D700") == NULL) {
printk(KERN_ERR "NCR D700: Failed to reserve IO region 0x%x\n", region);
kfree(hostdata);
continue;
}
/* Fill in the three required pieces of hostdata */
hostdata->base = region;
hostdata->differential = (((1<<i) & differential) != 0);
hostdata->clock = NCR_D700_CLOCK_MHZ;
/* and register the chip */
if((host = NCR_700_detect(tpnt, hostdata)) == NULL) {
kfree(hostdata);
release_region(host->base, 64);
continue;
}
host->irq = irq;
/* FIXME: Read this from SUS */
host->this_id = id_array[slot * 2 + i];
printk(KERN_NOTICE "NCR D700: SIOP%d, SCSI id is %d\n",
i, host->this_id);
if(request_irq(irq, NCR_700_intr, SA_SHIRQ, "NCR_D700", host)) {
printk(KERN_ERR "NCR D700, channel %d: irq problem, detatching\n", i);
scsi_unregister(host);
NCR_700_release(host);
continue;
}
found++;
mca_set_adapter_name(slot, "NCR D700 SCSI Adapter (version " NCR_D700_VERSION ")");
/* Fill in the three required pieces of hostdata */
hostdata->base = region;
hostdata->differential = (((1<<i) & differential) != 0);
hostdata->clock = NCR_D700_CLOCK_MHZ;
/* and register the chip */
if((host = NCR_700_detect(info->tpnt, hostdata)) == NULL) {
kfree(hostdata);
release_region(host->base, 64);
continue;
}
host->irq = irq;
/* FIXME: Read this from SUS */
host->this_id = id_array[slot * 2 + i];
printk(KERN_NOTICE "NCR D700: SIOP%d, SCSI id is %d\n",
i, host->this_id);
if(request_irq(irq, NCR_700_intr, SA_SHIRQ, "NCR_D700", host)) {
printk(KERN_ERR "NCR D700, channel %d: irq problem, detatching\n", i);
scsi_unregister(host);
NCR_700_release(host);
continue;
}
scsi_set_device(host, dev);
found++;
}
info->found += found;
if(found) {
mca_device_set_claim(mca_dev, 1);
strncpy(dev->name, "NCR_D700", sizeof(dev->name));
}
return found;
return found? 0 : -ENODEV;
}
......@@ -323,7 +333,38 @@ D700_release(struct Scsi_Host *host)
return 1;
}
static short NCR_D700_id_table[] = { NCR_D700_MCA_ID, 0 };
struct mca_driver NCR_D700_driver = {
.id_table = NCR_D700_id_table,
.driver = {
.name = "NCR_D700",
.bus = &mca_bus_type,
.probe = NCR_D700_probe,
},
};
STATIC int __init
D700_detect(Scsi_Host_Template *tpnt)
{
struct NCR_700_info info;
if(!MCA_bus)
return 0;
#ifdef MODULE
if(NCR_D700)
param_setup(NCR_D700);
#endif
info.tpnt = tpnt;
info.found = 0;
NCR_D700_driver.driver_data = &info;
mca_register_driver(&NCR_D700_driver);
return info.found;
}
static Scsi_Host_Template driver_template = NCR_D700_SCSI;
#include "scsi_module.c"
......
/* -*- mode: c; c-basic-offset: 8 -*- */
/* Platform specific MCA defines */
#ifndef _ASM_MCA_H
#define _ASM_MCA_H
/* Maximal number of MCA slots - actually, some machines have less, but
* they all have sufficient number of POS registers to cover 8.
*/
#define MCA_MAX_SLOT_NR 8
/* Most machines have only one MCA bus. The only multiple bus machines
* I know have at most two */
#define MAX_MCA_BUSSES 2
#define MCA_PRIMARY_BUS 0
#define MCA_SECONDARY_BUS 1
/* Dummy slot numbers on primary MCA for integrated functions */
#define MCA_INTEGSCSI (MCA_MAX_SLOT_NR)
#define MCA_INTEGVIDEO (MCA_MAX_SLOT_NR+1)
#define MCA_MOTHERBOARD (MCA_MAX_SLOT_NR+2)
/* Dummy POS values for integrated functions */
#define MCA_DUMMY_POS_START 0x10000
#define MCA_INTEGSCSI_POS (MCA_DUMMY_POS_START+1)
#define MCA_INTEGVIDEO_POS (MCA_DUMMY_POS_START+2)
#define MCA_MOTHERBOARD_POS (MCA_DUMMY_POS_START+3)
/* MCA registers */
#define MCA_MOTHERBOARD_SETUP_REG 0x94
#define MCA_ADAPTER_SETUP_REG 0x96
#define MCA_POS_REG(n) (0x100+(n))
#define MCA_ENABLED 0x01 /* POS 2, set if adapter enabled */
/* Max number of adapters, including both slots and various integrated
* things.
*/
#define MCA_NUMADAPTERS (MCA_MAX_SLOT_NR+3)
/* lock to protect access to the MCA registers */
extern spinlock_t mca_lock;
#endif
/* -*- mode: c; c-basic-offset: 8 -*- */
/* This is the function prototypes for the old legacy MCA interface
*
* Please move your driver to the new sysfs based one instead */
#ifndef _LINUX_MCA_LEGACY_H
#define _LINUX_MCA_LEGACY_H
/* MCA_NOTFOUND is an error condition. The other two indicate
* motherboard POS registers contain the adapter. They might be
* returned by the mca_find_adapter() function, and can be used as
* arguments to mca_read_stored_pos(). I'm not going to allow direct
* access to the motherboard registers until we run across an adapter
* that requires it. We don't know enough about them to know if it's
* safe.
*
* See Documentation/mca.txt or one of the existing drivers for
* more information.
*/
#define MCA_NOTFOUND (-1)
/* Returns the slot of the first enabled adapter matching id. User can
* specify a starting slot beyond zero, to deal with detecting multiple
* devices. Returns MCA_NOTFOUND if id not found. Also checks the
* integrated adapters.
*/
extern int mca_find_adapter(int id, int start);
extern int mca_find_unused_adapter(int id, int start);
/* adapter state info - returns 0 if no */
extern int mca_isadapter(int slot);
extern int mca_isenabled(int slot);
extern int mca_is_adapter_used(int slot);
extern int mca_mark_as_used(int slot);
extern void mca_mark_as_unused(int slot);
/* gets a byte out of POS register (stored in memory) */
extern unsigned char mca_read_stored_pos(int slot, int reg);
/* This can be expanded later. Right now, it gives us a way of
* getting meaningful information into the MCA_info structure,
* so we can have a more interesting /proc/mca.
*/
extern void mca_set_adapter_name(int slot, char* name);
extern char* mca_get_adapter_name(int slot);
/* These routines actually mess with the hardware POS registers. They
* temporarily disable the device (and interrupts), so make sure you know
* what you're doing if you use them. Furthermore, writing to a POS may
* result in two devices trying to share a resource, which in turn can
* result in multiple devices sharing memory spaces, IRQs, or even trashing
* hardware. YOU HAVE BEEN WARNED.
*
* You can only access slots with this. Motherboard registers are off
* limits.
*/
/* read a byte from the specified POS register. */
extern unsigned char mca_read_pos(int slot, int reg);
/* write a byte to the specified POS register. */
extern void mca_write_pos(int slot, int reg, unsigned char byte);
#endif
......@@ -6,6 +6,14 @@
#ifndef _LINUX_MCA_H
#define _LINUX_MCA_H
/* FIXME: This shouldn't happen, but we need everything that previously
* included mca.h to compile. Take it out later when the MCA #includes
* are sorted out */
#include <linux/device.h>
/* get the platform specific defines */
#include <asm/mca.h>
/* The detection of MCA bus is done in the real mode (using BIOS).
* The information is exported to the protected code, where this
* variable is set to one in case MCA bus was detected.
......@@ -14,58 +22,6 @@
extern int MCA_bus;
#endif
/* Maximal number of MCA slots - actually, some machines have less, but
* they all have sufficient number of POS registers to cover 8.
*/
#define MCA_MAX_SLOT_NR 8
/* MCA_NOTFOUND is an error condition. The other two indicate
* motherboard POS registers contain the adapter. They might be
* returned by the mca_find_adapter() function, and can be used as
* arguments to mca_read_stored_pos(). I'm not going to allow direct
* access to the motherboard registers until we run across an adapter
* that requires it. We don't know enough about them to know if it's
* safe.
*
* See Documentation/mca.txt or one of the existing drivers for
* more information.
*/
#define MCA_NOTFOUND (-1)
#define MCA_INTEGSCSI (MCA_MAX_SLOT_NR)
#define MCA_INTEGVIDEO (MCA_MAX_SLOT_NR+1)
#define MCA_MOTHERBOARD (MCA_MAX_SLOT_NR+2)
/* Max number of adapters, including both slots and various integrated
* things.
*/
#define MCA_NUMADAPTERS (MCA_MAX_SLOT_NR+3)
/* Returns the slot of the first enabled adapter matching id. User can
* specify a starting slot beyond zero, to deal with detecting multiple
* devices. Returns MCA_NOTFOUND if id not found. Also checks the
* integrated adapters.
*/
extern int mca_find_adapter(int id, int start);
extern int mca_find_unused_adapter(int id, int start);
/* adapter state info - returns 0 if no */
extern int mca_isadapter(int slot);
extern int mca_isenabled(int slot);
extern int mca_is_adapter_used(int slot);
extern int mca_mark_as_used(int slot);
extern void mca_mark_as_unused(int slot);
/* gets a byte out of POS register (stored in memory) */
extern unsigned char mca_read_stored_pos(int slot, int reg);
/* This can be expanded later. Right now, it gives us a way of
* getting meaningful information into the MCA_info structure,
* so we can have a more interesting /proc/mca.
*/
extern void mca_set_adapter_name(int slot, char* name);
extern char* mca_get_adapter_name(int slot);
/* This sets up an information callback for /proc/mca/slot?. The
* function is called with the buffer, slot, and device pointer (or
* some equally informative context information, or nothing, if you
......@@ -78,28 +34,115 @@ extern char* mca_get_adapter_name(int slot);
* nastiness.
*/
typedef int (*MCA_ProcFn)(char* buf, int slot, void* dev);
extern void mca_set_adapter_procfn(int slot, MCA_ProcFn, void* dev);
/* These routines actually mess with the hardware POS registers. They
* temporarily disable the device (and interrupts), so make sure you know
* what you're doing if you use them. Furthermore, writing to a POS may
* result in two devices trying to share a resource, which in turn can
* result in multiple devices sharing memory spaces, IRQs, or even trashing
* hardware. YOU HAVE BEEN WARNED.
*
* You can only access slots with this. Motherboard registers are off
* limits.
*/
/* read a byte from the specified POS register. */
extern unsigned char mca_read_pos(int slot, int reg);
/* write a byte to the specified POS register. */
extern void mca_write_pos(int slot, int reg, unsigned char byte);
/* Should only be called by the NMI interrupt handler, this will do some
* fancy stuff to figure out what might have generated a NMI.
*/
extern void mca_handle_nmi(void);
enum MCA_AdapterStatus {
MCA_ADAPTER_NORMAL = 0,
MCA_ADAPTER_NONE = 1,
MCA_ADAPTER_DISABLED = 2,
MCA_ADAPTER_ERROR = 3
};
struct mca_device {
u64 dma_mask;
int pos_id;
int slot;
/* index into id_table, set by the bus match routine */
int index;
/* is there a driver installed? 0 - No, 1 - Yes */
int driver_loaded;
/* POS registers */
unsigned char pos[8];
/* if a pseudo adapter of the motherboard, this is the motherboard
* register value to use for setup cycles */
short pos_register;
enum MCA_AdapterStatus status;
#ifdef CONFIG_MCA_PROC_FS
/* name of the proc/mca file */
char procname[8];
/* /proc info callback */
MCA_ProcFn procfn;
/* device/context info for proc callback */
void *proc_dev;
#endif
struct device dev;
};
#define to_mca_device(mdev) container_of(mdev, struct mca_device, dev)
struct mca_bus_accessor_functions {
unsigned char (*mca_read_pos)(struct mca_device *, int reg);
void (*mca_write_pos)(struct mca_device *, int reg,
unsigned char byte);
int (*mca_transform_irq)(struct mca_device *, int irq);
int (*mca_transform_ioport)(struct mca_device *,
int region);
void * (*mca_transform_memory)(struct mca_device *,
void *memory);
};
struct mca_bus {
u64 default_dma_mask;
int number;
struct mca_bus_accessor_functions f;
struct device dev;
};
#define to_mca_bus(mdev) container_of(mdev, struct mca_bus, dev)
struct mca_driver {
const short *id_table;
void *driver_data;
struct device_driver driver;
};
#define to_mca_driver(mdriver) container_of(mdriver, struct mca_driver, driver)
/* Ongoing supported API functions */
extern struct mca_device *mca_find_device_by_slot(int slot);
extern int mca_system_init(void);
extern struct mca_bus *mca_attach_bus(int);
extern unsigned char mca_device_read_stored_pos(struct mca_device *mca_dev,
int reg);
extern unsigned char mca_device_read_pos(struct mca_device *mca_dev, int reg);
extern void mca_device_write_pos(struct mca_device *mca_dev, int reg,
unsigned char byte);
extern int mca_device_transform_irq(struct mca_device *mca_dev, int irq);
extern int mca_device_transform_ioport(struct mca_device *mca_dev, int port);
extern void *mca_device_transform_memory(struct mca_device *mca_dev,
void *mem);
extern int mca_device_claimed(struct mca_device *mca_dev);
extern void mca_device_set_claim(struct mca_device *mca_dev, int val);
extern enum MCA_AdapterStatus mca_device_status(struct mca_device *mca_dev);
extern struct bus_type mca_bus_type;
extern int mca_register_driver(struct mca_driver *drv);
extern void mca_unregister_driver(struct mca_driver *drv);
/* WARNING: only called by the boot time device setup */
extern int mca_register_device(int bus, struct mca_device *mca_dev);
#ifdef CONFIG_MCA_LEGACY
#include <linux/mca-legacy.h>
#endif
#ifdef CONFIG_MCA_PROC_FS
extern void mca_do_proc_init(void);
extern void mca_set_adapter_procfn(int slot, MCA_ProcFn, void* dev);
#else
static inline void mca_do_proc_init(void)
{
}
static inline void mca_set_adapter_procfn(int slot, MCA_ProcFn *fn, void* dev)
{
}
#endif
#endif /* _LINUX_MCA_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment