[linux-yocto] [PATCH 02/30] drivers/rapidio: new files
Charlie Paul
cpaul.windriver at gmail.com
Thu Apr 17 19:36:50 PDT 2014
From: Paul Butler <paul.butler at windriver.com>
Signed-off-by: Paul Butler <paul.butler at windriver.com>
---
drivers/rapidio/rio-destid.c | 1550 +++++++++++++++++++++++++++++
drivers/rapidio/rio-destid.h | 85 ++
drivers/rapidio/rio-dio.c | 872 +++++++++++++++++
drivers/rapidio/rio-hotplug.c | 138 +++
drivers/rapidio/rio-hotplug.h | 34 +
drivers/rapidio/rio-job.h | 31 +
drivers/rapidio/rio-locks.c | 713 ++++++++++++++
drivers/rapidio/rio-locks.h | 67 ++
drivers/rapidio/rio-multicast.h | 28 +
drivers/rapidio/rio-net.h | 26 +
drivers/rapidio/rio-net2.c | 2033 +++++++++++++++++++++++++++++++++++++++
drivers/rapidio/rio-quirks.c | 59 ++
drivers/rapidio/rio-route.c | 290 ++++++
drivers/rapidio/rio-route.h | 31 +
14 files changed, 5957 insertions(+)
create mode 100644 drivers/rapidio/rio-destid.c
create mode 100644 drivers/rapidio/rio-destid.h
create mode 100644 drivers/rapidio/rio-dio.c
create mode 100644 drivers/rapidio/rio-hotplug.c
create mode 100644 drivers/rapidio/rio-hotplug.h
create mode 100644 drivers/rapidio/rio-job.h
create mode 100644 drivers/rapidio/rio-locks.c
create mode 100644 drivers/rapidio/rio-locks.h
create mode 100644 drivers/rapidio/rio-multicast.h
create mode 100644 drivers/rapidio/rio-net.h
create mode 100644 drivers/rapidio/rio-net2.c
create mode 100644 drivers/rapidio/rio-quirks.c
create mode 100644 drivers/rapidio/rio-route.c
create mode 100644 drivers/rapidio/rio-route.h
diff --git a/drivers/rapidio/rio-destid.c b/drivers/rapidio/rio-destid.c
new file mode 100644
index 0000000..08aacd6
--- /dev/null
+++ b/drivers/rapidio/rio-destid.c
@@ -0,0 +1,1550 @@
+/*
+ * RapidIO device destination ID assingment support
+ *
+ * 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/types.h>
+#include <linux/kernel.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/slab.h>
+#include <linux/radix-tree.h>
+#include <linux/hardirq.h>
+#include <linux/err.h>
+
+#include "rio.h"
+
+struct rio_net_node {
+ struct kref kref;
+ u16 destid;
+ u16 comptag;
+ u8 lock_hw;
+ u8 lut_update;
+ u8 return_port;
+ u8 pinned;
+#if defined(CONFIG_RAPIDIO_STATIC_DESTID)
+ struct radix_tree_root route_tree;
+ atomic_t rio_route_num;
+#endif
+};
+
+struct rio_net_id {
+ struct list_head node;
+ struct kref kref;
+ u16 destid;
+ u16 comptag;
+ int net_id;
+ int pinned;
+};
+
+static LIST_HEAD(rio_net);
+static DEFINE_SPINLOCK(rio_net_lock);
+
+#define RIO_DESTID_KEY(h, pp, pd) ( \
+ (u8)((h) & 0xff) << 24 | \
+ (u8)((pp) & 0xff) << 16 | \
+ (u16)((pd) & 0xffff))
+
+#define RIO_GET_PARENT_DEST(key) ((u16)((key) & 0xffff))
+#define RIO_GET_PARENT_PORT(key) ((u8)(((key) & 0xff0000) >> 16))
+#define RIO_GET_DEVICE_HOP(key) ((u8)(((key) & 0xff000000) >> 24))
+
+#define RIO_DESTID_ONE_WAY_TAG (0)
+#define RIO_DESTID_LEGACY_TAG (1)
+#define RIO_DESTID_REDUNDANT_TAG (2)
+
+struct rio_dest {
+ u16 destid;
+ u16 comptag;
+ u16 flags;
+ u16 return_port;
+};
+
+#define RIO_HW_LOCK_ENABLE (1)
+#define RIO_UPDATE_LUT_ENABLE (1 << 1)
+#define RIO_ONE_WAY_ENABLE (1 << 2)
+#define RIO_LEGACY_ENABLE (1 << 3)
+#define RIO_REDUNDANT_ENABLE (1 << 4)
+#define RIO_DEFAULT_FLAGS ((u16)(RIO_HW_LOCK_ENABLE | \
+ RIO_UPDATE_LUT_ENABLE))
+
+#define RIO_FLAG_GET(p, flag) (((p)->flags & flag) ? 1 : 0)
+#define RIO_FLAG_ADD(p, flag) ((p)->flags |= (u16)(flag))
+#define RIO_DEF_FLAGS_SET(p) ((p)->flags = RIO_DEFAULT_FLAGS)
+
+#define WARN_MSG \
+ "Operation aborted - Node destid tables are only probed during boot\n"
+
+/**
+ * RIO destid internal
+ */
+static void rio_net_release(struct kref *kref)
+{
+ struct rio_net_id *net = container_of(kref, struct rio_net_id, kref);
+
+ pr_info("RIO: kfree net id %d\n", net->net_id);
+ kfree(net);
+}
+
+static struct rio_net_id *rio_net_get(struct rio_net_id *net)
+{
+ if (net)
+ kref_get(&net->kref);
+
+ return net;
+}
+
+static void rio_net_put(struct rio_net_id *net)
+{
+ if (net)
+ kref_put(&net->kref, rio_net_release);
+}
+
+static int __rio_add_netid(u16 mport_destid, int net_id, u16 comptag)
+{
+ struct rio_net_id *net = kzalloc(sizeof(*net), GFP_KERNEL);
+
+ if (!net)
+ return -ENOMEM;
+
+ net->destid = mport_destid;
+ net->net_id = net_id;
+ INIT_LIST_HEAD(&net->node);
+ kref_init(&net->kref);
+ net->pinned = 0;
+ spin_lock(&rio_net_lock);
+ list_add_tail(&net->node, &rio_net);
+ spin_unlock(&rio_net_lock);
+
+ return 0;
+}
+static int __rio_remove_netid(u16 mport_destid, int net_id)
+{
+ struct rio_net_id *net, *next;
+ int rc = -ENODEV;
+
+ spin_lock(&rio_net_lock);
+ list_for_each_entry_safe(net, next, &rio_net, node) {
+ if (net->destid == mport_destid && net->net_id == net_id) {
+ if (!net->pinned) {
+ pr_info("RIO: removing net id %d\n",
+ net->net_id);
+ list_del_init(&net->node);
+ rio_net_put(net);
+ rc = 0;
+ } else {
+ pr_warn("RIO: Not removing Net id %d -in use\n",
+ net->net_id);
+ rc = -EBUSY;
+ }
+ goto done;
+ }
+ }
+done:
+ spin_unlock(&rio_net_lock);
+ return rc;
+}
+
+static struct rio_net_id *find_rio_net_id(u16 mport_destid,
+ struct rio_net_id *from)
+{
+ struct rio_net_id *net;
+ struct list_head *n;
+
+ spin_lock(&rio_net_lock);
+
+ n = from ? from->node.next : rio_net.next;
+
+ while (n && (n != &rio_net)) {
+ net = list_entry(n, struct rio_net_id, node);
+ if (net->destid == mport_destid)
+ goto exit;
+ n = n->next;
+ }
+ net = NULL;
+exit:
+ rio_net_put(from);
+ net = rio_net_get(net);
+ spin_unlock(&rio_net_lock);
+ return net;
+}
+
+int rio_pin_netid(u16 host_deviceid, int net_id)
+{
+ struct rio_net_id *net, *next;
+
+ spin_lock(&rio_net_lock);
+ list_for_each_entry_safe(net, next, &rio_net, node) {
+ if (net->destid == host_deviceid && net->net_id == net_id) {
+ net->pinned++;
+ pr_info("RIO: pinn net id %d\n", net->net_id);
+ spin_unlock(&rio_net_lock);
+ return 0;
+ }
+ }
+ spin_unlock(&rio_net_lock);
+ return -ENODEV;
+}
+
+int rio_unlock_netid(u16 host_deviceid, int net_id)
+{
+ struct rio_net_id *net, *next;
+
+ spin_lock(&rio_net_lock);
+ list_for_each_entry_safe(net, next, &rio_net, node) {
+ if (net->destid == host_deviceid && net->net_id == net_id) {
+ net->pinned--;
+ pr_info("RIO: unlocknet id %d\n", net->net_id);
+ spin_unlock(&rio_net_lock);
+ return 0;
+ }
+ }
+ spin_unlock(&rio_net_lock);
+ return -ENODEV;
+}
+
+static void rio_node_release(struct kref *kref)
+{
+ struct rio_net_node *node = container_of(kref,
+ struct rio_net_node,
+ kref);
+ kfree(node);
+}
+
+static struct rio_net_node *rio_node_get(struct rio_net_node *node)
+{
+ if (node)
+ kref_get(&node->kref);
+
+ return node;
+}
+
+static void rio_node_put(struct rio_net_node *node)
+{
+ if (node)
+ kref_put(&node->kref, rio_node_release);
+}
+
+static int __rio_add_destid(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, struct rio_dest *dest)
+{
+ unsigned long key = RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid);
+ struct rio_net_node *node = NULL;
+ int rc;
+
+ rcu_read_lock();
+ node = radix_tree_lookup(&mport->net.dst_tree, key);
+ rcu_read_unlock();
+ if (node)
+ return -EBUSY;
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return -ENOMEM;
+
+ node->destid = dest->destid;
+ node->comptag = dest->comptag;
+ if (RIO_FLAG_GET(dest, RIO_ONE_WAY_ENABLE))
+ node->return_port = dest->return_port;
+ if (RIO_FLAG_GET(dest, RIO_HW_LOCK_ENABLE))
+ node->lock_hw = 1;
+ if (RIO_FLAG_GET(dest, RIO_UPDATE_LUT_ENABLE))
+ node->lut_update = 1;
+#if defined(CONFIG_RAPIDIO_STATIC_DESTID)
+ INIT_RADIX_TREE(&node->route_tree, GFP_KERNEL);
+ atomic_set(&node->rio_route_num, 0);
+#endif
+ kref_init(&node->kref);
+ spin_lock(&mport->net.tree_lock);
+ rc = radix_tree_insert(&mport->net.dst_tree, key, node);
+ if (rc) {
+ rio_node_put(node);
+ } else {
+ struct rio_net_node *tmp = NULL;
+
+ if (RIO_FLAG_GET(dest, RIO_LEGACY_ENABLE)) {
+ tmp = radix_tree_tag_set(&mport->net.dst_tree,
+ key, RIO_DESTID_LEGACY_TAG);
+ BUG_ON(tmp != node);
+ }
+ if (RIO_FLAG_GET(dest, RIO_REDUNDANT_ENABLE)) {
+ tmp = radix_tree_tag_set(&mport->net.dst_tree,
+ key, RIO_DESTID_REDUNDANT_TAG);
+ BUG_ON(tmp != node);
+ }
+ if (RIO_FLAG_GET(dest, RIO_ONE_WAY_ENABLE)) {
+ tmp = radix_tree_tag_set(&mport->net.dst_tree,
+ key, RIO_DESTID_ONE_WAY_TAG);
+ BUG_ON(tmp != node);
+ }
+ atomic_inc(&mport->net.rio_dst_num);
+ if (dest->destid > atomic_read(&mport->net.rio_max_dest))
+ atomic_set(&mport->net.rio_max_dest, dest->destid);
+ }
+ spin_unlock(&mport->net.tree_lock);
+ return rc;
+}
+
+static struct rio_net_node *rio_get_net_node(struct rio_mport *mport,
+ u16 parent_destid,
+ int parent_port,
+ int hopcount)
+{
+ struct rio_net_node *node = NULL;
+ unsigned long key = RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid);
+
+ rcu_read_lock();
+ node = radix_tree_lookup(&mport->net.dst_tree, key);
+ if (node)
+ node = rio_node_get(node);
+ rcu_read_unlock();
+ return node;
+}
+
+static int get_destid_tag(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount, unsigned int tag)
+{
+ unsigned long key = RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid);
+ int set;
+
+ rcu_read_lock();
+ set = radix_tree_tag_get(&mport->net.dst_tree, key, tag);
+ rcu_read_unlock();
+
+ return set;
+}
+
+#if defined(CONFIG_RAPIDIO_STATIC_DESTID)
+
+static int __remove_static_routes_for_node(struct rio_mport *mport,
+ struct rio_net_node *node, int items)
+{
+ unsigned long *keys = NULL;
+ void **nptr = NULL;
+ int i, num, rc = -ENOMEM;
+
+ if (items <= 0)
+ return 0;
+
+ keys = kmalloc(sizeof(*keys) * items, GFP_KERNEL);
+ if (!keys)
+ goto done_keys;
+
+ nptr = kzalloc(sizeof(void *) * items, GFP_KERNEL);
+ if (!nptr)
+ goto done_nptr;
+
+ spin_lock(&mport->net.tree_lock);
+
+ num = radix_tree_gang_lookup_slot(&node->route_tree,
+ (void ***)nptr,
+ keys, 0, items);
+ for (i = 0; i < num; i++) {
+ u8 *curr_port = radix_tree_deref_slot((void **)nptr[i]);
+
+ if (unlikely(!curr_port))
+ continue;
+
+ curr_port = radix_tree_delete(&node->route_tree, keys[i]);
+ atomic_dec(&node->rio_route_num);
+ }
+ spin_unlock(&mport->net.tree_lock);
+
+ synchronize_rcu();
+
+ rc = (atomic_read(&node->rio_route_num) == 0 ? 0 : -EFAULT);
+
+ kfree(nptr);
+done_nptr:
+ kfree(keys);
+done_keys:
+ if (rc)
+ pr_warn("RIO: (%s) destid %hx rc %d\n",
+ __func__, node->destid, rc);
+ return rc;
+}
+#endif
+
+static int __rio_release_destid(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount)
+{
+ struct rio_net_node *node = NULL;
+ unsigned long key = RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid);
+
+ spin_lock(&mport->net.tree_lock);
+ node = radix_tree_lookup(&mport->net.dst_tree, key);
+ if (node && node->pinned) {
+ spin_unlock(&mport->net.tree_lock);
+ return -EBUSY;
+ }
+ node = radix_tree_delete(&mport->net.dst_tree, key);
+ spin_unlock(&mport->net.tree_lock);
+ if (node) {
+ synchronize_rcu();
+#if defined(CONFIG_RAPIDIO_STATIC_DESTID)
+ /* remove static routes if added */
+ __remove_static_routes_for_node(mport, node,
+ atomic_read(&node->rio_route_num));
+#endif
+ rio_node_put(node);
+ atomic_dec(&mport->net.rio_dst_num);
+ }
+ return 0;
+}
+
+int rio_pin_destid(struct rio_mport *mport, u16 parent_destid, int parent_port,
+ int hopcount)
+{
+ struct rio_net_node *node = NULL;
+ unsigned long key = RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid);
+ int rc = 0;
+
+ spin_lock(&mport->net.tree_lock);
+ node = radix_tree_lookup(&mport->net.dst_tree, key);
+ if (node)
+ node->pinned++;
+ else
+ rc = -ENODEV;
+ spin_unlock(&mport->net.tree_lock);
+ return rc;
+}
+
+int rio_unlock_destid(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount)
+{
+ struct rio_net_node *node = NULL;
+ unsigned long key = RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid);
+ int rc = 0;
+
+ spin_lock(&mport->net.tree_lock);
+ node = radix_tree_lookup(&mport->net.dst_tree, key);
+ if (node && node->pinned)
+ node->pinned--;
+ else
+ rc = -ENODEV;
+ spin_unlock(&mport->net.tree_lock);
+ return rc;
+}
+
+#if defined(CONFIG_RAPIDIO_STATIC_DESTID)
+
+int __rio_release_node_table(struct rio_mport *mport)
+{
+ int parent_port, hopcount;
+ u16 parent_destid;
+ struct rio_net_node *mp_node;
+ int rc = 0;
+
+ if (!mport)
+ return -EINVAL;
+
+ mp_node = rio_get_net_node(mport, -1, -1, -1);
+ if (mp_node) {
+ rc = __rio_release_destid(mport, -1, -1, -1);
+ rio_node_put(mp_node);
+ if (rc)
+ return rc;
+ }
+ for (hopcount = 0; hopcount < 256; hopcount++) {
+ for (parent_port = -1;; parent_port++) {
+
+ if (parent_port > 20)
+ break;
+ for (parent_destid = 0;
+ parent_destid <=
+ atomic_read(&mport->net.rio_max_dest);
+ parent_destid++) {
+
+ struct rio_net_node *node =
+ rio_get_net_node(mport,
+ parent_destid,
+ parent_port,
+ hopcount);
+ if (node) {
+ rc = __rio_release_destid(mport,
+ parent_destid,
+ parent_port,
+ hopcount);
+ rio_node_put(node);
+ if (rc)
+ return rc;
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+struct rio_route {
+ unsigned long key;
+ u8 *port;
+};
+
+static int __lookup_static_route(struct rio_mport *mport,
+ unsigned long node_key,
+ struct rio_static_route *sroute,
+ int num)
+{
+ int rc = 0;
+ struct rio_net_node *node = NULL;
+
+ rcu_read_lock();
+ node = radix_tree_lookup(&mport->net.dst_tree, node_key);
+ if (node) {
+ int i;
+ for (i = 0; i < num; i++) {
+ u8 *curr_port = NULL;
+ unsigned long key = sroute[i].sw_destid;
+ curr_port = radix_tree_lookup(&node->route_tree, key);
+ if (curr_port)
+ sroute[i].sw_port = *curr_port;
+ }
+ } else {
+ rc = -ENODEV;
+ }
+ rcu_read_unlock();
+ return rc;
+}
+
+static int __static_route_table(struct rio_mport *mport, unsigned long node_key,
+ struct rio_static_route *sroute, int items)
+{
+ int rc = 0;
+ unsigned long *keys = kmalloc(sizeof(*keys) * items, GFP_KERNEL);
+ void **nptr = NULL;
+ struct rio_net_node *node = NULL;
+
+ if (!keys)
+ return -ENOMEM;
+
+ nptr = kzalloc(sizeof(void *) * items, GFP_KERNEL);
+ if (!nptr) {
+ kfree(keys);
+ return -ENOMEM;
+ }
+
+ rcu_read_lock();
+ node = radix_tree_lookup(&mport->net.dst_tree, node_key);
+ if (node) {
+ int i;
+ int num;
+retry:
+ num = radix_tree_gang_lookup_slot(&node->route_tree,
+ (void ***)nptr,
+ keys, 0, items);
+ for (i = 0; i < num; i++) {
+ u8 *curr_port = radix_tree_deref_slot((void **)nptr[i]);
+
+ if (unlikely(!curr_port)) {
+ sroute[i].sw_destid = RIO_INVALID_DESTID;
+ sroute[i].sw_port = RIO_INVALID_ROUTE;
+ continue;
+ }
+ if (radix_tree_deref_retry(curr_port))
+ goto retry;
+
+ sroute[i].sw_destid = keys[i];
+ sroute[i].sw_port = *curr_port;
+ }
+ for (; i < items; i++) {
+ sroute[i].sw_destid = RIO_INVALID_DESTID;
+ sroute[i].sw_port = RIO_INVALID_ROUTE;
+ }
+ } else
+ rc = -ENODEV;
+
+ rcu_read_unlock();
+
+ kfree(keys);
+ kfree(nptr);
+
+ return rc;
+}
+
+static int __remove_static_route(struct rio_mport *mport,
+ unsigned long node_key,
+ struct rio_route *route)
+{
+ struct rio_net_node *node = NULL;
+ int rc = -ENODEV;
+
+ spin_lock(&mport->net.tree_lock);
+ node = radix_tree_lookup(&mport->net.dst_tree, node_key);
+ if (node) {
+ if (node->pinned) {
+ rc = -EBUSY;
+ goto done;
+ }
+ while (route->key != RIO_INVALID_DESTID) {
+ u8 *curr_port = NULL;
+ curr_port = radix_tree_lookup(&node->route_tree,
+ route->key);
+ if (curr_port) {
+ curr_port = radix_tree_delete(&node->route_tree,
+ route->key);
+ route->port = curr_port;
+ atomic_dec(&node->rio_route_num);
+ } else {
+ rc = -ENODEV;
+ goto done;
+ }
+ route++;
+ }
+ }
+done:
+ spin_unlock(&mport->net.tree_lock);
+ return rc;
+}
+
+static int __add_static_route(struct rio_mport *mport, unsigned long node_key,
+ struct rio_route *route, int update)
+{
+ struct rio_net_node *node = NULL;
+ int rc = -ENODEV;
+
+ spin_lock(&mport->net.tree_lock);
+ node = radix_tree_lookup(&mport->net.dst_tree, node_key);
+ if (node) {
+ if (node->pinned) {
+ rc = -EBUSY;
+ goto done;
+ }
+ while (route->port) {
+ void **rp = NULL;
+ u8 *curr_port = NULL;
+ rp = radix_tree_lookup_slot(&node->route_tree,
+ route->key);
+ if (rp) {
+ if (update) {
+ curr_port = radix_tree_deref_slot(rp);
+ if (unlikely(!curr_port))
+ goto next;
+ radix_tree_replace_slot(rp,
+ route->port);
+ route->port = curr_port;
+ } else {
+ rc = -EBUSY;
+ goto done;
+ }
+ } else {
+ rc = radix_tree_insert(&node->route_tree,
+ route->key, route->port);
+ if (rc)
+ goto done;
+ atomic_inc(&node->rio_route_num);
+ route->port = NULL;
+ }
+next:
+ route++;
+ }
+ }
+done:
+ spin_unlock(&mport->net.tree_lock);
+ return rc;
+}
+
+static struct rio_route *__alloc_route_table(struct rio_static_route *route,
+ int num_routes, int add)
+{
+ struct rio_route *rp;
+ int i;
+
+ rp = kzalloc(sizeof(*rp) * (num_routes + 1), GFP_KERNEL);
+ if (!rp)
+ return ERR_PTR(-ENOMEM);
+ for (i = 0; i < num_routes; i++) {
+ if (add) {
+ rp[i].port = kmalloc(sizeof(u8), GFP_KERNEL);
+ if (!rp[i].port)
+ goto cleanup;
+
+ *rp[i].port = route[i].sw_port;
+ } else
+ rp[i].port = NULL;
+
+ rp[i].key = route[i].sw_destid;
+ }
+ rp[i].port = NULL;
+ rp[i].key = RIO_INVALID_DESTID;
+ return rp;
+
+cleanup:
+ for (i = 0; i < num_routes; i++) {
+ if (rp[i].port != NULL)
+ kfree(rp[i].port);
+ }
+ kfree(rp);
+ return ERR_PTR(-ENOMEM);
+}
+
+static int add_static_route(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount,
+ struct rio_static_route *route,
+ int num_routes, int update)
+{
+ struct rio_route *rp;
+ int i, rc = -ENOMEM;
+
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+ BUG_ON(!mport || !route || !num_routes);
+
+ rp = __alloc_route_table(route, num_routes, 1);
+ if (IS_ERR(rp))
+ return PTR_ERR(rp);
+
+ rc = __add_static_route(mport,
+ RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid),
+ &rp[0],
+ update);
+ if (update)
+ synchronize_rcu();
+
+ for (i = 0; i < num_routes; i++) {
+ if (rp[i].port != NULL)
+ kfree(rp[i].port);
+ }
+ kfree(rp);
+
+ return rc;
+}
+
+int rio_remove_static_route(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount,
+ struct rio_static_route *route, int num_routes)
+{
+ struct rio_route *rp;
+ int i, rc = -ENOMEM;
+
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+ BUG_ON(!mport || !route || !num_routes);
+
+ rp = __alloc_route_table(route, num_routes, 0);
+ if (IS_ERR(rp))
+ return PTR_ERR(rp);
+
+ rc = __remove_static_route(mport,
+ RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid),
+ &rp[0]);
+ synchronize_rcu();
+
+ for (i = 0; i < num_routes; i++) {
+ if (rp[i].port) {
+ pr_debug("RIO: removed %lx %hhu from static route table\n",
+ rp[i].key, *rp[i].port);
+ kfree(rp[i].port);
+ }
+ }
+ kfree(rp);
+
+ return rc;
+}
+EXPORT_SYMBOL(rio_remove_static_route);
+
+int rio_add_static_route(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount,
+ struct rio_static_route *route, int num_routes)
+{
+ return add_static_route(mport, parent_destid, parent_port,
+ hopcount, route, num_routes, 0);
+}
+EXPORT_SYMBOL(rio_add_static_route);
+
+int rio_update_static_route(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount,
+ struct rio_static_route *route, int num_routes)
+{
+ return add_static_route(mport, parent_destid, parent_port,
+ hopcount, route, num_routes, 1);
+}
+EXPORT_SYMBOL(rio_update_static_route);
+
+int rio_lookup_static_route(struct rio_dev *rdev, u16 sw_dest, u8 *route_port)
+{
+ struct rio_static_route sroute = { sw_dest, RIO_INVALID_ROUTE };
+ int rc;
+
+ rc = __lookup_static_route(rdev->hport,
+ RIO_DESTID_KEY(rdev->hopcount,
+ rdev->prev_port,
+ rdev->prev_destid),
+ &sroute, 1);
+
+ *route_port = sroute.sw_port;
+ return rc;
+}
+EXPORT_SYMBOL(rio_lookup_static_route);
+
+int rio_lookup_static_routes(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount,
+ struct rio_static_route *sroute, int num_routes)
+{
+ return __lookup_static_route(mport,
+ RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid),
+ sroute, num_routes);
+}
+EXPORT_SYMBOL(rio_lookup_static_routes);
+
+struct rio_static_route *rio_static_route_table(struct rio_mport *mport,
+ u16 parent_destid,
+ int parent_port, int hopcount,
+ u16 *destid,
+ int *n)
+{
+ struct rio_net_node *node = rio_get_net_node(mport, parent_destid,
+ parent_port, hopcount);
+ struct rio_static_route *sroute = NULL;
+ int num = (node ? atomic_read(&node->rio_route_num) : 0);
+ int rc;
+
+ if (!node)
+ return ERR_PTR(-ENODEV);
+
+ *destid = node->destid;
+ *n = num;
+ rio_node_put(node);
+ if (!num)
+ return NULL;
+
+ sroute = kzalloc(sizeof(*sroute) * num, GFP_KERNEL);
+ if (!sroute)
+ return ERR_PTR(-ENOMEM);
+
+ rc = __static_route_table(mport,
+ RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid),
+ sroute, num);
+ if (rc) {
+ kfree(sroute);
+ return ERR_PTR(rc);
+ }
+ return sroute;
+}
+EXPORT_SYMBOL(rio_static_route_table);
+
+/**
+ * RIO static destid support - Exported KAPI
+ */
+
+int rio_get_next_destid(struct rio_mport *mport, u16 parent_destid,
+ int port_num, u8 hopcount, u16 *id, int *comptag)
+{
+ struct rio_net_node *node = NULL;
+
+ node = rio_get_net_node(mport, parent_destid, port_num, hopcount);
+
+ if (node) {
+ pr_debug("Assign destid %4.4x to device\n"
+ "At hopcount %u port %d parent destid %4.4x\n",
+ node->destid, hopcount,
+ port_num, parent_destid);
+ *comptag = node->comptag;
+ *id = node->destid;
+ rio_node_put(node);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int rio_get_next_netid(u16 host_deviceid, int *net_id, int *comptag)
+{
+ struct rio_net_id *net = NULL;
+
+ net = find_rio_net_id(host_deviceid, net);
+
+ if (net) {
+ pr_debug("Assign DUS net_id %d to mport %4.4x net\n",
+ net->net_id, host_deviceid);
+ *net_id = net->net_id;
+ *comptag = net->comptag;
+ rio_net_put(net);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#else
+
+/** Provide dynamic destid assignment **/
+
+static int next_destid;
+static int next_netid;
+
+int rio_get_next_destid(struct rio_mport *mport, u16 parent_destid,
+ int port_num, u8 hopcount, u16 *id, int *comptag)
+{
+ struct rio_net_node *node = NULL;
+ int rc = 0;
+
+ node = rio_get_net_node(mport, parent_destid, port_num, hopcount);
+
+ if (node) {
+ *comptag = node->comptag;
+ *id = node->destid;
+ rio_node_put(node);
+ } else {
+ struct rio_dest dest = {0};
+ if (next_destid == mport->host_deviceid)
+ next_destid++;
+ RIO_DEF_FLAGS_SET(&dest);
+ dest.destid = next_destid;
+ dest.comptag = next_destid;
+
+ rc = __rio_add_destid(mport, parent_destid, port_num,
+ hopcount, &dest);
+ if (rc)
+ goto done;
+ *comptag = next_destid;
+ *id = next_destid;
+ next_destid++;
+ }
+ pr_debug("Assign destid %4.4x to device\n"
+ "At hopcount %u port %d parent destid %4.4x\n",
+ *id, hopcount, port_num, parent_destid);
+
+done:
+ return rc;
+}
+
+int rio_get_next_netid(u16 host_deviceid, int *net_id, int *comptag)
+{
+ struct rio_net_id *net = NULL;
+ int rc = 0;
+
+ net = find_rio_net_id(host_deviceid, net);
+
+ if (net) {
+ *net_id = net->net_id;
+ *comptag = net->comptag;
+ rio_net_put(net);
+ } else {
+ rc = __rio_add_netid(host_deviceid, next_netid, next_destid);
+ if (rc)
+ goto done;
+ *net_id = next_netid;
+ *comptag = next_destid;
+ next_netid++;
+ next_destid++;
+ }
+ pr_debug("Assign net_id %d to mport %4.4x net\n",
+ *net_id, host_deviceid);
+
+done:
+ return rc;
+}
+#endif
+
+
+#if defined(CONFIG_RAPIDIO_HOTPLUG) || defined(CONFIG_RAPIDIO_STATIC_DESTID)
+
+static int dump_node(char *buf, struct rio_mport *mport,
+ struct rio_net_node *node, unsigned long key)
+{
+ char *str = buf;
+ int rd_tag, st_tag, ow_tag;
+
+ rcu_read_lock();
+ rd_tag = radix_tree_tag_get(&mport->net.dst_tree, key,
+ RIO_DESTID_REDUNDANT_TAG);
+ st_tag = radix_tree_tag_get(&mport->net.dst_tree, key,
+ RIO_DESTID_LEGACY_TAG);
+ ow_tag = radix_tree_tag_get(&mport->net.dst_tree, key,
+ RIO_DESTID_ONE_WAY_TAG);
+ rcu_read_unlock();
+ str += sprintf(str,
+ "%4.4x\t%d\t%d\t%4.4x\t%4.4x\t%s",
+ RIO_GET_PARENT_DEST(key),
+ RIO_GET_PARENT_PORT(key),
+ RIO_GET_DEVICE_HOP(key),
+ node->destid,
+ node->comptag,
+ (rd_tag ? "BLOCKED" : \
+ (st_tag ? "LEGACY" : \
+ (ow_tag ? "ONE-WAY" : "DEFAULT"))));
+ if (st_tag)
+ str += sprintf(str,
+ "\t(lock=%d, lut_update=%d)",
+ node->lock_hw,
+ node->lut_update);
+ if (ow_tag)
+ str += sprintf(str,
+ "\t(ret_port=%d)",
+ node->return_port);
+ str += sprintf(str, "\n");
+
+ return str - buf;
+}
+
+static ssize_t __rio_net_nodes_show(struct rio_mport *mport, char *buf)
+{
+ char *str = buf;
+ int parent_port, hopcount;
+ u16 parent_destid;
+ struct rio_net_node *mp_node;
+
+ if (atomic_read(&mport->net.rio_dst_num))
+ str += sprintf(str,
+ "parent\tparent\n"
+ "id\tport\thop\tdestid\tcomptag\ttag\n");
+
+ mp_node = rio_get_net_node(mport, -1, -1, -1);
+ if (mp_node) {
+ str += dump_node(str, mport, mp_node,
+ RIO_DESTID_KEY(-1, -1, -1));
+ rio_node_put(mp_node);
+ }
+ for (hopcount = 0; hopcount < 256; hopcount++) {
+ for (parent_port = -1;; parent_port++) {
+
+ if (parent_port > 20)
+ break;
+ for (parent_destid = 0;
+ parent_destid <=
+ atomic_read(&mport->net.rio_max_dest);
+ parent_destid++) {
+
+ struct rio_net_node *node =
+ rio_get_net_node(mport,
+ parent_destid,
+ parent_port,
+ hopcount);
+ if (node) {
+ str +=
+ dump_node(str, mport, node,
+ RIO_DESTID_KEY(hopcount,
+ parent_port,
+ parent_destid));
+ rio_node_put(node);
+ }
+ }
+ }
+ }
+ return str - buf;
+}
+
+#endif
+
+
+int rio_destid_sysfs_init(struct rio_mport *mport)
+{
+/* return sysfs_create_group(&mport->dev.kobj, &rio_attribute_group); */
+ return 0;
+}
+
+
+int rio_lookup_next_destid(struct rio_mport *mport, u16 parent_destid,
+ int port_num, u8 hopcount, u16 *id)
+{
+ struct rio_net_node *node = NULL;
+
+ node = rio_get_net_node(mport, parent_destid, port_num, hopcount);
+
+ if (node) {
+ *id = node->destid;
+ rio_node_put(node);
+ return 0;
+ }
+ return -ENODEV;
+}
+EXPORT_SYMBOL(rio_lookup_next_destid);
+
+int rio_dest_is_redundant(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount)
+{
+ return get_destid_tag(mport, parent_destid, parent_port, hopcount,
+ RIO_DESTID_REDUNDANT_TAG);
+}
+
+int rio_dest_is_legacy(struct rio_mport *mport, u16 parent_destid,
+ int parent_port,
+ int hopcount)
+{
+ return get_destid_tag(mport, parent_destid, parent_port, hopcount,
+ RIO_DESTID_LEGACY_TAG);
+}
+
+int rio_dest_is_one_way(struct rio_mport *mport, u16 parent_destid,
+ int parent_port,
+ int hopcount)
+{
+ return get_destid_tag(mport, parent_destid, parent_port, hopcount,
+ RIO_DESTID_ONE_WAY_TAG);
+}
+
+int rio_get_return_port(struct rio_mport *mport, u16 parent_destid,
+ int parent_port,
+ int hopcount, u8 *rport)
+{
+ struct rio_net_node *node = NULL;
+
+ node = rio_get_net_node(mport, parent_destid, parent_port, hopcount);
+
+ if (node) {
+ *rport = node->return_port;
+ rio_node_put(node);
+ return 0;
+ }
+ return -ENODEV;
+}
+
+int rio_get_legacy_properties(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount, u8 *lock_hw,
+ u8 *lut_update)
+{
+ struct rio_net_node *node = NULL;
+
+ node = rio_get_net_node(mport, parent_destid, parent_port, hopcount);
+
+ if (node) {
+ *lock_hw = node->lock_hw;
+ *lut_update = node->lut_update;
+ rio_node_put(node);
+ return 0;
+ }
+ return -ENODEV;
+}
+
+int rio_eval_destid(struct rio_dev *new_rdev)
+{
+ struct rio_dev *rdev = NULL;
+ int rc = 0;
+
+ rcu_read_lock();
+ rdev = radix_tree_lookup(&new_rdev->hport->net.dev_tree,
+ new_rdev->destid);
+ if (rdev) {
+ pr_warn("RIO: Duplicate DestID %hx found - New device is not added\n"
+ "Possible causes:"
+ "- You have found a BUG in the rio driver\n"
+ "- You made a mistake when defining destid tables?\n",
+ rdev->destid);
+ rc = -EBUSY;
+ }
+ rcu_read_unlock();
+
+ return rc;
+}
+
+/**
+ * rio_get_destid - 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 success or failure.
+ */
+int rio_get_destid(struct rio_mport *mport, u16 destid,
+ u8 hopcount, u16 *res_destid)
+{
+ int rc = 0;
+ u32 result;
+
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_DID_CSR, &result);
+ *res_destid = RIO_GET_DID(mport->sys_size, result);
+ return rc;
+}
+
+/**
+ * rio_read_comptag - Get the comptag for a device
+ * @port: RIO master port
+ * @destid: Destination ID of device
+ * @hopcount: Hopcount to device
+ *
+ * Reads the comptag from a device.
+ * Returns success or failure.
+ */
+int rio_read_comptag(struct rio_mport *mport, u16 destid,
+ u8 hopcount, u32 *comptag)
+{
+ int rc = 0;
+ u32 regval;
+
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_COMPONENT_TAG_CSR, ®val);
+ *comptag = regval & 0xffff;
+
+ return rc;
+}
+
+/**
+ * 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.
+ */
+int rio_set_master_destid(struct rio_mport *mport, u16 did)
+{
+ return rio_local_write_config_32(mport, RIO_DID_CSR,
+ RIO_SET_DID(mport->sys_size, did));
+}
+
+/**
+ * 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.
+ */
+int rio_has_destid(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_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.
+ */
+int rio_set_device_id(struct rio_mport *mport, u16 destid, u8 hopcount, u16 did)
+{
+ return rio_mport_write_config_32(mport, destid, hopcount, RIO_DID_CSR,
+ RIO_SET_DID(mport->sys_size, did));
+}
+
+int rio_assign_destid(struct rio_dev *rdev, struct rio_mport *mport, u16 destid,
+ int hopcount, u16 *id)
+{
+ int rc = 0;
+ if (rio_is_switch(rdev)) {
+ rdev->destid = rdev->comp_tag;
+ } else {
+ rc = rio_set_device_id(mport, destid, hopcount, *id);
+ if (rc)
+ goto done;
+ rdev->destid = *id;
+ }
+done:
+ return rc;
+}
+
+int rio_add_netid(u16 mport_destid, int net_id, int comptag)
+{
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+ return __rio_add_netid(mport_destid, net_id, comptag);
+}
+
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+EXPORT_SYMBOL(rio_add_netid);
+#endif
+
+int rio_remove_netid(u16 mport_destid, int net_id)
+{
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+ return __rio_remove_netid(mport_destid, net_id);
+}
+
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+EXPORT_SYMBOL(rio_remove_netid);
+#endif
+
+int rio_find_netid(u16 mport_destid, int *net_id)
+{
+ struct rio_net_id *net = NULL;
+
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+ net = find_rio_net_id(mport_destid, net);
+ if (net) {
+ *net_id = net->net_id;
+ rio_net_put(net);
+ return 0;
+ } else {
+ return -ENODEV;
+ }
+}
+
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+EXPORT_SYMBOL(rio_find_netid);
+#endif
+
+/**
+ * rio_add_destid - Add destid lookup entry with default properties
+ *
+ * @mport: Master port from which this device can be reached
+ * @parent_destid: device ID of switch/master port that routes/connects
+ * to this device
+ * @parent_port: Switch port that shall be used in parent device to
+ * reach the device (-1 if parent is master port)
+ * @hopcount: Number of hops, starting from @mport to reach this device
+ * @destid: device ID that shall be assigned to this device
+ * @comtag: device comptag that will be assigned to this device.
+ *
+ * This is a standard device meaning that:
+ * - It is assumed that this device shall be used and that the same route
+ * shall be used for transmit to device as shall be used for transmit
+ * from device.
+ * - If the device is a switch, the lut table will be configured with HW locks
+ * taken. The in-port will be used for return route setup. In single
+ * enumeration host mode, only the enum host will modify lut setup but
+ * discovery nodes will still claim the device HW lock while testing
+ * for enum completed flag. In multiple enumeration domain mode, discovery
+ * nodes will update the lut when adding domain return paths and they will
+ * do so with the HW lock taken.
+ */
+
+int rio_add_destid(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, u16 destid, u16 comptag)
+{
+ struct rio_dest dest = {0};
+
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+ RIO_DEF_FLAGS_SET(&dest);
+ dest.destid = destid;
+ dest.comptag = comptag;
+
+ return __rio_add_destid(mport, parent_destid, parent_port,
+ hopcount, &dest);
+}
+
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+EXPORT_SYMBOL(rio_add_destid);
+#endif
+/**
+ * rio_block_destid_route - Add destid lookup entry which blocks routing
+ * to device trough this path.
+ *
+ * @mport: Master port from which this device can be reached
+ * @parent_destid: device ID of switch/master port that connects
+ * to this device
+ * @parent_port: Switch port that shall is used in parent device to
+ * reach the device.
+ * @hopcount: Number of hops, starting from @mport to reach this device
+ * @destid: device ID
+ * @comtag: device comptag
+ *
+ * When blocking a route:
+ * It is assumed that this device shall not be used/or at least not
+ * be reachable trough this path. The device ID will not be added to
+ * the @parent_port in parent switch device lut.
+ * NOTE:
+ * It is also assumed that the parent device is a switch and not the
+ * @mport itself.
+ * It is an error to add the first switch in a @mport net as a blocked
+ * device. If the master port shall not be used then the master port
+ * /first switch should not be enabled in the first place.
+ */
+int rio_block_destid_route(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, u16 destid, u16 comptag)
+{
+ struct rio_dest dest = {0};
+
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+ RIO_DEF_FLAGS_SET(&dest);
+ RIO_FLAG_ADD(&dest, RIO_REDUNDANT_ENABLE);
+ dest.destid = destid;
+ dest.comptag = comptag;
+
+ return __rio_add_destid(mport, parent_destid, parent_port,
+ hopcount, &dest);
+}
+
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+EXPORT_SYMBOL(rio_block_destid_route);
+#endif
+
+/**
+ * rio_split_destid_route - Add destid lookup entry with separat transmit
+ * and return paths.
+ *
+ * @mport: Master port from which this device can be reached
+ * @parent_destid: device ID of switch/master port that routes/connects
+ * to this device
+ * @parent_port: Switch port that shall be used in parent device to
+ * reach the device.
+ * @hopcount: Number of hops, starting from @mport to reach this device
+ * @destid: device ID that shall be assigned to this device
+ * @comtag: device comptag that will be assigned to this device.
+ * @return_port: port used in device for routing traffic back to parent device
+ *
+ * Split routes:
+ * If you have redundant connections between switches you may use
+ * use split routes to prevent network loops. In that case links are
+ * used in one-way mode and lut tables are setup so that one link,
+ * the @parent_port, is used for all traffic from the parent device
+ * to the @destid device, while all traffic from the @destid device to
+ * the parent device will use the @return_port.
+ *
+ * All other properties, e.g. lut updates, hw_locking, etc. is handled
+ * in the same way as it is for standard devices.
+ *
+ * NOTE:
+ * Adding a split route is only supported when both parent and destid
+ * are switches.
+ */
+int rio_split_destid_route(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, u16 destid, u16 comptag,
+ u8 return_port)
+{
+ struct rio_dest dest = {0};
+
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+ RIO_DEF_FLAGS_SET(&dest);
+ RIO_FLAG_ADD(&dest, RIO_ONE_WAY_ENABLE);
+ dest.destid = destid;
+ dest.comptag = comptag;
+ dest.return_port = return_port;
+
+ return __rio_add_destid(mport, parent_destid, parent_port,
+ hopcount, &dest);
+}
+
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+EXPORT_SYMBOL(rio_split_destid_route);
+#endif
+/**
+ * rio_legacy_destid_route - Add destid lookup entry for devices
+ * reciding in domains handled by non-linux
+ * hosts.
+ *
+ * @mport: Master port from which this device can be reached
+ * @parent_destid: device ID of switch/master port that routes/connects
+ * to this device
+ * @parent_port: Switch port that shall be used in parent device to
+ * reach the device.
+ * @hopcount: Number of hops, starting from @mport to reach this device
+ * @destid: device ID
+ * @comtag: device comptag
+ * @lock_hw: HW locks may be taken on device
+ * @lut_update: Device lut table may be updated
+ *
+ * Split routes:
+ * ???
+ *
+ * NOTE:
+ * ???
+ */
+int rio_legacy_destid_route(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, u16 destid, u16 comptag,
+ u8 lock_hw, u8 lut_update)
+{
+ struct rio_dest dest = {0};
+
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+ if (lock_hw)
+ RIO_FLAG_ADD(&dest, RIO_HW_LOCK_ENABLE);
+ if (lut_update)
+ RIO_FLAG_ADD(&dest, RIO_UPDATE_LUT_ENABLE);
+
+ RIO_FLAG_ADD(&dest, RIO_LEGACY_ENABLE);
+
+ dest.destid = destid;
+ dest.comptag = comptag;
+
+ return __rio_add_destid(mport, parent_destid, parent_port,
+ hopcount, &dest);
+}
+
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+EXPORT_SYMBOL(rio_legacy_destid_route);
+#endif
+
+void rio_release_destid(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount)
+{
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return;
+#endif
+ __rio_release_destid(mport, parent_destid, parent_port,
+ hopcount);
+}
+
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+EXPORT_SYMBOL(rio_release_destid);
+#endif
+
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+int rio_release_node_table(struct rio_mport *mport)
+{
+#if !defined(CONFIG_RAPIDIO_HOTPLUG)
+ if (WARN(system_state != SYSTEM_BOOTING, WARN_MSG))
+ return -EINVAL;
+#endif
+
+ return __rio_release_node_table(mport);
+}
+EXPORT_SYMBOL(rio_release_node_table);
+#endif
+
+#if defined(CONFIG_RAPIDIO_HOTPLUG) || defined(CONFIG_RAPIDIO_STATIC_DESTID)
+
+/**
+ * debug helper
+ */
+ssize_t rio_net_nodes_show(struct rio_mport *mport, char *buf)
+{
+ return __rio_net_nodes_show(mport, buf);
+}
+EXPORT_SYMBOL(rio_net_nodes_show);
+#endif
diff --git a/drivers/rapidio/rio-destid.h b/drivers/rapidio/rio-destid.h
new file mode 100644
index 0000000..caa0b9f
--- /dev/null
+++ b/drivers/rapidio/rio-destid.h
@@ -0,0 +1,85 @@
+#ifndef RIO_DESTID_H
+#define RIO_DESTID_H
+
+/*
+ * RapidIO interconnect services
+ *
+ * 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>
+
+extern int rio_pin_destid(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount);
+extern int rio_unlock_destid(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount);
+
+extern int rio_set_device_id(struct rio_mport *mport, u16 destid,
+ u8 hopcount, u16 did);
+extern int rio_assign_destid(struct rio_dev *rdev, struct rio_mport *port,
+ u16 destid, int hopcount, u16 *id);
+extern int rio_get_next_destid(struct rio_mport *mport, u16 parent_destid,
+ int port_num, u8 hopcount,
+ u16 *id, int *comptag);
+extern int rio_get_next_netid(u16 host_deviceid, int *net_id, int *comptag);
+
+extern int rio_pin_netid(u16 host_deviceid, int net_id);
+extern int rio_unlock_netid(u16 host_deviceid, int net_id);
+
+extern int rio_dest_is_redundant(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount);
+extern int rio_dest_is_legacy(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount);
+extern int rio_dest_is_one_way(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount);
+
+extern int rio_eval_destid(struct rio_dev *new_rdev);
+extern int rio_get_destid(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u16 *res_destid);
+extern int rio_read_comptag(struct rio_mport *mport, u16 destid, u8 hopcount,
+ u32 *comptag);
+extern int rio_set_master_destid(struct rio_mport *mport, u16 did);
+extern int rio_has_destid(int src_ops, int dst_ops);
+extern int rio_destid_sysfs_init(struct rio_mport *mport);
+extern int rio_get_return_port(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount, u8 *rport);
+extern int rio_get_legacy_properties(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, u8 *lock_hw,
+ u8 *lut_update);
+extern int rio_destid_get_parent_port(struct rio_mport *mport,
+ u16 destid, u8 *port);
+
+#if !defined(CONFIG_RAPIDIO_STATIC_DESTID)
+
+extern int rio_add_netid(u16 mport_destid, int net_id, int comptag);
+
+extern int rio_add_destid(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, u16 destid, u16 comptag);
+extern int rio_block_destid_route(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, u16 destid, u16 comptag);
+extern int rio_split_destid_route(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, u16 destid, u16 comptag,
+ u8 return_port);
+extern int rio_legacy_destid_route(struct rio_mport *mport,
+ u16 parent_destid, int parent_port,
+ int hopcount, u16 destid, u16 comptag,
+ u8 lock_hw, u8 lut_update);
+extern void rio_release_destid(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount);
+#else
+
+extern int rio_lookup_static_route(struct rio_dev *rdev, u16 sw_dest,
+ u8 *route_port);
+
+#endif
+
+#endif
diff --git a/drivers/rapidio/rio-dio.c b/drivers/rapidio/rio-dio.c
new file mode 100644
index 0000000..272fc41
--- /dev/null
+++ b/drivers/rapidio/rio-dio.c
@@ -0,0 +1,872 @@
+/*
+ * RapidIO Direct I/O driver
+ *
+ * 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/gfp.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/rio_dio.h>
+
+static LIST_HEAD(io_wins);
+static LIST_HEAD(dio_channels);
+static DEFINE_SPINLOCK(rio_dio_lock);
+
+#if defined(CONFIG_RAPIDIO_DIO_DMA)
+
+struct rio_dio_dma {
+ struct list_head node;
+ struct dma_chan *txdmachan;
+ enum dma_ctrl_flags flags;
+ struct dma_device *txdmadev;
+ struct kref kref;
+};
+
+/**
+ * __dma_register
+ *
+ * Allocate and initialize a direct I/O channel for
+ * DMA transfers. Initialize reference counting
+ * and ad channel to rio_dio global channel
+ * list.
+ *
+ * Returns: On success - Pointer to I/O channel data
+ * On failure - NULL
+ */
+static struct rio_dio_dma *__dma_register(void)
+{
+ dma_cap_mask_t mask;
+ unsigned long flags;
+ struct rio_dio_dma *dma = kzalloc(sizeof(*dma),
+ GFP_KERNEL);
+ if (!dma)
+ return NULL;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ dma->txdmachan = dma_request_channel(mask, NULL, NULL);
+ if (!dma->txdmachan) {
+ kfree(dma);
+ return NULL;
+ }
+ dma->flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
+ dma->txdmadev = dma->txdmachan->device;
+ kref_init(&dma->kref);
+ spin_lock_irqsave(&rio_dio_lock, flags);
+ list_add_tail(&dma->node, &dio_channels);
+ spin_unlock_irqrestore(&rio_dio_lock, flags);
+ dev_dbg(dma->txdmadev->dev,
+ "rio_dio Register DMA channel\n");
+ return dma;
+}
+/**
+ * __rio_dio_method_setup
+ *
+ * @dio_channel: RIO direct I/O method data.
+ *
+ * DMA version of generic method
+ *
+ * Returns: On success - 0
+ * On failure != 0
+ */
+static int __rio_dio_method_setup(void **dio_channel)
+{
+ *dio_channel = __dma_register();
+ if (!*dio_channel)
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * __dma_get
+ *
+ * @dio_dma: RIO direct I/O method data.
+ *
+ * Increment reference count if @dio_dma is a valid
+ * method data pointer, i.e. if it is found in the
+ * rio_dio global channel list.
+ *
+ * Returns: On success - @dio_dma
+ * On failure - NULL
+ */
+static inline struct rio_dio_dma *__dma_get(struct rio_dio_dma *dio_dma)
+{
+ struct rio_dio_dma *entry, *next, *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rio_dio_lock, flags);
+ list_for_each_entry_safe(entry, next, &dio_channels, node) {
+ if (dio_dma && dio_dma == entry) {
+ kref_get(&dio_dma->kref);
+ ret = entry;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&rio_dio_lock, flags);
+ return ret;
+}
+
+/**
+ * __rio_dio_method_get
+ *
+ * @dio_channel: RIO direct I/O method data.
+ *
+ * DMA version of generic method
+ *
+ * Returns: On success - @dio_channel
+ * On failure - NULL
+ */
+static void *__rio_dio_method_get(void *dio_channel)
+{
+ struct rio_dio_dma *dio_dma = (struct rio_dio_dma *)dio_channel;
+
+ return __dma_get(dio_dma);
+}
+/**
+ * __dma_release
+ *
+ * @kref:
+ *
+ * Remove dio_channel from the rio_dio global channel list.
+ * Release DMA channel and free direct I/O channel data
+ *
+ */
+
+static void __dma_release(struct kref *kref)
+{
+ struct rio_dio_dma *dio_dma = container_of(kref,
+ struct rio_dio_dma, kref);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rio_dio_lock, flags);
+ list_del(&dio_dma->node);
+ spin_unlock_irqrestore(&rio_dio_lock, flags);
+
+ dev_dbg(dio_dma->txdmadev->dev,
+ "rio_dio Release DMA channel\n");
+ dma_release_channel(dio_dma->txdmachan);
+ kfree(dio_dma);
+}
+
+/**
+ * __rio_dio_method_put
+ *
+ * @dio_channel: RIO direct I/O method data.
+ *
+ * DMA version of generic method
+ *
+ */
+
+static void __rio_dio_method_put(void *dio_channel)
+{
+ struct rio_dio_dma *dio_dma = (struct rio_dio_dma *)dio_channel;
+
+ if (dio_dma)
+ kref_put(&dio_dma->kref, __dma_release);
+}
+
+/**
+ * __dma_callback
+ *
+ * @completion: DMA semaphore
+ *
+ * Used by the DMA driver to signal that a transfer is done
+ *
+ */
+
+static void __dma_callback(void *completion)
+{
+ complete(completion);
+}
+/**
+ * __dma_cpy
+ *
+ * @dio_dma: RIO direct I/O method data.
+ * @phys: Physical (SRIO device) read/write address
+ * @buf: Pointer from/to where data shall be read/written
+ * @len: Number of bytes to read/write
+ * @dir: Transfer direction to/from SRIO device
+ *
+ * Setup DMA transfer and wait for it to finish
+ * There may be multiple threads running this function
+ * in parallel for a single channel, and multiple channels
+ * may be used in parallel.
+ *
+ * Returns: On success - 0
+ * On failure != 0
+ */
+
+static int __dma_cpy(struct rio_dio_dma *dio_dma,
+ resource_size_t phys,
+ void *buf, u32 len,
+ enum dma_data_direction dir)
+{
+ struct dma_async_tx_descriptor *tx;
+ struct dma_slave_config config;
+ dma_addr_t dma_src, dma_dst, dma_map;
+ dma_cookie_t cookie;
+ unsigned long tmo = msecs_to_jiffies(3000);
+ struct completion cmp;
+ enum dma_status status = DMA_ERROR;
+ enum dma_ctrl_flags eflag;
+ u8 align;
+ char *message = NULL;
+
+ if (!__rio_dio_method_get(dio_dma))
+ return -EFAULT;
+
+ align = dio_dma->txdmadev->copy_align;
+ if (1 << align > len) {
+ message = "DMA Buffer alignment error";
+ goto out_err;
+ }
+ config.direction = dir;
+
+ if (dir == DMA_TO_DEVICE) {
+ config.dst_addr_width = 16;
+ config.dst_maxburst = 16;
+ dma_map = dma_src = dma_map_single(dio_dma->txdmadev->dev,
+ buf, len, dir);
+ dma_dst = (dma_addr_t)phys;
+ eflag = DMA_COMPL_SKIP_DEST_UNMAP;
+ } else {
+ config.src_addr_width = 16;
+ config.src_maxburst = 16;
+ dma_src = (dma_addr_t)phys;
+ dma_map = dma_dst = dma_map_single(dio_dma->txdmadev->dev,
+ buf, len, dir);
+ eflag = DMA_COMPL_SKIP_SRC_UNMAP;
+ }
+ dmaengine_slave_config(dio_dma->txdmachan, &config);
+
+ tx = dio_dma->txdmadev->device_prep_dma_memcpy(dio_dma->txdmachan,
+ dma_dst,
+ dma_src,
+ len,
+ dio_dma->flags | eflag);
+ if (!tx) {
+ message = "DMA channel prepare error";
+ goto out_prep;
+ }
+ init_completion(&cmp);
+ tx->callback = __dma_callback;
+ tx->callback_param = &cmp;
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ message = "DMA channel submit error";
+ goto out_submit;
+ }
+ dma_async_issue_pending(dio_dma->txdmachan);
+ tmo = wait_for_completion_timeout(&cmp, tmo);
+ status = dma_async_is_tx_complete(dio_dma->txdmachan,
+ cookie, NULL, NULL);
+ if (!tmo) {
+ message = "DMA transfer timeout";
+ status = -ETIME;
+ dmaengine_terminate_all(dio_dma->txdmachan);
+ goto out_err;
+ }
+ goto out;
+
+out_prep:
+out_submit:
+ dma_unmap_single(dio_dma->txdmadev->dev, dma_map, len, dir);
+
+out_err:
+ dev_info(dio_dma->txdmadev->dev, "%s\n", message);
+
+out:
+ __rio_dio_method_put(dio_dma);
+ if (status != DMA_SUCCESS)
+ return status == -ETIME ? status : -EFAULT;
+ return 0;
+
+}
+/**
+ * __rio_dio
+ *
+ * @dio_channel: RIO direct I/O method data.
+ * @res: RapidIO mapping region phys and virt start address
+ * @buf: Pointer from/to where data shall be read/written
+ * @len: Number of bytes to read/write
+ * @dir: Transfer direction to/from SRIO device
+ *
+ * DMA version of generic method
+ *
+ */
+
+static inline int __rio_dio(void *dio_channel,
+ struct rio_map_addr *res,
+ void *buf,
+ u32 len,
+ enum dma_data_direction dir)
+{
+ struct rio_dio_dma *dio_dma = (struct rio_dio_dma *)dio_channel;
+
+ return __dma_cpy(dio_dma, (resource_size_t)res->phys,
+ buf, len, dir);
+}
+
+#else
+
+/**
+ * __rio_dio_method_setup
+ *
+ * @dio_channel: NULL.
+ *
+ * memcpy version of generic method
+ *
+ * Returns: 0
+ */
+
+static int __rio_dio_method_setup(void **dio_channel)
+{
+ *dio_channel = NULL;
+ return 0;
+}
+/**
+ * __rio_dio_method_get
+ *
+ * @dio_channel: NULL
+ *
+ * memcpy version of generic method
+ *
+ * Returns: NULL
+ */
+
+static void *__rio_dio_method_get(void *dio_channel)
+{
+ return NULL;
+}
+/**
+ * __rio_dio_method_put
+ *
+ * @dio_channel: NULL
+ *
+ * memcpy version of generic method
+ *
+ */
+
+static void __rio_dio_method_put(void *dio_channel)
+{
+}
+/**
+ * __rio_dio
+ *
+ * @dio_channel: RIO direct I/O method data.
+ * @res: RapidIO mapping region phys and virt start address
+ * @buf: Pointer from/to where data shall be read/written
+ * @len: Number of bytes to read/write
+ * @dir: Transfer direction to/from SRIO device
+ *
+ * memcpy version of generic method
+ *
+ */
+
+static inline int __rio_dio(void *dio_channel,
+ struct rio_map_addr *res,
+ void *buf,
+ u32 len,
+ enum dma_data_direction dir)
+{
+ u8 *src, *dst;
+ int rc = 0;
+
+ if (dir == DMA_FROM_DEVICE) {
+ int size = len;
+
+ dst = (u8 *)buf;
+ src = (u8 *)res->va;
+
+ while (size > 0 && !rc) {
+ int tsize = (size > 3 ? 4 : (size < 2 ? 1 : 2));
+ switch (tsize) {
+ case 1:
+ rc = rio_in_8(dst, (u8 __iomem *)src);
+ break;
+ case 2:
+ rc = rio_in_be16((u16 *)dst,
+ (u16 __iomem *)src);
+ break;
+ case 4:
+ rc = rio_in_be32((u32 *)dst,
+ (u32 __iomem *)src);
+ break;
+ default:
+ pr_warn("(%s): illegal read tsize %d\n",
+ __func__, tsize);
+ rc = -EINVAL;
+ }
+ src += tsize;
+ dst += tsize;
+ size -= tsize;
+ }
+ } else {
+ dst = (u8 *)res->va;
+ src = (u8 *)buf;
+ memcpy(dst, src, len);
+ }
+
+ return rc;
+}
+
+#endif
+/**
+ * __rio_dio_release_region
+ *
+ * @kref:
+ *
+ * Remove @io_win from the rio_dio global io window list.
+ * Give up reference to @dio_channel
+ * Free outbound RapidIO bus region and io_win data.
+ *
+ */
+
+static void __rio_dio_release_region(struct kref *kref)
+{
+ struct rio_dio_win *io_win = container_of(kref,
+ struct rio_dio_win,
+ kref);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rio_dio_lock, flags);
+ list_del(&io_win->node);
+ spin_unlock_irqrestore(&rio_dio_lock, flags);
+
+ pr_debug("RIO: Return ATMU window %u\n", io_win->outb_win);
+ rio_release_outb_region(io_win->rdev->hport, io_win->outb_win);
+ __rio_dio_method_put(io_win->dio_channel);
+
+ kfree(io_win);
+}
+/**
+ * __rio_dio_region_get
+ *
+ * @io_win: RIO I/O window data.
+ *
+ * Increment reference count if @io_win is a valid
+ * pointer, i.e. if it is found in the
+ * rio_dio global I/O window list.
+ *
+ * Returns: On success - @io_win
+ * On failure - NULL
+ */
+
+static inline
+struct rio_dio_win *__rio_dio_region_get(struct rio_dio_win *io_win)
+{
+ struct rio_dio_win *entry, *next, *ret = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rio_dio_lock, flags);
+ list_for_each_entry_safe(entry, next, &io_wins, node) {
+ if (io_win == entry) {
+ kref_get(&io_win->kref);
+ ret = entry;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&rio_dio_lock, flags);
+ return ret;
+}
+
+/**
+ * __rio_dio_write
+ *
+ * @io_win: RIO I/O window data.
+ * @offset: Offset into device direct I/O memory space
+ * @buf: Pointer from where data shall be read
+ * @len: Number of bytes to write
+ *
+ * Create mapping from local iomem to rio space
+ * and write data to device.
+ *
+ * Returns: On success - 0
+ * On failure != 0
+ *
+ */
+
+static int __rio_dio_write(struct rio_dio_win *io_win,
+ u32 offset, void *buf, u32 mflags, u32 len)
+{
+ struct rio_map_addr res;
+ u8 *dst;
+ int rc;
+
+ if (!__rio_dio_region_get(io_win))
+ return -ENODEV;
+
+ rc = rio_map_outb_mem(io_win->rdev->hport,
+ io_win->outb_win,
+ io_win->rdev->destid,
+ offset,
+ mflags,
+ &res);
+ if (rc)
+ goto out;
+
+ dst = (u8 *)res.va;
+ switch (len) {
+ case 1:
+ out_8(dst, *((u8 *)buf));
+ break;
+ case 2:
+ out_be16((u16 *)dst, *((u16 *)buf));
+ break;
+ default:
+ rc = __rio_dio(io_win->dio_channel,
+ &res,
+ buf,
+ len,
+ DMA_TO_DEVICE);
+ }
+out:
+ rio_dio_region_put(io_win);
+ return rc;
+}
+
+
+/**
+ * __rio_dio_read
+ *
+ * @io_win: RIO I/O window data.
+ * @offset: Offset into device direct I/O memory space
+ * @buf: Pointer to where data shall be stored
+ * @len: Number of bytes to read
+ *
+ * Create mapping from local iomem to rio space
+ * and read data from device.
+ *
+ * Returns: On success - 0
+ * On failure != 0
+ *
+ */
+
+static int __rio_dio_read(struct rio_dio_win *io_win,
+ u32 offset, void *buf, u32 mflags, u32 len)
+{
+ struct rio_map_addr res;
+ int rc;
+
+ if (!__rio_dio_region_get(io_win))
+ return -ENODEV;
+
+ rc = rio_map_outb_mem(io_win->rdev->hport,
+ io_win->outb_win,
+ io_win->rdev->destid,
+ offset,
+ mflags,
+ &res);
+
+ if (rc)
+ goto out;
+
+ switch (len) {
+ case 1:
+ rc = rio_in_8((u8 *)buf, res.va);
+ break;
+ case 2:
+ rc = rio_in_be16((u16 *)buf, res.va);
+ break;
+ default:
+ rc = __rio_dio(io_win->dio_channel,
+ &res,
+ buf,
+ len,
+ DMA_FROM_DEVICE);
+ }
+out:
+ rio_dio_region_put(io_win);
+ return rc;
+}
+#define RIO_DIO_8_BAD 0
+#define RIO_DIO_16_BAD (offset & 1)
+#define RIO_DIO_32_BAD (offset & 3)
+#define RIO_DIO_BUFF_BAD (len > io_win->win_size)
+
+/**
+ * RIO_DIO_READ - Generate rio_dio_read_* functions
+ *
+ * @size: Size of direct I/O space read (8, 16, 32 bit)
+ * @type: C type of value argument
+ * @len: Length of direct I/O space read in bytes
+ *
+ * Generates rio_dio_read_* functions used for direct I/O access
+ * to end-point device.
+ *
+ * Returns: 0 on success and != 0 on failure
+ *
+ * NOTE: With regard to @io_win, these functions are not re-entrant.
+ * It is assumed that the user provides proper synchronization
+ * methods assuring that calls, pertaining to the same @io_win,
+ * are serialized.
+ *
+ */
+#define RIO_DIO_READ(size, type, len) \
+ int rio_dio_read_##size \
+ (struct rio_dio_win *io_win, u32 offset, u32 mflags, type *value) \
+ { \
+ int rc; \
+ u32 dst; \
+ \
+ if (RIO_DIO_##size##_BAD) \
+ return RIO_BAD_SIZE; \
+ \
+ rc = __rio_dio_read(io_win, offset, &dst, mflags, len); \
+ *value = (type)dst; \
+ return rc; \
+ }
+
+/**
+ * RIO_DIO_WRITE - Generate rio_dio_write_* functions
+ *
+ * @size: Size of direct I/O space write (8, 16, 32 bits)
+ * @type: C type of value argument
+ * @len: Length of direct I/O space write in bytes
+ *
+ * Generates rio_dio_write_* functions used for direct I/O access
+ * to end-point device.
+ *
+ * Returns: 0 on success and != 0 on failure
+ *
+ * NOTE: With regard to @io_win, these function are not re-entrant.
+ * It is assumed that the user provides proper synchronization
+ * methods assuring that calls, pertaining to the same @io_win,
+ * are serialized.
+ *
+ */
+#define RIO_DIO_WRITE(size, type, len) \
+ int rio_dio_write_##size \
+ (struct rio_dio_win *io_win, u32 offset, u32 mflags, type *src) \
+ { \
+ int rc; \
+ \
+ if (RIO_DIO_##size##_BAD) \
+ return RIO_BAD_SIZE; \
+ \
+ rc = __rio_dio_write(io_win, offset, src, mflags, len); \
+ return rc; \
+ }
+
+RIO_DIO_READ(8, u8, 1)
+EXPORT_SYMBOL_GPL(rio_dio_read_8);
+RIO_DIO_READ(16, u16, 2)
+EXPORT_SYMBOL_GPL(rio_dio_read_16);
+RIO_DIO_READ(32, u32, 4)
+EXPORT_SYMBOL_GPL(rio_dio_read_32);
+RIO_DIO_WRITE(8, u8, 1)
+EXPORT_SYMBOL_GPL(rio_dio_write_8);
+RIO_DIO_WRITE(16, u16, 2)
+EXPORT_SYMBOL_GPL(rio_dio_write_16);
+RIO_DIO_WRITE(32, u32, 4)
+EXPORT_SYMBOL_GPL(rio_dio_write_32);
+
+/**
+ * rio_dio_read_buff
+ *
+ * @io_win: RIO I/O window data.
+ * @offset: Offset into device direct I/O memory space
+ * @dst: Pointer where read data will be stored
+ * @len: Length of direct I/O space read in bytes
+ *
+ * used for direct I/O access
+ * to end-point device.
+ *
+ * Returns: 0 on success and != 0 on failure
+ *
+ * NOTE: With regard to @io_win, this function is not re-entrant.
+ * It is assumed that the user provides proper synchronization
+ * methods assuring that calls, pertaining to the same @io_win,
+ * are serialized.
+ *
+ */
+int rio_dio_read_buff(struct rio_dio_win *io_win, u32 offset,
+ u8 *dst, u32 mflags, u32 len)
+{
+ if (RIO_DIO_BUFF_BAD)
+ return RIO_BAD_SIZE;
+
+ return __rio_dio_read(io_win, offset, dst, mflags, len);
+}
+EXPORT_SYMBOL_GPL(rio_dio_read_buff);
+/**
+ * rio_dio_write_buff
+ *
+ * @io_win: RIO I/O window data.
+ * @offset: Offset into device direct I/O memory space
+ * @dst: Data to be written
+ * @len: Length of direct I/O space read in bytes
+ *
+ * used for direct I/O access
+ * to end-point device.
+ *
+ * Returns: 0 on success and != 0 on failure
+ *
+ * NOTE: With regard to @io_win, this function is not re-entrant.
+ * It is assumed that the user provides proper synchronization
+ * methods assuring that calls, pertaining to the same @io_win,
+ * are serialized.
+ *
+ */
+int rio_dio_write_buff(struct rio_dio_win *io_win, u32 offset,
+ u8 *src, u32 mflags, u32 len)
+{
+ if (RIO_DIO_BUFF_BAD)
+ return RIO_BAD_SIZE;
+
+ return __rio_dio_write(io_win, offset, src, mflags, len);
+}
+EXPORT_SYMBOL_GPL(rio_dio_write_buff);
+
+int rio_dio_const_win(struct rio_dio_win *io_win,
+ void *buf, u32 len,
+ enum dma_data_direction dir,
+ struct rio_map_addr *res)
+{
+ int rc;
+
+ if (!__rio_dio_region_get(io_win))
+ return -ENODEV;
+
+ rc = __rio_dio(io_win->dio_channel,
+ res,
+ buf,
+ len,
+ dir);
+
+ rio_dio_region_put(io_win);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rio_dio_const_win);
+
+/**
+ * rio_dio_req_region
+ *
+ * @dio_channel: Direct I/O mode, must be allocated prior to
+ * this call using rio_dio_method_setup().
+ * @rdev: RIO device
+ * @size: Size of ATMU window
+ * @transaction_type: Flags for mapping. 0 for using default
+ * (NREAD/NWRITE) transaction type.
+ *
+ * Allocate and initialize a RIO direct I/O ATMU window.
+ * Reserves an outbound region in the RapidIO bus address space.
+ * The reserved region may be used to create mappings from local
+ * iomem to rio space.
+ *
+ * Returns: On success - Pointer to RIO I/O window data.
+ * On failure - NULL
+ *
+ * NOTE: Once you have called this function, use rio_dio_region_put()
+ * to give up your reference instead of freeing the
+ * RIO I/O window data structure directly.
+ */
+struct rio_dio_win *rio_dio_req_region(void *dio_channel,
+ struct rio_dev *rdev,
+ resource_size_t size,
+ u32 transaction_type)
+{
+ unsigned long flags;
+ struct rio_dio_win *io_win = kzalloc(sizeof(*io_win), GFP_KERNEL);
+ char *message = NULL;
+
+ if (!io_win) {
+ message = "Out of memory";
+ goto out_err;
+ }
+ io_win->rdev = rdev;
+ io_win->dio_channel = __rio_dio_method_get(dio_channel);
+ io_win->win_size = size;
+ /* Setup outbound ATMU I/O window
+ */
+ if (rio_req_outb_region(rdev->hport, size, rdev->dev.init_name,
+ transaction_type, &io_win->outb_win)) {
+ message = "Mapping outbound ATMU window failed";
+ goto out_map;
+ }
+ kref_init(&io_win->kref);
+ spin_lock_irqsave(&rio_dio_lock, flags);
+ list_add_tail(&io_win->node, &io_wins);
+ spin_unlock_irqrestore(&rio_dio_lock, flags);
+ pr_debug("RIO: Alloc ATMU window %u\n", io_win->outb_win);
+ goto out;
+
+out_map:
+ __rio_dio_method_put(dio_channel);
+ kfree(io_win);
+ io_win = NULL;
+out_err:
+ pr_warn("RIO: %s\n", message);
+out:
+ return io_win;
+}
+EXPORT_SYMBOL_GPL(rio_dio_req_region);
+
+/**
+ * rio_dio_region_put - decrement reference count.
+ *
+ * @io_win: RIO I/O window in question.
+ *
+ * Frees outbound RapidIO bus region and io_win data
+ * when the reference count drops to zero
+ */
+
+void rio_dio_region_put(struct rio_dio_win *io_win)
+{
+ if (io_win)
+ kref_put(&io_win->kref, __rio_dio_release_region);
+}
+EXPORT_SYMBOL_GPL(rio_dio_region_put);
+
+/**
+ * rio_dio_method_setup
+ *
+ * @dio_channel: RIO direct I/O method data.
+ *
+ * Allocate and initialize a direct I/O channel.
+ * The I/O method used for RapidIO transfers > 4 bytes
+ * is configurable at kernel build time. If DMA is choosen
+ * a successful method setup implies that a DMA channel has
+ * been allocated. The default option is to use memcpy, in
+ * which case not particular setup is required and the method
+ * data ptr will be set to NULL.
+ * It is, regardless of selected method, entirely possible to
+ * use the same @dio_channel in multiple I/O windows.
+ *
+ * RapidIO transfers <= 4 bytes will use the standar io.h in_*
+ * and out_* access functions, this is not configurable.
+ *
+ * Returns: On success - 0
+ * On failure != 0
+ *
+ * NOTE: Once you have called this function, use rio_dio_method_put()
+ * to give up your reference instead of freeing the
+ * RIO direct I/O method data structure directly.
+ */
+
+int rio_dio_method_setup(void **dio_channel)
+{
+ return __rio_dio_method_setup(dio_channel);
+}
+EXPORT_SYMBOL_GPL(rio_dio_method_setup);
+
+/**
+ * rio_dio_method_put - decrement reference count.
+ *
+ * @dio_channel: RIO direct I/O channel in question.
+ *
+ * Frees direct I/O channel when the reference count
+ * drops to zero
+ */
+
+void rio_dio_method_put(void *dio_channel)
+{
+ __rio_dio_method_put(dio_channel);
+}
+EXPORT_SYMBOL_GPL(rio_dio_method_put);
diff --git a/drivers/rapidio/rio-hotplug.c b/drivers/rapidio/rio-hotplug.c
new file mode 100644
index 0000000..14115d0
--- /dev/null
+++ b/drivers/rapidio/rio-hotplug.c
@@ -0,0 +1,138 @@
+/*
+ * RapidIO hotplug support
+ *
+ * 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.
+ */
+
+/* #define DEBUG */
+
+#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 <linux/jiffies.h>
+#include <linux/slab.h>
+
+#include "rio.h"
+#include "rio-hotplug.h"
+
+
+#ifdef CONFIG_RAPIDIO_HOTPLUG
+/**
+ * Hotplug API internal to RIO driver - pw-event driven.
+ */
+/**
+ * store_new_id - sysfs frontend to rio_add_dynid()
+ * @driver: target device driver
+ * @buf: buffer for scanning device ID data
+ * @count: input size
+ *
+ * Allow RIO IDs to be added to an existing driver via sysfs.
+ */
+static ssize_t
+store_new_id(struct device_driver *driver, const char *buf, size_t count)
+{
+ return count;
+}
+static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+
+/**
+ * store_remove_id - remove a RIO device ID from this driver
+ * @driver: target device driver
+ * @buf: buffer for scanning device ID data
+ * @count: input size
+ *
+ * Removes a dynamic RIO device ID to this driver.
+ */
+static ssize_t
+store_remove_id(struct device_driver *driver, const char *buf, size_t count)
+{
+ return count;
+}
+static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
+
+int rio_create_newid_file(struct rio_driver *drv)
+{
+ int error = 0;
+ if (drv->probe != NULL)
+ error = driver_create_file(&drv->driver, &driver_attr_new_id);
+ return error;
+}
+
+void rio_remove_newid_file(struct rio_driver *drv)
+{
+ driver_remove_file(&drv->driver, &driver_attr_new_id);
+}
+
+int rio_create_removeid_file(struct rio_driver *drv)
+{
+ int error = 0;
+ if (drv->probe != NULL)
+ error = driver_create_file(&drv->driver,
+ &driver_attr_remove_id);
+ return error;
+}
+
+void rio_remove_removeid_file(struct rio_driver *drv)
+{
+ driver_remove_file(&drv->driver, &driver_attr_remove_id);
+}
+
+/**
+ * Hotplug API exported
+ */
+
+void rio_remove_mport_net(struct rio_mport *port, int hw_access)
+{
+ rio_job_init(port, NULL, -1, 0, hw_access, RIO_DEVICE_EXTRACTION);
+}
+EXPORT_SYMBOL_GPL(rio_remove_mport_net);
+void rio_rescan_mport(struct rio_mport *port)
+{
+ rio_job_init(port, NULL, -1, 0, 1, RIO_DEVICE_INSERTION);
+}
+EXPORT_SYMBOL_GPL(rio_rescan_mport);
+
+#endif
+
+/*
+ * FIXME!!! - remove as soon as hp is fixed on DUL
+ */
+
+/**
+ * rio_init_device - sets up a RIO device
+ * @port: Master port to send transactions
+ * @rdev:
+ * @hopcount: Current hopcount
+ *
+ */
+int rio_init_device(struct rio_dev *rdev)
+{
+ struct rio_mport *port = rdev->hport;
+ u8 hopcount = rdev->hopcount;
+ u16 destid = rdev->destid;
+
+ if (rio_has_destid(rdev->src_ops, rdev->dst_ops)) {
+ rio_assign_destid(rdev, port, rdev->destid, rdev->hopcount,
+ &rdev->destid);
+ }
+ rio_fixup_dev(rio_fixup_early, rdev, destid, hopcount);
+ /* Assign component tag to device */
+ rio_mport_write_config_32(port, destid, hopcount,
+ RIO_COMPONENT_TAG_CSR, rdev->comp_tag);
+
+ return 0;
+}
+EXPORT_SYMBOL(rio_init_device);
diff --git a/drivers/rapidio/rio-hotplug.h b/drivers/rapidio/rio-hotplug.h
new file mode 100644
index 0000000..8d1260f
--- /dev/null
+++ b/drivers/rapidio/rio-hotplug.h
@@ -0,0 +1,34 @@
+#ifndef RIO_HOTPLUG_H
+#define RIO_HOTPLUG_H
+
+/*
+ * RapidIO interconnect services
+ *
+ * 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>
+
+
+#if defined(CONFIG_RAPIDIO_HOTPLUG)
+
+extern int rio_create_newid_file(struct rio_driver *drv);
+extern void rio_remove_newid_file(struct rio_driver *drv);
+extern int rio_create_removeid_file(struct rio_driver *drv);
+extern void rio_remove_removeid_file(struct rio_driver *drv);
+
+#else
+
+static inline int rio_create_newid_file(struct rio_driver *drv) { return 0; }
+static inline void rio_remove_newid_file(struct rio_driver *drv) {}
+static inline int rio_create_removeid_file(struct rio_driver *drv) { return 0; }
+static inline void rio_remove_removeid_file(struct rio_driver *drv) {}
+
+#endif
+
+#endif
diff --git a/drivers/rapidio/rio-job.h b/drivers/rapidio/rio-job.h
new file mode 100644
index 0000000..3aaca9c
--- /dev/null
+++ b/drivers/rapidio/rio-job.h
@@ -0,0 +1,31 @@
+#ifndef _RIO_JOB_H
+#define _RIO_JOB_H
+
+/*
+ * RapidIO job support
+ *
+ * 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>
+
+#define RIO_JOB_STATE_GO 0
+#define RIO_JOB_STATE_INIT 1
+#define RIO_JOB_STATE_PENDING 2
+#define RIO_JOB_STATE_ABORT 3
+
+struct rio_job {
+ struct rio_mport *mport;
+ struct rio_dev *rdev;
+ int port;
+ int event;
+ u32 flags;
+ int srio_down;
+};
+
+#endif
diff --git a/drivers/rapidio/rio-locks.c b/drivers/rapidio/rio-locks.c
new file mode 100644
index 0000000..02c4e89
--- /dev/null
+++ b/drivers/rapidio/rio-locks.c
@@ -0,0 +1,713 @@
+/*
+ * RapidIO enumeration and discovery support
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter at kernel.crashing.org>
+ *
+ * Copyright 2009 Integrated Device Technology, Inc.
+ * Alex Bounine <alexandre.bounine at idt.com>
+ * - Added Port-Write/Error Management initialization and handling
+ *
+ * Copyright 2009 Sysgo AG
+ * Thomas Moll <thomas.moll at sysgo.com>
+ * - Added Input- Output- enable functionality, to allow full communication
+ *
+ * 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.
+ */
+
+/* #define DEBUG */
+
+#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 <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/radix-tree.h>
+
+#include "rio.h"
+
+DECLARE_RWSEM(rio_tree_sem);
+DEFINE_SPINLOCK(rio_lock_lock);
+
+/* #define RIO_LOCK_DEBUG */
+
+struct rio_dev *lookup_rdev(struct rio_mport *mport, u16 destid)
+{
+ struct rio_dev *rdev;
+
+ rcu_read_lock();
+ rdev = radix_tree_lookup(&mport->net.dev_tree, destid);
+ if (rdev)
+ rdev = rio_dev_get(rdev);
+ rcu_read_unlock();
+ return rdev;
+}
+
+struct rio_dev *lookup_rdev_next(struct rio_dev *rdev, int port_num)
+{
+ struct rio_dev *next = NULL;
+ struct rio_mport *mport = rdev->hport;
+ int rc;
+ u16 device_destid;
+
+ rc = rio_lookup_next_destid(mport, rdev->destid,
+ port_num, rdev->hopcount + 1,
+ &device_destid);
+ if (rc)
+ return ERR_PTR(rc);
+
+ next = lookup_rdev(mport, device_destid);
+ if (!next)
+ return ERR_PTR(-ENODEV);
+
+ return next;
+}
+
+/**
+ * rio_set_host_lock - Sets the Host Device ID Lock CSR on a device
+ * @port: Master port to send transaction
+ * @destid: Destination ID of device
+ * @hopcount: Number of hops to the device
+ * @lock: Lock to be set on device
+ *
+ * Used during enumeration to set the Host Device ID Lock CSR on a
+ * RIO device. Returns success of write.
+ */
+static int rio_set_host_lock(struct rio_mport *port, u16 destid, u8 hopcount)
+{
+ int rc;
+
+ if (destid == port->host_deviceid) {
+ rc = rio_local_write_config_32(port, RIO_HOST_DID_LOCK_CSR,
+ port->host_deviceid);
+ } else
+ rc = rio_mport_write_config_32(port, destid, hopcount,
+ RIO_HOST_DID_LOCK_CSR, port->host_deviceid);
+ if (rc)
+ pr_debug("RIO:(%s) destid %hu hopcount %d - Write error\n",
+ __func__, destid, hopcount);
+ return rc;
+}
+
+/**
+ * rio_hw_lock - Acquires host device lock for specified device
+ * @port: Master port to send transaction
+ * @destid: Destination ID for device/switch
+ * @hopcount: Hopcount to reach switch
+ *
+ * Attepts to acquire host device lock for specified device
+ * Returns 0 if device lock acquired or ETIME if timeout expires.
+ */
+static int rio_hw_lock(struct rio_mport *port, u16 destid, u8 hopcount)
+{
+ u16 lock;
+ int rc = 0;
+
+ spin_lock(&rio_lock_lock);
+
+ rc = rio_get_host_lock(port, destid, hopcount, &lock);
+ if (rc)
+ goto done;
+
+ if (lock == port->host_deviceid)
+ goto lock_err;
+ /* Attempt to acquire device lock */
+ rc = rio_set_host_lock(port, destid, hopcount);
+ if (rc)
+ goto done;
+ rc = rio_get_host_lock(port, destid, hopcount, &lock);
+ if (rc)
+ goto done;
+
+ if (lock != port->host_deviceid)
+ goto to_err;
+
+#if defined(RIO_LOCK_DEBUG)
+ pr_debug("RIO: Device destid 0x%x hopcount %hhu locked by 0x%x\n",
+ destid, hopcount, lock);
+#endif
+done:
+ spin_unlock(&rio_lock_lock);
+ return rc;
+
+to_err:
+ pr_debug("RIO: timeout when locking device %hx:%hhu lock owned by %hu\n",
+ destid, hopcount, lock);
+ rc = -ETIME;
+ goto done;
+lock_err:
+ pr_debug("RIO: Device destid %hx hopcount %hhu is already locked by %hx\n",
+ destid, hopcount, port->host_deviceid);
+ rc = -EFAULT;
+ goto done;
+
+}
+
+
+static int __rio_clear_locks(struct rio_dev *from, struct rio_dev *to)
+{
+ struct rio_dev *prev, *curr;
+ int rc = 0;
+
+ if (!from)
+ return 0;
+
+ if (!to)
+ return 0;
+
+ if (to->use_hw_lock) {
+ rc = rio_hw_unlock(to->hport, to->destid, to->hopcount);
+ if (rc)
+ goto done;
+ }
+
+ pr_debug("RIO: Unlocked %s\n", rio_name(to));
+
+ curr = to;
+
+ rcu_read_lock();
+ while (curr) {
+ prev = radix_tree_lookup(&curr->hport->net.dev_tree,
+ curr->prev_destid);
+ if (prev) {
+ if (prev->use_hw_lock) {
+ rc = rio_hw_unlock(prev->hport, prev->destid,
+ prev->hopcount);
+ if (rc) {
+ pr_debug("RIO: Failed to unlock %s\n",
+ rio_name(prev));
+ rcu_read_unlock();
+ goto done;
+ }
+ }
+ pr_debug("RIO: Unlocked %s\n", rio_name(prev));
+ if (prev == from) {
+ rcu_read_unlock();
+ goto done;
+ }
+ }
+ curr = prev;
+ }
+ rcu_read_unlock();
+
+done:
+ return rc;
+}
+static int __rio_take_locks(struct rio_dev *from, struct rio_dev *to)
+{
+ struct rio_dev *prev, *curr;
+ int rc = 0;
+
+ if (!from)
+ return 0;
+ if (!to)
+ return 0;
+
+ if (to->use_hw_lock) {
+ rc = rio_hw_lock(to->hport, to->destid, to->hopcount);
+ if (rc)
+ goto done;
+ }
+
+ pr_debug("RIO: Add lock to %s\n", rio_name(to));
+
+ curr = to;
+
+ rcu_read_lock();
+ while (curr) {
+ prev = radix_tree_lookup(&curr->hport->net.dev_tree,
+ curr->prev_destid);
+ if (prev) {
+ if (prev->use_hw_lock)
+ rc = rio_hw_lock(prev->hport, prev->destid,
+ prev->hopcount);
+ if (rc) {
+ rcu_read_unlock();
+ goto unlock;
+ }
+ pr_debug("RIO: Add lock to %s\n", rio_name(prev));
+ if (prev == from) {
+ rcu_read_unlock();
+ goto done;
+ }
+ }
+ curr = prev;
+ }
+ rcu_read_unlock();
+done:
+ return rc;
+
+unlock:
+ __rio_clear_locks(curr, to);
+ goto done;
+}
+static int rio_hw_lock_devices(struct rio_mport *mport, struct rio_dev *from,
+ struct rio_dev *to)
+{
+ return __rio_take_locks(from, to);
+}
+
+static void rio_timeout(unsigned long data)
+{
+ /* timed out, set flag */
+ *(int *)data = 1;
+}
+
+/**
+ * rio_job_hw_lock_wait - Tries to take the @host_device_lock on a RIO device
+ * or a group of RIO devices in a RIO network. A number
+ * of attempts will be made to take the lock/locks.
+ * Before each attempt, RIO job status GO is verified
+ * and after each unsuccessful attempt the
+ * rio_hw_lock() return status and timeout status is
+ * checked. As long as the @tmo is not up and the
+ * rio_hw_lock() return status indicates that
+ * someone else owns the lock, the caller will be put
+ * to sleep for a period of 10 ms.
+ *
+ * @job: RIO Job description
+ * @from: Root Device to start locking from - set to @NULL if not applicable
+ * @to: Last device to take lock on - set to @NULL if not applicable
+ * @destid: Destination ID of device
+ * @hopcount: Number of hops to the device
+ * @tmo: Max time to wait in units of seconds
+ *
+ * If @from is defined it is assumed that the user have incremented the
+ * @from reference count before calling rio_job_hw_lock_wait()
+ *
+ * Returns 0 at success and != 0 at failure
+ *
+ * In any case, the @from reference count is always consumed
+ * if @from was defined.
+ */
+int rio_job_hw_lock_wait(struct rio_job *job, struct rio_dev *from,
+ struct rio_dev *to,
+ u16 destid, u8 hopcount, int tmo)
+{
+ int rc = 0;
+ int timeout_flag = 0;
+ struct timer_list rio_timer =
+ TIMER_INITIALIZER(rio_timeout, 0, 0);
+
+ rio_timer.expires = jiffies + tmo * HZ;
+ rio_timer.data = (unsigned long)&timeout_flag;
+ add_timer(&rio_timer);
+
+ do {
+ if (from)
+ rc = rio_hw_lock_devices(job->mport, from, to);
+ else
+ rc = rio_hw_lock(job->mport, destid, hopcount);
+
+ if (rc != -ETIME)
+ goto done;
+
+ msleep(10);
+
+ } while (!timeout_flag);
+done:
+ del_timer_sync(&rio_timer);
+ return rc;
+}
+
+/**
+ * rio_job_hw_lock_wait_cond - Tries to take the @host_device_lock on a
+ * RIO device. A number of attempts will be made
+ * to take the lock. Before each attempt, RIO job
+ * status GO is verified and after each
+ * unsuccessful attempt the rio_hw_lock() return
+ * status and timeout status is checked. As long
+ * as the @tmo is not up and the rio_hw_lock()
+ * return status indicates that someone else owns
+ * the lock, the caller will be put to sleep
+ * for a period of 50 ms. When the
+ * @host_device_lock is set a user defined function
+ * is invoked. A != 0 return value from this
+ * function will terminate
+* rio_job_hw_lock_wait_cond() otherwise the
+ * @host_device_lock is relead and the wait
+ * loop continues.
+ *
+ * @job: RIO Job description
+ * @destid: Destination ID of device
+ * @hopcount: Number of hops to the device
+ * @tmo: Max time to wait in units of seconds
+ *
+ * Returns 0 at success and != 0 at failure
+ */
+int rio_job_hw_lock_wait_cond(struct rio_job *job, u16 destid,
+ u8 hopcount, int tmo, int lock,
+ int (*cond)(struct rio_mport *, u16, u8))
+{
+ int rc = 0;
+ int timeout_flag = 0;
+ struct timer_list rio_timer =
+ TIMER_INITIALIZER(rio_timeout, 0, 0);
+
+ rio_timer.expires = jiffies + tmo * HZ;
+ rio_timer.data = (unsigned long)&timeout_flag;
+ add_timer(&rio_timer);
+
+ do {
+ rc = rio_hw_lock(job->mport, destid, hopcount);
+ if ((!lock) || (rc == 0)) {
+ rc = cond(job->mport, destid, hopcount);
+ if (rc == 0)
+ goto done;
+ rio_hw_unlock(job->mport, destid, hopcount);
+ }
+ if (rc != -ETIME)
+ goto done;
+ if (timeout_flag)
+ break;
+ msleep(50);
+
+ } while (!timeout_flag);
+done:
+ del_timer_sync(&rio_timer);
+ return rc;
+}
+
+int rio_job_hw_wait_cond(struct rio_job *job, u16 destid,
+ u8 hopcount, int tmo,
+ int (*cond)(struct rio_mport *, u16, u8))
+{
+ int rc = 0;
+ int timeout_flag = 0;
+ struct timer_list rio_timer =
+ TIMER_INITIALIZER(rio_timeout, 0, 0);
+
+ rio_timer.expires = jiffies + tmo * HZ;
+ rio_timer.data = (unsigned long)&timeout_flag;
+ add_timer(&rio_timer);
+
+ do {
+ rc = cond(job->mport, destid, hopcount);
+ if (rc == 1)
+ goto done;
+ if (timeout_flag)
+ break;
+ msleep(100);
+
+ } while (!timeout_flag);
+done:
+ del_timer_sync(&rio_timer);
+ return rc;
+}
+
+/**
+ * rio_hw_busy_lock_wait - Tries to take the @host_device_lock on a RIO device.
+ * A number of attempts will be made to take the lock.
+ * After each unsuccessful attempt the rio_hw_lock()
+ * return status and timeout status is checked. As
+ long as the @wait_ms is not up and the
+ rio_hw_lock() return status indicates that
+ * someone else owns the lock, the caller will be
+ * delayed in a busy loop for a period of 1 ms.
+ *
+ * @mport: Master port to send transaction
+ * @destid: Destination ID of device
+ * @hopcount: Number of hops to the device
+ * @wait_ms: Max time to wait in units of milli seconds
+ *
+ * Returns 0 at success and != 0 at failure
+ */
+int rio_hw_lock_busy_wait(struct rio_mport *mport, u16 destid,
+ u8 hopcount, int wait_ms)
+{
+ int tcnt = 0;
+ int rc = rio_hw_lock(mport, destid, hopcount);
+
+ while (rc == -ETIME) {
+ if (tcnt >= wait_ms)
+ break;
+ mdelay(1);
+ tcnt++;
+ rc = rio_hw_lock(mport, destid, hopcount);
+ }
+ return rc;
+}
+
+/**
+ * rio_hw_lock_wait - Tries to take the @host_device_lock on a RIO device.
+ * A number of attempts will be made to take the lock.
+ * After each unsuccessful attempt the rio_hw_lock() return
+ * status and timeout status is checked. As long as the
+ * @tmo is not up and the rio_hw_lock() return status
+ * indicates that someone else owns the lock, the caller
+ * will be put to sleep for a period of 10 ms.
+ *
+ * @mport: Master port to send transaction
+ * @destid: Destination ID of device
+ * @hopcount: Number of hops to the device
+ * @tmo: Max time to wait in units of seconds
+ *
+ * Returns 0 at success and != 0 at failure
+ */
+int rio_hw_lock_wait(struct rio_mport *mport, u16 destid, u8 hopcount, int tmo)
+{
+ int rc = 0;
+ int timeout_flag = 0;
+ struct timer_list rio_timer =
+ TIMER_INITIALIZER(rio_timeout, 0, 0);
+
+ rio_timer.expires = jiffies + tmo * HZ;
+ rio_timer.data = (unsigned long)&timeout_flag;
+ add_timer(&rio_timer);
+
+ do {
+ rc = rio_hw_lock(mport, destid, hopcount);
+
+ if (rc != -ETIME)
+ goto done;
+
+ msleep(10);
+
+ } while (!timeout_flag);
+done:
+ del_timer_sync(&rio_timer);
+ return rc;
+}
+
+/**
+ * rio_get_by_ptr - search for a matching device in the global list
+ *
+ * @rdev: RIO device
+ *
+ * Increments rdev reference counter and returns rdev pointer
+ * if a matching device pointer is found in the global list.
+ * Users are responsible to decrement the reference count, i.e.
+ * rio_dev_put() when done with the device.
+ *
+ * Returns NULL if RIO device doesn't exist in the global list.
+ */
+struct rio_dev *rio_get_by_ptr(struct rio_dev *rdev)
+{
+ struct rio_dev *tmp = NULL;
+
+ if (!rdev)
+ return NULL;
+
+ while ((tmp = rio_get_device(RIO_ANY_ID, RIO_ANY_ID, tmp)) != NULL) {
+ if (tmp == rdev)
+ return tmp;
+ }
+ return NULL;
+}
+
+/**
+ * rio_get_root_node - Find switch connected to local master port
+ * in a RIO network.
+ *
+ * @mport: Local master port
+ *
+ * Increments switch reference counter and returns switch rdev pointer
+ * if a matching switch device is found in the global list.
+ * Users are responsible to decrement the reference count, i.e.
+ * rio_dev_put() when done with the device.
+ *
+ * Returns NULL if a root switch doesn't exist in the global list.
+ */
+struct rio_dev *rio_get_root_node(struct rio_mport *mport)
+{
+ int rc;
+ u16 device_destid;
+ struct rio_dev *rdev = NULL;
+
+ BUG_ON(!mport);
+
+ rc = rio_lookup_next_destid(mport, mport->host_deviceid,
+ -1, 0, &device_destid);
+ if (rc)
+ return ERR_PTR(rc);
+
+ rdev = lookup_rdev(mport, device_destid);
+ if (!rdev)
+ return ERR_PTR(-ENODEV);
+
+ return rdev;
+}
+
+/**
+ * rio_get_host_lock - Reads the Host Device ID Lock CSR on a device
+ * @port: Master port to send transaction
+ * @destid: Destination ID of device
+ * @hopcount: Number of hops to the device
+ * @result: Location where ID lock is stored
+ *
+ * Returns the RIO device access status.
+ */
+int rio_get_host_lock(struct rio_mport *port, u16 destid,
+ u8 hopcount, u16 *result)
+{
+ u32 regval;
+ int rc;
+
+ if (destid == port->host_deviceid) {
+ rc = rio_local_read_config_32(port,
+ RIO_HOST_DID_LOCK_CSR, ®val);
+ } else {
+ rc = rio_mport_read_config_32(port, destid, hopcount,
+ RIO_HOST_DID_LOCK_CSR, ®val);
+ }
+ *result = regval & 0xffff;
+
+ if (rc)
+ pr_debug("RIO:(%s) destid %hx hopcount %d - Read error\n",
+ __func__, destid, hopcount);
+ return rc;
+}
+
+/**
+ * rio_clear_host_lock - Clear @lockid on a device
+ * @port: Master port to send transaction
+ * @destid: Destination ID of device
+ * @hopcount: Number of hops to the device
+ * @lockid: Device ID Lock to be cleared
+ *
+ * Returns 0 if the @lockid is cleared
+ *
+ * Returns < 0 if:
+ * - At RIO device access faults
+ * - If @lockid was not set on the device
+ * - If the device was not unlocked
+ */
+
+int rio_clear_host_lock(struct rio_mport *port, u16 destid,
+ u8 hopcount, u16 lockid)
+{
+ int rc;
+ u16 result;
+
+ spin_lock(&rio_lock_lock);
+ rc = rio_get_host_lock(port, destid, hopcount, &result);
+ if (rc)
+ goto done;
+ if (result != lockid) {
+ pr_debug("RIO: device lock on %hx:%hhu is owned by %hu\n",
+ destid, hopcount, result);
+ rc = -EINVAL;
+ goto done;
+ }
+ if (destid == port->host_deviceid) {
+ rc = rio_local_write_config_32(port, RIO_HOST_DID_LOCK_CSR,
+ lockid);
+ } else
+ rc = rio_mport_write_config_32(port, destid, hopcount,
+ RIO_HOST_DID_LOCK_CSR,
+ lockid);
+ if (rc) {
+ pr_debug("RIO: destid %hu hopcount %d - Write error\n",
+ destid, hopcount);
+ goto done;
+ }
+ rc = rio_get_host_lock(port, destid, hopcount, &result);
+ if (rc)
+ goto done;
+
+ if (result != 0xffff) {
+ pr_debug("RIO: badness when releasing device lock %hx:%hhu\n",
+ destid, hopcount);
+ rc = -EINVAL;
+ }
+done:
+ spin_unlock(&rio_lock_lock);
+ return rc;
+
+}
+
+/**
+ * rio_unlock - Releases @host_device_lock for specified device
+ *
+ * @port: Master port to send transaction
+ * @destid: Destination ID for device/switch
+ * @hopcount: Hopcount to reach switch
+ *
+ * Returns 0 if the @host_device_lock is cleared
+ *
+ * Returns < 0 if:
+ * - At RIO device access faults
+ * - If @host_device_lock was not set on the device
+ * - If the device was not unlocked
+ */
+int rio_hw_unlock(struct rio_mport *port, u16 destid, u8 hopcount)
+{
+ u16 lock;
+ int rc = 0;
+
+ spin_lock(&rio_lock_lock);
+ /* Release device lock */
+ rc = rio_get_host_lock(port, destid, hopcount, &lock);
+ if (rc)
+ goto done;
+
+ if (lock != port->host_deviceid)
+ goto lock_err;
+
+ rc = rio_set_host_lock(port, destid, hopcount);
+ if (rc)
+ goto done;
+
+ rc = rio_get_host_lock(port, destid, hopcount, &lock);
+ if (rc)
+ goto done;
+
+ if (lock != 0xffff)
+ goto unlock_err;
+#if defined(RIO_LOCK_DEBUG)
+ pr_debug("RIO: Device destid %hx hopcount %hhu is unlocked by %hx\n",
+ destid, hopcount, port->host_deviceid);
+#endif
+done:
+ spin_unlock(&rio_lock_lock);
+ return rc;
+lock_err:
+ pr_debug("RIO: release lock err - lock is not taken, destid %hx, hopcount %hhu, lock 0x%x",
+ destid, hopcount, lock);
+ rc = -EINVAL;
+ goto done;
+unlock_err:
+ pr_debug("RIO: badness when releasing device lock %hx:%hhu\n",
+ destid, hopcount);
+ rc = -EINVAL;
+ goto done;
+}
+
+/**
+ * rio_job_hw_unlock_devices - Releases @host_device_lock for a group
+ * of RIO devices. If the job description defines
+ * @rdev, locks are released on all devices
+ * between and including the switch connected
+ * to local master port and the job @rdev.
+ * The master port device ID Lock is
+ * always released.
+ * @job: RIO Job description
+ *
+ */
+void rio_job_hw_unlock_devices(struct rio_job *job)
+{
+ struct rio_dev *from = NULL;
+ struct rio_dev *to = job->rdev;
+
+ if (job->rdev)
+ from = rio_get_root_node(job->mport);
+ if (from && !IS_ERR(from)) {
+ pr_debug("RIO: clear locks from %s to %s\n",
+ rio_name(from), rio_name(to));
+ __rio_clear_locks(from, to);
+ rio_dev_put(from);
+ }
+}
diff --git a/drivers/rapidio/rio-locks.h b/drivers/rapidio/rio-locks.h
new file mode 100644
index 0000000..d0cc151
--- /dev/null
+++ b/drivers/rapidio/rio-locks.h
@@ -0,0 +1,67 @@
+#ifndef _RIO_LOCK_H
+#define _RIO_LOCK_H
+
+/*
+ * RapidIO job support
+ *
+ * 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.
+ */
+
+extern struct rw_semaphore rio_tree_sem;
+
+static inline void rio_tree_read_lock(void)
+{
+ down_read(&rio_tree_sem);
+#if defined(RIO_LOCK_DEBUG)
+ pr_debug("RIO: PID %u got the rio_tree_read_lock\n", current->pid);
+#endif
+}
+static inline void rio_tree_read_unlock(void)
+{
+#if defined(RIO_LOCK_DEBUG)
+ pr_debug("RIO: PID %u release the rio_tree_read_lock\n", current->pid);
+#endif
+ up_read(&rio_tree_sem);
+}
+static inline void rio_tree_write_lock(void)
+{
+ down_write(&rio_tree_sem);
+#if defined(RIO_LOCK_DEBUG)
+ pr_debug("RIO: PID %u got the rio_tree_write_lock\n", current->pid);
+#endif
+}
+static inline void rio_tree_write_unlock(void)
+{
+#if defined(RIO_LOCK_DEBUG)
+ pr_debug("RIO: PID %u release the rio_tree_write_lock\n",
+ current->pid);
+#endif
+ up_write(&rio_tree_sem);
+}
+extern struct rio_dev *lookup_rdev_next(struct rio_dev *rdev, int port_num);
+extern struct rio_dev *rio_get_by_ptr(struct rio_dev *rdev);
+extern int rio_get_host_lock(struct rio_mport *port, u16 destid, u8 hopcount,
+ u16 *result);
+extern int rio_clear_host_lock(struct rio_mport *port, u16 destid,
+ u8 hopcount, u16 lockid);
+extern int rio_hw_unlock(struct rio_mport *port, u16 destid, u8 hopcount);
+extern void rio_job_hw_unlock_devices(struct rio_job *job);
+extern int rio_hw_lock_busy_wait(struct rio_mport *port, u16 destid,
+ u8 hopcount, int wait_ms);
+extern int rio_job_hw_lock_wait(struct rio_job *job, struct rio_dev *from,
+ struct rio_dev *to,
+ u16 destid, u8 hopcount, int tmo);
+extern int rio_job_hw_lock_wait_cond(struct rio_job *job, u16 destid,
+ u8 hopcount, int tmo, int lock,
+ int (*unlock)(struct rio_mport *,
+ u16, u8));
+
+extern int rio_hw_lock_wait(struct rio_mport *mport, u16 destid,
+ u8 hopcount, int tmo);
+extern int rio_job_hw_wait_cond(struct rio_job *job, u16 destid,
+ u8 hopcount, int tmo,
+ int (*cond)(struct rio_mport *, u16, u8));
+#endif
diff --git a/drivers/rapidio/rio-multicast.h b/drivers/rapidio/rio-multicast.h
new file mode 100644
index 0000000..78887db
--- /dev/null
+++ b/drivers/rapidio/rio-multicast.h
@@ -0,0 +1,28 @@
+#ifndef RIO_MULTICAST_H
+#define RIO_MULTICAST_H
+
+/*
+ * RapidIO multicast
+ *
+ * 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>
+
+extern int rio_multicast_del_all_ports(struct rio_dev *rdev, int maskId);
+extern int rio_multicast_add_port(struct rio_dev *rdev,
+ int maskId, int port_num);
+extern int rio_multicast_add_assoc(struct rio_dev *rdev,
+ int maskId, int destId);
+extern int rio_multicast_del_port(struct rio_dev *rdev,
+ int maskId, int port_num);
+
+extern int rio_init_switch_pw(struct rio_dev *rdev);
+extern int rio_update_pw(struct rio_dev *rdev, int sw_port,
+ int add, int lock);
+
+
+#endif
diff --git a/drivers/rapidio/rio-net.h b/drivers/rapidio/rio-net.h
new file mode 100644
index 0000000..d8fafe6
--- /dev/null
+++ b/drivers/rapidio/rio-net.h
@@ -0,0 +1,26 @@
+#ifndef RIO_NET_H
+#define RIO_NET_H
+
+/*
+ * RapidIO interconnect services
+ *
+ * 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>
+
+#define RIO_DEV_NOT_ADDED (0)
+#define RIO_DEV_IS_SWITCH (1)
+#define RIO_DEV_DISABLED (2)
+
+
+extern int rio_device_enable(struct rio_dev *rdev);
+extern int rio_device_disable(struct rio_dev *rdev);
+extern int rio_switch_port_is_active(struct rio_dev *rdev, int sport);
+
+#endif
diff --git a/drivers/rapidio/rio-net2.c b/drivers/rapidio/rio-net2.c
new file mode 100644
index 0000000..f9a8b1c
--- /dev/null
+++ b/drivers/rapidio/rio-net2.c
@@ -0,0 +1,2033 @@
+/*
+ * RapidIO enumeration and discovery support
+ *
+ * Copyright 2005 MontaVista Software, Inc.
+ * Matt Porter <mporter at kernel.crashing.org>
+ *
+ * Copyright 2009 Integrated Device Technology, Inc.
+ * Alex Bounine <alexandre.bounine at idt.com>
+ * - Added Port-Write/Error Management initialization and handling
+ *
+ * Copyright 2009 Sysgo AG
+ * Thomas Moll <thomas.moll at sysgo.com>
+ * - Added Input- Output- enable functionality, to allow full communication
+ *
+ * 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/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 <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/radix-tree.h>
+#include <linux/hardirq.h>
+
+#include "rio.h"
+
+DEFINE_SPINLOCK(rio_global_list_lock);
+
+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,
+};
+
+/**
+ * rio_mport_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
+ * %RIO_PORT_N_ERR_STS_PORT_OK if the master port is active
+ * or %0 if it is inactive.
+ */
+static int rio_mport_active(struct rio_mport *port)
+{
+ u32 result = 0;
+ u32 ext_ftr_ptr;
+ int *entry = rio_mport_phys_table;
+ int rc;
+
+ do {
+ rc = rio_mport_get_feature(port, 1, 0, 0, *entry, &ext_ftr_ptr);
+ if (rc)
+ return 0;
+ if (ext_ftr_ptr)
+ break;
+ } while (*++entry >= 0);
+
+ if (ext_ftr_ptr)
+ rc = rio_local_read_config_32(port,
+ ext_ftr_ptr + RIO_PORT_N_ERR_STS_CSR(port->index),
+ &result);
+
+ return rc ? 0 : result & RIO_PORT_N_ERR_STS_PORT_OK;
+}
+
+static int rio_update_dst_tree(struct rio_mport *mport, u16 parent_destid,
+ int parent_port, int hopcount,
+ u16 destid, u16 comptag, int redundant)
+{
+ u16 tmp_dst;
+
+ int rc = rio_lookup_next_destid(mport, parent_destid, parent_port,
+ hopcount, &tmp_dst);
+
+ if (!rc) {
+ pr_debug("RIO:(%s) Not adding new device dest %hx\n",
+ __func__, destid);
+ pr_debug("RIO:(%s) found pdest %hx pport %d hop %d dest %hx\n",
+ __func__, parent_destid, parent_port, hopcount,
+ tmp_dst);
+ return rc;
+ }
+ if (redundant)
+ return rio_block_destid_route(mport, parent_destid,
+ parent_port, hopcount,
+ destid, comptag);
+ else
+ return rio_add_destid(mport, parent_destid,
+ parent_port, hopcount,
+ destid, comptag);
+}
+
+static int add_new_dev2tree(struct rio_dev *rdev,
+ struct rio_dev *prev,
+ u16 prev_dest,
+ int prev_port)
+{
+ int rc;
+ struct rio_mport *mport = rdev->hport;
+ struct rio_dev *tmp;
+
+ rc = rio_pin_destid(mport, prev_dest, prev_port, rdev->hopcount);
+ if (unlikely(rc))
+ return rc;
+
+ spin_lock(&mport->net.tree_lock);
+
+ rc = radix_tree_insert(&mport->net.dev_tree, rdev->destid, rdev);
+ if (unlikely(rc)) {
+ spin_unlock(&mport->net.tree_lock);
+ return rc;
+ }
+ tmp = radix_tree_tag_set(&mport->net.dev_tree,
+ rdev->destid,
+ RIO_DEV_NOT_ADDED);
+ BUG_ON(tmp != rdev);
+ atomic_inc(&mport->net.rio_dev_num);
+
+ if (rio_is_switch(rdev)) {
+ tmp = radix_tree_tag_set(&mport->net.dev_tree,
+ rdev->destid,
+ RIO_DEV_IS_SWITCH);
+ BUG_ON(tmp != rdev);
+ }
+ if (rdev->local_domain) {
+ tmp = radix_tree_tag_set(&mport->net.dev_tree,
+ rdev->destid,
+ RIO_DEV_DISABLED);
+ BUG_ON(tmp != rdev);
+ }
+ /*
+ * Ensure that rdev is kept in circulation
+ * until enum/disc is done
+ */
+ rdev = rio_dev_get(rdev);
+
+ spin_unlock(&mport->net.tree_lock);
+
+ return 0;
+}
+
+/**
+ * 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);
+ pr_debug("last call for %s\n", rio_name(rdev));
+
+ kfree(rdev);
+}
+
+/**
+ * rio_release_device - clear enumeration
+ * complete flag
+ * @rdev: rio_device
+ *
+ * Returns 0 on success or %-EINVAL on failure.
+ */
+static int rio_release_device(struct rio_dev *rdev)
+{
+ u32 result;
+ int rc;
+
+ /* Un-claim device */
+ rc = rio_read_config_32(rdev,
+ rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR,
+ &result);
+ if (rc)
+ return rc;
+
+ result &= ~(RIO_PORT_GEN_DISCOVERED | RIO_PORT_GEN_MASTER);
+ rc = rio_write_config_32(rdev,
+ rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR,
+ result);
+ return rc;
+}
+
+static inline int rdev_is_mport(struct rio_dev *rdev)
+{
+ return (rdev->hport->host_deviceid == rdev->destid);
+}
+
+static void __rio_remove_device(struct rio_dev *rdev,
+ int dev_access,
+ int srio_down)
+{
+ int rio_access = (srio_down ? 0 : rio_mport_active(rdev->hport));
+ struct rio_mport *mport = rdev->hport;
+ int device_lock = 0;
+ int not_added;
+
+ pr_debug("Removing rio device %s from lists\n", rio_name(rdev));
+
+ rio_tree_write_lock();
+
+ spin_lock(&mport->net.tree_lock);
+ /*
+ * Remove device ref. from global list and mport net list
+ */
+ not_added = radix_tree_tag_get(&mport->net.dev_tree, rdev->destid,
+ RIO_DEV_NOT_ADDED);
+ rdev = radix_tree_delete(&mport->net.dev_tree, rdev->destid);
+ if (!rdev) {
+ pr_warn("device %s not found in glb tree\n", rio_name(rdev));
+ spin_unlock(&mport->net.tree_lock);
+ return;
+ }
+ atomic_dec(&mport->net.rio_dev_num);
+
+ spin_unlock(&mport->net.tree_lock);
+ synchronize_rcu();
+
+ rio_tree_write_unlock();
+
+ WARN(rio_unlock_destid(mport, rdev->prev_destid,
+ rdev->prev_port, rdev->hopcount),
+ "RIO: Destid table entry pinned after removing device %s",
+ rio_name(rdev));
+
+ if (!rdev_is_mport(rdev) && rio_access &&
+ dev_access && rdev->local_domain) {
+ /* try lock device */
+ pr_debug("try lock %s\n", rio_name(rdev));
+ if (rdev->use_hw_lock) {
+ if (rio_hw_lock_busy_wait(rdev->hport, rdev->destid,
+ rdev->hopcount, 10)) {
+ pr_warn("Can not claim device ID lock, HW release not possible");
+ } else {
+ pr_debug("Got %s device ID lock\n",
+ rio_name(rdev));
+ device_lock = 1;
+ }
+ } else {
+ device_lock = 1;
+ }
+ }
+ if (not_added) {
+ pr_warn("rio device %s is not added - skip remove!\n",
+ rio_name(rdev));
+ } else {
+ pr_debug("rio device %s is added - remove!\n", rio_name(rdev));
+ /*
+ * sysfs cleanup
+ */
+ rio_remove_sysfs_dev_files(rdev);
+ /*
+ * Remove device from kernel global device list
+ * If a driver has claimed the device its release
+ * function will be called.
+ */
+ device_del(&rdev->dev);
+ }
+ rio_dev_put(rdev);
+ if (device_lock) {
+ /* Disable device and clear enum flag */
+ pr_debug("Release %s HW\n", rio_name(rdev));
+ rio_release_device(rdev);
+ if (rdev->use_hw_lock)
+ rio_hw_unlock(rdev->hport,
+ rdev->destid,
+ rdev->hopcount);
+ }
+ /*
+ * Cleanup-up routing tables
+ */
+ if (!rdev_is_mport(rdev) && rio_access) {
+ pr_debug("Remove route to %s\n", rio_name(rdev));
+ rio_remove_route_for_destid(rdev->hport, rdev->destid, 1);
+ }
+ /*
+ * Give up last ref to device - the rio_release_dev
+ * function will do the final cleanup
+ */
+ rio_dev_put(rdev);
+}
+
+/**
+ * rio_remove_devices - remove device node and, if node is switch,
+ * recursively remove all devices reached through
+ * node
+ *
+ * @rdev: RIO device node
+ * @rio_access: Cleanup RIO device HW
+ *
+ */
+static void rio_remove_devices(struct rio_dev *rdev, int dev_access,
+ int srio_down)
+{
+ int i;
+ dev_access = 0;
+ if (rio_is_switch(rdev)) {
+ pr_debug("found switch %s - check ports\n", rio_name(rdev));
+ for (i = 0; i < RIO_GET_TOTAL_PORTS(rdev->swpinfo); i++) {
+ struct rio_dev *next = lookup_rdev_next(rdev, i);
+ int type = (IS_ERR(next) ?
+ RIO_PORT_UNUSED : rio_type_of_next(rdev, next));
+ if (IS_ERR(next))
+ next = NULL;
+
+ switch (type) {
+ case RIO_SWITCH:
+ rio_remove_devices(next, dev_access, srio_down);
+ break;
+ case RIO_END_POINT:
+ pr_debug("remove endpoint %s from switch %s\n",
+ rio_name(next), rio_name(rdev));
+ __rio_remove_device(next,
+ dev_access,
+ srio_down);
+ rio_dev_put(next);
+ break;
+ case RIO_PORT_UNUSED:
+ default:
+ rio_dev_put(next);
+ break;
+ }
+ }
+ }
+ pr_debug("Remove %s %s\n",
+ (rio_is_switch(rdev) ? "switch" : "endpoint"),
+ rio_name(rdev));
+ __rio_remove_device(rdev, dev_access, 0);
+}
+
+/**
+ * rio_switch_init - Sets switch operations for a particular vendor switch
+ * @rdev: RIO device
+ * @do_enum: Enumeration/Discovery mode flag
+ *
+ * Searches the RIO switch ops table for known switch types. If the vid
+ * and did match a switch table entry, then call switch initialization
+ * routine to setup switch-specific routines.
+ */
+static void rio_switch_init(struct rio_dev *rdev, int do_enum)
+{
+ struct rio_switch_ops *cur = __start_rio_switch_ops;
+ struct rio_switch_ops *end = __end_rio_switch_ops;
+
+ while (cur < end) {
+ if ((cur->vid == rdev->vid) && (cur->did == rdev->did)) {
+ cur->init_hook(rdev, do_enum);
+ break;
+ }
+ cur++;
+ }
+
+ if ((cur >= end) && (rdev->pef & RIO_PEF_STD_RT)) {
+ pr_debug("RIO: adding STD routing ops for %s\n",
+ rio_name(rdev));
+ rdev->rswitch->add_entry = rio_std_route_add_entry;
+ rdev->rswitch->get_entry = rio_std_route_get_entry;
+ rdev->rswitch->clr_table = rio_std_route_clr_table;
+ }
+
+ if (!rdev->rswitch->add_entry || !rdev->rswitch->get_entry)
+ pr_warn("RIO: missing routing ops for %s\n",
+ rio_name(rdev));
+}
+
+static int init_switch_pw(struct rio_dev *rdev)
+{
+ if ((rdev->src_ops & RIO_SRC_OPS_PORT_WRITE) &&
+ (rdev->em_efptr)) {
+ return rio_write_config_32(rdev,
+ rdev->em_efptr + RIO_EM_PW_TGT_DEVID,
+ (rdev->hport->host_deviceid << 16) |
+ (rdev->hport->sys_size << 15));
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * 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 int rio_add_device(struct rio_dev *rdev)
+{
+ int err;
+ pr_debug("RIO: %s %s\n", __func__, rio_name(rdev));
+ err = device_add(&rdev->dev);
+ if (err)
+ return err;
+
+ rio_create_sysfs_dev_files(rdev);
+
+ return 0;
+}
+
+/**
+ * rio_enable_rx_tx_port - enable input receiver and output transmitter of
+ * given port
+ * @port: Master port associated with the RIO network
+ * @local: local=1 select local port otherwise a far device is reached
+ * @destid: Destination ID of the device to check host bit
+ * @hopcount: Number of hops to reach the target
+ * @port_num: Port (-number on switch) to enable on a far end device
+ *
+ * Returns 0 or 1 from on General Control Command and Status Register
+ * (EXT_PTR+0x3C)
+ */
+static int rio_enable_dio(struct rio_mport *port,
+ int local, u16 destid,
+ u8 hopcount, u8 port_num)
+{
+ int rc = 0;
+
+#ifdef CONFIG_RAPIDIO_ENABLE_RX_TX_PORTS
+ u32 regval;
+ u32 ext_ftr_ptr;
+
+
+ rc = rio_mport_get_physefb(port, local, destid, hopcount, &ext_ftr_ptr);
+ if (rc)
+ goto done;
+ if (local)
+ rc = rio_local_read_config_32(port, ext_ftr_ptr +
+ RIO_PORT_N_CTL_CSR(0),
+ ®val);
+ else
+ rc = rio_mport_read_config_32(port, destid, hopcount,
+ ext_ftr_ptr +
+ RIO_PORT_N_CTL_CSR(port_num),
+ ®val);
+ if (rc)
+ goto done;
+
+ if (regval & RIO_PORT_N_CTL_P_TYP_SER)
+ /* serial */
+ regval = regval | RIO_PORT_N_CTL_EN_RX_SER
+ | RIO_PORT_N_CTL_EN_TX_SER;
+ else
+ /* parallel */
+ regval = regval | RIO_PORT_N_CTL_EN_RX_PAR
+ | RIO_PORT_N_CTL_EN_TX_PAR;
+
+ if (local)
+ rc = rio_local_write_config_32(port, ext_ftr_ptr +
+ RIO_PORT_N_CTL_CSR(0), regval);
+ else
+ rc = rio_mport_write_config_32(port, destid, hopcount,
+ ext_ftr_ptr +
+ RIO_PORT_N_CTL_CSR(port_num),
+ regval);
+done:
+#endif
+ return rc;
+}
+
+static int switch_port_is_active(struct rio_dev *rdev, int sport, u8 *active)
+{
+ u32 result = 0;
+ u32 ext_ftr_ptr;
+ int rc;
+
+ rc = rio_mport_get_efb(rdev->hport, 0,
+ rdev->destid, rdev->hopcount,
+ 0, &ext_ftr_ptr);
+ if (rc)
+ goto access_err;
+ while (ext_ftr_ptr) {
+ rc = rio_read_config_32(rdev, ext_ftr_ptr, &result);
+ if (rc)
+ goto access_err;
+
+ result = RIO_GET_BLOCK_ID(result);
+ if ((result == RIO_EFB_SER_EP_FREE_ID) ||
+ (result == RIO_EFB_SER_EP_FREE_ID_V13P) ||
+ (result == RIO_EFB_SER_EP_FREC_ID))
+ break;
+
+ rc = rio_mport_get_efb(rdev->hport, 0, rdev->destid,
+ rdev->hopcount, ext_ftr_ptr,
+ &ext_ftr_ptr);
+ if (rc)
+ goto access_err;
+ }
+
+ if (ext_ftr_ptr) {
+ rc = rio_get_err_and_status(rdev, sport, &result);
+ if (rc)
+ goto access_err;
+ }
+ *active = (result & RIO_PORT_N_ERR_STS_PORT_OK ? 1 : 0);
+done:
+ return rc;
+access_err:
+ pr_warn("RIO:(%s) Access err at destid %hu hop %d\n",
+ __func__, rdev->destid, rdev->hopcount);
+ goto done;
+}
+
+static int rio_disc_switch_port(struct rio_job *job, struct rio_dev *rdev,
+ int port_num, u16 *destid, int tmo)
+{
+ int rc;
+ u8 route_port;
+ u16 tmp_dst;
+
+ rc = rio_job_hw_lock_wait(job, NULL, NULL, rdev->destid,
+ rdev->hopcount, tmo);
+ if ((rdev->use_hw_lock) && (rc != 0))
+ goto done;
+
+ for (tmp_dst = 0;
+ tmp_dst < RIO_ANY_DESTID(job->mport->sys_size);
+ tmp_dst++) {
+ rc = rdev->rswitch->get_entry(rdev->hport, rdev->destid,
+ rdev->hopcount, RIO_GLOBAL_TABLE,
+ tmp_dst, &route_port);
+ if (rc)
+ goto unlock;
+
+ if (route_port == port_num) {
+ if (tmp_dst != RIO_ANY_DESTID(job->mport->sys_size)) {
+ *destid = tmp_dst;
+ break;
+ }
+ }
+ }
+unlock:
+ if (rdev->use_hw_lock)
+ rio_hw_unlock(job->mport, rdev->destid, rdev->hopcount);
+done:
+ return rc;
+}
+
+static int rio_get_enum_boundary(struct rio_dev *rdev, int port_num, u8 *remote)
+{
+ u32 regval;
+ int rc;
+
+ rc = rio_mport_read_config_32(rdev->hport, rdev->destid,
+ rdev->hopcount,
+ rdev->phys_efptr +
+ RIO_PORT_N_CTL_CSR(port_num),
+ ®val);
+ if (rc)
+ return rc;
+
+ *remote = (regval & RIO_PORT_N_CTL_ENUM_BOUNDARY ? 1 : 0);
+ pr_debug("RIO: %s port %d (%8.8x) links to %s\n",
+ rio_name(rdev), port_num, regval,
+ (*remote ? "enum boundary" : "local domain"));
+ return rc;
+}
+
+static int rio_is_remote_domain(struct rio_dev *rdev, int port_num, u8 *remote)
+{
+ int rc = 0;
+
+ if (!rdev->local_domain)
+ *remote = 1;
+ else
+ rc = rio_get_enum_boundary(rdev, port_num, remote);
+
+ return rc;
+}
+static u8 __get_lock_mode(struct rio_mport *mport,
+ u16 parent_destid, int prev_port,
+ u8 hopcount)
+{
+ u8 lock_hw = 1, tmp;
+
+ if (rio_dest_is_legacy(mport, parent_destid, prev_port, hopcount)) {
+ if ((rio_get_legacy_properties(mport, parent_destid, prev_port,
+ hopcount, &lock_hw,
+ &tmp)))
+ return 1;
+ }
+ return lock_hw;
+}
+static int rio_enum_start(struct rio_job *job)
+{
+ int rc = -EFAULT;
+ struct rio_dev *from = NULL;
+ struct rio_dev *to = job->rdev;
+
+ if (job->rdev) {
+ from = rio_get_root_node(job->mport);
+ if (IS_ERR(from))
+ return 0;
+ pr_debug("RIO: lock net from %s to %s\n",
+ rio_name(from), rio_name(to));
+ rc = rio_job_hw_lock_wait(job, from, to, 0, 0, 10);
+ rio_dev_put(from);
+ } else {
+ u8 lock_mode = __get_lock_mode(job->mport, -1, -1, -1);
+
+ if (lock_mode) {
+ pr_debug("RIO: lock master port\n");
+ rc = rio_job_hw_lock_wait(job, NULL, NULL,
+ job->mport->host_deviceid,
+ 0, 10);
+ } else {
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+static int rio_enum_complete(struct rio_mport *mport, u16 destid, u8 hopcount)
+{
+ u32 phys_efptr = 0;
+ u32 result;
+ int rc;
+
+ rc = rio_mport_get_physefb(mport, 0, destid, hopcount, &phys_efptr);
+ if (rc)
+ return rc;
+
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ phys_efptr + RIO_PORT_GEN_CTL_CSR,
+ &result);
+ if (rc < 0)
+ return rc;
+
+ if (result & RIO_PORT_GEN_DISCOVERED) {
+ pr_debug("RIO: dest %hu hop %hhu Domain enumeration completed %8.8x\n",
+ destid, hopcount, result);
+ return 0;
+ } else {
+ pr_debug("RIO: dest %hu hop %hhu Domain enumeration NOT completed %8.8x\n",
+ destid, hopcount, result);
+ return -ETIME;
+ }
+}
+
+/**
+ * rio_mport_enum_complete- Tests if enumeration of a network is complete
+ * @port: Master port to send transaction
+ *
+ * Tests the Component Tag CSR for non-zero value (enumeration
+ * complete flag). Return %1 if enumeration is complete or %0 if
+ * enumeration is incomplete.
+ */
+static int rio_mport_enum_complete(struct rio_mport *port, u16 destid,
+ u8 hopcount)
+{
+ u32 regval;
+
+ rio_local_read_config_32(port, port->phys_efptr + RIO_PORT_GEN_CTL_CSR,
+ ®val);
+
+ return (regval & RIO_PORT_GEN_DISCOVERED) ? 1 : 0;
+}
+
+static int rio_init_net(struct rio_mport *mport, int *comptag)
+{
+ int net_id = 0;
+ int rc = 0;
+
+ spin_lock(&mport->net.tree_lock);
+
+ if (atomic_read(&mport->net.rio_dev_num) != 0)
+ goto err_unlock;
+
+ spin_unlock(&mport->net.tree_lock);
+
+ rc = rio_get_next_netid(mport->host_deviceid, &net_id, comptag);
+ if (rc) {
+ pr_warn("RIO: Failed to get net id\n");
+ goto done;
+ }
+ rc = rio_pin_netid(mport->host_deviceid, net_id);
+ if (rc) {
+ pr_warn("RIO: Failed to lock net id\n");
+ goto done;
+ }
+ mport->net.id = net_id;
+
+done:
+ return rc;
+
+err_unlock:
+ spin_unlock(&mport->net.tree_lock);
+ pr_warn("RIO: Master port net is not empty\n");
+ rc = -EINVAL;
+ goto done;
+}
+
+/**
+ * rio_init_em - Initializes RIO Error Management (for switches)
+ * @rdev: RIO device
+ *
+ * For each enumerated switch, call device-specific error management
+ * initialization routine (if supplied by the switch driver).
+ */
+static void rio_init_em(struct rio_dev *rdev)
+{
+ if (rio_is_switch(rdev) && (rdev->em_efptr) && (rdev->rswitch->em_init))
+ rdev->rswitch->em_init(rdev);
+}
+static void rio_net_register_devices(struct rio_mport *mport)
+{
+ int i, num_dev = 0;
+ int cleanup_all = 0;
+ struct rio_dev *tmp;
+ struct rio_dev **dptr = rio_get_tagged_devices(mport,
+ RIO_DEV_NOT_ADDED,
+ &num_dev);
+
+ if (!dptr) {
+ pr_info("RIO: No new deviced detected when scanning net\n");
+ return;
+ }
+ if (IS_ERR(dptr)) {
+ pr_warn("RIO: Out of memory - detected devices are not added\n");
+ return;
+ }
+ if (rio_update_routes(mport, dptr, num_dev)) {
+ cleanup_all = 1;
+ pr_warn("RIO: update routes failed\n");
+ }
+ for (i = 0; i < num_dev; i++) {
+ struct rio_dev *rdev = dptr[i];
+ int disabled, access = 1;
+
+ if (unlikely(!rdev))
+ continue;
+
+ if (cleanup_all)
+ goto cleanup;
+
+ spin_lock(&mport->net.tree_lock);
+ disabled = radix_tree_tag_get(&mport->net.dev_tree,
+ rdev->destid,
+ RIO_DEV_DISABLED);
+ if (disabled) {
+ if ((rio_device_enable(rdev))) {
+ spin_unlock(&mport->net.tree_lock);
+ pr_warn("RIO: Error when enabling device %s\n",
+ rio_name(rdev));
+ goto cleanup;
+ }
+ tmp = radix_tree_tag_clear(&mport->net.dev_tree,
+ rdev->destid,
+ RIO_DEV_DISABLED);
+ if (tmp != rdev) {
+ pr_warn("RIO: Error when clearing tag %i form device %s\n",
+ RIO_DEV_DISABLED, rio_name(rdev));
+ goto cleanup;
+ }
+ }
+ spin_unlock(&mport->net.tree_lock);
+ rio_tree_write_lock();
+ if (rio_add_device(rdev)) {
+ pr_warn("RIO: Error when adding device %s to kernel model\n",
+ rio_name(rdev));
+ rio_tree_write_unlock();
+ goto cleanup;
+ } else {
+ spin_lock(&mport->net.tree_lock);
+ tmp = radix_tree_tag_clear(&mport->net.dev_tree,
+ rdev->destid,
+ RIO_DEV_NOT_ADDED);
+ spin_unlock(&mport->net.tree_lock);
+ if (tmp != rdev) {
+ pr_warn("RIO: Error when clearing tag %i form device %s\n",
+ RIO_DEV_NOT_ADDED, rio_name(rdev));
+ rio_tree_write_unlock();
+ goto cleanup;
+ }
+ }
+ rio_tree_write_unlock();
+ rio_dev_put(rdev);
+ continue;
+cleanup:
+ __rio_remove_device(rdev, access, 0);
+ rio_dev_put(rdev);
+ }
+ kfree(dptr);
+}
+
+static int rio_redundant_path(struct rio_mport *mport,
+ struct rio_dev *prev, int prev_port,
+ u16 destid, u8 hopcount, u8 lock_mode,
+ u8 *redundant)
+{
+ u16 lock;
+ u32 comptag;
+ int rc = 0;
+
+ BUG_ON(!prev);
+
+ *redundant = rio_dest_is_redundant(mport, prev->destid,
+ prev_port, hopcount);
+ if (*redundant)
+ goto out;
+
+ if (rdev_is_mport(prev))
+ goto out;
+
+ rc = rio_get_host_lock(prev->hport, destid, hopcount, &lock);
+ if (rc)
+ goto access_err;
+
+ if (lock == prev->hport->host_deviceid || !lock_mode) {
+ struct rio_dev *rdev;
+
+ rc = rio_read_comptag(prev->hport, destid, hopcount, &comptag);
+ if (rc)
+ goto access_err;
+
+ rdev = rio_get_comptag(mport, (u32)comptag);
+
+ if (rdev) {
+ rio_update_dst_tree(mport, prev->destid,
+ prev_port, hopcount,
+ rdev->destid,
+ rdev->comp_tag, 1);
+
+ pr_debug("RIO: redundant path to %s\n",
+ rio_name(rdev));
+ rio_dev_put(rdev);
+ *redundant = 1;
+ } else {
+ if (lock_mode)
+ goto lock_fault;
+ }
+ }
+out:
+ return rc;
+
+access_err:
+ pr_warn("RIO: Access fault at port %d destid %hu hopcount %d\n",
+ prev_port, destid, hopcount);
+ goto out;
+
+lock_fault:
+ pr_warn("RIO: %hu Unexpectedly owns lock on destid %hu hopcount %d\n",
+ lock, destid, hopcount);
+ rc = -EFAULT;
+ goto out;
+}
+
+int rio_device_enable(struct rio_dev *rdev)
+{
+ u32 result;
+ int rc = 0;
+
+ BUG_ON(!rdev->local_domain);
+
+ pr_debug("RIO: Enable device %s\n", rio_name(rdev));
+
+ /* Mark device as discovered and enable master */
+ rc = rio_read_config_32(rdev,
+ rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR,
+ &result);
+ if (rc != 0)
+ goto abort;
+
+ result |= RIO_PORT_GEN_DISCOVERED | RIO_PORT_GEN_MASTER;
+ rc = rio_write_config_32(rdev,
+ rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR,
+ result);
+ if (rc != 0)
+ goto abort;
+
+ if (rdev->use_hw_lock)
+ rc = rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount);
+done:
+ return rc;
+abort:
+ pr_warn("RIO:(%s) RIO_PORT_GEN_CTL_CSR access fault\n",
+ __func__);
+ goto done;
+}
+
+int rio_device_disable(struct rio_dev *rdev)
+{
+ /* Mark device as undiscovered */
+ return rio_write_config_32(rdev,
+ rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR,
+ 0);
+}
+
+static int rio_enum_unlock(struct rio_mport *mport, struct rio_dev *rdev,
+ u16 destid, u8 hopcount)
+{
+ u16 dest = (rdev ? rdev->destid : destid);
+ u8 hop = (rdev ? rdev->hopcount : hopcount);
+ int rc;
+
+ rc = rio_mport_chk_dev_access(mport, dest, hop);
+ if (rc)
+ return rc;
+ return rio_hw_unlock(mport, dest, hop);
+}
+
+static void do_port_cleanup(struct rio_dev *rdev, int port_num)
+{
+ struct rio_dev *next = lookup_rdev_next(rdev, port_num);
+
+ if (!IS_ERR(next)) {
+ rio_remove_devices(next, 0, 0);
+ rio_dev_put(next);
+ }
+ return;
+}
+
+static int rio_add_enum_device(struct rio_dev *prev, int prev_port,
+ struct rio_dev *rdev, u16 tmp_destid)
+{
+ struct rio_mport *mport = rdev->hport;
+ int rc = 0;
+
+ BUG_ON(!prev);
+
+ if ((lookup_rdev(mport, rdev->destid)) == rdev)
+ return rc;
+
+ rc = rio_add_route_for_destid(mport, prev->destid,
+ prev_port, rdev->destid, 0);
+ if (rc)
+ goto abort;
+
+ if (rio_is_switch(rdev)) {
+ rio_init_em(rdev);
+ rc = init_switch_pw(rdev);
+ if (rc)
+ goto cleanup;
+
+ rc = rio_init_lut(rdev, 0);
+ if (rc)
+ goto cleanup;
+
+ } else {
+ /* Enable Input Output Port (transmitter reviever) */
+ rio_enable_dio(mport, 0, rdev->destid, rdev->hopcount, 0);
+ }
+
+ rc = add_new_dev2tree(rdev, prev, prev->destid, prev_port);
+ if (rc)
+ goto cleanup;
+
+done:
+ return rc;
+cleanup:
+ rio_remove_route_for_destid(mport, rdev->destid, 0);
+
+abort:
+ pr_warn("RIO: bail and release lock on destid %hu at hp %d\n",
+ tmp_destid, rdev->hopcount);
+ if (rdev->use_hw_lock)
+ rio_enum_unlock(mport, NULL, tmp_destid, rdev->hopcount);
+ goto done;
+}
+
+static struct rio_dev *rio_alloc_new_device(struct rio_mport *mport,
+ u16 destid, u8 hopcount,
+ u16 parent_destid, int prev_port)
+{
+ struct rio_dev *rdev = NULL;
+ struct rio_switch *rswitch = NULL;
+ int rc;
+ size_t size;
+ u32 result, swpinfo = 0;
+
+ size = sizeof(struct rio_dev);
+
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_PEF_CAR, &result);
+ if (rc)
+ goto access_err;
+
+ if (result & (RIO_PEF_SWITCH | RIO_PEF_MULTIPORT)) {
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_SWP_INFO_CAR, &swpinfo);
+ if (rc)
+ goto access_err;
+
+ if (result & RIO_PEF_SWITCH)
+ size += sizeof(*rswitch);
+ }
+ rdev = kzalloc(size, GFP_KERNEL);
+ if (!rdev)
+ return ERR_PTR(-ENOMEM);
+
+ rdev->hport = mport;
+ rdev->pef = result;
+ rdev->swpinfo = swpinfo;
+ rdev->prev_port = prev_port;
+ rdev->prev_destid = parent_destid;
+
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_DEV_ID_CAR, &result);
+ if (rc)
+ goto access_err;
+
+ rdev->did = result >> 16;
+ rdev->vid = result & 0xffff;
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_DEV_INFO_CAR, &rdev->device_rev);
+ if (rc)
+ goto access_err;
+
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_ASM_ID_CAR, &result);
+ if (rc)
+ goto access_err;
+
+ rdev->asm_did = result >> 16;
+ rdev->asm_vid = result & 0xffff;
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_ASM_INFO_CAR, &result);
+ if (rc)
+ goto access_err;
+
+ rdev->asm_rev = result >> 16;
+ if (rdev->pef & RIO_PEF_EXT_FEATURES) {
+ rdev->efptr = result & 0xffff;
+ rc = rio_mport_get_physefb(mport, 0, destid,
+ hopcount, &rdev->phys_efptr);
+ if (rc)
+ goto access_err;
+
+ rc = rio_mport_get_feature(mport, 0, destid,
+ hopcount, RIO_EFB_ERR_MGMNT,
+ &rdev->em_efptr);
+ if (rc)
+ goto access_err;
+ }
+
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_SRC_OPS_CAR, &rdev->src_ops);
+ if (rc)
+ goto access_err;
+
+ rc = rio_mport_read_config_32(mport, destid, hopcount,
+ RIO_DST_OPS_CAR, &rdev->dst_ops);
+ if (rc)
+ goto access_err;
+
+ return rdev;
+
+access_err:
+ pr_warn("RIO: RIO:(%s) destid %hx hopcount %d - ACCESS ERROR\n",
+ __func__, destid, hopcount);
+
+ if (rdev && !IS_ERR(rdev))
+ kfree(rdev);
+
+ return ERR_PTR(rc);
+}
+
+static struct rio_dev *rio_alloc_mport_device(struct rio_mport *mport)
+{
+ struct rio_dev *rdev = NULL;
+ int rc;
+ size_t size;
+ u32 result;
+
+ size = sizeof(struct rio_dev);
+
+ rc = rio_local_read_config_32(mport, RIO_PEF_CAR, &result);
+ if (rc)
+ goto access_err;
+
+ rdev = kzalloc(size, GFP_KERNEL);
+ if (!rdev)
+ return ERR_PTR(-ENOMEM);
+
+ rdev->hport = mport;
+ rdev->pef = result;
+ rdev->swpinfo = 0;
+ rdev->prev_destid = RIO_INVALID_DESTID;
+ rdev->prev_port = -1;
+
+ rc = rio_local_read_config_32(mport, RIO_DEV_ID_CAR, &result);
+ if (rc)
+ goto access_err;
+
+ rdev->did = result >> 16;
+ rdev->vid = result & 0xffff;
+ rc = rio_local_read_config_32(mport, RIO_DEV_INFO_CAR,
+ &rdev->device_rev);
+ if (rc)
+ goto access_err;
+
+ rc = rio_local_read_config_32(mport, RIO_ASM_ID_CAR, &result);
+ if (rc)
+ goto access_err;
+
+ rdev->asm_did = result >> 16;
+ rdev->asm_vid = result & 0xffff;
+ rc = rio_local_read_config_32(mport, RIO_ASM_INFO_CAR, &result);
+ if (rc)
+ goto access_err;
+
+ rdev->asm_rev = result >> 16;
+ if (rdev->pef & RIO_PEF_EXT_FEATURES) {
+ rdev->efptr = result & 0xffff;
+ rc = rio_mport_get_physefb(mport, 1, mport->host_deviceid,
+ 0, &rdev->phys_efptr);
+ if (rc)
+ goto access_err;
+
+ rc = rio_mport_get_feature(mport, 1, mport->host_deviceid,
+ 0 , RIO_EFB_ERR_MGMNT,
+ &rdev->em_efptr);
+ if (rc)
+ goto access_err;
+ }
+
+ rc = rio_local_read_config_32(mport, RIO_SRC_OPS_CAR, &rdev->src_ops);
+ if (rc)
+ goto access_err;
+
+ rc = rio_local_read_config_32(mport, RIO_DST_OPS_CAR, &rdev->dst_ops);
+ if (rc)
+ goto access_err;
+
+ rc = rio_local_read_config_32(mport, RIO_COMPONENT_TAG_CSR,
+ &rdev->comp_tag);
+ if (rc)
+ goto access_err;
+
+ return rdev;
+
+access_err:
+ pr_warn("RIO: RIO:(%s) destid %hx - ACCESS ERROR\n",
+ __func__, mport->host_deviceid);
+ kfree(rdev);
+
+ return ERR_PTR(rc);
+}
+
+static void rio_dev_init(struct rio_dev *rdev)
+{
+ rdev->dev.bus = &rio_bus_type;
+ rdev->dev.parent = &rio_bus;
+
+ device_initialize(&rdev->dev);
+ rdev->dev.release = rio_release_dev;
+ rio_dev_get(rdev);
+
+ rdev->dma_mask = DMA_BIT_MASK(32);
+ rdev->dev.dma_mask = &rdev->dma_mask;
+ rdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+ 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);
+}
+
+static struct rio_dev *rio_setup_disc_mport(struct rio_mport *mport,
+ int do_enum)
+{
+ struct rio_dev *rdev = rio_alloc_mport_device(mport);
+ u8 lock_hw = 1, dummy;
+ int rc;
+
+ if (IS_ERR(rdev))
+ return rdev;
+
+ if (rio_dest_is_legacy(mport, -1, -1, -1)) {
+ rc = rio_get_legacy_properties(mport, -1, -1, -1,
+ &lock_hw, &dummy);
+ if (rc)
+ goto cleanup;
+ } else {
+ pr_debug("RIO: Failed to lookup destid info for mport, using default flags\n");
+ rc = rio_update_dst_tree(mport, -1, -1, -1,
+ mport->host_deviceid,
+ mport->host_deviceid, 0);
+ if (rc)
+ goto cleanup;
+ }
+ rdev->local_domain = do_enum;
+ rdev->destid = mport->host_deviceid;
+ rdev->hopcount = 0xff;
+ rdev->use_hw_lock = lock_hw;
+
+ if (rio_eval_destid(rdev)) {
+ rc = -EINVAL;
+ goto cleanup;
+ }
+ dev_set_name(&rdev->dev, "%02x:m:%04x", rdev->hport->net.id,
+ rdev->destid);
+ rio_dev_init(rdev);
+ return rdev;
+
+cleanup:
+ kfree(rdev);
+ return ERR_PTR(rc);
+}
+
+static struct rio_dev *rio_setup_enum_device(struct rio_mport *mport,
+ u16 prev_destid, int prev_port,
+ u16 destid, u8 hopcount)
+{
+ struct rio_dev *rdev;
+ int device_comptag;
+ u16 device_destid;
+ u8 lock_hw = 1, lut_update = 1;
+ int rc;
+
+ rc = rio_get_next_destid(mport, prev_destid,
+ prev_port, hopcount,
+ &device_destid, &device_comptag);
+ if (rc) {
+ pr_warn("RIO: Failed to get destid\n");
+ return ERR_PTR(rc);
+ }
+
+ rdev = lookup_rdev(mport, device_destid);
+ if (rdev)
+ /* dev is already known and added - return ptr */
+ return rdev;
+
+ rdev = rio_alloc_new_device(mport, destid, hopcount,
+ prev_destid, prev_port);
+ if (IS_ERR(rdev))
+ return rdev;
+
+ rdev->local_domain = 1;
+ if (rio_dest_is_legacy(mport, prev_destid, prev_port, hopcount)) {
+ rc = rio_get_legacy_properties(mport, prev_destid, prev_port,
+ hopcount, &lock_hw,
+ &lut_update);
+ if (rc)
+ goto cleanup;
+ }
+ rdev->use_hw_lock = lock_hw;
+
+ rio_fixup_dev(rio_fixup_early, rdev, destid, hopcount);
+ /* Assign component tag to device */
+ if (device_comptag >= 0x10000) {
+ pr_warn("RIO: Component Tag Counter Overflow\n");
+ rc = -EFAULT;
+ goto cleanup;
+ }
+ rio_mport_write_config_32(mport, destid, hopcount,
+ RIO_COMPONENT_TAG_CSR, device_comptag);
+ rdev->comp_tag = device_comptag;
+ rdev->hopcount = hopcount;
+ rio_assign_destid(rdev, mport, destid, hopcount, &device_destid);
+ if (rio_eval_destid(rdev)) {
+ rc = -EINVAL;
+ goto cleanup;
+ }
+ if (rio_is_switch(rdev)) {
+ rdev->return_port = RIO_GET_PORT_NUM(rdev->swpinfo);
+
+ if (rio_dest_is_one_way(mport, prev_destid,
+ prev_port, hopcount)) {
+ rc = rio_get_return_port(mport, prev_destid, prev_port,
+ hopcount, &rdev->return_port);
+ if (rc)
+ goto cleanup;
+ }
+
+ rdev->rswitch->switchid = rdev->comp_tag & RIO_CTAG_UDEVID;
+ rdev->rswitch->update_lut = lut_update;
+
+ dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->hport->net.id,
+ rdev->rswitch->switchid);
+ rio_switch_init(rdev, 1);
+ if (rdev->rswitch->clr_table)
+ rdev->rswitch->clr_table(mport, destid, hopcount,
+ RIO_GLOBAL_TABLE);
+ } else {
+ dev_set_name(&rdev->dev, "%02x:e:%04x", rdev->hport->net.id,
+ rdev->destid);
+ }
+ rio_dev_init(rdev);
+ return rdev;
+
+cleanup:
+ kfree(rdev);
+
+ return ERR_PTR(rc);
+}
+static int rio_enum(struct rio_job *job,
+ struct rio_dev *prev,
+ u16 prev_destid, int prev_port,
+ u8 hopcount)
+{
+ int rc = 0;
+ struct rio_mport *mport = job->mport;
+ struct rio_dev *rdev = NULL;
+ u8 lock_mode = __get_lock_mode(mport, prev_destid, prev_port, hopcount);
+ u8 redundant;
+
+ pr_debug("RIO: rio_enum prev_dest %hx, prev_port %d hop %hhu\n",
+ prev_destid, prev_port, hopcount);
+
+ rc = rio_redundant_path(mport, prev, prev_port,
+ RIO_ANY_DESTID(job->mport->sys_size),
+ hopcount, lock_mode, &redundant);
+ if (rc || redundant)
+ goto done;
+
+ if (lock_mode) {
+ rc = rio_job_hw_lock_wait(job, NULL, NULL,
+ RIO_ANY_DESTID(job->mport->sys_size),
+ hopcount, 1);
+ if (unlikely(rc))
+ goto done;
+ }
+
+ rdev = rio_setup_enum_device(mport, prev_destid, prev_port,
+ RIO_ANY_DESTID(job->mport->sys_size),
+ hopcount);
+
+ if (unlikely(IS_ERR(rdev))) {
+ rc = PTR_ERR(rdev);
+ goto unlock;
+ }
+ rc = rio_add_enum_device(prev, prev_port, rdev,
+ RIO_ANY_DESTID(job->mport->sys_size));
+ if (unlikely(rc))
+ goto done;
+
+ if (rio_is_switch(rdev)) {
+ int port_num;
+ int sw_inport;
+ int num_ports;
+
+ sw_inport = RIO_GET_PORT_NUM(rdev->swpinfo);
+ num_ports = RIO_GET_TOTAL_PORTS(rdev->swpinfo);
+
+ 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++) {
+ u8 remote = 0, active = 0;
+ if (lock_mode)
+ rc = rio_enable_dio(rdev->hport, 0,
+ rdev->destid,
+ rdev->hopcount,
+ port_num);
+ if (rc < 0)
+ goto unlock;
+ if (sw_inport == port_num) {
+ if (rdev_is_mport(prev)) {
+ rio_update_dst_tree(job->mport,
+ rdev->destid,
+ port_num,
+ hopcount + 1,
+ prev->destid,
+ prev->comp_tag,
+ 0);
+ }
+ continue;
+ }
+ rc = switch_port_is_active(rdev, port_num, &active);
+ if (rc < 0)
+ goto unlock;
+ if (!active) {
+ do_port_cleanup(rdev, port_num);
+ continue;
+ }
+
+ rc = rio_get_enum_boundary(rdev, port_num, &remote);
+ if (rc < 0)
+ goto unlock;
+
+ if (remote)
+ continue;
+
+ rc = rio_route_add(rdev, RIO_GLOBAL_TABLE,
+ RIO_ANY_DESTID(mport->sys_size),
+ port_num, 0);
+ if (rc < 0)
+ goto unlock;
+
+ rc = rio_enum(job, rdev, rdev->destid, port_num,
+ hopcount + 1);
+ if (rc)
+ return rc;
+ }
+ } else {
+ pr_debug("RIO: found ep %s (vid %4.4x did %4.4x)\n",
+ rio_name(rdev), rdev->vid, rdev->did);
+ }
+
+ /* final fixup pass for device */
+ rio_fixup_dev(rio_fixup_enable, rdev, rdev->destid, rdev->hopcount);
+done:
+ if (rdev && !IS_ERR(rdev))
+ rio_dev_put(rdev);
+ if (rc != RIO_JOB_STATE_ABORT)
+ return 0;
+ return rc;
+unlock:
+ if (IS_ERR(rdev))
+ rdev = NULL;
+ if (lock_mode)
+ rio_enum_unlock(mport, rdev,
+ RIO_ANY_DESTID(job->mport->sys_size),
+ hopcount);
+ goto done;
+}
+
+static int __rio_mport_net_empty(struct rio_mport *mport)
+{
+ int empty = 0;
+
+ spin_lock(&mport->net.tree_lock);
+ if (atomic_read(&mport->net.rio_dev_num) != 0) {
+ pr_debug("RIO: Master port Net devices - list not empty\n");
+ } else {
+ pr_debug("RIO: Master port Net devices - list empty\n");
+ empty = 1;
+ }
+ spin_unlock(&mport->net.tree_lock);
+ return empty;
+}
+
+static int rio_dev_local(struct rio_job *job, u8 *remote)
+{
+ int rc = 0;
+
+ if (job->rdev) {
+ rc = rio_is_remote_domain(job->rdev, job->port, remote);
+ if (rc < 0)
+ return rc; /* access fault */
+
+ pr_debug("RIO: Insertion at port %d in %s domain\n",
+ job->port, (*remote ? "remote" : "local"));
+ } else {
+ *remote = (job->mport->enum_host ? 0 : 1);
+ pr_debug("RIO: %s Master port - %s\n",
+ (*remote ? "Discover" : "Enum"),
+ (__rio_mport_net_empty(job->mport) ?
+ "net empty" : "net exists"));
+ }
+
+ return rc;
+}
+
+static struct rio_dev *rio_enum_master_port(struct rio_job *job)
+{
+ struct rio_dev *rdev = NULL;
+ int comptag = 1;
+ int rc;
+
+ printk(KERN_INFO "RIO: enumerate master port %d, %s\n", job->mport->id,
+ job->mport->name);
+
+ if (!rio_mport_active(job->mport)) {
+ pr_warn("RIO: master port %d link inactive\n",
+ job->mport->id);
+ return ERR_PTR(-ENODEV);
+ }
+ /* If master port has an active link, allocate net and enum peers */
+ rc = rio_init_net(job->mport, &comptag);
+ if (rc < 0) {
+ pr_warn("RIO: failed to init new net\n");
+ return ERR_PTR(rc);
+ }
+ pr_debug("RIO:(%s) set master destid %hu\n",
+ __func__, job->mport->host_deviceid);
+ /* Set master port destid and init destid ctr */
+ rio_set_master_destid(job->mport, job->mport->host_deviceid);
+ pr_debug("RIO:(%s) set master comptag %d\n", __func__,
+ job->mport->host_deviceid);
+ /* Set component tag for host */
+ rio_local_write_config_32(job->mport, RIO_COMPONENT_TAG_CSR,
+ job->mport->host_deviceid);
+ pr_debug("RIO:(%s) enable master DIO\n", __func__);
+ /* Enable Input Output Port (transmitter reviever) */
+ rio_enable_dio(job->mport, 1, 0, 0, 0);
+ rdev = rio_setup_disc_mport(job->mport, 1);
+
+ return rdev;
+}
+
+static int rio_add_device_local(struct rio_job *job)
+{
+ int rc;
+ struct rio_dev *rdev = job->rdev;
+ struct rio_mport *mport = job->mport;
+ int port_num;
+ u8 hopcount;
+
+ pr_debug("RIO: Handle job in local domain\n");
+ rc = rio_enum_start(job);
+ if (rc)
+ return rc;
+
+ if (!rdev) {
+ rdev = rio_enum_master_port(job);
+
+ if (unlikely(IS_ERR(rdev))) {
+ rc = PTR_ERR(rdev);
+ rdev = NULL;
+ goto mport_unlock;
+ }
+ rc = add_new_dev2tree(rdev, NULL, RIO_INVALID_DESTID, -1);
+ if (rc) {
+ pr_warn("RIO: add master port to tree failed\n");
+ goto mport_unlock;
+ }
+ if (job->mport->ops->pwenable)
+ job->mport->ops->pwenable(job->mport, 1);
+
+ port_num = -1;
+ hopcount = 0;
+ } else {
+ u8 port_active;
+
+ port_num = job->port;
+ hopcount = rdev->hopcount + 1;
+
+ rc = switch_port_is_active(rdev, port_num, &port_active);
+ if (rc < 0)
+ goto unlock;
+
+ if (!port_active)
+ goto unlock;
+
+ rc = rio_add_route_for_destid(mport, rdev->destid, port_num,
+ RIO_ANY_DESTID(mport->sys_size),
+ 0);
+ if (rc < 0)
+ goto unlock;
+
+ }
+
+ rc = rio_enum(job, rdev, rdev->destid, port_num, hopcount);
+
+done:
+ if (rdev && rdev_is_mport(rdev))
+ rio_dev_put(rdev);
+unlock:
+ rio_job_hw_unlock_devices(job);
+ return rc;
+mport_unlock:
+ rio_hw_unlock(job->mport, job->mport->host_deviceid, 0);
+ goto done;
+}
+
+static int rio_add_disc_device(struct rio_dev *prev, int prev_port,
+ struct rio_dev *rdev, u16 tmp_destid)
+{
+ struct rio_mport *mport = rdev->hport;
+ int rc = 0;
+
+ BUG_ON(!prev);
+
+ if ((lookup_rdev(mport, rdev->destid)) == rdev)
+ return rc;
+
+ if (rdev->destid != tmp_destid) {
+ rc = rio_add_route_for_destid(mport, prev->destid, prev_port,
+ rdev->destid, 1);
+ if (rc < 0)
+ goto cleanup;
+ }
+
+ if (rio_is_switch(rdev)) {
+ rc = rio_init_lut(rdev, 1);
+ if (rc)
+ goto cleanup;
+ }
+
+ rc = add_new_dev2tree(rdev, prev, prev->destid, prev_port);
+ if (rc)
+ goto cleanup;
+done:
+ return rc;
+cleanup:
+ rio_remove_route_for_destid(mport, rdev->destid, 0);
+ pr_warn("RIO: bail on destid %hu at hp %d\n",
+ tmp_destid, rdev->hopcount);
+ goto done;
+}
+
+static struct rio_dev *rio_setup_disc_device(struct rio_mport *mport,
+ u16 parent_destid, int prev_port,
+ u16 destid, u8 hopcount)
+{
+ struct rio_dev *rdev = rio_alloc_new_device(mport, destid, hopcount,
+ parent_destid, prev_port);
+ u8 lock_hw = 1, lut_update = 1;
+ int rc;
+
+ if (IS_ERR(rdev))
+ return rdev;
+
+ rdev->local_domain = 0;
+ rc = rio_read_comptag(mport, destid, hopcount, &rdev->comp_tag);
+ if (rc)
+ goto access_err;
+
+ if (rio_has_destid(rdev->src_ops, rdev->dst_ops)) {
+ rc = rio_get_destid(mport, destid, hopcount, &rdev->destid);
+ if (rc)
+ goto access_err;
+ } else {
+ rdev->destid = rdev->comp_tag;
+ }
+ rdev->hopcount = hopcount;
+ if (rio_eval_destid(rdev)) {
+ rc = -EINVAL;
+ goto cleanup;
+ }
+ rc = rio_update_dst_tree(mport, parent_destid, prev_port,
+ rdev->hopcount, rdev->destid,
+ rdev->comp_tag, 0);
+ if (rc)
+ goto cleanup;
+
+ if (rio_dest_is_legacy(mport, parent_destid,
+ prev_port, rdev->hopcount)) {
+ rc = rio_get_legacy_properties(mport, parent_destid, prev_port,
+ rdev->hopcount, &lock_hw,
+ &lut_update);
+ if (rc)
+ goto cleanup;
+ }
+ rdev->use_hw_lock = lock_hw;
+
+ if (rio_is_switch(rdev)) {
+ rdev->return_port = RIO_GET_PORT_NUM(rdev->swpinfo);
+
+ if (rio_dest_is_one_way(mport, parent_destid,
+ prev_port, hopcount)) {
+ rc = rio_get_return_port(mport, parent_destid,
+ prev_port, hopcount,
+ &rdev->return_port);
+ if (rc)
+ goto cleanup;
+ }
+
+ rdev->rswitch->switchid = rdev->comp_tag & RIO_CTAG_UDEVID;
+ rdev->rswitch->update_lut = lut_update;
+
+ dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->hport->net.id,
+ rdev->rswitch->switchid);
+
+ rio_switch_init(rdev, 0);
+ } else {
+ dev_set_name(&rdev->dev, "%02x:e:%04x", rdev->hport->net.id,
+ rdev->destid);
+ }
+ rio_dev_init(rdev);
+ return rdev;
+access_err:
+ pr_warn("RIO: RIO:(%s) destid %hx hopcount %d - ACCESS ERROR\n",
+ __func__, destid, hopcount);
+cleanup:
+ kfree(rdev);
+ return ERR_PTR(rc);
+}
+
+static int rio_disc(struct rio_job *job, struct rio_dev *prev,
+ int prev_port, u16 destid, u8 hopcount,
+ int tmo)
+{
+ int rc = 0;
+ struct rio_mport *mport = job->mport;
+ struct rio_dev *rdev = NULL;
+ u8 lock_mode = __get_lock_mode(mport, prev->destid,
+ prev_port, hopcount);
+
+ pr_debug("RIO: do_disc prev %s, prev port %d, destid 0x%x, hop %hhu\n",
+ rio_name(prev), prev_port, destid, hopcount);
+
+ if (job->flags & RIO_JOB_FLAG_STATIC) {
+ u16 tmp_dst;
+ /* destid node required if discover static */
+ if (rio_lookup_next_destid(mport, prev->destid, prev_port,
+ hopcount, &tmp_dst)) {
+ pr_debug("RIO: No static destid found for device\n");
+ goto done;
+ }
+ }
+ rc = rio_job_hw_lock_wait_cond(job, destid, hopcount,
+ tmo, lock_mode,
+ rio_enum_complete);
+ if (unlikely(rc)) {
+ u32 tmp = 0;
+
+ rio_read_comptag(prev->hport, destid,
+ hopcount, &tmp);
+ pr_warn("RIO: lock fail comptag %x\n", tmp);
+ goto done;
+ }
+ tmo = 1;
+ rdev = rio_setup_disc_device(mport, prev->destid,
+ prev_port, destid, hopcount);
+ if (lock_mode)
+ rc = rio_hw_unlock(job->mport, destid, hopcount);
+
+ if (unlikely(IS_ERR(rdev)))
+ rc = PTR_ERR(rdev);
+
+ if (rc)
+ goto done;
+
+ rc = rio_add_disc_device(prev, prev_port, rdev, destid);
+ if (unlikely(rc))
+ goto done;
+
+ if (rio_is_switch(rdev)) {
+ int port_num;
+ int sw_inport;
+ int num_ports;
+
+ sw_inport = RIO_GET_PORT_NUM(rdev->swpinfo);
+ num_ports = RIO_GET_TOTAL_PORTS(rdev->swpinfo);
+
+ pr_debug("RIO: disc %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++) {
+ u8 active = 0;
+ u8 remote = 0;
+ u16 ndestid = RIO_ANY_DESTID(job->mport->sys_size);
+ u16 result = 0;
+
+ if (sw_inport == port_num) {
+ if (rdev_is_mport(prev)) {
+ rio_update_dst_tree(job->mport,
+ rdev->destid,
+ port_num,
+ hopcount + 1,
+ prev->destid,
+ prev->comp_tag,
+ 0);
+ }
+ continue;
+ }
+ rc = switch_port_is_active(rdev, port_num, &active);
+ if (rc < 0)
+ goto done; /* switch fault during disc ?*/
+
+ if (!active) {
+ do_port_cleanup(rdev, port_num);
+ continue;
+ }
+ rc = rio_get_enum_boundary(rdev, port_num, &remote);
+ if (rc < 0)
+ goto done;
+
+ if (remote)
+ continue;
+
+ rc = rio_disc_switch_port(job, rdev, port_num,
+ &ndestid, tmo);
+ if (rc < 0)
+ goto done; /* switch fault during disc ?*/
+
+ result = RIO_ANY_DESTID(job->mport->sys_size);
+ if (ndestid == result) {
+ pr_debug("RIO: No destid setup at active port %d\n",
+ port_num);
+ continue;
+ }
+ rc = rio_add_route_for_destid(mport, rdev->destid,
+ port_num, ndestid, 1);
+ if (rc != 0)
+ goto done; /* switch fault during disc ?*/
+
+ rc = rio_disc(job, rdev, port_num,
+ ndestid, hopcount + 1, tmo);
+ if (rc)
+ return rc;
+ }
+ } else
+ pr_debug("RIO: disc ep %s (vid %4.4x did %4.4x)\n",
+ rio_name(rdev), rdev->vid, rdev->did);
+done:
+ if (rdev && !IS_ERR(rdev))
+ rio_dev_put(rdev);
+
+ if (rc != RIO_JOB_STATE_ABORT)
+ return 0;
+ pr_debug("RIO: do_disc done rc %d\n", rc);
+ return rc;
+}
+
+static struct rio_dev *rio_disc_master_port(struct rio_job *job)
+{
+ struct rio_dev *rdev = NULL;
+ int comptag = 1;
+ int rc;
+
+ pr_debug("RIO: discover master port %d, %s\n", job->mport->id,
+ job->mport->name);
+
+ if (!rio_mport_active(job->mport))
+ goto link_fault;
+
+ if ((rio_job_hw_wait_cond(job, job->mport->host_deviceid, 0,
+ CONFIG_RAPIDIO_DISC_TIMEOUT,
+ rio_mport_enum_complete)) <= 0)
+ goto enum_to;
+
+ rio_local_read_config_32(job->mport, RIO_DID_CSR,
+ &job->mport->host_deviceid);
+
+ job->mport->host_deviceid = RIO_GET_DID(job->mport->sys_size,
+ job->mport->host_deviceid);
+
+ if (rio_job_hw_lock_wait(job, NULL, NULL, job->mport->host_deviceid,
+ 0, CONFIG_RAPIDIO_DISC_TIMEOUT))
+ goto enum_to;
+
+ rc = rio_init_net(job->mport, &comptag);
+ if (rc < 0)
+ goto err_net;
+
+ rdev = rio_setup_disc_mport(job->mport, 0);
+unlock:
+ rio_hw_unlock(job->mport, job->mport->host_deviceid, 0);
+done:
+ return rdev;
+
+link_fault:
+ pr_warn("RIO: master port %d link inactive\n",
+ job->mport->id);
+ rdev = ERR_PTR(-ENODEV);
+ goto done;
+enum_to:
+ pr_warn("RIO: timeout waiting for enumeration complete\n");
+ job->mport->host_deviceid = RIO_ANY_ID;
+ rdev = ERR_PTR(-ETIME);
+ goto done;
+err_net:
+ pr_warn("RIO: failed to init new net\n");
+ job->mport->host_deviceid = RIO_ANY_ID;
+ rdev = ERR_PTR(rc);
+ goto unlock;
+}
+
+static int rio_add_device_remote(struct rio_job *job)
+{
+ int rc = RIO_JOB_STATE_ABORT;
+ struct rio_dev *rdev = job->rdev;
+ struct rio_mport *mport = job->mport;
+ int port_num;
+ u8 hopcount;
+ u16 destid = RIO_ANY_DESTID(job->mport->sys_size);
+ int tmo;
+
+ pr_debug("RIO: Insertion in remote domains\n");
+ if (!rdev) {
+ rdev = rio_disc_master_port(job);
+
+ if (unlikely(IS_ERR(rdev)))
+ return PTR_ERR(rdev);
+
+ rc = add_new_dev2tree(rdev, NULL, RIO_INVALID_DESTID, -1);
+ if (rc != 0)
+ goto done;
+
+ if (job->mport->ops->pwenable)
+ job->mport->ops->pwenable(job->mport, 1);
+
+ port_num = -1;
+ hopcount = 0;
+ tmo = 1;
+ } else {
+ u8 port_active;
+ port_num = job->port;
+ hopcount = rdev->hopcount + 1;
+ tmo = CONFIG_RAPIDIO_DISC_TIMEOUT;
+
+ rc = switch_port_is_active(rdev, port_num, &port_active);
+ if (rc < 0)
+ goto done;
+
+ if (!port_active)
+ goto done;
+ if (rdev->local_domain) {
+ rc = rio_add_route_for_destid(mport,
+ rdev->destid,
+ port_num,
+ destid, 1);
+ if (rc != 0)
+ goto done;
+ } else {
+ rc = rio_disc_switch_port(job, rdev,
+ port_num,
+ &destid, tmo);
+ if (rc < 0)
+ goto done;
+ if (destid == RIO_ANY_DESTID(job->mport->sys_size))
+ goto done;
+ rc = rio_add_route_for_destid(mport,
+ rdev->destid,
+ port_num,
+ destid, 1);
+ if (rc != 0)
+ goto done;
+ }
+ }
+ rc = rio_disc(job, rdev, port_num, destid, hopcount, tmo);
+done:
+ if (rdev_is_mport(rdev))
+ rio_dev_put(rdev);
+ return rc;
+}
+
+static int rio_remove(struct rio_job *job, struct rio_dev *rdev)
+{
+ int dev_access = (job->rdev ? 0 : 1);
+ int rc = 0;
+
+ rio_remove_devices(rdev, dev_access, job->srio_down);
+
+ return rc;
+}
+
+struct rio_dev *rio_remove_device_root(struct rio_job *job)
+{
+ struct rio_mport *mport = job->mport;
+ struct rio_dev *rdev = NULL;
+
+ BUG_ON(!mport);
+
+ if (!job->rdev)
+ rdev = rio_get_root_node(job->mport);
+ else
+ rdev = lookup_rdev_next(job->rdev, job->port);
+
+ if (!rdev || IS_ERR(rdev))
+ return ERR_PTR(-ENODEV);
+
+ return rdev;
+}
+
+static int rio_job_add_device(struct rio_job *job)
+{
+ int rc;
+ u8 remote = 0;
+
+ rc = rio_dev_local(job, &remote);
+ if (rc < 0) {
+ pr_warn("RIO: Can not handle job request\n");
+ return rc;
+ }
+ if (!remote) {
+ pr_debug("Handle local job\n");
+ rc = rio_add_device_local(job);
+ } else {
+ pr_debug("Handle remote job\n");
+ rc = rio_add_device_remote(job);
+ }
+ if (rc)
+ pr_warn("RIO: Job aborted rc %d\n", rc);
+
+ rio_net_register_devices(job->mport);
+ return rc;
+}
+
+static int rio_job_remove_device(struct rio_job *job)
+{
+ int rc;
+ struct rio_dev *rdev = NULL;
+
+ pr_debug("RIO: remove job\n");
+
+ rdev = rio_remove_device_root(job);
+ if (IS_ERR(rdev)) {
+ rc = PTR_ERR(rdev);
+ goto done;
+ }
+
+ rc = rio_remove(job, rdev);
+ rio_dev_put(rdev);
+
+ if (!rc) {
+ if (__rio_mport_net_empty(job->mport)) {
+ if (job->mport->ops->pwenable)
+ job->mport->ops->pwenable(job->mport, 0);
+ iosync();
+ if (rio_unlock_netid(job->mport->host_deviceid,
+ job->mport->net.id))
+ pr_warn("RIO: Fail to unlock mport net_id %d\n",
+ job->mport->net.id);
+ }
+ }
+done:
+ if (rc)
+ pr_warn("RIO: remove job aborted rc %d\n", rc);
+ return rc;
+}
+
+int rio_job_init(struct rio_mport *mport, struct rio_dev *rdev,
+ int port, u32 flags, int hw_access, int event)
+{
+ struct rio_job job;
+
+ job.rdev = rdev;
+ job.mport = mport;
+ job.flags = flags;
+ job.srio_down = (hw_access ? 0 : 1);
+ job.port = port;
+
+ if (event == RIO_DEVICE_INSERTION)
+ return rio_job_add_device(&job);
+ else if (event == RIO_DEVICE_EXTRACTION)
+ return rio_job_remove_device(&job);
+ else
+ return -EINVAL;
+}
diff --git a/drivers/rapidio/rio-quirks.c b/drivers/rapidio/rio-quirks.c
new file mode 100644
index 0000000..0a155f3
--- /dev/null
+++ b/drivers/rapidio/rio-quirks.c
@@ -0,0 +1,59 @@
+/*
+ * RapidIO quirk support - heavily inspired by drivers/pci/quirks.c
+ *
+ * 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/types.h>
+#include <linux/kernel.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 "rio.h"
+
+static void rio_dev_do_fixups(struct rio_dev *rdev,
+ u16 destid,
+ u8 hopcount,
+ struct rio_dev_fixup *f,
+ struct rio_dev_fixup *end)
+{
+ while (f < end) {
+ if ((f->vid == rdev->vid) && (f->did == rdev->did)) {
+ pr_debug("RIO: calling %pF\n", f->fixup_hook);
+ f->fixup_hook(rdev, destid, hopcount);
+ }
+ f++;
+ }
+}
+
+void rio_fixup_dev(enum rio_fixup_pass pass,
+ struct rio_dev *rdev,
+ u16 destid,
+ u8 hopcount)
+{
+ struct rio_dev_fixup *start, *end;
+
+ switch (pass) {
+ case rio_fixup_early:
+ start = __start_rio_dev_fixup_early;
+ end = __end_rio_dev_fixup_early;
+ break;
+
+ case rio_fixup_enable:
+ start = __start_rio_dev_fixup_enable;
+ end = __end_rio_dev_fixup_enable;
+ break;
+
+ default:
+ return;
+ }
+ rio_dev_do_fixups(rdev, destid, hopcount, start, end);
+}
+EXPORT_SYMBOL(rio_fixup_dev);
diff --git a/drivers/rapidio/rio-route.c b/drivers/rapidio/rio-route.c
new file mode 100644
index 0000000..c0f3d9d
--- /dev/null
+++ b/drivers/rapidio/rio-route.c
@@ -0,0 +1,290 @@
+/*
+ * RapidIO route support
+ *
+ * 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/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 <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/radix-tree.h>
+
+#include "rio.h"
+
+/**
+ * rio_route_add - Add a route entry to a switch routing table
+ * @rdev: RIO device
+ * @table: Routing table ID
+ * @route_destid: Destination ID to be routed
+ * @route_port: Port number to be routed
+ * @lock: lock switch device flag
+ *
+ * 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.
+ */
+int rio_route_add(struct rio_dev *rdev, u16 table, u16 route_destid,
+u8 route_port, int lock)
+{
+ int err = 0;
+ int rc = 0;
+
+ if (!(rdev->rswitch->update_lut))
+ return rc;
+
+ if (lock && rdev->use_hw_lock) {
+ rc = rio_hw_lock_wait(rdev->hport, rdev->destid,
+ rdev->hopcount, 1);
+ if (rc)
+ return rc;
+ }
+
+ err = rdev->rswitch->add_entry(rdev->hport, rdev->destid,
+ rdev->hopcount, table,
+ route_destid, route_port);
+
+ if (lock && rdev->use_hw_lock)
+ rc = rio_hw_unlock(rdev->hport, rdev->destid,
+ rdev->hopcount);
+
+ return err ? err : rc;
+}
+
+int rio_route_get_port(struct rio_dev *rdev, u16 destid, u8 *port, int lock)
+{
+ int rc = 0;
+
+ if (lock && rdev->use_hw_lock) {
+ rc = rio_hw_lock_wait(rdev->hport, rdev->destid,
+ rdev->hopcount, 1);
+ if (rc)
+ return rc;
+ }
+
+ rdev->rswitch->get_entry(rdev->hport, rdev->destid,
+ rdev->hopcount, RIO_GLOBAL_TABLE,
+ destid, port);
+
+ if (lock && rdev->use_hw_lock)
+ rc = rio_hw_unlock(rdev->hport, rdev->destid,
+ rdev->hopcount);
+
+ return rc;
+}
+
+int rio_add_route_for_destid(struct rio_mport *mport,
+ u16 parent_dest, u8 port_num,
+ u16 destid, int lock)
+{
+ struct rio_dev *curr;
+ u8 sport = port_num;
+ u16 curr_dest = parent_dest;
+ int rc = 0;
+
+ do {
+ curr = lookup_rdev(mport, curr_dest);
+ if (!curr)
+ break;
+
+ if (!rio_is_switch(curr) || !curr->rswitch->update_lut) {
+ rio_dev_put(curr);
+ break;
+ }
+ if (curr->local_domain) {
+
+ pr_debug("RIO: add route for %hhu in %s at port %d\n",
+ destid, rio_name(curr), sport);
+
+ rc = rio_route_add(curr, RIO_GLOBAL_TABLE,
+ destid,
+ sport, lock);
+ if (rc < 0) {
+ rio_dev_put(curr);
+ break;
+ }
+ }
+ curr_dest = curr->prev_destid;
+ sport = curr->prev_port;
+ rio_dev_put(curr);
+
+ } while (curr_dest != RIO_INVALID_DESTID);
+
+ return rc;
+}
+int rio_remove_route_for_destid(struct rio_mport *mport, u16 destid, int lock)
+{
+ int i, num = 0, rc = 0;
+ struct rio_dev **dptr = rio_get_tagged_devices(mport,
+ RIO_DEV_IS_SWITCH,
+ &num);
+
+ if (!dptr)
+ return 0;
+ if (IS_ERR(dptr))
+ return PTR_ERR(dptr);
+
+ for (i = 0; i < num; i++) {
+ struct rio_dev *tmp = dptr[i];
+ if (unlikely(!tmp))
+ continue;
+ if (!tmp->local_domain || !tmp->rswitch->update_lut) {
+ rio_dev_put(tmp);
+ continue;
+ }
+ pr_debug("RIO:%s remove route for %hu in %s\n",
+ __func__, destid, rio_name(tmp));
+
+ rc = rio_route_add(tmp, RIO_GLOBAL_TABLE,
+ destid, RIO_INVALID_ROUTE, lock);
+ if (rc)
+ tmp->rswitch->update_lut = 0;
+ rio_dev_put(tmp);
+ }
+ if (dptr != NULL)
+ kfree(dptr);
+ return rc;
+}
+
+int rio_update_routes(struct rio_mport *mport, struct rio_dev **rptr, int rnum)
+{
+ int d, i, num = 0, rio_fault = 0, rc = 0;
+ struct rio_dev **dptr = rio_get_tagged_devices(mport,
+ RIO_DEV_IS_SWITCH,
+ &num);
+
+ if (!dptr)
+ return 0;
+ if (IS_ERR(dptr))
+ return PTR_ERR(dptr);
+
+ for (d = 0; d < rnum; d++) {
+ u16 destid;
+ u32 result;
+ if (rio_fault)
+ break;
+ if (!rptr[d])
+ continue;
+ destid = rptr[d]->destid;
+ for (i = 0; i < num; i++) {
+ u8 port, static_port = RIO_INVALID_ROUTE;
+ int disabled;
+ struct rio_dev *tmp = dptr[i];
+
+ if (!tmp)
+ continue;
+ if (tmp->destid == destid ||
+ !tmp->local_domain ||
+ !tmp->rswitch->update_lut)
+ continue;
+
+ rcu_read_lock();
+ disabled = radix_tree_tag_get(&mport->net.dev_tree,
+ tmp->destid,
+ RIO_DEV_DISABLED);
+ rcu_read_unlock();
+
+ rc = rio_route_get_port(tmp, destid, &port,
+ (disabled ? 0 : 1));
+ if (rc) {
+ if (++rio_fault >
+ CONFIG_RAPIDIO_ACCESS_ERR_LIMIT)
+ break;
+ continue;
+ }
+#ifdef CONFIG_RAPIDIO_STATIC_DESTID
+ rio_lookup_static_route(rptr[d], tmp->destid,
+ &static_port);
+#endif
+ pr_debug("RIO: Check route for destid %hx in %s static port %hhu\n",
+ destid, rio_name(tmp), static_port);
+ if ((port == RIO_INVALID_ROUTE) ||
+ ((static_port != RIO_INVALID_ROUTE) &&
+ (static_port != port))) {
+ u8 sw_port = tmp->return_port;
+
+ if (static_port != RIO_INVALID_ROUTE)
+ sw_port = static_port;
+ pr_debug("RIO: Add route for destid %hx at port %d in %s\n",
+ destid, sw_port, rio_name(tmp));
+ rc = rio_route_add(tmp, RIO_GLOBAL_TABLE,
+ destid, sw_port,
+ (disabled ? 0 : 1));
+ if (rc) {
+ if (++rio_fault >
+ CONFIG_RAPIDIO_ACCESS_ERR_LIMIT)
+ break;
+ }
+ }
+ }
+ /* verify dev access after route update */
+ rc = rio_read_config_32(rptr[d],
+ rptr[d]->phys_efptr +
+ RIO_PORT_GEN_CTL_CSR,
+ &result);
+ if (rc != 0) {
+ if (++rio_fault > CONFIG_RAPIDIO_ACCESS_ERR_LIMIT)
+ break;
+ }
+ }
+ for (i = 0; i < num; i++) {
+ if (dptr[i])
+ rio_dev_put(dptr[i]);
+ }
+ kfree(dptr);
+ return (rio_fault > CONFIG_RAPIDIO_ACCESS_ERR_LIMIT ? rc : 0);
+}
+
+int rio_init_lut(struct rio_dev *rdev, int lock)
+{
+ struct rio_mport *mport = rdev->hport;
+ int i, num = 0, rio_fault = 0, rc = 0;
+ struct rio_dev **dptr = NULL;
+
+ if (!rdev->rswitch->update_lut)
+ return 0;
+
+ dptr = rio_get_all_devices(mport, &num);
+ if (!dptr)
+ return 0;
+ if (IS_ERR(dptr))
+ return PTR_ERR(dptr);
+
+ for (i = 0; i < num; i++) {
+ struct rio_dev *tmp = dptr[i];
+
+ if (unlikely(!tmp))
+ continue;
+ if (rio_fault || (!tmp->local_domain && !rdev->local_domain)) {
+ rio_dev_put(tmp);
+ continue;
+ }
+ pr_debug("RIO:%s add %hhu to %s at port %d\n",
+ __func__, tmp->destid, rio_name(rdev),
+ rdev->return_port);
+ rc = rio_route_add(rdev, RIO_GLOBAL_TABLE,
+ tmp->destid, rdev->return_port, lock);
+ rio_dev_put(tmp);
+ if (rc)
+ rio_fault++;
+ }
+ kfree(dptr);
+ return rc;
+}
diff --git a/drivers/rapidio/rio-route.h b/drivers/rapidio/rio-route.h
new file mode 100644
index 0000000..9156808
--- /dev/null
+++ b/drivers/rapidio/rio-route.h
@@ -0,0 +1,31 @@
+#ifndef _RIO_ROUTE_H
+#define _RIO_ROUTE_H
+
+/*
+ * RapidIO job support
+ *
+ * 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/rio.h>
+
+extern int rio_route_add(struct rio_dev *rdev, u16 table, u16 route_destid,
+ u8 route_port, int lock);
+extern int rio_route_get_port(struct rio_dev *rdev,
+ u16 destid, u8 *port, int lock);
+extern int rio_add_route_for_destid(struct rio_mport *mport,
+ u16 parent_dest, u8 port_num,
+ u16 destid, int lock);
+extern int rio_remove_route_for_destid(struct rio_mport *mport,
+ u16 destid, int lock);
+extern int rio_update_route_for_destid(struct rio_mport *mport,
+ u16 destid, int lock);
+extern int rio_init_lut(struct rio_dev *rdev, int lock);
+extern int rio_update_routes(struct rio_mport *mport,
+ struct rio_dev **rptr, int rnum);
+
+#endif
--
1.7.9.5
More information about the linux-yocto
mailing list