summaryrefslogtreecommitdiff
path: root/drivers/mca
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mca')
-rw-r--r--drivers/mca/Kconfig14
-rw-r--r--drivers/mca/Makefile7
-rw-r--r--drivers/mca/mca-bus.c149
-rw-r--r--drivers/mca/mca-device.c217
-rw-r--r--drivers/mca/mca-driver.c50
-rw-r--r--drivers/mca/mca-legacy.c348
-rw-r--r--drivers/mca/mca-proc.c249
7 files changed, 1034 insertions, 0 deletions
diff --git a/drivers/mca/Kconfig b/drivers/mca/Kconfig
new file mode 100644
index 000000000000..a7a0220ab4bd
--- /dev/null
+++ b/drivers/mca/Kconfig
@@ -0,0 +1,14 @@
+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.
diff --git a/drivers/mca/Makefile b/drivers/mca/Makefile
new file mode 100644
index 000000000000..0794b122520e
--- /dev/null
+++ b/drivers/mca/Makefile
@@ -0,0 +1,7 @@
+# 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
+
diff --git a/drivers/mca/mca-bus.c b/drivers/mca/mca-bus.c
new file mode 100644
index 000000000000..ff9be67c2a15
--- /dev/null
+++ b/drivers/mca/mca-bus.c
@@ -0,0 +1,149 @@
+/* -*- 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 */
+static 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)
+{
+ /* four digits, \n and trailing \0 */
+ struct mca_device *mca_dev = to_mca_device(dev);
+ int len;
+
+ if(mca_dev->pos_id < MCA_DUMMY_POS_START)
+ len = sprintf(buf, "%04x\n", mca_dev->pos_id);
+ else
+ len = sprintf(buf, "none\n");
+ return len;
+}
+static ssize_t mca_show_pos(struct device *dev, char *buf)
+{
+ /* enough for 8 two byte hex chars plus space and new line */
+ int j, len=0;
+ struct mca_device *mca_dev = to_mca_device(dev);
+
+ for(j=0; j<8; j++)
+ len += sprintf(buf+len, "%02x ", mca_dev->pos[j]);
+ /* change last trailing space to new line */
+ buf[len-1] = '\n';
+ 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;
+ mca_dev->dev.coherent_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->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);
+}
diff --git a/drivers/mca/mca-device.c b/drivers/mca/mca-device.c
new file mode 100644
index 000000000000..76d430aa243f
--- /dev/null
+++ b/drivers/mca/mca-device.c
@@ -0,0 +1,217 @@
+/* -*- 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;
+}
+EXPORT_SYMBOL(mca_device_status);
+
+/**
+ * mca_device_set_name - set the name of the device
+ * @mca_device: device to set the name of
+ * @name: name to set
+ */
+void mca_device_set_name(struct mca_device *mca_dev, const char *name)
+{
+ if(!mca_dev)
+ return;
+
+ strlcpy(mca_dev->name, name, sizeof(mca_dev->name));
+}
+EXPORT_SYMBOL(mca_device_set_name);
diff --git a/drivers/mca/mca-driver.c b/drivers/mca/mca-driver.c
new file mode 100644
index 000000000000..2223466b3d8a
--- /dev/null
+++ b/drivers/mca/mca-driver.c
@@ -0,0 +1,50 @@
+/* -*- 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;
+
+ if (MCA_bus) {
+ 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)
+{
+ if (MCA_bus)
+ driver_unregister(&mca_drv->driver);
+}
+EXPORT_SYMBOL(mca_unregister_driver);
diff --git a/drivers/mca/mca-legacy.c b/drivers/mca/mca-legacy.c
new file mode 100644
index 000000000000..af56313ba0af
--- /dev/null
+++ b/drivers/mca/mca-legacy.c
@@ -0,0 +1,348 @@
+/* -*- 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-legacy.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 (!MCA_bus || 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;
+
+ mca_device_set_name(mca_dev, name);
+}
+EXPORT_SYMBOL(mca_set_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);
+
diff --git a/drivers/mca/mca-proc.c b/drivers/mca/mca-proc.c
new file mode 100644
index 000000000000..33d5e0820cc5
--- /dev/null
+++ b/drivers/mca/mca-proc.c
@@ -0,0 +1,249 @@
+/* -*- 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->name : "");
+ return len;
+}
+
+static 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->name[0]) {
+
+ /* Drivers might register a name without /proc handler... */
+
+ len += sprintf(buf+len, "Adapter Name: %s\n",
+ mca_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_device_status(mca_dev) == MCA_ADAPTER_NORMAL ?
+ "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_device_claimed(mca_dev) ? "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++) {
+ enum MCA_AdapterStatus status;
+ 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");
+
+ status = mca_device_status(mca_dev);
+ if (status != MCA_ADAPTER_NORMAL &&
+ status != MCA_ADAPTER_DISABLED)
+ 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);