summaryrefslogtreecommitdiffstats
path: root/drivers/rapidio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rapidio')
-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-scan.c945
-rw-r--r--drivers/rapidio/rio-sysfs.c230
-rw-r--r--drivers/rapidio/rio.c510
-rw-r--r--drivers/rapidio/rio.h60
-rw-r--r--drivers/rapidio/switches/Makefile5
-rw-r--r--drivers/rapidio/switches/tsi500.c60
10 files changed, 2238 insertions, 0 deletions
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-scan.c b/drivers/rapidio/rio-scan.c
new file mode 100644
index 00000000000..4f7ed4bd3be
--- /dev/null
+++ b/drivers/rapidio/rio-scan.c
@@ -0,0 +1,945 @@
+/*
+ * RapidIO enumeration and discovery 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/types.h>
+#include <linux/kernel.h>
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.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 <linux/timer.h>
+
+#include "rio.h"
+
+LIST_HEAD(rio_devices);
+static LIST_HEAD(rio_switches);
+
+#define RIO_ENUM_CMPL_MAGIC 0xdeadbeef
+
+static void rio_enum_timeout(unsigned long);
+
+DEFINE_SPINLOCK(rio_global_list_lock);
+
+static int next_destid = 0;
+static int next_switchid = 0;
+static int next_net = 0;
+
+static struct timer_list rio_enum_timer =
+TIMER_INITIALIZER(rio_enum_timeout, 0, 0);
+
+static int rio_mport_phys_table[] = {
+ RIO_EFB_PAR_EP_ID,
+ RIO_EFB_PAR_EP_REC_ID,
+ RIO_EFB_SER_EP_ID,
+ RIO_EFB_SER_EP_REC_ID,
+ -1,
+};
+
+static int rio_sport_phys_table[] = {
+ RIO_EFB_PAR_EP_FREE_ID,
+ RIO_EFB_SER_EP_FREE_ID,
+ -1,
+};
+
+/**
+ * rio_get_device_id - Get the base/extended device id for a device
+ * @port: RIO master port
+ * @destid: Destination ID of device
+ * @hopcount: Hopcount to device
+ *
+ * Reads the base/extended device id from a device. Returns the
+ * 8/16-bit device ID.
+ */
+static u16 rio_get_device_id(struct rio_mport *port, u16 destid, u8 hopcount)
+{
+ u32 result;
+
+ rio_mport_read_config_32(port, destid, hopcount, RIO_DID_CSR, &result);
+
+ return RIO_GET_DID(result);
+}
+
+/**
+ * rio_set_device_id - Set the base/extended device id for a device
+ * @port: RIO master port
+ * @destid: Destination ID of device
+ * @hopcount: Hopcount to device
+ * @did: Device ID value to be written
+ *
+ * Writes the base/extended device id from a device.
+ */
+static void rio_set_device_id(struct rio_mport *port, u16 destid, u8 hopcount, u16 did)
+{
+ rio_mport_write_config_32(port, destid, hopcount, RIO_DID_CSR,
+ RIO_SET_DID(did));
+}
+
+/**
+ * rio_local_set_device_id - Set the base/extended device id for a port
+ * @port: RIO master port
+ * @did: Device ID value to be written
+ *
+ * Writes the base/extended device id from a device.
+ */
+static void rio_local_set_device_id(struct rio_mport *port, u16 did)
+{
+ rio_local_write_config_32(port, RIO_DID_CSR, RIO_SET_DID(did));
+}
+
+/**
+ * rio_clear_locks- Release all host locks and signal enumeration complete
+ * @port: Master port to issue transaction
+ *
+ * Marks the component tag CSR on each device with the enumeration
+ * complete flag. When complete, it then release the host locks on
+ * each device. Returns 0 on success or %-EINVAL on failure.
+ */
+static int rio_clear_locks(struct rio_mport *port)
+{
+ struct rio_dev *rdev;
+ u32 result;
+ int ret = 0;
+
+ /* Write component tag CSR magic complete value */
+ rio_local_write_config_32(port, RIO_COMPONENT_TAG_CSR,
+ RIO_ENUM_CMPL_MAGIC);
+ list_for_each_entry(rdev, &rio_devices, global_list)
+ rio_write_config_32(rdev, RIO_COMPONENT_TAG_CSR,
+ RIO_ENUM_CMPL_MAGIC);
+
+ /* Release host device id locks */
+ rio_local_write_config_32(port, RIO_HOST_DID_LOCK_CSR,
+ port->host_deviceid);
+ rio_local_read_config_32(port, RIO_HOST_DID_LOCK_CSR, &result);
+ if ((result & 0xffff) != 0xffff) {
+ printk(KERN_INFO
+ "RIO: badness when releasing host lock on master port, result %8.8x\n",
+ result);
+ ret = -EINVAL;
+ }
+ list_for_each_entry(rdev, &rio_devices, global_list) {
+ rio_write_config_32(rdev, RIO_HOST_DID_LOCK_CSR,
+ port->host_deviceid);
+ rio_read_config_32(rdev, RIO_HOST_DID_LOCK_CSR, &result);
+ if ((result & 0xffff) != 0xffff) {
+ printk(KERN_INFO
+ "RIO: badness when releasing host lock on vid %4.4x did %4.4x\n",
+ rdev->vid, rdev->did);
+ ret = -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * rio_enum_host- Set host lock and initialize host destination ID
+ * @port: Master port to issue transaction
+ *
+ * Sets the local host master port lock and destination ID register
+ * with the host device ID value. The host device ID value is provided
+ * by the platform. Returns %0 on success or %-1 on failure.
+ */
+static int rio_enum_host(struct rio_mport *port)
+{
+ u32 result;
+
+ /* Set master port host device id lock */
+ rio_local_write_config_32(port, RIO_HOST_DID_LOCK_CSR,
+ port->host_deviceid);
+
+ rio_local_read_config_32(port, RIO_HOST_DID_LOCK_CSR, &result);
+ if ((result & 0xffff) != port->host_deviceid)
+ return -1;
+
+ /* Set master port destid and init destid ctr */
+ rio_local_set_device_id(port, port->host_deviceid);
+
+ if (next_destid == port->host_deviceid)
+ next_destid++;
+
+ return 0;
+}
+
+/**
+ * rio_device_has_destid- Test if a device contains a destination ID register
+ * @port: Master port to issue transaction
+ * @src_ops: RIO device source operations
+ * @dst_ops: RIO device destination operations
+ *
+ * Checks the provided @src_ops and @dst_ops for the necessary transaction
+ * capabilities that indicate whether or not a device will implement a
+ * destination ID register. Returns 1 if true or 0 if false.
+ */
+static int rio_device_has_destid(struct rio_mport *port, int src_ops,
+ int dst_ops)
+{
+ u32 mask = RIO_OPS_READ | RIO_OPS_WRITE | RIO_OPS_ATOMIC_TST_SWP | RIO_OPS_ATOMIC_INC | RIO_OPS_ATOMIC_DEC | RIO_OPS_ATOMIC_SET | RIO_OPS_ATOMIC_CLR;
+
+ return !!((src_ops | dst_ops) & mask);
+}
+
+/**
+ * rio_release_dev- Frees a RIO device struct
+ * @dev: LDM device associated with a RIO device struct
+ *
+ * Gets the RIO device struct associated a RIO device struct.
+ * The RIO device struct is freed.
+ */
+static void rio_release_dev(struct device *dev)
+{
+ struct rio_dev *rdev;
+
+ rdev = to_rio_dev(dev);
+ kfree(rdev);
+}
+
+/**
+ * rio_is_switch- Tests if a RIO device has switch capabilities
+ * @rdev: RIO device
+ *
+ * Gets the RIO device Processing Element Features register
+ * contents and tests for switch capabilities. Returns 1 if
+ * the device is a switch or 0 if it is not a switch.
+ * The RIO device struct is freed.
+ */
+static int rio_is_switch(struct rio_dev *rdev)
+{
+ if (rdev->pef & RIO_PEF_SWITCH)
+ return 1;
+ return 0;
+}
+
+/**
+ * rio_route_set_ops- Sets routing operations for a particular vendor switch
+ * @rdev: RIO device
+ *
+ * Searches the RIO route ops table for known switch types. If the vid
+ * and did match a switch table entry, then set the add_entry() and
+ * get_entry() ops to the table entry values.
+ */
+static void rio_route_set_ops(struct rio_dev *rdev)
+{
+ struct rio_route_ops *cur = __start_rio_route_ops;
+ struct rio_route_ops *end = __end_rio_route_ops;
+
+ while (cur < end) {
+ if ((cur->vid == rdev->vid) && (cur->did == rdev->did)) {
+ pr_debug("RIO: adding routing ops for %s\n", rio_name(rdev));
+ rdev->rswitch->add_entry = cur->add_hook;
+ rdev->rswitch->get_entry = cur->get_hook;
+ }
+ cur++;
+ }
+
+ if (!rdev->rswitch->add_entry || !rdev->rswitch->get_entry)
+ printk(KERN_ERR "RIO: missing routing ops for %s\n",
+ rio_name(rdev));
+}
+
+/**
+ * rio_add_device- Adds a RIO device to the device model
+ * @rdev: RIO device
+ *
+ * Adds the RIO device to the global device list and adds the RIO
+ * device to the RIO device list. Creates the generic sysfs nodes
+ * for an RIO device.
+ */
+static void __devinit rio_add_device(struct rio_dev *rdev)
+{
+ device_add(&rdev->dev);
+
+ spin_lock(&rio_global_list_lock);
+ list_add_tail(&rdev->global_list, &rio_devices);
+ spin_unlock(&rio_global_list_lock);
+
+ rio_create_sysfs_dev_files(rdev);
+}
+
+/**
+ * rio_setup_device- Allocates and sets up a RIO device
+ * @net: RIO network
+ * @port: Master port to send transactions
+ * @destid: Current destination ID
+ * @hopcount: Current hopcount
+ * @do_enum: Enumeration/Discovery mode flag
+ *
+ * Allocates a RIO device and configures fields based on configuration
+ * space contents. If device has a destination ID register, a destination
+ * ID is either assigned in enumeration mode or read from configuration
+ * space in discovery mode. If the device has switch capabilities, then
+ * a switch is allocated and configured appropriately. Returns a pointer
+ * to a RIO device on success or NULL on failure.
+ *
+ */
+static struct rio_dev *rio_setup_device(struct rio_net *net,
+ struct rio_mport *port, u16 destid,
+ u8 hopcount, int do_enum)
+{
+ struct rio_dev *rdev;
+ struct rio_switch *rswitch;
+ int result, rdid;
+
+ rdev = kmalloc(sizeof(struct rio_dev), GFP_KERNEL);
+ if (!rdev)
+ goto out;
+
+ memset(rdev, 0, sizeof(struct rio_dev));
+ rdev->net = net;
+ rio_mport_read_config_32(port, destid, hopcount, RIO_DEV_ID_CAR,
+ &result);
+ rdev->did = result >> 16;
+ rdev->vid = result & 0xffff;
+ rio_mport_read_config_32(port, destid, hopcount, RIO_DEV_INFO_CAR,
+ &rdev->device_rev);
+ rio_mport_read_config_32(port, destid, hopcount, RIO_ASM_ID_CAR,
+ &result);
+ rdev->asm_did = result >> 16;
+ rdev->asm_vid = result & 0xffff;
+ rio_mport_read_config_32(port, destid, hopcount, RIO_ASM_INFO_CAR,
+ &result);
+ rdev->asm_rev = result >> 16;
+ rio_mport_read_config_32(port, destid, hopcount, RIO_PEF_CAR,
+ &rdev->pef);
+ if (rdev->pef & RIO_PEF_EXT_FEATURES)
+ rdev->efptr = result & 0xffff;
+
+ rio_mport_read_config_32(port, destid, hopcount, RIO_SRC_OPS_CAR,
+ &rdev->src_ops);
+ rio_mport_read_config_32(port, destid, hopcount, RIO_DST_OPS_CAR,
+ &rdev->dst_ops);
+
+ if (rio_device_has_destid(port, rdev->src_ops, rdev->dst_ops)
+ && do_enum) {
+ rio_set_device_id(port, destid, hopcount, next_destid);
+ rdev->destid = next_destid++;
+ if (next_destid == port->host_deviceid)
+ next_destid++;
+ } else
+ rdev->destid = rio_get_device_id(port, destid, hopcount);
+
+ /* If a PE has both switch and other functions, show it as a switch */
+ if (rio_is_switch(rdev)) {
+ rio_mport_read_config_32(port, destid, hopcount,
+ RIO_SWP_INFO_CAR, &rdev->swpinfo);
+ rswitch = kmalloc(sizeof(struct rio_switch), GFP_KERNEL);
+ if (!rswitch) {
+ kfree(rdev);
+ rdev = NULL;
+ goto out;
+ }
+ rswitch->switchid = next_switchid;
+ rswitch->hopcount = hopcount;
+ rswitch->destid = 0xffff;
+ /* Initialize switch route table */
+ for (rdid = 0; rdid < RIO_MAX_ROUTE_ENTRIES; rdid++)
+ rswitch->route_table[rdid] = RIO_INVALID_ROUTE;
+ rdev->rswitch = rswitch;
+ sprintf(rio_name(rdev), "%02x:s:%04x", rdev->net->id,
+ rdev->rswitch->switchid);
+ rio_route_set_ops(rdev);
+
+ list_add_tail(&rswitch->node, &rio_switches);
+
+ } else
+ sprintf(rio_name(rdev), "%02x:e:%04x", rdev->net->id,
+ rdev->destid);
+
+ rdev->dev.bus = &rio_bus_type;
+
+ device_initialize(&rdev->dev);
+ rdev->dev.release = rio_release_dev;
+ rio_dev_get(rdev);
+
+ rdev->dma_mask = DMA_32BIT_MASK;
+ rdev->dev.dma_mask = &rdev->dma_mask;
+ rdev->dev.coherent_dma_mask = DMA_32BIT_MASK;
+
+ if ((rdev->pef & RIO_PEF_INB_DOORBELL) &&
+ (rdev->dst_ops & RIO_DST_OPS_DOORBELL))
+ rio_init_dbell_res(&rdev->riores[RIO_DOORBELL_RESOURCE],
+ 0, 0xffff);
+
+ rio_add_device(rdev);
+
+ out:
+ return rdev;
+}
+
+/**
+ * rio_sport_is_active- Tests if a switch port has an active connection.
+ * @port: Master port to send transaction
+ * @destid: Associated destination ID for switch
+ * @hopcount: Hopcount to reach switch
+ * @sport: Switch port number
+ *
+ * Reads the port error status CSR for a particular switch port to
+ * determine if the port has an active link. Returns
+ * %PORT_N_ERR_STS_PORT_OK if the port is active or %0 if it is
+ * inactive.
+ */
+static int
+rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport)
+{
+ u32 result;
+ u32 ext_ftr_ptr;
+
+ int *entry = rio_sport_phys_table;
+
+ do {
+ if ((ext_ftr_ptr =
+ rio_mport_get_feature(port, 0, destid, hopcount, *entry)))
+
+ break;
+ } while (*++entry >= 0);
+
+ if (ext_ftr_ptr)
+ rio_mport_read_config_32(port, destid, hopcount,
+ ext_ftr_ptr +
+ RIO_PORT_N_ERR_STS_CSR(sport),
+ &result);
+
+ return (result & PORT_N_ERR_STS_PORT_OK);
+}
+
+/**
+ * rio_route_add_entry- Add a route entry to a switch routing table
+ * @mport: Master port to send transaction
+ * @rdev: Switch device
+ * @table: Routing table ID
+ * @route_destid: Destination ID to be routed
+ * @route_port: Port number to be routed
+ *
+ * Calls the switch specific add_entry() method to add a route entry
+ * on a switch. The route table can be specified using the @table
+ * argument if a switch has per port routing tables or the normal
+ * use is to specific all tables (or the global table) by passing
+ * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL
+ * on failure.
+ */
+static int rio_route_add_entry(struct rio_mport *mport, struct rio_dev *rdev,
+ u16 table, u16 route_destid, u8 route_port)
+{
+ return rdev->rswitch->add_entry(mport, rdev->rswitch->destid,
+ rdev->rswitch->hopcount, table,
+ route_destid, route_port);
+}
+
+/**
+ * rio_route_get_entry- Read a route entry in a switch routing table
+ * @mport: Master port to send transaction
+ * @rdev: Switch device
+ * @table: Routing table ID
+ * @route_destid: Destination ID to be routed
+ * @route_port: Pointer to read port number into
+ *
+ * Calls the switch specific get_entry() method to read a route entry
+ * in a switch. The route table can be specified using the @table
+ * argument if a switch has per port routing tables or the normal
+ * use is to specific all tables (or the global table) by passing
+ * %RIO_GLOBAL_TABLE in @table. Returns %0 on success or %-EINVAL
+ * on failure.
+ */
+static int
+rio_route_get_entry(struct rio_mport *mport, struct rio_dev *rdev, u16 table,
+ u16 route_destid, u8 * route_port)
+{
+ return rdev->rswitch->get_entry(mport, rdev->rswitch->destid,
+ rdev->rswitch->hopcount, table,
+ route_destid, route_port);
+}
+
+/**
+ * rio_get_host_deviceid_lock- Reads the Host Device ID Lock CSR on a device
+ * @port: Master port to send transaction
+ * @hopcount: Number of hops to the device
+ *
+ * Used during enumeration to read the Host Device ID Lock CSR on a
+ * RIO device. Returns the value of the lock register.
+ */
+static u16 rio_get_host_deviceid_lock(struct rio_mport *port, u8 hopcount)
+{
+ u32 result;
+
+ rio_mport_read_config_32(port, RIO_ANY_DESTID, hopcount,
+ RIO_HOST_DID_LOCK_CSR, &result);
+
+ return (u16) (result & 0xffff);
+}
+
+/**
+ * rio_get_swpinfo_inport- Gets the ingress port number
+ * @mport: Master port to send transaction
+ * @destid: Destination ID associated with the switch
+ * @hopcount: Number of hops to the device
+ *
+ * Returns port number being used to access the switch device.
+ */
+static u8
+rio_get_swpinfo_inport(struct rio_mport *mport, u16 destid, u8 hopcount)
+{
+ u32 result;
+
+ rio_mport_read_config_32(mport, destid, hopcount, RIO_SWP_INFO_CAR,
+ &result);
+
+ return (u8) (result & 0xff);
+}
+
+/**
+ * rio_get_swpinfo_tports- Gets total number of ports on the switch
+ * @mport: Master port to send transaction
+ * @destid: Destination ID associated with the switch
+ * @hopcount: Number of hops to the device
+ *
+ * Returns total numbers of ports implemented by the switch device.
+ */
+static u8 rio_get_swpinfo_tports(struct rio_mport *mport, u16 destid,
+ u8 hopcount)
+{
+ u32 result;
+
+ rio_mport_read_config_32(mport, destid, hopcount, RIO_SWP_INFO_CAR,
+ &result);
+
+ return RIO_GET_TOTAL_PORTS(result);
+}
+
+/**
+ * rio_net_add_mport- Add a master port to a RIO network
+ * @net: RIO network
+ * @port: Master port to add
+ *
+ * Adds a master port to the network list of associated master
+ * ports..
+ */
+static void rio_net_add_mport(struct rio_net *net, struct rio_mport *port)
+{
+ spin_lock(&rio_global_list_lock);
+ list_add_tail(&port->nnode, &net->mports);
+ spin_unlock(&rio_global_list_lock);
+}
+
+/**
+ * rio_enum_peer- Recursively enumerate a RIO network through a master port
+ * @net: RIO network being enumerated
+ * @port: Master port to send transactions
+ * @hopcount: Number of hops into the network
+ *
+ * Recursively enumerates a RIO network. Transactions are sent via the
+ * master port passed in @port.
+ */
+static int rio_enum_peer(struct rio_net *net, struct rio_mport *port,
+ u8 hopcount)
+{
+ int port_num;
+ int num_ports;
+ int cur_destid;
+ struct rio_dev *rdev;
+ u16 destid;
+ int tmp;
+
+ if (rio_get_host_deviceid_lock(port, hopcount) == port->host_deviceid) {
+ pr_debug("RIO: PE already discovered by this host\n");
+ /*
+ * Already discovered by this host. Add it as another
+ * master port for the current network.
+ */
+ rio_net_add_mport(net, port);
+ return 0;
+ }
+
+ /* Attempt to acquire device lock */
+ rio_mport_write_config_32(port, RIO_ANY_DESTID, hopcount,
+ RIO_HOST_DID_LOCK_CSR, port->host_deviceid);
+ while ((tmp = rio_get_host_deviceid_lock(port, hopcount))
+ < port->host_deviceid) {
+ /* Delay a bit */
+ mdelay(1);
+ /* Attempt to acquire device lock again */
+ rio_mport_write_config_32(port, RIO_ANY_DESTID, hopcount,
+ RIO_HOST_DID_LOCK_CSR,
+ port->host_deviceid);
+ }
+
+ if (rio_get_host_deviceid_lock(port, hopcount) > port->host_deviceid) {
+ pr_debug(
+ "RIO: PE locked by a higher priority host...retreating\n");
+ return -1;
+ }
+
+ /* Setup new RIO device */
+ if ((rdev = rio_setup_device(net, port, RIO_ANY_DESTID, hopcount, 1))) {
+ /* Add device to the global and bus/net specific list. */
+ list_add_tail(&rdev->net_list, &net->devices);
+ } else
+ return -1;
+
+ if (rio_is_switch(rdev)) {
+ next_switchid++;
+
+ for (destid = 0; destid < next_destid; destid++) {
+ rio_route_add_entry(port, rdev, RIO_GLOBAL_TABLE,
+ destid, rio_get_swpinfo_inport(port,
+ RIO_ANY_DESTID,
+ hopcount));
+ rdev->rswitch->route_table[destid] =
+ rio_get_swpinfo_inport(port, RIO_ANY_DESTID,
+ hopcount);
+ }
+
+ num_ports =
+ rio_get_swpinfo_tports(port, RIO_ANY_DESTID, hopcount);
+ pr_debug(
+ "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
+ rio_name(rdev), rdev->vid, rdev->did, num_ports);
+ for (port_num = 0; port_num < num_ports; port_num++) {
+ if (rio_get_swpinfo_inport
+ (port, RIO_ANY_DESTID, hopcount) == port_num)
+ continue;
+
+ cur_destid = next_destid;
+
+ if (rio_sport_is_active
+ (port, RIO_ANY_DESTID, hopcount, port_num)) {
+ pr_debug(
+ "RIO: scanning device on port %d\n",
+ port_num);
+ rio_route_add_entry(port, rdev,
+ RIO_GLOBAL_TABLE,
+ RIO_ANY_DESTID, port_num);
+
+ if (rio_enum_peer(net, port, hopcount + 1) < 0)
+ return -1;
+
+ /* Update routing tables */
+ if (next_destid > cur_destid) {
+ for (destid = cur_destid;
+ destid < next_destid; destid++) {
+ rio_route_add_entry(port, rdev,
+ RIO_GLOBAL_TABLE,
+ destid,
+ port_num);
+ rdev->rswitch->
+ route_table[destid] =
+ port_num;
+ }
+ rdev->rswitch->destid = cur_destid;
+ }
+ }
+ }
+ } else
+ pr_debug("RIO: found %s (vid %4.4x did %4.4x)\n",
+ rio_name(rdev), rdev->vid, rdev->did);
+
+ return 0;
+}
+
+/**
+ * rio_enum_complete- Tests if enumeration of a network is complete
+ * @port: Master port to send transaction
+ *
+ * Tests the Component Tag CSR for presence of the magic enumeration
+ * complete flag. Return %1 if enumeration is complete or %0 if
+ * enumeration is incomplete.
+ */
+static int rio_enum_complete(struct rio_mport *port)
+{
+ u32 tag_csr;
+ int ret = 0;
+
+ rio_local_read_config_32(port, RIO_COMPONENT_TAG_CSR, &tag_csr);
+
+ if (tag_csr == RIO_ENUM_CMPL_MAGIC)
+ ret = 1;
+
+ return ret;
+}
+
+/**
+ * rio_disc_peer- Recursively discovers a RIO network through a master port
+ * @net: RIO network being discovered
+ * @port: Master port to send transactions
+ * @destid: Current destination ID in network
+ * @hopcount: Number of hops into the network
+ *
+ * Recursively discovers a RIO network. Transactions are sent via the
+ * master port passed in @port.
+ */
+static int
+rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
+ u8 hopcount)
+{
+ u8 port_num, route_port;
+ int num_ports;
+ struct rio_dev *rdev;
+ u16 ndestid;
+
+ /* Setup new RIO device */
+ if ((rdev = rio_setup_device(net, port, destid, hopcount, 0))) {
+ /* Add device to the global and bus/net specific list. */
+ list_add_tail(&rdev->net_list, &net->devices);
+ } else
+ return -1;
+
+ if (rio_is_switch(rdev)) {
+ next_switchid++;
+
+ /* Associated destid is how we accessed this switch */
+ rdev->rswitch->destid = destid;
+
+ num_ports = rio_get_swpinfo_tports(port, destid, hopcount);
+ pr_debug(
+ "RIO: found %s (vid %4.4x did %4.4x) with %d ports\n",
+ rio_name(rdev), rdev->vid, rdev->did, num_ports);
+ for (port_num = 0; port_num < num_ports; port_num++) {
+ if (rio_get_swpinfo_inport(port, destid, hopcount) ==
+ port_num)
+ continue;
+
+ if (rio_sport_is_active
+ (port, destid, hopcount, port_num)) {
+ pr_debug(
+ "RIO: scanning device on port %d\n",
+ port_num);
+ for (ndestid = 0; ndestid < RIO_ANY_DESTID;
+ ndestid++) {
+ rio_route_get_entry(port, rdev,
+ RIO_GLOBAL_TABLE,
+ ndestid,
+ &route_port);
+ if (route_port == port_num)
+ break;
+ }
+
+ if (rio_disc_peer
+ (net, port, ndestid, hopcount + 1) < 0)
+ return -1;
+ }
+ }
+ } else
+ pr_debug("RIO: found %s (vid %4.4x did %4.4x)\n",
+ rio_name(rdev), rdev->vid, rdev->did);
+
+ return 0;
+}
+
+/**
+ * rio_mport_is_active- Tests if master port link is active
+ * @port: Master port to test
+ *
+ * Reads the port error status CSR for the master port to
+ * determine if the port has an active link. Returns
+ * %PORT_N_ERR_STS_PORT_OK if the master port is active
+ * or %0 if it is inactive.
+ */
+static int rio_mport_is_active(struct rio_mport *port)
+{
+ u32 result = 0;
+ u32 ext_ftr_ptr;
+ int *entry = rio_mport_phys_table;
+
+ do {
+ if ((ext_ftr_ptr =
+ rio_mport_get_feature(port, 1, 0, 0, *entry)))
+ break;
+ } while (*++entry >= 0);
+
+ if (ext_ftr_ptr)
+ rio_local_read_config_32(port,
+ ext_ftr_ptr +
+ RIO_PORT_N_ERR_STS_CSR(port->index),
+ &result);
+
+ return (result & PORT_N_ERR_STS_PORT_OK);
+}
+
+/**
+ * rio_alloc_net- Allocate and configure a new RIO network
+ * @port: Master port associated with the RIO network
+ *
+ * Allocates a RIO network structure, initializes per-network
+ * list heads, and adds the associated master port to the
+ * network list of associated master ports. Returns a
+ * RIO network pointer on success or %NULL on failure.
+ */
+static struct rio_net __devinit *rio_alloc_net(struct rio_mport *port)
+{
+ struct rio_net *net;
+
+ net = kmalloc(sizeof(struct rio_net), GFP_KERNEL);
+ if (net) {
+ memset(net, 0, sizeof(struct rio_net));
+ INIT_LIST_HEAD(&net->node);
+ INIT_LIST_HEAD(&net->devices);
+ INIT_LIST_HEAD(&net->mports);
+ list_add_tail(&port->nnode, &net->mports);
+ net->hport = port;
+ net->id = next_net++;
+ }
+ return net;
+}
+
+/**
+ * rio_enum_mport- Start enumeration through a master port
+ * @mport: Master port to send transactions
+ *
+ * Starts the enumeration process. If somebody has enumerated our
+ * master port device, then give up. If not and we have an active
+ * link, then start recursive peer enumeration. Returns %0 if
+ * enumeration succeeds or %-EBUSY if enumeration fails.
+ */
+int rio_enum_mport(struct rio_mport *mport)
+{
+ struct rio_net *net = NULL;
+ int rc = 0;
+
+ printk(KERN_INFO "RIO: enumerate master port %d, %s\n", mport->id,
+ mport->name);
+ /* If somebody else enumerated our master port device, bail. */
+ if (rio_enum_host(mport) < 0) {
+ printk(KERN_INFO
+ "RIO: master port %d device has been enumerated by a remote host\n",
+ mport->id);
+ rc = -EBUSY;
+ goto out;
+ }
+
+ /* If master port has an active link, allocate net and enum peers */
+ if (rio_mport_is_active(mport)) {
+ if (!(net = rio_alloc_net(mport))) {
+ printk(KERN_ERR "RIO: failed to allocate new net\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+ if (rio_enum_peer(net, mport, 0) < 0) {
+ /* A higher priority host won enumeration, bail. */
+ printk(KERN_INFO
+ "RIO: master port %d device has lost enumeration to a remote host\n",
+ mport->id);
+ rio_clear_locks(mport);
+ rc = -EBUSY;
+ goto out;
+ }
+ rio_clear_locks(mport);
+ } else {
+ printk(KERN_INFO "RIO: master port %d link inactive\n",
+ mport->id);
+ rc = -EINVAL;
+ }
+
+ out:
+ return rc;
+}
+
+/**
+ * rio_build_route_tables- Generate route tables from switch route entries
+ *
+ * For each switch device, generate a route table by copying existing
+ * route entries from the switch.
+ */
+static void rio_build_route_tables(void)
+{
+ struct rio_dev *rdev;
+ int i;
+ u8 sport;
+
+ list_for_each_entry(rdev, &rio_devices, global_list)
+ if (rio_is_switch(rdev))
+ for (i = 0; i < RIO_MAX_ROUTE_ENTRIES; i++) {
+ if (rio_route_get_entry
+ (rdev->net->hport, rdev, RIO_GLOBAL_TABLE, i,
+ &sport) < 0)
+ continue;
+ rdev->rswitch->route_table[i] = sport;
+ }
+}
+
+/**
+ * rio_enum_timeout- Signal that enumeration timed out
+ * @data: Address of timeout flag.
+ *
+ * When the enumeration complete timer expires, set a flag that
+ * signals to the discovery process that enumeration did not
+ * complete in a sane amount of time.
+ */
+static void rio_enum_timeout(unsigned long data)
+{
+ /* Enumeration timed out, set flag */
+ *(int *)data = 1;
+}
+
+/**
+ * rio_disc_mport- Start discovery through a master port
+ * @mport: Master port to send transactions
+ *
+ * Starts the discovery process. If we have an active link,
+ * then wait for the signal that enumeration is complete.
+ * When enumeration completion is signaled, start recursive
+ * peer discovery. Returns %0 if discovery succeeds or %-EBUSY
+ * on failure.
+ */
+int rio_disc_mport(struct rio_mport *mport)
+{
+ struct rio_net *net = NULL;
+ int enum_timeout_flag = 0;
+
+ printk(KERN_INFO "RIO: discover master port %d, %s\n", mport->id,
+ mport->name);
+
+ /* If master port has an active link, allocate net and discover peers */
+ if (rio_mport_is_active(mport)) {
+ if (!(net = rio_alloc_net(mport))) {
+ printk(KERN_ERR "RIO: Failed to allocate new net\n");
+ goto bail;
+ }
+
+ pr_debug("RIO: wait for enumeration complete...");
+
+ rio_enum_timer.expires =
+ jiffies + CONFIG_RAPIDIO_DISC_TIMEOUT * HZ;
+ rio_enum_timer.data = (unsigned long)&enum_timeout_flag;
+ add_timer(&rio_enum_timer);
+ while (!rio_enum_complete(mport)) {
+ mdelay(1);
+ if (enum_timeout_flag) {
+ del_timer_sync(&rio_enum_timer);
+ goto timeout;
+ }
+ }
+ del_timer_sync(&rio_enum_timer);
+
+ pr_debug("done\n");
+ if (rio_disc_peer(net, mport, RIO_ANY_DESTID, 0) < 0) {
+ printk(KERN_INFO
+ "RIO: master port %d device has failed discovery\n",
+ mport->id);
+ goto bail;
+ }
+
+ rio_build_route_tables();
+ }
+
+ return 0;
+
+ timeout:
+ pr_debug("timeout\n");
+ bail:
+ return -EBUSY;
+}
diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c
new file mode 100644
index 00000000000..30a11436e24
--- /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, struct device_attribute *attr, 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, struct device_attribute *attr, 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..3ca1011ceaa
--- /dev/null
+++ b/drivers/rapidio/rio.c
@@ -0,0 +1,510 @@
+/*
+ * 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
+ * @dev_id: Device specific pointer to pass on event
+ * @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,
+ void *dev_id,
+ int mbox,
+ int entries,
+ void (*minb) (struct rio_mport * mport, void *dev_id, 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, dev_id, 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
+ * @dev_id: Device specific pointer to pass on event
+ * @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,
+ void *dev_id,
+ int mbox,
+ int entries,
+ void (*moutb) (struct rio_mport * mport, void *dev_id, 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, dev_id, 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
+ * @dev_id: Device specific pointer to pass on event
+ * @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, void *dev_id, struct resource *res,
+ void (*dinb) (struct rio_mport * mport, void *dev_id, 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;
+ dbell->dev_id = dev_id;
+
+ 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
+ * @dev_id: Device specific pointer to pass on event
+ * @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,
+ void *dev_id,
+ u16 start,
+ u16 end,
+ void (*dinb) (struct rio_mport * mport, void *dev_id, 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, dev_id, 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..b242cee656e
--- /dev/null
+++ b/drivers/rapidio/rio.h
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+extern struct rio_route_ops __start_rio_route_ops[];
+extern struct rio_route_ops __end_rio_route_ops[];
+
+/* 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
diff --git a/drivers/rapidio/switches/Makefile b/drivers/rapidio/switches/Makefile
new file mode 100644
index 00000000000..b924f830176
--- /dev/null
+++ b/drivers/rapidio/switches/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for RIO switches
+#
+
+obj-$(CONFIG_RAPIDIO) += tsi500.o
diff --git a/drivers/rapidio/switches/tsi500.c b/drivers/rapidio/switches/tsi500.c
new file mode 100644
index 00000000000..c77c23bd984
--- /dev/null
+++ b/drivers/rapidio/switches/tsi500.c
@@ -0,0 +1,60 @@
+/*
+ * RapidIO Tsi500 switch 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/rio_drv.h>
+#include <linux/rio_ids.h>
+#include "../rio.h"
+
+static int
+tsi500_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, u16 table, u16 route_destid, u8 route_port)
+{
+ int i;
+ u32 offset = 0x10000 + 0xa00 + ((route_destid / 2)&~0x3);
+ u32 result;
+
+ if (table == 0xff) {
+ rio_mport_read_config_32(mport, destid, hopcount, offset, &result);
+ result &= ~(0xf << (4*(route_destid & 0x7)));
+ for (i=0;i<4;i++)
+ rio_mport_write_config_32(mport, destid, hopcount, offset + (0x20000*i), result | (route_port << (4*(route_destid & 0x7))));
+ }
+ else {
+ rio_mport_read_config_32(mport, destid, hopcount, offset + (0x20000*table), &result);
+ result &= ~(0xf << (4*(route_destid & 0x7)));
+ rio_mport_write_config_32(mport, destid, hopcount, offset + (0x20000*table), result | (route_port << (4*(route_destid & 0x7))));
+ }
+
+ return 0;
+}
+
+static int
+tsi500_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, u16 table, u16 route_destid, u8 *route_port)
+{
+ int ret = 0;
+ u32 offset = 0x10000 + 0xa00 + ((route_destid / 2)&~0x3);
+ u32 result;
+
+ if (table == 0xff)
+ rio_mport_read_config_32(mport, destid, hopcount, offset, &result);
+ else
+ rio_mport_read_config_32(mport, destid, hopcount, offset + (0x20000*table), &result);
+
+ result &= 0xf << (4*(route_destid & 0x7));
+ *route_port = result >> (4*(route_destid & 0x7));
+ if (*route_port > 3)
+ ret = -1;
+
+ return ret;
+}
+
+DECLARE_RIO_ROUTE_OPS(RIO_VID_TUNDRA, RIO_DID_TSI500, tsi500_route_add_entry, tsi500_route_get_entry);