aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMatt Porter <mporter@kernel.crashing.org>2005-11-07 01:00:15 -0800
committerLinus Torvalds <torvalds@g5.osdl.org>2005-11-07 07:53:46 -0800
commit394b701ce4fbfde919a9bcbf84cb4820a7c6d47c (patch)
treea397fff4043a9810e753fb81224349bd36db45b8 /drivers
parentd217d5450f11d8c907c0458d175b0dc999b4d06d (diff)
[PATCH] RapidIO support: core base
Adds a RapidIO subsystem to the kernel. RIO is a switched fabric interconnect used in higher-end embedded applications. The curious can look at the specs over at http://www.rapidio.org The core code implements enumeration/discovery, management of devices/resources, and interfaces for RIO drivers. There's a lot more to do to take advantages of all the hardware features. However, this should provide a good base for folks with RIO hardware to start contributing. Signed-off-by: Matt Porter <mporter@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/rapidio/Kconfig18
-rw-r--r--drivers/rapidio/Makefile6
-rw-r--r--drivers/rapidio/rio-access.c175
-rw-r--r--drivers/rapidio/rio-driver.c229
-rw-r--r--drivers/rapidio/rio-sysfs.c230
-rw-r--r--drivers/rapidio/rio.c503
-rw-r--r--drivers/rapidio/rio.h57
8 files changed, 1219 insertions, 0 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 61c64f76836..fac1e160309 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_PCI) += pci/ usb/
obj-$(CONFIG_PARISC) += parisc/
+obj-$(CONFIG_RAPIDIO) += rapidio/
obj-y += video/
obj-$(CONFIG_ACPI) += acpi/
# PnP must come after ACPI since it will eventually need to check if acpi
diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig
new file mode 100644
index 00000000000..0b2d2c3579a
--- /dev/null
+++ b/drivers/rapidio/Kconfig
@@ -0,0 +1,18 @@
+#
+# RapidIO configuration
+#
+config RAPIDIO_8_BIT_TRANSPORT
+ bool "8-bit transport addressing"
+ depends on RAPIDIO
+ ---help---
+ By default, the kernel assumes a 16-bit addressed RapidIO
+ network. By selecting this option, the kernel will support
+ an 8-bit addressed network.
+
+config RAPIDIO_DISC_TIMEOUT
+ int "Discovery timeout duration (seconds)"
+ depends on RAPIDIO
+ default "30"
+ ---help---
+ Amount of time a discovery node waits for a host to complete
+ enumeration beforing giving up.
diff --git a/drivers/rapidio/Makefile b/drivers/rapidio/Makefile
new file mode 100644
index 00000000000..7c0e1818de5
--- /dev/null
+++ b/drivers/rapidio/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for RapidIO interconnect services
+#
+obj-y += rio.o rio-access.o rio-driver.o rio-scan.o rio-sysfs.o
+
+obj-$(CONFIG_RAPIDIO) += switches/
diff --git a/drivers/rapidio/rio-access.c b/drivers/rapidio/rio-access.c
new file mode 100644
index 00000000000..b9fab2ae3a3
--- /dev/null
+++ b/drivers/rapidio/rio-access.c
@@ -0,0 +1,175 @@
+/*
+ * RapidIO configuration space access support
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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.
+ */
+
+#include <linux/rio.h>
+#include <linux/module.h>
+
+/*
+ * These interrupt-safe spinlocks protect all accesses to RIO
+ * configuration space and doorbell access.
+ */
+static spinlock_t rio_config_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t rio_doorbell_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Wrappers for all RIO configuration access functions. They just check
+ * alignment, do locking and call the low-level functions pointed to
+ * by rio_mport->ops.
+ */
+
+#define RIO_8_BAD 0
+#define RIO_16_BAD (offset & 1)
+#define RIO_32_BAD (offset & 3)
+
+/**
+ * RIO_LOP_READ - Generate rio_local_read_config_* functions
+ * @size: Size of configuration space read (8, 16, 32 bits)
+ * @type: C type of value argument
+ * @len: Length of configuration space read (1, 2, 4 bytes)
+ *
+ * Generates rio_local_read_config_* functions used to access
+ * configuration space registers on the local device.
+ */
+#define RIO_LOP_READ(size,type,len) \
+int __rio_local_read_config_##size \
+ (struct rio_mport *mport, u32 offset, type *value) \
+{ \
+ int res; \
+ unsigned long flags; \
+ u32 data = 0; \
+ if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
+ spin_lock_irqsave(&rio_config_lock, flags); \
+ res = mport->ops->lcread(mport->id, offset, len, &data); \
+ *value = (type)data; \
+ spin_unlock_irqrestore(&rio_config_lock, flags); \
+ return res; \
+}
+
+/**
+ * RIO_LOP_WRITE - Generate rio_local_write_config_* functions
+ * @size: Size of configuration space write (8, 16, 32 bits)
+ * @type: C type of value argument
+ * @len: Length of configuration space write (1, 2, 4 bytes)
+ *
+ * Generates rio_local_write_config_* functions used to access
+ * configuration space registers on the local device.
+ */
+#define RIO_LOP_WRITE(size,type,len) \
+int __rio_local_write_config_##size \
+ (struct rio_mport *mport, u32 offset, type value) \
+{ \
+ int res; \
+ unsigned long flags; \
+ if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
+ spin_lock_irqsave(&rio_config_lock, flags); \
+ res = mport->ops->lcwrite(mport->id, offset, len, value); \
+ spin_unlock_irqrestore(&rio_config_lock, flags); \
+ return res; \
+}
+
+RIO_LOP_READ(8, u8, 1)
+RIO_LOP_READ(16, u16, 2)
+RIO_LOP_READ(32, u32, 4)
+RIO_LOP_WRITE(8, u8, 1)
+RIO_LOP_WRITE(16, u16, 2)
+RIO_LOP_WRITE(32, u32, 4)
+
+EXPORT_SYMBOL_GPL(__rio_local_read_config_8);
+EXPORT_SYMBOL_GPL(__rio_local_read_config_16);
+EXPORT_SYMBOL_GPL(__rio_local_read_config_32);
+EXPORT_SYMBOL_GPL(__rio_local_write_config_8);
+EXPORT_SYMBOL_GPL(__rio_local_write_config_16);
+EXPORT_SYMBOL_GPL(__rio_local_write_config_32);
+
+/**
+ * RIO_OP_READ - Generate rio_mport_read_config_* functions
+ * @size: Size of configuration space read (8, 16, 32 bits)
+ * @type: C type of value argument
+ * @len: Length of configuration space read (1, 2, 4 bytes)
+ *
+ * Generates rio_mport_read_config_* functions used to access
+ * configuration space registers on the local device.
+ */
+#define RIO_OP_READ(size,type,len) \
+int rio_mport_read_config_##size \
+ (struct rio_mport *mport, u16 destid, u8 hopcount, u32 offset, type *value) \
+{ \
+ int res; \
+ unsigned long flags; \
+ u32 data = 0; \
+ if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
+ spin_lock_irqsave(&rio_config_lock, flags); \
+ res = mport->ops->cread(mport->id, destid, hopcount, offset, len, &data); \
+ *value = (type)data; \
+ spin_unlock_irqrestore(&rio_config_lock, flags); \
+ return res; \
+}
+
+/**
+ * RIO_OP_WRITE - Generate rio_mport_write_config_* functions
+ * @size: Size of configuration space write (8, 16, 32 bits)
+ * @type: C type of value argument
+ * @len: Length of configuration space write (1, 2, 4 bytes)
+ *
+ * Generates rio_mport_write_config_* functions used to access
+ * configuration space registers on the local device.
+ */
+#define RIO_OP_WRITE(size,type,len) \
+int rio_mport_write_config_##size \
+ (struct rio_mport *mport, u16 destid, u8 hopcount, u32 offset, type value) \
+{ \
+ int res; \
+ unsigned long flags; \
+ if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
+ spin_lock_irqsave(&rio_config_lock, flags); \
+ res = mport->ops->cwrite(mport->id, destid, hopcount, offset, len, value); \
+ spin_unlock_irqrestore(&rio_config_lock, flags); \
+ return res; \
+}
+
+RIO_OP_READ(8, u8, 1)
+RIO_OP_READ(16, u16, 2)
+RIO_OP_READ(32, u32, 4)
+RIO_OP_WRITE(8, u8, 1)
+RIO_OP_WRITE(16, u16, 2)
+RIO_OP_WRITE(32, u32, 4)
+
+EXPORT_SYMBOL_GPL(rio_mport_read_config_8);
+EXPORT_SYMBOL_GPL(rio_mport_read_config_16);
+EXPORT_SYMBOL_GPL(rio_mport_read_config_32);
+EXPORT_SYMBOL_GPL(rio_mport_write_config_8);
+EXPORT_SYMBOL_GPL(rio_mport_write_config_16);
+EXPORT_SYMBOL_GPL(rio_mport_write_config_32);
+
+/**
+ * rio_mport_send_doorbell - Send a doorbell message
+ *
+ * @mport: RIO master port
+ * @destid: RIO device destination ID
+ * @data: Doorbell message data
+ *
+ * Send a doorbell message to a RIO device. The doorbell message
+ * has a 16-bit info field provided by the data argument.
+ */
+int rio_mport_send_doorbell(struct rio_mport *mport, u16 destid, u16 data)
+{
+ int res;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rio_doorbell_lock, flags);
+ res = mport->ops->dsend(mport->id, destid, data);
+ spin_unlock_irqrestore(&rio_doorbell_lock, flags);
+
+ return res;
+}
+
+EXPORT_SYMBOL_GPL(rio_mport_send_doorbell);
diff --git a/drivers/rapidio/rio-driver.c b/drivers/rapidio/rio-driver.c
new file mode 100644
index 00000000000..dc749609699
--- /dev/null
+++ b/drivers/rapidio/rio-driver.c
@@ -0,0 +1,229 @@
+/*
+ * RapidIO driver support
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/rio.h>
+#include <linux/rio_ids.h>
+
+#include "rio.h"
+
+/**
+ * rio_match_device - Tell if a RIO device has a matching RIO device id structure
+ * @id: the RIO device id structure to match against
+ * @rdev: the RIO device structure to match against
+ *
+ * Used from driver probe and bus matching to check whether a RIO device
+ * matches a device id structure provided by a RIO driver. Returns the
+ * matching &struct rio_device_id or %NULL if there is no match.
+ */
+static const struct rio_device_id *rio_match_device(const struct rio_device_id
+ *id,
+ const struct rio_dev *rdev)
+{
+ while (id->vid || id->asm_vid) {
+ if (((id->vid == RIO_ANY_ID) || (id->vid == rdev->vid)) &&
+ ((id->did == RIO_ANY_ID) || (id->did == rdev->did)) &&
+ ((id->asm_vid == RIO_ANY_ID)
+ || (id->asm_vid == rdev->asm_vid))
+ && ((id->asm_did == RIO_ANY_ID)
+ || (id->asm_did == rdev->asm_did)))
+ return id;
+ id++;
+ }
+ return NULL;
+}
+
+/**
+ * rio_dev_get - Increments the reference count of the RIO device structure
+ *
+ * @rdev: RIO device being referenced
+ *
+ * Each live reference to a device should be refcounted.
+ *
+ * Drivers for RIO devices should normally record such references in
+ * their probe() methods, when they bind to a device, and release
+ * them by calling rio_dev_put(), in their disconnect() methods.
+ */
+struct rio_dev *rio_dev_get(struct rio_dev *rdev)
+{
+ if (rdev)
+ get_device(&rdev->dev);
+
+ return rdev;
+}
+
+/**
+ * rio_dev_put - Release a use of the RIO device structure
+ *
+ * @rdev: RIO device being disconnected
+ *
+ * Must be called when a user of a device is finished with it.
+ * When the last user of the device calls this function, the
+ * memory of the device is freed.
+ */
+void rio_dev_put(struct rio_dev *rdev)
+{
+ if (rdev)
+ put_device(&rdev->dev);
+}
+
+/**
+ * rio_device_probe - Tell if a RIO device structure has a matching RIO
+ * device id structure
+ * @id: the RIO device id structure to match against
+ * @dev: the RIO device structure to match against
+ *
+ * return 0 and set rio_dev->driver when drv claims rio_dev, else error
+ */
+static int rio_device_probe(struct device *dev)
+{
+ struct rio_driver *rdrv = to_rio_driver(dev->driver);
+ struct rio_dev *rdev = to_rio_dev(dev);
+ int error = -ENODEV;
+ const struct rio_device_id *id;
+
+ if (!rdev->driver && rdrv->probe) {
+ if (!rdrv->id_table)
+ return error;
+ id = rio_match_device(rdrv->id_table, rdev);
+ rio_dev_get(rdev);
+ if (id)
+ error = rdrv->probe(rdev, id);
+ if (error >= 0) {
+ rdev->driver = rdrv;
+ error = 0;
+ rio_dev_put(rdev);
+ }
+ }
+ return error;
+}
+
+/**
+ * rio_device_remove - Remove a RIO device from the system
+ *
+ * @dev: the RIO device structure to match against
+ *
+ * Remove a RIO device from the system. If it has an associated
+ * driver, then run the driver remove() method. Then update
+ * the reference count.
+ */
+static int rio_device_remove(struct device *dev)
+{
+ struct rio_dev *rdev = to_rio_dev(dev);
+ struct rio_driver *rdrv = rdev->driver;
+
+ if (rdrv) {
+ if (rdrv->remove)
+ rdrv->remove(rdev);
+ rdev->driver = NULL;
+ }
+
+ rio_dev_put(rdev);
+
+ return 0;
+}
+
+/**
+ * rio_register_driver - register a new RIO driver
+ * @rdrv: the RIO driver structure to register
+ *
+ * Adds a &struct rio_driver to the list of registered drivers
+ * Returns a negative value on error, otherwise 0. If no error
+ * occurred, the driver remains registered even if no device
+ * was claimed during registration.
+ */
+int rio_register_driver(struct rio_driver *rdrv)
+{
+ /* initialize common driver fields */
+ rdrv->driver.name = rdrv->name;
+ rdrv->driver.bus = &rio_bus_type;
+ rdrv->driver.probe = rio_device_probe;
+ rdrv->driver.remove = rio_device_remove;
+
+ /* register with core */
+ return driver_register(&rdrv->driver);
+}
+
+/**
+ * rio_unregister_driver - unregister a RIO driver
+ * @rdrv: the RIO driver structure to unregister
+ *
+ * Deletes the &struct rio_driver from the list of registered RIO
+ * drivers, gives it a chance to clean up by calling its remove()
+ * function for each device it was responsible for, and marks those
+ * devices as driverless.
+ */
+void rio_unregister_driver(struct rio_driver *rdrv)
+{
+ driver_unregister(&rdrv->driver);
+}
+
+/**
+ * rio_match_bus - Tell if a RIO device structure has a matching RIO
+ * driver device id structure
+ * @dev: the standard device structure to match against
+ * @drv: the standard driver structure containing the ids to match against
+ *
+ * Used by a driver to check whether a RIO device present in the
+ * system is in its list of supported devices. Returns 1 if
+ * there is a matching &struct rio_device_id or 0 if there is
+ * no match.
+ */
+static int rio_match_bus(struct device *dev, struct device_driver *drv)
+{
+ struct rio_dev *rdev = to_rio_dev(dev);
+ struct rio_driver *rdrv = to_rio_driver(drv);
+ const struct rio_device_id *id = rdrv->id_table;
+ const struct rio_device_id *found_id;
+
+ if (!id)
+ goto out;
+
+ found_id = rio_match_device(id, rdev);
+
+ if (found_id)
+ return 1;
+
+ out:return 0;
+}
+
+static struct device rio_bus = {
+ .bus_id = "rapidio",
+};
+
+struct bus_type rio_bus_type = {
+ .name = "rapidio",
+ .match = rio_match_bus,
+ .dev_attrs = rio_dev_attrs
+};
+
+/**
+ * rio_bus_init - Register the RapidIO bus with the device model
+ *
+ * Registers the RIO bus device and RIO bus type with the Linux
+ * device model.
+ */
+static int __init rio_bus_init(void)
+{
+ if (device_register(&rio_bus) < 0)
+ printk("RIO: failed to register RIO bus device\n");
+ return bus_register(&rio_bus_type);
+}
+
+postcore_initcall(rio_bus_init);
+
+EXPORT_SYMBOL_GPL(rio_register_driver);
+EXPORT_SYMBOL_GPL(rio_unregister_driver);
+EXPORT_SYMBOL_GPL(rio_bus_type);
+EXPORT_SYMBOL_GPL(rio_dev_get);
+EXPORT_SYMBOL_GPL(rio_dev_put);
diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c
new file mode 100644
index 00000000000..73218a37506
--- /dev/null
+++ b/drivers/rapidio/rio-sysfs.c
@@ -0,0 +1,230 @@
+/*
+ * RapidIO sysfs attributes and support
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/stat.h>
+
+#include "rio.h"
+
+/* Sysfs support */
+#define rio_config_attr(field, format_string) \
+static ssize_t \
+ field##_show(struct device *dev, char *buf) \
+{ \
+ struct rio_dev *rdev = to_rio_dev(dev); \
+ \
+ return sprintf(buf, format_string, rdev->field); \
+} \
+
+rio_config_attr(did, "0x%04x\n");
+rio_config_attr(vid, "0x%04x\n");
+rio_config_attr(device_rev, "0x%08x\n");
+rio_config_attr(asm_did, "0x%04x\n");
+rio_config_attr(asm_vid, "0x%04x\n");
+rio_config_attr(asm_rev, "0x%04x\n");
+
+static ssize_t routes_show(struct device *dev, char *buf)
+{
+ struct rio_dev *rdev = to_rio_dev(dev);
+ char *str = buf;
+ int i;
+
+ if (!rdev->rswitch)
+ goto out;
+
+ for (i = 0; i < RIO_MAX_ROUTE_ENTRIES; i++) {
+ if (rdev->rswitch->route_table[i] == RIO_INVALID_ROUTE)
+ continue;
+ str +=
+ sprintf(str, "%04x %02x\n", i,
+ rdev->rswitch->route_table[i]);
+ }
+
+ out:
+ return (str - buf);
+}
+
+struct device_attribute rio_dev_attrs[] = {
+ __ATTR_RO(did),
+ __ATTR_RO(vid),
+ __ATTR_RO(device_rev),
+ __ATTR_RO(asm_did),
+ __ATTR_RO(asm_vid),
+ __ATTR_RO(asm_rev),
+ __ATTR_RO(routes),
+ __ATTR_NULL,
+};
+
+static ssize_t
+rio_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+ struct rio_dev *dev =
+ to_rio_dev(container_of(kobj, struct device, kobj));
+ unsigned int size = 0x100;
+ loff_t init_off = off;
+ u8 *data = (u8 *) buf;
+
+ /* Several chips lock up trying to read undefined config space */
+ if (capable(CAP_SYS_ADMIN))
+ size = 0x200000;
+
+ if (off > size)
+ return 0;
+ if (off + count > size) {
+ size -= off;
+ count = size;
+ } else {
+ size = count;
+ }
+
+ if ((off & 1) && size) {
+ u8 val;
+ rio_read_config_8(dev, off, &val);
+ data[off - init_off] = val;
+ off++;
+ size--;
+ }
+
+ if ((off & 3) && size > 2) {
+ u16 val;
+ rio_read_config_16(dev, off, &val);
+ data[off - init_off] = (val >> 8) & 0xff;
+ data[off - init_off + 1] = val & 0xff;
+ off += 2;
+ size -= 2;
+ }
+
+ while (size > 3) {
+ u32 val;
+ rio_read_config_32(dev, off, &val);
+ data[off - init_off] = (val >> 24) & 0xff;
+ data[off - init_off + 1] = (val >> 16) & 0xff;
+ data[off - init_off + 2] = (val >> 8) & 0xff;
+ data[off - init_off + 3] = val & 0xff;
+ off += 4;
+ size -= 4;
+ }
+
+ if (size >= 2) {
+ u16 val;
+ rio_read_config_16(dev, off, &val);
+ data[off - init_off] = (val >> 8) & 0xff;
+ data[off - init_off + 1] = val & 0xff;
+ off += 2;
+ size -= 2;
+ }
+
+ if (size > 0) {
+ u8 val;
+ rio_read_config_8(dev, off, &val);
+ data[off - init_off] = val;
+ off++;
+ --size;
+ }
+
+ return count;
+}
+
+static ssize_t
+rio_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+ struct rio_dev *dev =
+ to_rio_dev(container_of(kobj, struct device, kobj));
+ unsigned int size = count;
+ loff_t init_off = off;
+ u8 *data = (u8 *) buf;
+
+ if (off > 0x200000)
+ return 0;
+ if (off + count > 0x200000) {
+ size = 0x200000 - off;
+ count = size;
+ }
+
+ if ((off & 1) && size) {
+ rio_write_config_8(dev, off, data[off - init_off]);
+ off++;
+ size--;
+ }
+
+ if ((off & 3) && (size > 2)) {
+ u16 val = data[off - init_off + 1];
+ val |= (u16) data[off - init_off] << 8;
+ rio_write_config_16(dev, off, val);
+ off += 2;
+ size -= 2;
+ }
+
+ while (size > 3) {
+ u32 val = data[off - init_off + 3];
+ val |= (u32) data[off - init_off + 2] << 8;
+ val |= (u32) data[off - init_off + 1] << 16;
+ val |= (u32) data[off - init_off] << 24;
+ rio_write_config_32(dev, off, val);
+ off += 4;
+ size -= 4;
+ }
+
+ if (size >= 2) {
+ u16 val = data[off - init_off + 1];
+ val |= (u16) data[off - init_off] << 8;
+ rio_write_config_16(dev, off, val);
+ off += 2;
+ size -= 2;
+ }
+
+ if (size) {
+ rio_write_config_8(dev, off, data[off - init_off]);
+ off++;
+ --size;
+ }
+
+ return count;
+}
+
+static struct bin_attribute rio_config_attr = {
+ .attr = {
+ .name = "config",
+ .mode = S_IRUGO | S_IWUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = 0x200000,
+ .read = rio_read_config,
+ .write = rio_write_config,
+};
+
+/**
+ * rio_create_sysfs_dev_files - create RIO specific sysfs files
+ * @rdev: device whose entries should be created
+ *
+ * Create files when @rdev is added to sysfs.
+ */
+int rio_create_sysfs_dev_files(struct rio_dev *rdev)
+{
+ sysfs_create_bin_file(&rdev->dev.kobj, &rio_config_attr);
+
+ return 0;
+}
+
+/**
+ * rio_remove_sysfs_dev_files - cleanup RIO specific sysfs files
+ * @rdev: device whose entries we should free
+ *
+ * Cleanup when @rdev is removed from sysfs.
+ */
+void rio_remove_sysfs_dev_files(struct rio_dev *rdev)
+{
+ sysfs_remove_bin_file(&rdev->dev.kobj, &rio_config_attr);
+}
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
new file mode 100644
index 00000000000..adc299e2b07
--- /dev/null
+++ b/drivers/rapidio/rio.c
@@ -0,0 +1,503 @@
+/*
+ * RapidIO interconnect services
+ * (RapidIO Interconnect Specification, http://www.rapidio.org)
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/rio_ids.h>
+#include <linux/rio_regs.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "rio.h"
+
+static LIST_HEAD(rio_mports);
+
+/**
+ * rio_local_get_device_id - Get the base/extended device id for a port
+ * @port: RIO master port from which to get the deviceid
+ *
+ * Reads the base/extended device id from the local device
+ * implementing the master port. Returns the 8/16-bit device
+ * id.
+ */
+u16 rio_local_get_device_id(struct rio_mport *port)
+{
+ u32 result;
+
+ rio_local_read_config_32(port, RIO_DID_CSR, &result);
+
+ return (RIO_GET_DID(result));
+}
+
+/**
+ * rio_request_inb_mbox - request inbound mailbox service
+ * @mport: RIO master port from which to allocate the mailbox resource
+ * @mbox: Mailbox number to claim
+ * @entries: Number of entries in inbound mailbox queue
+ * @minb: Callback to execute when inbound message is received
+ *
+ * Requests ownership of an inbound mailbox resource and binds
+ * a callback function to the resource. Returns %0 on success.
+ */
+int rio_request_inb_mbox(struct rio_mport *mport,
+ int mbox,
+ int entries,
+ void (*minb) (struct rio_mport * mport, int mbox,
+ int slot))
+{
+ int rc = 0;
+
+ struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
+
+ if (res) {
+ rio_init_mbox_res(res, mbox, mbox);
+
+ /* Make sure this mailbox isn't in use */
+ if ((rc =
+ request_resource(&mport->riores[RIO_INB_MBOX_RESOURCE],
+ res)) < 0) {
+ kfree(res);
+ goto out;
+ }
+
+ mport->inb_msg[mbox].res = res;
+
+ /* Hook the inbound message callback */
+ mport->inb_msg[mbox].mcback = minb;
+
+ rc = rio_open_inb_mbox(mport, mbox, entries);
+ } else
+ rc = -ENOMEM;
+
+ out:
+ return rc;
+}
+
+/**
+ * rio_release_inb_mbox - release inbound mailbox message service
+ * @mport: RIO master port from which to release the mailbox resource
+ * @mbox: Mailbox number to release
+ *
+ * Releases ownership of an inbound mailbox resource. Returns 0
+ * if the request has been satisfied.
+ */
+int rio_release_inb_mbox(struct rio_mport *mport, int mbox)
+{
+ rio_close_inb_mbox(mport, mbox);
+
+ /* Release the mailbox resource */
+ return release_resource(mport->inb_msg[mbox].res);
+}
+
+/**
+ * rio_request_outb_mbox - request outbound mailbox service
+ * @mport: RIO master port from which to allocate the mailbox resource
+ * @mbox: Mailbox number to claim
+ * @entries: Number of entries in outbound mailbox queue
+ * @moutb: Callback to execute when outbound message is sent
+ *
+ * Requests ownership of an outbound mailbox resource and binds
+ * a callback function to the resource. Returns 0 on success.
+ */
+int rio_request_outb_mbox(struct rio_mport *mport,
+ int mbox,
+ int entries,
+ void (*moutb) (struct rio_mport * mport, int mbox,
+ int slot))
+{
+ int rc = 0;
+
+ struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
+
+ if (res) {
+ rio_init_mbox_res(res, mbox, mbox);
+
+ /* Make sure this outbound mailbox isn't in use */
+ if ((rc =
+ request_resource(&mport->riores[RIO_OUTB_MBOX_RESOURCE],
+ res)) < 0) {
+ kfree(res);
+ goto out;
+ }
+
+ mport->outb_msg[mbox].res = res;
+
+ /* Hook the inbound message callback */
+ mport->outb_msg[mbox].mcback = moutb;
+
+ rc = rio_open_outb_mbox(mport, mbox, entries);
+ } else
+ rc = -ENOMEM;
+
+ out:
+ return rc;
+}
+
+/**
+ * rio_release_outb_mbox - release outbound mailbox message service
+ * @mport: RIO master port from which to release the mailbox resource
+ * @mbox: Mailbox number to release
+ *
+ * Releases ownership of an inbound mailbox resource. Returns 0
+ * if the request has been satisfied.
+ */
+int rio_release_outb_mbox(struct rio_mport *mport, int mbox)
+{
+ rio_close_outb_mbox(mport, mbox);
+
+ /* Release the mailbox resource */
+ return release_resource(mport->outb_msg[mbox].res);
+}
+
+/**
+ * rio_setup_inb_dbell - bind inbound doorbell callback
+ * @mport: RIO master port to bind the doorbell callback
+ * @res: Doorbell message resource
+ * @dinb: Callback to execute when doorbell is received
+ *
+ * Adds a doorbell resource/callback pair into a port's
+ * doorbell event list. Returns 0 if the request has been
+ * satisfied.
+ */
+static int
+rio_setup_inb_dbell(struct rio_mport *mport, struct resource *res,
+ void (*dinb) (struct rio_mport * mport, u16 src, u16 dst,
+ u16 info))
+{
+ int rc = 0;
+ struct rio_dbell *dbell;
+
+ if (!(dbell = kmalloc(sizeof(struct rio_dbell), GFP_KERNEL))) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ dbell->res = res;
+ dbell->dinb = dinb;
+
+ list_add_tail(&dbell->node, &mport->dbells);
+
+ out:
+ return rc;
+}
+
+/**
+ * rio_request_inb_dbell - request inbound doorbell message service
+ * @mport: RIO master port from which to allocate the doorbell resource
+ * @start: Doorbell info range start
+ * @end: Doorbell info range end
+ * @dinb: Callback to execute when doorbell is received
+ *
+ * Requests ownership of an inbound doorbell resource and binds
+ * a callback function to the resource. Returns 0 if the request
+ * has been satisfied.
+ */
+int rio_request_inb_dbell(struct rio_mport *mport,
+ u16 start,
+ u16 end,
+ void (*dinb) (struct rio_mport * mport, u16 src,
+ u16 dst, u16 info))
+{
+ int rc = 0;
+
+ struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
+
+ if (res) {
+ rio_init_dbell_res(res, start, end);
+
+ /* Make sure these doorbells aren't in use */
+ if ((rc =
+ request_resource(&mport->riores[RIO_DOORBELL_RESOURCE],
+ res)) < 0) {
+ kfree(res);
+ goto out;
+ }
+
+ /* Hook the doorbell callback */
+ rc = rio_setup_inb_dbell(mport, res, dinb);
+ } else
+ rc = -ENOMEM;
+
+ out:
+ return rc;
+}
+
+/**
+ * rio_release_inb_dbell - release inbound doorbell message service
+ * @mport: RIO master port from which to release the doorbell resource
+ * @start: Doorbell info range start
+ * @end: Doorbell info range end
+ *
+ * Releases ownership of an inbound doorbell resource and removes
+ * callback from the doorbell event list. Returns 0 if the request
+ * has been satisfied.
+ */
+int rio_release_inb_dbell(struct rio_mport *mport, u16 start, u16 end)
+{
+ int rc = 0, found = 0;
+ struct rio_dbell *dbell;
+
+ list_for_each_entry(dbell, &mport->dbells, node) {
+ if ((dbell->res->start == start) && (dbell->res->end == end)) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* If we can't find an exact match, fail */
+ if (!found) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Delete from list */
+ list_del(&dbell->node);
+
+ /* Release the doorbell resource */
+ rc = release_resource(dbell->res);
+
+ /* Free the doorbell event */
+ kfree(dbell);
+
+ out:
+ return rc;
+}
+
+/**
+ * rio_request_outb_dbell - request outbound doorbell message range
+ * @rdev: RIO device from which to allocate the doorbell resource
+ * @start: Doorbell message range start
+ * @end: Doorbell message range end
+ *
+ * Requests ownership of a doorbell message range. Returns a resource
+ * if the request has been satisfied or %NULL on failure.
+ */
+struct resource *rio_request_outb_dbell(struct rio_dev *rdev, u16 start,
+ u16 end)
+{
+ struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
+
+ if (res) {
+ rio_init_dbell_res(res, start, end);
+
+ /* Make sure these doorbells aren't in use */
+ if (request_resource(&rdev->riores[RIO_DOORBELL_RESOURCE], res)
+ < 0) {
+ kfree(res);
+ res = NULL;
+ }
+ }
+
+ return res;
+}
+
+/**
+ * rio_release_outb_dbell - release outbound doorbell message range
+ * @rdev: RIO device from which to release the doorbell resource
+ * @res: Doorbell resource to be freed
+ *
+ * Releases ownership of a doorbell message range. Returns 0 if the
+ * request has been satisfied.
+ */
+int rio_release_outb_dbell(struct rio_dev *rdev, struct resource *res)
+{
+ int rc = release_resource(res);
+
+ kfree(res);
+
+ return rc;
+}
+
+/**
+ * rio_mport_get_feature - query for devices' extended features
+ * @port: Master port to issue transaction
+ * @local: Indicate a local master port or remote device access
+ * @destid: Destination ID of the device
+ * @hopcount: Number of switch hops to the device
+ * @ftr: Extended feature code
+ *
+ * Tell if a device supports a given RapidIO capability.
+ * Returns the offset of the requested extended feature
+ * block within the device's RIO configuration space or
+ * 0 in case the device does not support it. Possible
+ * values for @ftr:
+ *
+ * %RIO_EFB_PAR_EP_ID LP/LVDS EP Devices
+ *
+ * %RIO_EFB_PAR_EP_REC_ID LP/LVDS EP Recovery Devices
+ *
+ * %RIO_EFB_PAR_EP_FREE_ID LP/LVDS EP Free Devices
+ *
+ * %RIO_EFB_SER_EP_ID LP/Serial EP Devices
+ *
+ * %RIO_EFB_SER_EP_REC_ID LP/Serial EP Recovery Devices
+ *
+ * %RIO_EFB_SER_EP_FREE_ID LP/Serial EP Free Devices
+ */
+u32
+rio_mport_get_feature(struct rio_mport * port, int local, u16 destid,
+ u8 hopcount, int ftr)
+{
+ u32 asm_info, ext_ftr_ptr, ftr_header;
+
+ if (local)
+ rio_local_read_config_32(port, RIO_ASM_INFO_CAR, &asm_info);
+ else
+ rio_mport_read_config_32(port, destid, hopcount,
+ RIO_ASM_INFO_CAR, &asm_info);
+
+ ext_ftr_ptr = asm_info & RIO_EXT_FTR_PTR_MASK;
+
+ while (ext_ftr_ptr) {
+ if (local)
+ rio_local_read_config_32(port, ext_ftr_ptr,
+ &ftr_header);
+ else
+ rio_mport_read_config_32(port, destid, hopcount,
+ ext_ftr_ptr, &ftr_header);
+ if (RIO_GET_BLOCK_ID(ftr_header) == ftr)
+ return ext_ftr_ptr;
+ if (!(ext_ftr_ptr = RIO_GET_BLOCK_PTR(ftr_header)))
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * rio_get_asm - Begin or continue searching for a RIO device by vid/did/asm_vid/asm_did
+ * @vid: RIO vid to match or %RIO_ANY_ID to match all vids
+ * @did: RIO did to match or %RIO_ANY_ID to match all dids
+ * @asm_vid: RIO asm_vid to match or %RIO_ANY_ID to match all asm_vids
+ * @asm_did: RIO asm_did to match or %RIO_ANY_ID to match all asm_dids
+ * @from: Previous RIO device found in search, or %NULL for new search
+ *
+ * Iterates through the list of known RIO devices. If a RIO device is
+ * found with a matching @vid, @did, @asm_vid, @asm_did, the reference
+ * count to the device is incrememted and a pointer to its device
+ * structure is returned. Otherwise, %NULL is returned. A new search
+ * is initiated by passing %NULL to the @from argument. Otherwise, if
+ * @from is not %NULL, searches continue from next device on the global
+ * list. The reference count for @from is always decremented if it is
+ * not %NULL.
+ */
+struct rio_dev *rio_get_asm(u16 vid, u16 did,
+ u16 asm_vid, u16 asm_did, struct rio_dev *from)
+{
+ struct list_head *n;
+ struct rio_dev *rdev;
+
+ WARN_ON(in_interrupt());
+ spin_lock(&rio_global_list_lock);
+ n = from ? from->global_list.next : rio_devices.next;
+
+ while (n && (n != &rio_devices)) {
+ rdev = rio_dev_g(n);
+ if ((vid == RIO_ANY_ID || rdev->vid == vid) &&
+ (did == RIO_ANY_ID || rdev->did == did) &&
+ (asm_vid == RIO_ANY_ID || rdev->asm_vid == asm_vid) &&
+ (asm_did == RIO_ANY_ID || rdev->asm_did == asm_did))
+ goto exit;
+ n = n->next;
+ }
+ rdev = NULL;
+ exit:
+ rio_dev_put(from);
+ rdev = rio_dev_get(rdev);
+ spin_unlock(&rio_global_list_lock);
+ return rdev;
+}
+
+/**
+ * rio_get_device - Begin or continue searching for a RIO device by vid/did
+ * @vid: RIO vid to match or %RIO_ANY_ID to match all vids
+ * @did: RIO did to match or %RIO_ANY_ID to match all dids
+ * @from: Previous RIO device found in search, or %NULL for new search
+ *
+ * Iterates through the list of known RIO devices. If a RIO device is
+ * found with a matching @vid and @did, the reference count to the
+ * device is incrememted and a pointer to its device structure is returned.
+ * Otherwise, %NULL is returned. A new search is initiated by passing %NULL
+ * to the @from argument. Otherwise, if @from is not %NULL, searches
+ * continue from next device on the global list. The reference count for
+ * @from is always decremented if it is not %NULL.
+ */
+struct rio_dev *rio_get_device(u16 vid, u16 did, struct rio_dev *from)
+{
+ return rio_get_asm(vid, did, RIO_ANY_ID, RIO_ANY_ID, from);
+}
+
+static void rio_fixup_device(struct rio_dev *dev)
+{
+}
+
+static int __devinit rio_init(void)
+{
+ struct rio_dev *dev = NULL;
+
+ while ((dev = rio_get_device(RIO_ANY_ID, RIO_ANY_ID, dev)) != NULL) {
+ rio_fixup_device(dev);
+ }
+ return 0;
+}
+
+device_initcall(rio_init);
+
+int rio_init_mports(void)
+{
+ int rc = 0;
+ struct rio_mport *port;
+
+ list_for_each_entry(port, &rio_mports, node) {
+ if (!request_mem_region(port->iores.start,
+ port->iores.end - port->iores.start,
+ port->name)) {
+ printk(KERN_ERR
+ "RIO: Error requesting master port region %8.8lx-%8.8lx\n",
+ port->iores.start, port->iores.end - 1);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (port->host_deviceid >= 0)
+ rio_enum_mport(port);
+ else
+ rio_disc_mport(port);
+ }
+
+ out:
+ return rc;
+}
+
+void rio_register_mport(struct rio_mport *port)
+{
+ list_add_tail(&port->node, &rio_mports);
+}
+
+EXPORT_SYMBOL_GPL(rio_local_get_device_id);
+EXPORT_SYMBOL_GPL(rio_get_device);
+EXPORT_SYMBOL_GPL(rio_get_asm);
+EXPORT_SYMBOL_GPL(rio_request_inb_dbell);
+EXPORT_SYMBOL_GPL(rio_release_inb_dbell);
+EXPORT_SYMBOL_GPL(rio_request_outb_dbell);
+EXPORT_SYMBOL_GPL(rio_release_outb_dbell);
+EXPORT_SYMBOL_GPL(rio_request_inb_mbox);
+EXPORT_SYMBOL_GPL(rio_release_inb_mbox);
+EXPORT_SYMBOL_GPL(rio_request_outb_mbox);
+EXPORT_SYMBOL_GPL(rio_release_outb_mbox);
diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h
new file mode 100644
index 00000000000..f865a68cd3d
--- /dev/null
+++ b/drivers/rapidio/rio.h
@@ -0,0 +1,57 @@
+/*
+ * RapidIO interconnect services
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/rio.h>
+
+/* Functions internal to the RIO core code */
+
+extern u32 rio_mport_get_feature(struct rio_mport *mport, int local, u16 destid,
+ u8 hopcount, int ftr);
+extern int rio_create_sysfs_dev_files(struct rio_dev *rdev);
+extern int rio_enum_mport(struct rio_mport *mport);
+extern int rio_disc_mport(struct rio_mport *mport);
+
+/* Structures internal to the RIO core code */
+extern struct device_attribute rio_dev_attrs[];
+extern spinlock_t rio_global_list_lock;
+
+/* Helpers internal to the RIO core code */
+#define DECLARE_RIO_ROUTE_SECTION(section, vid, did, add_hook, get_hook) \
+ static struct rio_route_ops __rio_route_ops __attribute_used__ \
+ __attribute__((__section__(#section))) = { vid, did, add_hook, get_hook };
+
+/**
+ * DECLARE_RIO_ROUTE_OPS - Registers switch routing operations
+ * @vid: RIO vendor ID
+ * @did: RIO device ID
+ * @add_hook: Callback that adds a route entry
+ * @get_hook: Callback that gets a route entry
+ *
+ * Manipulating switch route tables in RIO is switch specific. This
+ * registers a switch by vendor and device ID with two callbacks for
+ * modifying and retrieving route entries in a switch. A &struct
+ * rio_route_ops is initialized with the ops and placed into a
+ * RIO-specific kernel section.
+ */
+#define DECLARE_RIO_ROUTE_OPS(vid, did, add_hook, get_hook) \
+ DECLARE_RIO_ROUTE_SECTION(.rio_route_ops, \
+ vid, did, add_hook, get_hook)
+
+#ifdef CONFIG_RAPIDIO_8_BIT_TRANSPORT
+#define RIO_GET_DID(x) ((x & 0x00ff0000) >> 16)
+#define RIO_SET_DID(x) ((x & 0x000000ff) << 16)
+#else
+#define RIO_GET_DID(x) (x & 0xffff)
+#define RIO_SET_DID(x) (x & 0xffff)
+#endif