From: Paul Butler <paul.but...@windriver.com> Updated to include - Rapid IO driver to use DMA for direct I/O data transfers - Rapid Access Error Limit - number of device access errors accepted during net configuration - Rapid IO dynamic routing - Rapid IO Multicast write messages - Rapid IO assignment of destination IDs dynamically - Rapid IO support of multiple enumeration hosts - Rapid IO addition of a second destionation ID by the main RIO port - Rapid IO Multicast mask support for multiple port writes
Makefile updated with rapid io additional files Signed-off-by: Paul Butler <paul.but...@windriver.com> --- drivers/rapidio/Kconfig | 85 +++ drivers/rapidio/Makefile | 4 +- drivers/rapidio/rio-access.c | 89 +-- drivers/rapidio/rio-driver.c | 70 ++- drivers/rapidio/rio-sysfs.c | 218 ++++++- drivers/rapidio/rio.c | 1404 ++++++++++++++++++++++++++++++------------ drivers/rapidio/rio.h | 59 +- 7 files changed, 1473 insertions(+), 456 deletions(-) diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig index bc87192..20b86a5 100644 --- a/drivers/rapidio/Kconfig +++ b/drivers/rapidio/Kconfig @@ -22,6 +22,91 @@ config RAPIDIO_ENABLE_RX_TX_PORTS ports for Input/Output direction to allow other traffic than Maintenance transfers. +config RAPIDIO_DIO_DMA + bool "Use DMA for direct I/O access" + depends on RAPIDIO + default n + select DMA_ENGINE + ---help--- + Say Y here if you want the RapidIO driver to use DMA for direct I/O + data transfers. + +config RAPIDIO_ACCESS_ERR_LIMIT + int "Number of device access errors accepted during net configuration, before giving up." + depends on RAPIDIO + default 0 + ---help--- + RIO net setup will be aborted if the core driver discovers that + contact is lost to devices, which has previously been probed, during + final LUT setup phase. + Failed SRIO access attempts may result in machine check errors: + Depending on SRIO port controller, the SRIO driver may have the + ability to recover from such faults. If your port controller supports + MC recovery for SRIO access errors you may chose to tolerate a certain + number of faults. + MC fault recovery, however, take some time and the user is advised + not to set this value to high. + +config RAPIDIO_DYNAMIC_ROUTES + bool "Use dynamic routing (Experimental)" + depends on RAPIDIO + default n + ---help--- + Say Y here if you want the RapidIO driver to use dynamic routing and + calculate and adopt to the cheapest path between nodes. + +config RAPIDIO_MULTICAST_PW + bool "Port Write Multicast (Experimental)" + depends on RAPIDIO + default n + ---help--- + Say Y here if you want the RapidIO driver to enable multicast of + prot write messages. + +config RAPIDIO_HOTPLUG + bool "Support for RAPIDIO Hotplug" + depends on RAPIDIO && HOTPLUG && SYSFS + default n + ---help--- + This allows you to add and remove RIO devices while the machine is + powered up and running. + + When in doubt, say N. + +config RAPIDIO_STATIC_DESTID + bool "Use static RIO device destID" + depends on RAPIDIO + default n + ---help--- + The default enumeration mode in the rapidio driver is to assign destIDs + to RIO devices dynamically. If you say Y here, the rapidio driver allows + static or user defined numbering. + +config RAPIDIO_ENUM_DOMAIN + bool "Support multiple enumeration hosts in the RIO network (Experimental)" + depends on RAPIDIO && RAPIDIO_HOTPLUG && RAPIDIO_STATIC_DESTID + default n + ---help--- + Support multiple enumeration hosts in the RIO network + +config RAPIDIO_SECOND_DEST_ID + hex "Add a second destination id to main RIO port" + depends on RAPIDIO_HOTPLUG + default "0xdc" + ---help--- + Add a second destination id that will be recognized by the main RIO + port. Can for example be used to receive multicasts or equivalent + that has multiple recipients. + +config RAPIDIO_PW_MULTICAST_MASK_ID + int "Multicast mask id to use for multicast port writes in switches" + depends on RAPIDIO_HOTPLUG + default "1" + ---help--- + When using domains we need to multicast all port writes to all + domains. For this we need to set up a multicast mask dedicated for + this on all switches. This is the number for that mask. + config RAPIDIO_DEBUG bool "RapidIO subsystem debug messages" depends on RAPIDIO diff --git a/drivers/rapidio/Makefile b/drivers/rapidio/Makefile index ec3fb81..66db244 100644 --- a/drivers/rapidio/Makefile +++ b/drivers/rapidio/Makefile @@ -1,7 +1,9 @@ # # Makefile for RapidIO interconnect services # -obj-y += rio.o rio-access.o rio-driver.o rio-scan.o rio-sysfs.o +#obj-y += rio.o rio-access.o rio-driver.o rio-scan.o rio-net.o rio-locks.o rio-sysfs.o rio-quirks.o rio-dio.o rio-destid.o rio-domain.o rio-hotplug.o rio-multicast.o rio-job.o + +obj-y += rio.o rio-access.o rio-driver.o rio-net2.o rio-locks.o rio-sysfs.o rio-quirks.o rio-dio.o rio-destid.o rio-hotplug.o rio-route.o obj-$(CONFIG_RAPIDIO) += switches/ obj-$(CONFIG_RAPIDIO) += devices/ diff --git a/drivers/rapidio/rio-access.c b/drivers/rapidio/rio-access.c index a3824ba..1e05d2a 100644 --- a/drivers/rapidio/rio-access.c +++ b/drivers/rapidio/rio-access.c @@ -17,9 +17,11 @@ * These interrupt-safe spinlocks protect all accesses to RIO * configuration space and doorbell access. */ -static DEFINE_SPINLOCK(rio_config_lock); -static DEFINE_SPINLOCK(rio_doorbell_lock); +/* static DEFINE_SPINLOCK(rio_config_lock); */ +/* static DEFINE_SPINLOCK(rio_doorbell_lock); */ +static DEFINE_RAW_SPINLOCK(rio_config_lock); +static DEFINE_RAW_SPINLOCK(rio_doorbell_lock); /* * Wrappers for all RIO configuration access functions. They just check * alignment, do locking and call the low-level functions pointed to @@ -39,18 +41,19 @@ static DEFINE_SPINLOCK(rio_doorbell_lock); * Generates rio_local_read_config_* functions used to access * configuration space registers on the local device. */ -#define RIO_LOP_READ(size,type,len) \ -int __rio_local_read_config_##size \ +#define RIO_LOP_READ(size, type, len) \ +int __rio_local_read_config_##size \ (struct rio_mport *mport, u32 offset, type *value) \ { \ int res; \ unsigned long flags; \ u32 data = 0; \ - if (RIO_##size##_BAD) return RIO_BAD_SIZE; \ - spin_lock_irqsave(&rio_config_lock, flags); \ + if (RIO_##size##_BAD) \ + return RIO_BAD_SIZE; \ + raw_spin_lock_irqsave(&rio_config_lock, flags); \ res = mport->ops->lcread(mport, mport->id, offset, len, &data); \ *value = (type)data; \ - spin_unlock_irqrestore(&rio_config_lock, flags); \ + raw_spin_unlock_irqrestore(&rio_config_lock, flags); \ return res; \ } @@ -63,31 +66,31 @@ int __rio_local_read_config_##size \ * Generates rio_local_write_config_* functions used to access * configuration space registers on the local device. */ -#define RIO_LOP_WRITE(size,type,len) \ -int __rio_local_write_config_##size \ +#define RIO_LOP_WRITE(size, type, len) \ +int __rio_local_write_config_##size \ (struct rio_mport *mport, u32 offset, type value) \ { \ int res; \ unsigned long flags; \ - if (RIO_##size##_BAD) return RIO_BAD_SIZE; \ - spin_lock_irqsave(&rio_config_lock, flags); \ + if (RIO_##size##_BAD) \ + return RIO_BAD_SIZE; \ + raw_spin_lock_irqsave(&rio_config_lock, flags); \ res = mport->ops->lcwrite(mport, mport->id, offset, len, value);\ - spin_unlock_irqrestore(&rio_config_lock, flags); \ + raw_spin_unlock_irqrestore(&rio_config_lock, flags); \ return res; \ } RIO_LOP_READ(8, u8, 1) -RIO_LOP_READ(16, u16, 2) -RIO_LOP_READ(32, u32, 4) -RIO_LOP_WRITE(8, u8, 1) -RIO_LOP_WRITE(16, u16, 2) -RIO_LOP_WRITE(32, u32, 4) - EXPORT_SYMBOL_GPL(__rio_local_read_config_8); +RIO_LOP_READ(16, u16, 2) EXPORT_SYMBOL_GPL(__rio_local_read_config_16); +RIO_LOP_READ(32, u32, 4) EXPORT_SYMBOL_GPL(__rio_local_read_config_32); +RIO_LOP_WRITE(8, u8, 1) EXPORT_SYMBOL_GPL(__rio_local_write_config_8); +RIO_LOP_WRITE(16, u16, 2) EXPORT_SYMBOL_GPL(__rio_local_write_config_16); +RIO_LOP_WRITE(32, u32, 4) EXPORT_SYMBOL_GPL(__rio_local_write_config_32); /** @@ -99,18 +102,21 @@ EXPORT_SYMBOL_GPL(__rio_local_write_config_32); * Generates rio_mport_read_config_* functions used to access * configuration space registers on the local device. */ -#define RIO_OP_READ(size,type,len) \ -int rio_mport_read_config_##size \ - (struct rio_mport *mport, u16 destid, u8 hopcount, u32 offset, type *value) \ +#define RIO_OP_READ(size, type, len) \ +int rio_mport_read_config_##size \ + (struct rio_mport *mport, u16 destid, u8 hopcount, \ + u32 offset, type *value) \ { \ int res; \ unsigned long flags; \ u32 data = 0; \ - if (RIO_##size##_BAD) return RIO_BAD_SIZE; \ - spin_lock_irqsave(&rio_config_lock, flags); \ - res = mport->ops->cread(mport, mport->id, destid, hopcount, offset, len, &data); \ + if (RIO_##size##_BAD) \ + return RIO_BAD_SIZE; \ + raw_spin_lock_irqsave(&rio_config_lock, flags); \ + res = mport->ops->cread(mport, mport->id, destid, \ + hopcount, offset, len, &data); \ *value = (type)data; \ - spin_unlock_irqrestore(&rio_config_lock, flags); \ + raw_spin_unlock_irqrestore(&rio_config_lock, flags); \ return res; \ } @@ -123,31 +129,33 @@ int rio_mport_read_config_##size \ * Generates rio_mport_write_config_* functions used to access * configuration space registers on the local device. */ -#define RIO_OP_WRITE(size,type,len) \ -int rio_mport_write_config_##size \ - (struct rio_mport *mport, u16 destid, u8 hopcount, u32 offset, type value) \ +#define RIO_OP_WRITE(size, type, len) \ +int rio_mport_write_config_##size \ + (struct rio_mport *mport, u16 destid, u8 hopcount, \ + u32 offset, type value) \ { \ int res; \ unsigned long flags; \ - if (RIO_##size##_BAD) return RIO_BAD_SIZE; \ - spin_lock_irqsave(&rio_config_lock, flags); \ - res = mport->ops->cwrite(mport, mport->id, destid, hopcount, offset, len, value); \ - spin_unlock_irqrestore(&rio_config_lock, flags); \ + if (RIO_##size##_BAD) \ + return RIO_BAD_SIZE; \ + raw_spin_lock_irqsave(&rio_config_lock, flags); \ + res = mport->ops->cwrite(mport, mport->id, destid, hopcount, \ + offset, len, value); \ + raw_spin_unlock_irqrestore(&rio_config_lock, flags); \ return res; \ } RIO_OP_READ(8, u8, 1) -RIO_OP_READ(16, u16, 2) -RIO_OP_READ(32, u32, 4) -RIO_OP_WRITE(8, u8, 1) -RIO_OP_WRITE(16, u16, 2) -RIO_OP_WRITE(32, u32, 4) - EXPORT_SYMBOL_GPL(rio_mport_read_config_8); +RIO_OP_READ(16, u16, 2) EXPORT_SYMBOL_GPL(rio_mport_read_config_16); +RIO_OP_READ(32, u32, 4) EXPORT_SYMBOL_GPL(rio_mport_read_config_32); +RIO_OP_WRITE(8, u8, 1) EXPORT_SYMBOL_GPL(rio_mport_write_config_8); +RIO_OP_WRITE(16, u16, 2) EXPORT_SYMBOL_GPL(rio_mport_write_config_16); +RIO_OP_WRITE(32, u32, 4) EXPORT_SYMBOL_GPL(rio_mport_write_config_32); /** @@ -165,11 +173,10 @@ int rio_mport_send_doorbell(struct rio_mport *mport, u16 destid, u16 data) int res; unsigned long flags; - spin_lock_irqsave(&rio_doorbell_lock, flags); + raw_spin_lock_irqsave(&rio_doorbell_lock, flags); res = mport->ops->dsend(mport, mport->id, destid, data); - spin_unlock_irqrestore(&rio_doorbell_lock, flags); + raw_spin_unlock_irqrestore(&rio_doorbell_lock, flags); return res; } - EXPORT_SYMBOL_GPL(rio_mport_send_doorbell); diff --git a/drivers/rapidio/rio-driver.c b/drivers/rapidio/rio-driver.c index 0f4a53b..143b2ae 100644 --- a/drivers/rapidio/rio-driver.c +++ b/drivers/rapidio/rio-driver.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/rio.h> #include <linux/rio_ids.h> +#include <linux/rio_drv.h> #include "rio.h" @@ -58,9 +59,9 @@ struct rio_dev *rio_dev_get(struct rio_dev *rdev) { if (rdev) get_device(&rdev->dev); - return rdev; } +EXPORT_SYMBOL_GPL(rio_dev_get); /** * rio_dev_put - Release a use of the RIO device structure @@ -76,9 +77,11 @@ void rio_dev_put(struct rio_dev *rdev) if (rdev) put_device(&rdev->dev); } +EXPORT_SYMBOL_GPL(rio_dev_put); /** - * rio_device_probe - Tell if a RIO device structure has a matching RIO device id structure + * rio_device_probe - Tell if a RIO device structure has a matching + * RIO device id structure * @dev: the RIO device structure to match against * * return 0 and set rio_dev->driver when drv claims rio_dev, else error @@ -98,10 +101,15 @@ static int rio_device_probe(struct device *dev) if (id) error = rdrv->probe(rdev, id); if (error >= 0) { + pr_debug("%s: for %s got driver %pF\n", + __func__, rio_name(rdev), rdrv->probe); rdev->driver = rdrv; error = 0; - } else + } else { + pr_debug("%s: for %s probe fail\n", + __func__, rio_name(rdev)); rio_dev_put(rdev); + } } return error; } @@ -120,9 +128,16 @@ static int rio_device_remove(struct device *dev) struct rio_dev *rdev = to_rio_dev(dev); struct rio_driver *rdrv = rdev->driver; + pr_debug("device remove for %s\n", rio_name(rdev)); if (rdrv) { - if (rdrv->remove) + if (rdrv->remove) { + pr_debug("driver remove for %s using %pF\n", + rio_name(rdev), rdrv->remove); rdrv->remove(rdev); + } else { + pr_debug("driver not registered for %s\n", + rio_name(rdev)); + } rdev->driver = NULL; } @@ -142,13 +157,40 @@ static int rio_device_remove(struct device *dev) */ int rio_register_driver(struct rio_driver *rdrv) { + int rc; + /* initialize common driver fields */ rdrv->driver.name = rdrv->name; rdrv->driver.bus = &rio_bus_type; + rdrv->driver.owner = THIS_MODULE; + rdrv->driver.mod_name = KBUILD_MODNAME; + + spin_lock_init(&rdrv->dynids.lock); + INIT_LIST_HEAD(&rdrv->dynids.list); /* register with core */ - return driver_register(&rdrv->driver); + rc = driver_register(&rdrv->driver); + if (rc) + goto out; + + rc = rio_create_newid_file(rdrv); + if (rc) + goto out_newid; + + rc = rio_create_removeid_file(rdrv); + if (rc) + goto out_removeid; +out: + return rc; + +out_removeid: + rio_remove_newid_file(rdrv); +out_newid: + driver_unregister(&rdrv->driver); + goto out; + } +EXPORT_SYMBOL_GPL(rio_register_driver); /** * rio_unregister_driver - unregister a RIO driver @@ -161,11 +203,15 @@ int rio_register_driver(struct rio_driver *rdrv) */ void rio_unregister_driver(struct rio_driver *rdrv) { + rio_remove_removeid_file(rdrv); + rio_remove_newid_file(rdrv); driver_unregister(&rdrv->driver); } +EXPORT_SYMBOL_GPL(rio_unregister_driver); /** - * rio_match_bus - Tell if a RIO device structure has a matching RIO driver device id structure + * rio_match_bus - Tell if a RIO device structure has a matching RIO + * driver device id structure * @dev: the standard device structure to match against * @drv: the standard driver structure containing the ids to match against * @@ -189,7 +235,8 @@ static int rio_match_bus(struct device *dev, struct device_driver *drv) if (found_id) return 1; - out:return 0; +out: + return 0; } struct device rio_bus = { @@ -203,6 +250,7 @@ struct bus_type rio_bus_type = { .probe = rio_device_probe, .remove = rio_device_remove, }; +EXPORT_SYMBOL_GPL(rio_bus_type); /** * rio_bus_init - Register the RapidIO bus with the device model @@ -213,14 +261,8 @@ struct bus_type rio_bus_type = { static int __init rio_bus_init(void) { if (device_register(&rio_bus) < 0) - printk("RIO: failed to register RIO bus device\n"); + printk(KERN_ERR "RIO: failed to register RIO bus device\n"); return bus_register(&rio_bus_type); } postcore_initcall(rio_bus_init); - -EXPORT_SYMBOL_GPL(rio_register_driver); -EXPORT_SYMBOL_GPL(rio_unregister_driver); -EXPORT_SYMBOL_GPL(rio_bus_type); -EXPORT_SYMBOL_GPL(rio_dev_get); -EXPORT_SYMBOL_GPL(rio_dev_put); diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index 4dbe360..288b014 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -10,18 +10,23 @@ * option) any later version. */ +#include <linux/slab.h> +#include <linux/err.h> #include <linux/kernel.h> #include <linux/rio.h> #include <linux/rio_drv.h> #include <linux/stat.h> #include <linux/capability.h> +#ifdef NEW_STYLE +#include <linux/radix-tree.h> +#endif #include "rio.h" /* Sysfs support */ -#define rio_config_attr(field, format_string) \ +#define rio_config_attr(field, format_string) \ static ssize_t \ -field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ +field##_show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ struct rio_dev *rdev = to_rio_dev(dev); \ \ @@ -37,24 +42,152 @@ rio_config_attr(asm_rev, "0x%04x\n"); rio_config_attr(destid, "0x%04x\n"); rio_config_attr(hopcount, "0x%02x\n"); -static ssize_t routes_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t routes_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct rio_dev *rdev = to_rio_dev(dev); char *str = buf; int i; + u8 port; - for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size); + if (rio_hw_lock_wait(rdev->hport, rdev->destid, rdev->hopcount, 1)) + goto done; + + for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rdev->hport->sys_size); i++) { - if (rdev->rswitch->route_table[i] == RIO_INVALID_ROUTE) + if (rio_route_get_port(rdev, i, &port, 0)) + continue; + + if (port == RIO_INVALID_ROUTE) continue; str += - sprintf(str, "%04x %02x\n", i, - rdev->rswitch->route_table[i]); + sprintf(str, "%04x %02x\n", i, port); } - return (str - buf); + rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + +done: + return str - buf; } +static ssize_t port_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rio_dev *rdev = to_rio_dev(dev); + char *str = buf; + int no_ports, port, rc, link_ok; + u32 value, status_reg, ctrl_reg; + + rc = rio_read_config_32(rdev, RIO_SWP_INFO_CAR, &value); + if (rc) { + pr_debug( + "RIO: (%s) Failed to read status reg addr=%08x result=%d\n", + __func__, RIO_SWP_INFO_CAR, rc); + goto done; + } + no_ports = (value & RIO_SWP_INFO_PORT_TOTAL_MASK) >> 8; + + str += sprintf(str, "\nPort\tLink\tPORT_N_ERR\tPORT_N_CTL\n"); + str += sprintf(str, "------------------------------------------\n"); + + for (port = 0; port < no_ports; port++) { + rc = rio_read_config_32(rdev, + rdev->phys_efptr + + RIO_PORT_N_ERR_STS_CSR(port), + &status_reg); + if (rc) { + pr_debug("RIO: (%s) Failed to read status reg addr=%08x result=%d\n", + __func__, rdev->phys_efptr + + RIO_PORT_N_ERR_STS_CSR(port), rc); + goto done; + } + rc = rio_read_config_32(rdev, + rdev->phys_efptr + + RIO_PORT_N_CTL_CSR(port), + &ctrl_reg); + if (rc) { + pr_debug("RIO: (%s) Failed to read control reg addr=%08x result=%d\n", + __func__, rdev->phys_efptr + + RIO_PORT_N_CTL_CSR(port), rc); + goto done; + } + + link_ok = ((status_reg & RIO_PORT_N_ERR_STS_PORT_OK) != 0); + + str += sprintf(str, "%3d\t%s\t0x%08x\t0x%08x\n", + port, + (link_ok) ? "OK" : "NOK", + status_reg, + ctrl_reg); + } + str += sprintf(str, "------------------------------------------\n\n"); + +done: + return str - buf; +} + +static ssize_t lut_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rio_dev *rdev = to_rio_dev(dev); + char *str = buf; + u16 destid; + + for (destid = 0; + destid < RIO_ANY_DESTID(rdev->hport->sys_size); destid++) { + u8 route_port; + rdev->rswitch->get_entry(rdev->hport, rdev->destid, + rdev->hopcount, RIO_GLOBAL_TABLE, + destid, &route_port); + if (route_port != RIO_INVALID_ROUTE) + str += sprintf(str, "%04x %02x\n", destid, route_port); + } + return str - buf; +} + +#ifdef NEW_STYLE +static ssize_t lprev_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rio_dev *rdev = to_rio_dev(dev); + struct rio_dev *prev; + ssize_t count; + + rcu_read_lock(); + prev = radix_tree_lookup(&rdev->hport->net.dev_tree, + rdev->prev_destid); + count = sprintf(buf, "%s\n", + (prev) ? rio_name(prev) : "root"); + rcu_read_unlock(); + return count; +} + +static ssize_t lnext_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rio_dev *rdev = to_rio_dev(dev); + char *str = buf; + int i; + + if (rdev->pef & RIO_PEF_SWITCH) { + for (i = 0; i < RIO_GET_TOTAL_PORTS(rdev->swpinfo); i++) { + struct rio_dev *next = lookup_rdev_next(rdev, i); + + if (!IS_ERR(next)) { + str += sprintf(str, "%s\n", + rio_name(next)); + rio_dev_put(next); + } else + str += sprintf(str, "null\n"); + } + } + + return str - buf; +} + +#else + static ssize_t lprev_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -73,9 +206,9 @@ static ssize_t lnext_show(struct device *dev, if (rdev->pef & RIO_PEF_SWITCH) { for (i = 0; i < RIO_GET_TOTAL_PORTS(rdev->swpinfo); i++) { - if (rdev->rswitch->nextdev[i]) + if (rdev->rswitch->port[i].rdev) str += sprintf(str, "%s\n", - rio_name(rdev->rswitch->nextdev[i])); + rio_name(rdev->rswitch->port[i].rdev)); else str += sprintf(str, "null\n"); } @@ -83,6 +216,7 @@ static ssize_t lnext_show(struct device *dev, return str - buf; } +#endif struct device_attribute rio_dev_attrs[] = { __ATTR_RO(did), @@ -99,6 +233,9 @@ struct device_attribute rio_dev_attrs[] = { static DEVICE_ATTR(routes, S_IRUGO, routes_show, NULL); static DEVICE_ATTR(lnext, S_IRUGO, lnext_show, NULL); static DEVICE_ATTR(hopcount, S_IRUGO, hopcount_show, NULL); +static DEVICE_ATTR(port_status, S_IRUGO, port_status_show, NULL); +static DEVICE_ATTR(lut, S_IRUGO, lut_show, NULL); + static ssize_t rio_read_config(struct file *filp, struct kobject *kobj, @@ -257,8 +394,12 @@ int rio_create_sysfs_dev_files(struct rio_dev *rdev) err |= device_create_file(&rdev->dev, &dev_attr_routes); err |= device_create_file(&rdev->dev, &dev_attr_lnext); err |= device_create_file(&rdev->dev, &dev_attr_hopcount); + err |= device_create_file(&rdev->dev, &dev_attr_port_status); + err |= device_create_file(&rdev->dev, &dev_attr_lut); + if (!err && rdev->rswitch->sw_sysfs) - err = rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_CREATE); + err = rdev->rswitch->sw_sysfs(rdev, + RIO_SW_SYSFS_CREATE); } if (err) @@ -281,7 +422,62 @@ void rio_remove_sysfs_dev_files(struct rio_dev *rdev) device_remove_file(&rdev->dev, &dev_attr_routes); device_remove_file(&rdev->dev, &dev_attr_lnext); device_remove_file(&rdev->dev, &dev_attr_hopcount); + if (rdev->rswitch->sw_sysfs) rdev->rswitch->sw_sysfs(rdev, RIO_SW_SYSFS_REMOVE); } } + +#if defined(CONFIG_RAPIDIO_HOTPLUG) || defined(CONFIG_RAPIDIO_STATIC_DESTID) +static ssize_t rio_devices_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rio_mport *mport = container_of(dev, struct rio_mport, dev); + char *str = buf; + int i, num = 0; + struct rio_dev **dptr = NULL; + + if (!mport) + return -EINVAL; + + dptr = rio_get_all_devices(mport, &num); + if (!dptr) + return 0; + + if (IS_ERR(dptr)) + return PTR_ERR(dptr); + + str += sprintf(str, "Device table:\n"); + str += sprintf(str, + "name\t\tdid\tvid\tswpinfo\t\thops\thwlock\tlocal\n"); + for (i = 0; i < num; i++) { + struct rio_dev *node = dptr[i]; + + if (unlikely(!node)) + continue; + + str += sprintf(str, + "%s\t%4.4x\t%4.4x\t%8.8x\t%d\t%d\t%d\n", + rio_name(node), + node->did, + node->vid, + node->swpinfo, + node->hopcount, + node->use_hw_lock, + node->local_domain); + rio_dev_put(node); + } + kfree(dptr); + return str - buf; +} +static DEVICE_ATTR(devices, S_IRUGO, rio_devices_show, NULL); + +int rio_sysfs_init(struct rio_mport *mport) +{ + int rc = 0; + + rc |= device_create_file(&mport->dev, &dev_attr_devices); + + return rc; +} +#endif diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 86c9a09..5312777 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -28,12 +28,238 @@ #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/hardirq.h> +#include <linux/semaphore.h> #include "rio.h" +#include "rio-job.h" -static LIST_HEAD(rio_mports); +LIST_HEAD(rio_mports); static unsigned char next_portid; +struct rio_dev **rio_get_tagged_devices(struct rio_mport *mport, int tag, int *n) +{ + int items = atomic_read(&mport->net.rio_dev_num); + void **nptr = NULL; + struct rio_dev **dptr = NULL; + int i, num = 0; + + if (!items) + goto done; + + nptr = kzalloc(sizeof(void *) * items, GFP_KERNEL); + if (!nptr) + goto err; + dptr = kzalloc(sizeof(struct rio_dev *) * items, GFP_KERNEL); + if (!dptr) + goto err; + + rcu_read_lock(); +retry: + num = radix_tree_gang_lookup_tag_slot(&mport->net.dev_tree, (void ***)nptr, + 0, items, tag); + if (num > 0) { + for (i = 0; i < num; i++) { + struct rio_dev *rdev = radix_tree_deref_slot((void **)nptr[i]); + + if (unlikely(!rdev)) { + dptr[i] = NULL; + continue; + } + if (radix_tree_deref_retry(rdev)) + goto retry; + dptr[i] = rio_dev_get(rdev); + } + } + rcu_read_unlock(); +done: + if (nptr != NULL) + kfree(nptr); + *n = num; + return dptr; +err: + if (dptr != NULL) + kfree(dptr); + dptr = ERR_PTR(-ENOMEM); + goto done; +} + +struct rio_dev **rio_get_all_devices(struct rio_mport *mport, int *n) +{ + int items = atomic_read(&mport->net.rio_dev_num); + void **nptr = NULL; + struct rio_dev **dptr = NULL; + int i, num = 0; + + if (!items) + goto done; + + nptr = kzalloc(sizeof(void *) * items, GFP_KERNEL); + if (!nptr) + goto err; + dptr = kzalloc(sizeof(struct rio_dev *) * items, GFP_KERNEL); + if (!dptr) + goto err; + + rcu_read_lock(); +retry: + num = radix_tree_gang_lookup_slot(&mport->net.dev_tree, (void ***)nptr, + NULL, 0, items); + if (num > 0) { + for (i = 0; i < num; i++) { + struct rio_dev *rdev = radix_tree_deref_slot((void **)nptr[i]); + + if (unlikely(!rdev)) { + dptr[i] = NULL; + continue; + } + if (radix_tree_deref_retry(rdev)) + goto retry; + + dptr[i] = rio_dev_get(rdev); + } + } + rcu_read_unlock(); +done: + if (nptr != NULL) + kfree(nptr); + *n = num; + return dptr; +err: + if (dptr != NULL) + kfree(dptr); + dptr = ERR_PTR(-ENOMEM); + goto done; +} +EXPORT_SYMBOL_GPL(rio_get_all_devices); + +int rio_get_err_and_status(struct rio_dev *rdev, int portnum, u32 *err_status) +{ + int rc; + rc = rio_read_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), err_status); + if (rc) + pr_debug("RIO: Failed to read RIO_PORT_N_ERR_STS_CSR from device\n"); + return rc; +} + +/** + * rio_is_switch- Tests if a RIO device has switch capabilities + * @rdev: RIO device + * + * Gets the RIO device Processing Element Features register + * contents and tests for switch capabilities. Returns 1 if + * the device is a switch or 0 if it is not a switch. + * The RIO device struct is freed. + */ +int rio_is_switch(struct rio_dev *rdev) +{ + if (rdev->pef & RIO_PEF_SWITCH) + return 1; + return 0; +} +#ifdef NEW_STYLE +int rio_type_of_next(struct rio_dev *sw_curr, struct rio_dev *sw_next) +{ + if (!sw_next) + return RIO_PORT_UNUSED; + + if (rio_is_switch(sw_next)) { + rcu_read_lock(); + if (sw_next->prev_destid != sw_curr->destid + || sw_curr == sw_next) { + pr_debug("skip redundant path to %s\n", rio_name(sw_next)); + rcu_read_unlock(); + return RIO_REDUNDANT_PATH; + } + rcu_read_unlock(); + return RIO_SWITCH; + } + return RIO_END_POINT; +} +#else +int rio_type_of_next(struct rio_dev *sw_curr, struct rio_dev *sw_next) +{ + if (!sw_next) + return RIO_PORT_UNUSED; + + if (rio_is_switch(sw_next)) { + if (sw_next->prev != sw_curr || sw_curr == sw_next) { + pr_debug("skip redundant path to %s\n", rio_name(sw_next)); + return RIO_REDUNDANT_PATH; + } + return RIO_SWITCH; + } + return RIO_END_POINT; +} +#endif +/** + * rio_map_outb_mem -- Mapping outbound memory. + * @mport: RapidIO master port + * @win: Outbound ATMU window for this access + * - obtained by calling fsl_rio_req_outb_region. + * @destid: Destination ID of transaction + * @offset: RapidIO space start address. + * @res: Mapping region phys and virt start address + * + * Return: 0 -- Success. + * + */ +int rio_map_outb_mem(struct rio_mport *mport, u32 win, + u16 destid, u32 offset, u32 mflags, + struct rio_map_addr *res) +{ + int rc = 0; + + if (!mport->mops) + return -1; + rc = mport->mops->map_outb(mport, win, destid, offset, mflags, res); + return rc; +} +EXPORT_SYMBOL_GPL(rio_map_outb_mem); + +/** + * rio_req_outb_region -- Request outbound region in the + * RapidIO bus address space. + * @mport: RapidIO master port + * @size: The mapping region size. + * @name: Resource name + * @flags: Flags for mapping. 0 for using default flags. + * @id: Allocated outbound ATMU window id + * + * Return: 0 -- Success. + * + * This function will reserve a memory region that may + * be used to create mappings from local iomem to rio space. + */ +int rio_req_outb_region(struct rio_mport *mport, + resource_size_t size, + const char *name, + u32 mflags, u32 *win) +{ + int rc = 0; + + if (!mport->mops) + return -1; + rc = mport->mops->req_outb(mport, size, name, mflags, win); + return rc; +} +EXPORT_SYMBOL_GPL(rio_req_outb_region); + +/** + * rio_release_outb_region -- Unreserve outbound memory region. + * @mport: RapidIO master port + * @win: Allocated outbound ATMU window id + */ +void rio_release_outb_region(struct rio_mport *mport, u32 win) +{ + if (!mport->mops) + return; + mport->mops->release_outb(mport, win); +} +EXPORT_SYMBOL_GPL(rio_release_outb_region); + /** * rio_local_get_device_id - Get the base/extended device id for a port * @port: RIO master port from which to get the deviceid @@ -46,10 +272,12 @@ u16 rio_local_get_device_id(struct rio_mport *port) { u32 result; - rio_local_read_config_32(port, RIO_DID_CSR, &result); + if (rio_local_read_config_32(port, RIO_DID_CSR, &result)) + pr_debug("RIO: Failed to read RIO_DID_CSR\n"); - return (RIO_GET_DID(port->sys_size, result)); + return RIO_GET_DID(port->sys_size, result); } +EXPORT_SYMBOL_GPL(rio_local_get_device_id); /** * rio_request_inb_mbox - request inbound mailbox service @@ -66,7 +294,7 @@ int rio_request_inb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries, - void (*minb) (struct rio_mport * mport, void *dev_id, int mbox, + void (*minb) (struct rio_mport *mport, void *dev_id, int mbox, int slot)) { int rc = -ENOSYS; @@ -81,9 +309,8 @@ int rio_request_inb_mbox(struct rio_mport *mport, rio_init_mbox_res(res, mbox, mbox); /* Make sure this mailbox isn't in use */ - if ((rc = - request_resource(&mport->riores[RIO_INB_MBOX_RESOURCE], - res)) < 0) { + rc = request_resource(&mport->riores[RIO_INB_MBOX_RESOURCE], res); + if (rc < 0) { kfree(res); goto out; } @@ -97,9 +324,10 @@ int rio_request_inb_mbox(struct rio_mport *mport, } else rc = -ENOMEM; - out: +out: return rc; } +EXPORT_SYMBOL_GPL(rio_request_inb_mbox); /** * rio_release_inb_mbox - release inbound mailbox message service @@ -119,6 +347,7 @@ int rio_release_inb_mbox(struct rio_mport *mport, int mbox) } else return -ENOSYS; } +EXPORT_SYMBOL_GPL(rio_release_inb_mbox); /** * rio_request_outb_mbox - request outbound mailbox service @@ -135,7 +364,9 @@ int rio_request_outb_mbox(struct rio_mport *mport, void *dev_id, int mbox, int entries, - void (*moutb) (struct rio_mport * mport, void *dev_id, int mbox, int slot)) + int prio, + void (*moutb) (struct rio_mport *mport, void *dev_id, + int mbox, int slot, void *cookie)) { int rc = -ENOSYS; struct resource *res; @@ -149,9 +380,8 @@ int rio_request_outb_mbox(struct rio_mport *mport, rio_init_mbox_res(res, mbox, mbox); /* Make sure this outbound mailbox isn't in use */ - if ((rc = - request_resource(&mport->riores[RIO_OUTB_MBOX_RESOURCE], - res)) < 0) { + rc = request_resource(&mport->riores[RIO_OUTB_MBOX_RESOURCE], res); + if (rc < 0) { kfree(res); goto out; } @@ -161,13 +391,14 @@ int rio_request_outb_mbox(struct rio_mport *mport, /* Hook the inbound message callback */ mport->outb_msg[mbox].mcback = moutb; - rc = mport->ops->open_outb_mbox(mport, dev_id, mbox, entries); + rc = mport->ops->open_outb_mbox(mport, dev_id, mbox, entries, prio); } else rc = -ENOMEM; - out: +out: return rc; } +EXPORT_SYMBOL_GPL(rio_request_outb_mbox); /** * rio_release_outb_mbox - release outbound mailbox message service @@ -187,6 +418,7 @@ int rio_release_outb_mbox(struct rio_mport *mport, int mbox) } else return -ENOSYS; } +EXPORT_SYMBOL_GPL(rio_release_outb_mbox); /** * rio_setup_inb_dbell - bind inbound doorbell callback @@ -201,13 +433,13 @@ int rio_release_outb_mbox(struct rio_mport *mport, int mbox) */ static int rio_setup_inb_dbell(struct rio_mport *mport, void *dev_id, struct resource *res, - void (*dinb) (struct rio_mport * mport, void *dev_id, u16 src, u16 dst, + void (*dinb) (struct rio_mport *mport, void *dev_id, u16 src, u16 dst, u16 info)) { int rc = 0; struct rio_dbell *dbell; - - if (!(dbell = kmalloc(sizeof(struct rio_dbell), GFP_KERNEL))) { + dbell = kmalloc(sizeof(struct rio_dbell), GFP_KERNEL); + if (!dbell) { rc = -ENOMEM; goto out; } @@ -218,7 +450,7 @@ rio_setup_inb_dbell(struct rio_mport *mport, void *dev_id, struct resource *res, list_add_tail(&dbell->node, &mport->dbells); - out: +out: return rc; } @@ -238,7 +470,7 @@ int rio_request_inb_dbell(struct rio_mport *mport, void *dev_id, u16 start, u16 end, - void (*dinb) (struct rio_mport * mport, void *dev_id, u16 src, + void (*dinb) (struct rio_mport *mport, void *dev_id, u16 src, u16 dst, u16 info)) { int rc = 0; @@ -249,9 +481,10 @@ int rio_request_inb_dbell(struct rio_mport *mport, rio_init_dbell_res(res, start, end); /* Make sure these doorbells aren't in use */ - if ((rc = - request_resource(&mport->riores[RIO_DOORBELL_RESOURCE], - res)) < 0) { + rc = + request_resource(&mport->riores[RIO_DOORBELL_RESOURCE], + res); + if (rc < 0) { kfree(res); goto out; } @@ -261,9 +494,10 @@ int rio_request_inb_dbell(struct rio_mport *mport, } else rc = -ENOMEM; - out: +out: return rc; } +EXPORT_SYMBOL_GPL(rio_request_inb_dbell); /** * rio_release_inb_dbell - release inbound doorbell message service @@ -302,9 +536,10 @@ int rio_release_inb_dbell(struct rio_mport *mport, u16 start, u16 end) /* Free the doorbell event */ kfree(dbell); - out: +out: return rc; } +EXPORT_SYMBOL_GPL(rio_release_inb_dbell); /** * rio_request_outb_dbell - request outbound doorbell message range @@ -333,6 +568,7 @@ struct resource *rio_request_outb_dbell(struct rio_dev *rdev, u16 start, return res; } +EXPORT_SYMBOL_GPL(rio_request_outb_dbell); /** * rio_release_outb_dbell - release outbound doorbell message range @@ -350,6 +586,7 @@ int rio_release_outb_dbell(struct rio_dev *rdev, struct resource *res) return rc; } +EXPORT_SYMBOL_GPL(rio_release_outb_dbell); /** * rio_request_inb_pwrite - request inbound port-write message service @@ -360,7 +597,7 @@ int rio_release_outb_dbell(struct rio_dev *rdev, struct resource *res) * Returns 0 if the request has been satisfied. */ int rio_request_inb_pwrite(struct rio_dev *rdev, - int (*pwcback)(struct rio_dev *rdev, union rio_pw_msg *msg, int step)) + int (*pwcback)(struct rio_dev *rdev, union rio_pw_msg *msg, int step)) { int rc = 0; @@ -405,22 +642,26 @@ EXPORT_SYMBOL_GPL(rio_release_inb_pwrite); * @destid: Destination ID of the device * @hopcount: Number of switch hops to the device */ -u32 -rio_mport_get_physefb(struct rio_mport *port, int local, - u16 destid, u8 hopcount) -{ +int rio_mport_get_physefb(struct rio_mport *port, int local, u16 destid, + u8 hopcount, u32 *physefb) { u32 ext_ftr_ptr; u32 ftr_header; + int rc = 0; + rc = rio_mport_get_efb(port, local, destid, hopcount, 0, &ext_ftr_ptr); + if (rc) + goto done; - ext_ftr_ptr = rio_mport_get_efb(port, local, destid, hopcount, 0); - - while (ext_ftr_ptr) { - if (local) - rio_local_read_config_32(port, ext_ftr_ptr, - &ftr_header); - else - rio_mport_read_config_32(port, destid, hopcount, - ext_ftr_ptr, &ftr_header); + while (ext_ftr_ptr) { + if (local) { + rc = rio_local_read_config_32(port, ext_ftr_ptr, &ftr_header); + if (rc) + goto done; + } else { + rc = rio_mport_read_config_32(port, destid, hopcount, + ext_ftr_ptr, &ftr_header); + if (rc) + goto done; + } ftr_header = RIO_GET_BLOCK_ID(ftr_header); switch (ftr_header) { @@ -433,18 +674,22 @@ rio_mport_get_physefb(struct rio_mport *port, int local, case RIO_EFB_SER_EP_FREE_ID: case RIO_EFB_SER_EP_FREC_ID: - return ext_ftr_ptr; + goto done; default: break; } - - ext_ftr_ptr = rio_mport_get_efb(port, local, destid, - hopcount, ext_ftr_ptr); + rc = rio_mport_get_efb(port, local, destid, hopcount, ext_ftr_ptr, + &ext_ftr_ptr); + if (rc) + goto done; } - return ext_ftr_ptr; +done: + *physefb = ext_ftr_ptr; + return rc; } +EXPORT_SYMBOL_GPL(rio_mport_get_physefb); /** * rio_get_comptag - Begin or continue searching for a RIO device by component tag @@ -458,23 +703,39 @@ rio_mport_get_physefb(struct rio_mport *port, int local, * @from is not %NULL, searches continue from next device on the global * list. */ -struct rio_dev *rio_get_comptag(u32 comp_tag, struct rio_dev *from) +struct rio_dev *rio_get_comptag(struct rio_mport *mport, u32 comp_tag) { - struct list_head *n; - struct rio_dev *rdev; - - spin_lock(&rio_global_list_lock); - n = from ? from->global_list.next : rio_devices.next; - - while (n && (n != &rio_devices)) { - rdev = rio_dev_g(n); - if (rdev->comp_tag == comp_tag) - goto exit; - n = n->next; + int items = atomic_read(&mport->net.rio_dev_num); + void **nptr = kzalloc(sizeof(void *) * items, GFP_KERNEL); + int i, num; + struct rio_dev *rdev = NULL; + + if (!nptr) + return NULL; + + rcu_read_lock(); +retry: + num = radix_tree_gang_lookup_slot(&mport->net.dev_tree, (void ***)nptr, + NULL, 0, items); + if (num > 0) { + for (i = 0; i < num; i++) { + struct rio_dev *tmp = radix_tree_deref_slot((void **)nptr[i]); + + if (unlikely(!tmp)) + continue; + + if (radix_tree_deref_retry(tmp)) + goto retry; + + if (tmp->comp_tag == comp_tag) { + rdev = rio_dev_get(tmp); + goto done; + } + } } - rdev = NULL; -exit: - spin_unlock(&rio_global_list_lock); +done: + rcu_read_unlock(); + kfree(nptr); return rdev; } @@ -484,66 +745,45 @@ exit: * @pnum: Switch port number to set LOCKOUT bit * @lock: Operation : set (=1) or clear (=0) */ -int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock) +static int +rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock, int do_lock) { + int rc = 0; u32 regval; - rio_read_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum), - ®val); + if (do_lock) + rc = rio_hw_lock_wait(rdev->hport, rdev->destid, rdev->hopcount, 10); + if (rc) + goto done; + rc = rio_read_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum), ®val); + if (rc) + goto rel_lock; + if (lock) regval |= RIO_PORT_N_CTL_LOCKOUT; else regval &= ~RIO_PORT_N_CTL_LOCKOUT; - rio_write_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum), - regval); - return 0; -} + rc = rio_write_config_32(rdev, rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum), + regval); -/** - * rio_chk_dev_route - Validate route to the specified device. - * @rdev: RIO device failed to respond - * @nrdev: Last active device on the route to rdev - * @npnum: nrdev's port number on the route to rdev - * - * Follows a route to the specified RIO device to determine the last available - * device (and corresponding RIO port) on the route. - */ -static int -rio_chk_dev_route(struct rio_dev *rdev, struct rio_dev **nrdev, int *npnum) -{ - u32 result; - int p_port, rc = -EIO; - struct rio_dev *prev = NULL; + pr_debug("RIO_EM: %s_port_lockout %s port %d addr %8.8x == %8.8x\n", + (lock ? "set" : "clear"), rio_name(rdev), pnum, + rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum), regval); - /* Find switch with failed RIO link */ - while (rdev->prev && (rdev->prev->pef & RIO_PEF_SWITCH)) { - if (!rio_read_config_32(rdev->prev, RIO_DEV_ID_CAR, &result)) { - prev = rdev->prev; - break; - } - rdev = rdev->prev; +rel_lock: + if (do_lock) { + if (rc) + rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + else + rc = rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); } - - if (prev == NULL) - goto err_out; - - p_port = prev->rswitch->route_table[rdev->destid]; - - if (p_port != RIO_INVALID_ROUTE) { - pr_debug("RIO: link failed on [%s]-P%d\n", - rio_name(prev), p_port); - *nrdev = prev; - *npnum = p_port; - rc = 0; - } else - pr_debug("RIO: failed to trace route to %s\n", rio_name(rdev)); -err_out: +done: return rc; } + /** * rio_mport_chk_dev_access - Validate access to the specified device. * @mport: Master port to send transactions @@ -553,18 +793,10 @@ err_out: int rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid, u8 hopcount) { - int i = 0; u32 tmp; - while (rio_mport_read_config_32(mport, destid, hopcount, - RIO_DEV_ID_CAR, &tmp)) { - i++; - if (i == RIO_MAX_CHK_RETRY) - return -EIO; - mdelay(1); - } - - return 0; + return rio_mport_read_config_32(mport, destid, hopcount, + RIO_DEV_ID_CAR, &tmp); } /** @@ -573,7 +805,7 @@ rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid, u8 hopcount) */ static int rio_chk_dev_access(struct rio_dev *rdev) { - return rio_mport_chk_dev_access(rdev->net->hport, + return rio_mport_chk_dev_access(rdev->hport, rdev->destid, rdev->hopcount); } @@ -585,42 +817,48 @@ static int rio_chk_dev_access(struct rio_dev *rdev) * @lnkresp: Response from a link partner */ static int -rio_get_input_status(struct rio_dev *rdev, int pnum, u32 *lnkresp) +rio_get_input_status(struct rio_dev *rdev, int pnum, u32 request, u32 *lnkresp) { u32 regval; int checkcount; + int rc = 0; if (lnkresp) { /* Read from link maintenance response register * to clear valid bit */ - rio_read_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum), - ®val); + rc = rio_read_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum), ®val); + if (rc) + goto done; udelay(50); } /* Issue Input-status command */ - rio_write_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_MNT_REQ_CSR(pnum), - RIO_MNT_REQ_CMD_IS); + rc = rio_write_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_MNT_REQ_CSR(pnum), request); + if (rc) + goto done; /* Exit if the response is not expected */ if (lnkresp == NULL) - return 0; + goto done; checkcount = 3; while (checkcount--) { udelay(50); - rio_read_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum), - ®val); + rc = rio_read_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(pnum), ®val); + if (rc) + goto done; if (regval & RIO_PORT_N_MNT_RSP_RVAL) { *lnkresp = regval; - return 0; + goto done; } } - return -EIO; + rc = -EIO; +done: + return rc; } /** @@ -629,87 +867,443 @@ rio_get_input_status(struct rio_dev *rdev, int pnum, u32 *lnkresp) * @pnum: Switch port number to clear errors * @err_status: port error status (if 0 reads register from device) */ -static int rio_clr_err_stopped(struct rio_dev *rdev, u32 pnum, u32 err_status) +int +rio_clr_err_stopped(struct rio_dev *rdev, u32 pnum, u32 err_status, int *success) { - struct rio_dev *nextdev = rdev->rswitch->nextdev[pnum]; + struct rio_dev *nextdev; u32 regval; u32 far_ackid, far_linkstat, near_ackid; + int rc = 0; - if (err_status == 0) - rio_read_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum), - &err_status); + nextdev = lookup_rdev_next(rdev, pnum); + if (IS_ERR(nextdev)) + nextdev = NULL; + rc = rio_hw_lock_wait(rdev->hport, rdev->destid, + rdev->hopcount, 10); + if (rdev->use_hw_lock && (rc)) + goto done; + + if (err_status == 0) { + rc = rio_get_err_and_status(rdev, pnum, &err_status); + if (rc) + goto err; + } if (err_status & RIO_PORT_N_ERR_STS_PW_OUT_ES) { pr_debug("RIO_EM: servicing Output Error-Stopped state\n"); /* * Send a Link-Request/Input-Status control symbol */ - if (rio_get_input_status(rdev, pnum, ®val)) { + rc = rio_get_input_status(rdev, pnum, RIO_MNT_REQ_CMD_IS, ®val); + if (rc == -EIO) { pr_debug("RIO_EM: Input-status response timeout\n"); goto rd_err; + } else if (rc) { + goto err; } - pr_debug("RIO_EM: SP%d Input-status response=0x%08x\n", - pnum, regval); + pr_debug("RIO_EM: SP%d Input-status response=0x%08x\n", pnum, regval); far_ackid = (regval & RIO_PORT_N_MNT_RSP_ASTAT) >> 5; far_linkstat = regval & RIO_PORT_N_MNT_RSP_LSTAT; - rio_read_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum), - ®val); + rc = rio_read_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum), ®val); + if (rc) + goto err; pr_debug("RIO_EM: SP%d_ACK_STS_CSR=0x%08x\n", pnum, regval); near_ackid = (regval & RIO_PORT_N_ACK_INBOUND) >> 24; - pr_debug("RIO_EM: SP%d far_ackID=0x%02x far_linkstat=0x%02x" \ - " near_ackID=0x%02x\n", - pnum, far_ackid, far_linkstat, near_ackid); + pr_debug("RIO_EM: SP%d far_ackID=0x%02x far_linkstat=0x%02x near_ackID=0x%02x\n", + pnum, far_ackid, far_linkstat, near_ackid); /* * If required, synchronize ackIDs of near and * far sides. */ - if ((far_ackid != ((regval & RIO_PORT_N_ACK_OUTSTAND) >> 8)) || - (far_ackid != (regval & RIO_PORT_N_ACK_OUTBOUND))) { + if ((far_ackid != ((regval & RIO_PORT_N_ACK_OUTSTAND) >> 8)) + || (far_ackid != (regval & RIO_PORT_N_ACK_OUTBOUND))) { /* Align near outstanding/outbound ackIDs with * far inbound. */ - rio_write_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum), - (near_ackid << 24) | - (far_ackid << 8) | far_ackid); + rc = rio_write_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum), + (near_ackid << 24) | (far_ackid << 8) | far_ackid); + if (rc) + goto err; /* Align far outstanding/outbound ackIDs with * near inbound. */ far_ackid++; - if (nextdev) - rio_write_config_32(nextdev, - nextdev->phys_efptr + - RIO_PORT_N_ACK_STS_CSR(RIO_GET_PORT_NUM(nextdev->swpinfo)), - (far_ackid << 24) | - (near_ackid << 8) | near_ackid); - else + if (nextdev) { + rc = rio_write_config_32(nextdev, + nextdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR( + RIO_GET_PORT_NUM(nextdev->swpinfo)), + (far_ackid << 24) | (near_ackid << 8) | near_ackid); + if ((rc)) + goto err; + } else { pr_debug("RIO_EM: Invalid nextdev pointer (NULL)\n"); + } } rd_err: - rio_read_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum), - &err_status); + rc = rio_get_err_and_status(rdev, pnum, &err_status); + if (rc) + goto err; pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status); } if ((err_status & RIO_PORT_N_ERR_STS_PW_INP_ES) && nextdev) { pr_debug("RIO_EM: servicing Input Error-Stopped state\n"); - rio_get_input_status(nextdev, - RIO_GET_PORT_NUM(nextdev->swpinfo), NULL); + rio_get_input_status(nextdev, RIO_GET_PORT_NUM(nextdev->swpinfo), + RIO_MNT_REQ_CMD_IS, NULL); udelay(50); - - rio_read_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(pnum), - &err_status); + rc = rio_get_err_and_status(rdev, pnum, &err_status); + if (rc) + goto err; pr_debug("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, err_status); } - return (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES | - RIO_PORT_N_ERR_STS_PW_INP_ES)) ? 1 : 0; + *success = (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES | + RIO_PORT_N_ERR_STS_PW_INP_ES)) ? 0 : 1; + goto rel_lock; +err: + pr_debug("RIO_EM: Read/Write Error\n"); + *success = 0; +rel_lock: + if (rdev->use_hw_lock) { + if (rc) + rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + else + rc = rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + } +done: + if (nextdev) + rio_dev_put(nextdev); + return rc; +} +/** + * @brief Brute force sync ackid method + * + * Send link maint request to LP then configure + * near end so that inbound, outstanding and outbound + * ackid corresponds to far end next expected ackid. + * + * This will work as long as far end next expected + * inbound == outbound (which is usually the case) + * + * @note The proper is probably to first sync near + * end to far end next expected and then do maint + * request to LP to sync far en outbound to near end + * inbound. To do that, however, you'd need to know + * the phys extended feature ptr offset and the port + * number of the LP, and that information is hard to + * come by as long as near and far end ackid are not + * in sync + */ +static int rio_ackid_sync(struct rio_dev *rdev, u32 pnum) +{ + struct rio_dev *nextdev; + u32 regval; + u32 far_ackid, far_linkstat, near_ackid; + int rc = 0; + + nextdev = lookup_rdev_next(rdev, pnum); + if (IS_ERR(nextdev)) + nextdev = NULL; + rc = rio_hw_lock_wait(rdev->hport, rdev->destid, + rdev->hopcount, 10); + if (rdev->use_hw_lock && (rc)) + goto done; + + pr_debug("RIO_EM: Send link request\n"); + /* + * Send a Link-Request/Input-Status control symbol + */ + rc = rio_get_input_status(rdev, pnum, RIO_MNT_REQ_CMD_IS, ®val); + if (rc == -EIO) { + pr_warn("RIO_EM: Input-status response timeout\n"); + goto rd_err; + } else if (rc) { + pr_warn("RIO_EM: Input-status response other err %d\n", rc); + goto err; + } + + pr_debug("RIO_EM: SP%d Input-status response=0x%08x\n", pnum, regval); + far_ackid = (regval & RIO_PORT_N_MNT_RSP_ASTAT) >> 5; + far_linkstat = regval & RIO_PORT_N_MNT_RSP_LSTAT; + rc = rio_read_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum), ®val); + if (rc) + goto err; + + pr_debug("RIO_EM: SP%d_ACK_STS_CSR=0x%08x\n", pnum, regval); + near_ackid = (regval & RIO_PORT_N_ACK_INBOUND) >> 24; + pr_debug("RIO_EM: SP%d far_ackID=0x%02x far_linkstat=0x%02x near_ackID=0x%02x\n", + pnum, far_ackid, far_linkstat, near_ackid); + + if (far_linkstat != 0x10) { + pr_warn("RIO_EM: LP link state 0x%08x\n", far_linkstat); + rc = -EIO; + goto err; + } + /* + * If required, synchronize ackIDs of near and + * far sides. + */ + if ((far_ackid != ((regval & RIO_PORT_N_ACK_OUTSTAND) >> 8)) + || (far_ackid != (regval & RIO_PORT_N_ACK_OUTBOUND))) { + /* Align near outstanding/outbound ackIDs with + * far inbound. + */ + rc = rio_write_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(pnum), + (far_ackid << 24) | (far_ackid << 8) | far_ackid); + if (rc) + goto err; + } + goto rel_lock; +rd_err: + rc = rio_get_err_and_status(rdev, pnum, ®val); + if (rc) + goto err; + pr_warn("RIO_EM: SP%d_ERR_STS_CSR=0x%08x\n", pnum, regval); + goto rel_lock; +err: + pr_warn("RIO_EM: Read/Write Error\n"); +rel_lock: + if (rdev->use_hw_lock) { + if (rc) + rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + else + rc = rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + } +done: + if (nextdev) + rio_dev_put(nextdev); + return rc; +} + +static int +rio_pw_dump_msg(union rio_pw_msg *pw_msg) +{ +#ifdef DEBUG_PW + u32 i; + for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32);) { + pr_debug("0x%02x: %08x %08x %08x %08x\n", + i * 4, pw_msg->raw[i], pw_msg->raw[i + 1], + pw_msg->raw[i + 2], pw_msg->raw[i + 3]); + i += 4; + } +#endif + return 0; +} + +static int +rio_handle_local_domain(struct rio_dev *rdev, int portnum, u32 err_status) +{ + int rc = 0; + u32 em_perrdet, em_ltlerrdet; + + if (rdev->local_domain) { + rc = rio_hw_lock_wait(rdev->hport, rdev->destid, + rdev->hopcount, 10); + if (rdev->use_hw_lock && (rc)) + goto done; + rc = rio_read_config_32(rdev, + rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet); + if (rc) { + pr_debug( + "RIO_PW: Failed to read RIO_EM_PN_ERR_DETECT from device\n"); + goto rel_lock; + } + if (em_perrdet) { + /* Clear EM Port N Error Detect CSR */ + rc = rio_write_config_32(rdev, + rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0); + if (rc) { + pr_debug( + "RIO_PW: Failed to write RIO_EM_PN_ERR_DETECT to device\n"); + goto rel_lock; + } + } + rc = rio_read_config_32(rdev, + rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet); + if (rc) { + pr_debug( + "RIO_PW: Failed to read RIO_EM_LTL_ERR_DETECT from device\n"); + goto rel_lock; + } + if (em_ltlerrdet) { + /* Clear EM L/T Layer Error Detect CSR */ + rc = rio_write_config_32(rdev, + rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0); + if (rc) { + pr_debug( + "RIO_PW: Failed to write RIO_EM_LTL_ERR_DETECT to device\n"); + goto rel_lock; + } + } + /* Clear remaining error bits and Port-Write Pending bit */ + rc = rio_write_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), err_status); + if (rc) { + pr_debug( + "RIO_PW: Failed to write RIO_PORT_N_ERR_STS_CSR to device\n"); + goto rel_lock; + } +rel_lock: + if (rdev->use_hw_lock) { + if (rc) + rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + else + rc = rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + } + } +done: + return rc; +} + +static int +rio_handle_events(struct rio_dev *rdev, int portnum, u32 err_status, int *event) +{ + int rc = 0; + int success; + + rio_tree_write_lock(); + + if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) { + pr_debug("RIO: port OK\n"); + if ((!(rdev->rswitch->port_init & (1 << portnum))) + || (!(rdev->rswitch->port_ok & (1 << portnum)))) { + rdev->rswitch->port_init |= (1 << portnum); + rdev->rswitch->port_ok |= (1 << portnum); + if (rdev->local_domain) { + rc = rio_set_port_lockout(rdev, portnum, 0, 1); + if (rc) { + pr_warn("RIO: Failed to set port lockout bit.\n"); + goto done; + } + } + } + /* Schedule Insertion Service */ + pr_debug("RIO: Device Insertion on [%s]-P%d\n", rio_name(rdev), + portnum); + *event = RIO_DEVICE_INSERTION; + + if (rdev->local_domain) { + /* Clear error-stopped states (if reported). + * Depending on the link partner state, two attempts + * may be needed for successful recovery. + */ + if (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES | + RIO_PORT_N_ERR_STS_PW_INP_ES)) { + rc = rio_clr_err_stopped(rdev, portnum, err_status, &success); + if (rc) + goto done; + if (!success) { + rc = rio_clr_err_stopped(rdev, portnum, 0, &success); + if (rc) + goto done; + } + } + } + } else { /* if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) */ + if ((!(rdev->rswitch->port_init & (1 << portnum))) + || (rdev->rswitch->port_ok & (1 << portnum))) { + rdev->rswitch->port_init |= (1 << portnum); + rdev->rswitch->port_ok &= ~(1 << portnum); + if (rdev->local_domain) { + rc = rio_hw_lock_wait(rdev->hport, rdev->destid, rdev->hopcount, + 10); + if (rdev->use_hw_lock && (rc)) + goto done; + rc = rio_set_port_lockout(rdev, portnum, 1, 0); + if (rc) { + pr_warn("RIO: Failed to set port lockout bit.\n"); + if (rdev->use_hw_lock) + rio_hw_unlock(rdev->hport, rdev->destid, + rdev->hopcount); + goto done; + } + rc = rio_write_config_32(rdev, + rdev->phys_efptr + RIO_PORT_N_ACK_STS_CSR(portnum), + RIO_PORT_N_ACK_CLEAR); + if (rc) { + pr_warn( + "RIO: Failed to write to RIO_PORT_N_ACK_STS_CSR.\n"); + if (rdev->use_hw_lock) + rio_hw_unlock(rdev->hport, rdev->destid, + rdev->hopcount); + goto done; + } + rc = rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + if (rdev->use_hw_lock && (rc)) + goto done; + } + } + /* Schedule Extraction Service */ + pr_debug("RIO: Device Extraction on [%s]-P%d\n", rio_name(rdev), + portnum); + *event = RIO_DEVICE_EXTRACTION; + } +done: + rio_tree_write_unlock(); + return rc; +} +static int __rio_setup_event(struct rio_dev *rdev, int portnum, int event) +{ + int rc = 0; + int tmp_event = 0; + u32 err_status; + + rdev = rio_get_by_ptr(rdev); + if (!rdev) + goto done; + + if (rdev->local_domain) { + if (rdev->rswitch->em_handle) { + if ((!rdev->use_hw_lock) + || (!rio_hw_lock_wait(rdev->hport, rdev->destid, + rdev->hopcount, 10))) { + rdev->rswitch->em_handle(rdev, portnum); + if (rdev->use_hw_lock) + rio_hw_unlock(rdev->hport, rdev->destid, rdev->hopcount); + } + } + } + rc = rio_get_err_and_status(rdev, portnum, &err_status); + if (rc) + goto rel_dev; + pr_debug("port %d RIO_PORT_N_ERR_STS_CSR 0x%x\n", portnum, err_status); + rc = rio_handle_events(rdev, portnum, err_status, &tmp_event); + if (rc) + goto rel_dev; + + if (tmp_event != event) { + rc = 1; + if (event == RIO_DEVICE_EXTRACTION) + pr_err("RIO: Removal of not reseted device is not allowed.\n"); + else + pr_err("RIO: Insertion of device on not ok port is not allowed.\n"); + goto rel_dev; + } + rc = rio_handle_local_domain(rdev, portnum, err_status); + if (rc) + goto rel_dev; + +rel_dev: + rio_dev_put(rdev); +done: + return rc; +} +int rio_setup_event(struct rio_dev *rdev, int portnum, int event) +{ + return __rio_setup_event(rdev, portnum, event); +} +int rio_setup_event_force(struct rio_dev *rdev, int portnum, int event) +{ + int rc = __rio_setup_event(rdev, portnum, event); + if (!rc && (event == RIO_DEVICE_INSERTION)) + rio_ackid_sync(rdev, portnum); + return rc; } /** @@ -719,33 +1313,26 @@ rd_err: * Processes an inbound port-write message. Returns 0 if the request * has been satisfied. */ -int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) +int +rio_inb_pwrite_handler(struct rio_mport *mport, union rio_pw_msg *pw_msg) { struct rio_dev *rdev; - u32 err_status, em_perrdet, em_ltlerrdet; - int rc, portnum; + int rc = 0; + int portnum; + + pr_debug("RIO-EM: --- %s ---\n", __func__); - rdev = rio_get_comptag((pw_msg->em.comptag & RIO_CTAG_UDEVID), NULL); + rdev = rio_get_comptag(mport, (pw_msg->em.comptag & RIO_CTAG_UDEVID)); if (rdev == NULL) { /* Device removed or enumeration error */ - pr_debug("RIO: %s No matching device for CTag 0x%08x\n", - __func__, pw_msg->em.comptag); - return -EIO; + pr_debug("RIO_PW: %s No matching device for CTag 0x%08x\n", __func__, + pw_msg->em.comptag); + rc = -EIO; + goto done; } + pr_debug("RIO_PW: Port-Write message from %s\n", rio_name(rdev)); - pr_debug("RIO: Port-Write message from %s\n", rio_name(rdev)); - -#ifdef DEBUG_PW - { - u32 i; - for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32);) { - pr_debug("0x%02x: %08x %08x %08x %08x\n", - i*4, pw_msg->raw[i], pw_msg->raw[i + 1], - pw_msg->raw[i + 2], pw_msg->raw[i + 3]); - i += 4; - } - } -#endif + rio_pw_dump_msg(pw_msg); /* Call an external service function (if such is registered * for this device). This may be the service for endpoints that send @@ -756,7 +1343,7 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) if (rdev->pwcback != NULL) { rc = rdev->pwcback(rdev, pw_msg, 0); if (rc == 0) - return 0; + goto rel_dev; } portnum = pw_msg->em.is_port & 0xFF; @@ -766,102 +1353,61 @@ int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg) * powered down (or link being lost). */ if (rio_chk_dev_access(rdev)) { - pr_debug("RIO: device access failed - get link partner\n"); - /* Scan route to the device and identify failed link. - * This will replace device and port reported in PW message. - * PW message should not be used after this point. - */ - if (rio_chk_dev_route(rdev, &rdev, &portnum)) { - pr_err("RIO: Route trace for %s failed\n", - rio_name(rdev)); - return -EIO; - } - pw_msg = NULL; + pr_debug("RIO_PW: device access failed\n"); + rc = -EIO; + goto rel_dev; } /* For End-point devices processing stops here */ if (!(rdev->pef & RIO_PEF_SWITCH)) - return 0; + goto rel_dev; - if (rdev->phys_efptr == 0) { - pr_err("RIO_PW: Bad switch initialization for %s\n", - rio_name(rdev)); - return 0; + if (!rdev->phys_efptr) { + pr_err("RIO_PW: Bad switch initialization for %s\n", rio_name(rdev)); + goto rel_dev; } - /* - * Process the port-write notification from switch - */ - if (rdev->rswitch->em_handle) - rdev->rswitch->em_handle(rdev, portnum); - - rio_read_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), - &err_status); - pr_debug("RIO_PW: SP%d_ERR_STS_CSR=0x%08x\n", portnum, err_status); - - if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) { - - if (!(rdev->rswitch->port_ok & (1 << portnum))) { - rdev->rswitch->port_ok |= (1 << portnum); - rio_set_port_lockout(rdev, portnum, 0); - /* Schedule Insertion Service */ - pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n", - rio_name(rdev), portnum); - } - - /* Clear error-stopped states (if reported). - * Depending on the link partner state, two attempts - * may be needed for successful recovery. + if (rdev->local_domain) { + /* Process the port-write notification from switch */ - if (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES | - RIO_PORT_N_ERR_STS_PW_INP_ES)) { - if (rio_clr_err_stopped(rdev, portnum, err_status)) - rio_clr_err_stopped(rdev, portnum, 0); - } - } else { /* if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) */ - - if (rdev->rswitch->port_ok & (1 << portnum)) { - rdev->rswitch->port_ok &= ~(1 << portnum); - rio_set_port_lockout(rdev, portnum, 1); - - rio_write_config_32(rdev, - rdev->phys_efptr + - RIO_PORT_N_ACK_STS_CSR(portnum), - RIO_PORT_N_ACK_CLEAR); - - /* Schedule Extraction Service */ - pr_debug("RIO_PW: Device Extraction on [%s]-P%d\n", - rio_name(rdev), portnum); - } + if (rdev->rswitch->em_handle) + rdev->rswitch->em_handle(rdev, portnum); } - rio_read_config_32(rdev, - rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet); - if (em_perrdet) { - pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n", - portnum, em_perrdet); - /* Clear EM Port N Error Detect CSR */ - rio_write_config_32(rdev, - rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0); - } +#if !defined(CONFIG_RAPIDIO_HOTPLUG) + /** + * FIXME! + * Temporary fix to make ulma booting on DUL work + * with the old version of the ulma driver for DUL + * which is not adopted to hotplug + * Maybe we should do this anyway if user don't define + * hotplug - to keep the same level of PW handling as + * in original RIO driver - i.e. call rio_setup_event, + * not the rest obviously. + */ + { + u32 err_status; + int event = 0; + rc = rio_get_err_and_status(rdev, portnum, &err_status); + if (rc) + goto rel_dev; + + if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) + event = RIO_DEVICE_INSERTION; + else + event = RIO_DEVICE_EXTRACTION; - rio_read_config_32(rdev, - rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet); - if (em_ltlerrdet) { - pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n", - em_ltlerrdet); - /* Clear EM L/T Layer Error Detect CSR */ - rio_write_config_32(rdev, - rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0); - } + rc = rio_setup_event(rdev, portnum, event); - /* Clear remaining error bits and Port-Write Pending bit */ - rio_write_config_32(rdev, - rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), - err_status); + if (rdev->pwcback != NULL && rc == 0) + (void) rdev->pwcback(rdev, pw_msg, event); + } +#endif - return 0; +rel_dev: + rio_dev_put(rdev); +done: + return rc; } EXPORT_SYMBOL_GPL(rio_inb_pwrite_handler); @@ -874,28 +1420,31 @@ EXPORT_SYMBOL_GPL(rio_inb_pwrite_handler); * @from: Offset of current Extended Feature block header (if 0 starts * from ExtFeaturePtr) */ -u32 -rio_mport_get_efb(struct rio_mport *port, int local, u16 destid, - u8 hopcount, u32 from) -{ +int rio_mport_get_efb(struct rio_mport *port, int local, u16 destid, + u8 hopcount, u32 from, u32 *ext_ftr_ptr) { u32 reg_val; + int rc = 0; if (from == 0) { if (local) - rio_local_read_config_32(port, RIO_ASM_INFO_CAR, - ®_val); + rc = rio_local_read_config_32(port, RIO_ASM_INFO_CAR, ®_val); else - rio_mport_read_config_32(port, destid, hopcount, - RIO_ASM_INFO_CAR, ®_val); - return reg_val & RIO_EXT_FTR_PTR_MASK; + rc = rio_mport_read_config_32(port, destid, hopcount, + RIO_ASM_INFO_CAR, ®_val); + if (!rc) + *ext_ftr_ptr = reg_val & RIO_EXT_FTR_PTR_MASK; } else { if (local) - rio_local_read_config_32(port, from, ®_val); + rc = rio_local_read_config_32(port, from, ®_val); else - rio_mport_read_config_32(port, destid, hopcount, - from, ®_val); - return RIO_GET_BLOCK_ID(reg_val); + rc = rio_mport_read_config_32(port, destid, hopcount, from, + ®_val); + if (!rc) + *ext_ftr_ptr = RIO_GET_BLOCK_ID(reg_val); } + if (rc) + *ext_ftr_ptr = 0; + return rc; } /** @@ -924,36 +1473,45 @@ rio_mport_get_efb(struct rio_mport *port, int local, u16 destid, * * %RIO_EFB_SER_EP_FREE_ID LP/Serial EP Free Devices */ -u32 -rio_mport_get_feature(struct rio_mport * port, int local, u16 destid, - u8 hopcount, int ftr) -{ - u32 asm_info, ext_ftr_ptr, ftr_header; +int rio_mport_get_feature(struct rio_mport *port, int local, u16 destid, + u8 hopcount, int ftr, u32 *feature) { + u32 asm_info, ext_ftr_ptr = 0, ftr_header; + int rc = 0; - if (local) - rio_local_read_config_32(port, RIO_ASM_INFO_CAR, &asm_info); - else - rio_mport_read_config_32(port, destid, hopcount, - RIO_ASM_INFO_CAR, &asm_info); + if (local) { + rc = rio_local_read_config_32(port, RIO_ASM_INFO_CAR, &asm_info); + if (rc) + goto done; + } else { + rc = rio_mport_read_config_32(port, destid, hopcount, + RIO_ASM_INFO_CAR, &asm_info); + if (rc) + goto done; + } ext_ftr_ptr = asm_info & RIO_EXT_FTR_PTR_MASK; while (ext_ftr_ptr) { - if (local) - rio_local_read_config_32(port, ext_ftr_ptr, - &ftr_header); - else - rio_mport_read_config_32(port, destid, hopcount, - ext_ftr_ptr, &ftr_header); + if (local) { + rc = rio_local_read_config_32(port, ext_ftr_ptr, &ftr_header); + if (rc) + goto done; + } else { + rc = rio_mport_read_config_32(port, destid, hopcount, + ext_ftr_ptr, &ftr_header); + if (rc) + goto done; + } if (RIO_GET_BLOCK_ID(ftr_header) == ftr) - return ext_ftr_ptr; - if (!(ext_ftr_ptr = RIO_GET_BLOCK_PTR(ftr_header))) + goto done; + ext_ftr_ptr = RIO_GET_BLOCK_PTR(ftr_header); + if (!ext_ftr_ptr) break; } - - return 0; +done: + *feature = ext_ftr_ptr; + return rc; } - /** * rio_get_asm - Begin or continue searching for a RIO device by vid/did/asm_vid/asm_did * @vid: RIO vid to match or %RIO_ANY_ID to match all vids @@ -974,29 +1532,57 @@ rio_mport_get_feature(struct rio_mport * port, int local, u16 destid, struct rio_dev *rio_get_asm(u16 vid, u16 did, u16 asm_vid, u16 asm_did, struct rio_dev *from) { - struct list_head *n; - struct rio_dev *rdev; - - WARN_ON(in_interrupt()); - spin_lock(&rio_global_list_lock); - n = from ? from->global_list.next : rio_devices.next; - - while (n && (n != &rio_devices)) { - rdev = rio_dev_g(n); - if ((vid == RIO_ANY_ID || rdev->vid == vid) && - (did == RIO_ANY_ID || rdev->did == did) && - (asm_vid == RIO_ANY_ID || rdev->asm_vid == asm_vid) && - (asm_did == RIO_ANY_ID || rdev->asm_did == asm_did)) - goto exit; - n = n->next; + struct rio_mport *mport = (from ? from->hport : rio_get_mport(RIO_ANY_ID, NULL)); + int items; + void **nptr; + int i, num; + struct rio_dev *rdev = NULL; + unsigned long key = (from ? from->destid : 0); + + rcu_read_lock(); + while (mport) { + items = atomic_read(&mport->net.rio_dev_num); + if (!items) { + rcu_read_unlock(); + return NULL; + } + nptr = kzalloc(sizeof(void *) * items, GFP_KERNEL); + if (!nptr) { + rcu_read_unlock(); + return NULL; + } +retry: + num = radix_tree_gang_lookup_slot(&mport->net.dev_tree, (void ***)nptr, + NULL, key, items); + if (num > 0) { + for (i = 0; i < num; i++) { + rdev = radix_tree_deref_slot((void **)nptr[i]); + if (unlikely(!rdev)) + continue; + if (radix_tree_deref_retry(rdev)) + goto retry; + if (rdev == from) + continue; + if ((vid == RIO_ANY_ID || rdev->vid == vid) && + (did == RIO_ANY_ID || rdev->did == did) && + (asm_vid == RIO_ANY_ID || rdev->asm_vid == asm_vid) && + (asm_did == RIO_ANY_ID || rdev->asm_did == asm_did)) { + kfree(nptr); + goto done; + } + } + } + kfree(nptr); + mport = rio_get_mport(RIO_ANY_ID, mport); } rdev = NULL; - exit: +done: rio_dev_put(from); rdev = rio_dev_get(rdev); - spin_unlock(&rio_global_list_lock); + rcu_read_unlock(); return rdev; } +EXPORT_SYMBOL_GPL(rio_get_asm); /** * rio_get_device - Begin or continue searching for a RIO device by vid/did @@ -1016,6 +1602,7 @@ struct rio_dev *rio_get_device(u16 vid, u16 did, struct rio_dev *from) { return rio_get_asm(vid, did, RIO_ANY_ID, RIO_ANY_ID, from); } +EXPORT_SYMBOL_GPL(rio_get_device); /** * rio_std_route_add_entry - Add switch route table entry using standard @@ -1028,19 +1615,22 @@ struct rio_dev *rio_get_device(u16 vid, u16 did, struct rio_dev *from) * @route_port: destination port for specified destID */ int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table, u16 route_destid, u8 route_port) -{ + u16 table, u16 route_destid, u8 route_port) { + int rc = 0; if (table == RIO_GLOBAL_TABLE) { - rio_mport_write_config_32(mport, destid, hopcount, - RIO_STD_RTE_CONF_DESTID_SEL_CSR, - (u32)route_destid); - rio_mport_write_config_32(mport, destid, hopcount, - RIO_STD_RTE_CONF_PORT_SEL_CSR, - (u32)route_port); + rc = rio_mport_write_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_DESTID_SEL_CSR, (u32) route_destid); + if (rc) + goto done; + rc = rio_mport_write_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_PORT_SEL_CSR, (u32) route_port); + if (rc) + goto done; } udelay(10); - return 0; +done: + return rc; } /** @@ -1054,21 +1644,27 @@ int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, * @route_destid: destID entry in the RT * @route_port: returned destination port for specified destID */ -int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table, u16 route_destid, u8 *route_port) +int +rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, + u16 table, u16 route_destid, u8 *route_port) { - u32 result; + u32 result = 0; + int rc = 0; if (table == RIO_GLOBAL_TABLE) { - rio_mport_write_config_32(mport, destid, hopcount, + rc = rio_mport_write_config_32(mport, destid, hopcount, RIO_STD_RTE_CONF_DESTID_SEL_CSR, route_destid); - rio_mport_read_config_32(mport, destid, hopcount, + if (rc) + goto done; + rc = rio_mport_read_config_32(mport, destid, hopcount, RIO_STD_RTE_CONF_PORT_SEL_CSR, &result); + if (rc) + goto done; - *route_port = (u8)result; + *route_port = (u8) result; } - - return 0; +done: + return rc; } /** @@ -1079,46 +1675,53 @@ int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, * @hopcount: Number of switch hops to the device * @table: routing table ID (global or port-specific) */ -int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table) +int +rio_std_route_clr_table(struct rio_mport *mport, u16 destid, + u8 hopcount, u16 table) { u32 max_destid = 0xff; u32 i, pef, id_inc = 1, ext_cfg = 0; u32 port_sel = RIO_INVALID_ROUTE; + int rc = 0; if (table == RIO_GLOBAL_TABLE) { - rio_mport_read_config_32(mport, destid, hopcount, - RIO_PEF_CAR, &pef); + rc = rio_mport_read_config_32(mport, destid, hopcount, + RIO_PEF_CAR, &pef); + if (rc) + goto done; if (mport->sys_size) { - rio_mport_read_config_32(mport, destid, hopcount, - RIO_SWITCH_RT_LIMIT, - &max_destid); + rc = rio_mport_read_config_32(mport, destid, hopcount, + RIO_SWITCH_RT_LIMIT, &max_destid); + if (rc) + goto done; max_destid &= RIO_RT_MAX_DESTID; } if (pef & RIO_PEF_EXT_RT) { ext_cfg = 0x80000000; id_inc = 4; - port_sel = (RIO_INVALID_ROUTE << 24) | - (RIO_INVALID_ROUTE << 16) | - (RIO_INVALID_ROUTE << 8) | - RIO_INVALID_ROUTE; + port_sel = (RIO_INVALID_ROUTE << 24) | (RIO_INVALID_ROUTE << 16) + | (RIO_INVALID_ROUTE << 8) | + RIO_INVALID_ROUTE; } for (i = 0; i <= max_destid;) { - rio_mport_write_config_32(mport, destid, hopcount, - RIO_STD_RTE_CONF_DESTID_SEL_CSR, - ext_cfg | i); - rio_mport_write_config_32(mport, destid, hopcount, - RIO_STD_RTE_CONF_PORT_SEL_CSR, - port_sel); + rc = rio_mport_write_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_DESTID_SEL_CSR, ext_cfg | i); + if (rc) + goto done; + rc = rio_mport_write_config_32(mport, destid, hopcount, + RIO_STD_RTE_CONF_PORT_SEL_CSR, port_sel); + if (rc) + goto done; i += id_inc; } } +done: udelay(10); - return 0; + return rc; } static void rio_fixup_device(struct rio_dev *dev) @@ -1129,21 +1732,20 @@ static int __devinit rio_init(void) { struct rio_dev *dev = NULL; - while ((dev = rio_get_device(RIO_ANY_ID, RIO_ANY_ID, dev)) != NULL) { + while ((dev = rio_get_device(RIO_ANY_ID, RIO_ANY_ID, dev)) != NULL) rio_fixup_device(dev); - } return 0; } - +/** + * @note No lock; Assuming this is used at boot time only, + * before start of user space + */ int __devinit rio_init_mports(void) { struct rio_mport *port; list_for_each_entry(port, &rio_mports, node) { - if (port->host_deviceid >= 0) - rio_enum_mport(port); - else - rio_disc_mport(port); + rio_job_init(port, NULL, -1, 0, 1, RIO_DEVICE_INSERTION); } rio_init(); @@ -1171,27 +1773,75 @@ static int rio_hdid_setup(char *str) __setup("riohdid=", rio_hdid_setup); +#if defined(CONFIG_RAPIDIO_STATIC_DESTID) || defined(CONFIG_RAPIDIO_HOTPLUG) + +static void rio_add_mport_device(struct rio_mport *mport) +{ + dev_set_name(&mport->dev, "mport:%04x", mport->id); + mport->dev.bus = NULL; + mport->dev.parent = &rio_bus; + + if (device_register(&mport->dev)) + pr_warn("RIO: mport device register failure\n"); + else { + rio_destid_sysfs_init(mport); + rio_sysfs_init(mport); + } +} +#else +static inline void rio_add_mport_device(struct rio_mport *port) {} +#endif + +struct rio_net *rio_get_mport_net(struct rio_mport *port) +{ + return &port->net; +} + +struct rio_mport *rio_get_mport(int hostid, struct rio_mport *from) +{ + struct rio_mport *port; + struct list_head *n; + + if (list_empty(&rio_mports)) + return NULL; + + n = from ? from->node.next : rio_mports.next; + while (n && (n != &rio_mports)) { + port = list_entry(n, struct rio_mport, node); + if (hostid == RIO_ANY_ID || hostid == port->host_deviceid) + return port; + n = n->next; + } + return NULL; +} +EXPORT_SYMBOL_GPL(rio_get_mport); + + int rio_register_mport(struct rio_mport *port) { if (next_portid >= RIO_MAX_MPORTS) { pr_err("RIO: reached specified max number of mports\n"); return 1; } - port->id = next_portid++; port->host_deviceid = rio_get_hdid(port->id); +#ifndef CONFIG_RAPIDIO_STATIC_DESTID + if (port->host_deviceid == 0xb) + port->host_deviceid = RIO_ANY_ID; +#endif + +#ifdef NEW_STYLE + INIT_RADIX_TREE(&port->net.dev_tree, GFP_KERNEL); + INIT_RADIX_TREE(&port->net.dst_tree, GFP_KERNEL); + atomic_set(&port->net.rio_dev_num, 0); + atomic_set(&port->net.rio_dst_num, 0); + atomic_set(&port->net.rio_max_dest, 0); + spin_lock_init(&port->net.tree_lock); +#else + INIT_LIST_HEAD(&port->net.devices); +#endif + rio_add_mport_device(port); list_add_tail(&port->node, &rio_mports); return 0; } - -EXPORT_SYMBOL_GPL(rio_local_get_device_id); -EXPORT_SYMBOL_GPL(rio_get_device); -EXPORT_SYMBOL_GPL(rio_get_asm); -EXPORT_SYMBOL_GPL(rio_request_inb_dbell); -EXPORT_SYMBOL_GPL(rio_release_inb_dbell); -EXPORT_SYMBOL_GPL(rio_request_outb_dbell); -EXPORT_SYMBOL_GPL(rio_release_outb_dbell); -EXPORT_SYMBOL_GPL(rio_request_inb_mbox); -EXPORT_SYMBOL_GPL(rio_release_inb_mbox); -EXPORT_SYMBOL_GPL(rio_request_outb_mbox); -EXPORT_SYMBOL_GPL(rio_release_outb_mbox); +EXPORT_SYMBOL_GPL(rio_register_mport); diff --git a/drivers/rapidio/rio.h b/drivers/rapidio/rio.h index b1af414..6dceaa6 100644 --- a/drivers/rapidio/rio.h +++ b/drivers/rapidio/rio.h @@ -11,24 +11,46 @@ */ #include <linux/device.h> +#include <linux/export.h> +#include <linux/rwsem.h> #include <linux/list.h> #include <linux/rio.h> +#include "rio-hotplug.h" +#include "rio-multicast.h" +#include "rio-destid.h" +#include "rio-job.h" +#include "rio-locks.h" +#include "rio-net.h" +#include "rio-route.h" + #define RIO_MAX_CHK_RETRY 3 +#define RIO_PORT_UNUSED 0 +#define RIO_REDUNDANT_PATH 1 +#define RIO_END_POINT 2 +#define RIO_SWITCH 3 + /* Functions internal to the RIO core code */ +extern struct rio_dev **rio_get_tagged_devices(struct rio_mport *mport, + int tag, int *n); -extern u32 rio_mport_get_feature(struct rio_mport *mport, int local, u16 destid, - u8 hopcount, int ftr); -extern u32 rio_mport_get_physefb(struct rio_mport *port, int local, - u16 destid, u8 hopcount); -extern u32 rio_mport_get_efb(struct rio_mport *port, int local, u16 destid, - u8 hopcount, u32 from); +extern int rio_type_of_next(struct rio_dev *sw_curr, struct rio_dev *sw_next); +extern int rio_is_switch(struct rio_dev *rdev); +extern int rio_get_err_and_status(struct rio_dev *rdev, + int portnum, u32 *err_status); +extern int rio_mport_get_feature(struct rio_mport *port, + int local, u16 destid, + u8 hopcount, int ftr, u32 *feature); +extern int rio_mport_get_physefb(struct rio_mport *port, int local, + u16 destid, u8 hopcount, u32 *physefb); +extern int rio_mport_get_efb(struct rio_mport *port, int local, u16 destid, + u8 hopcount, u32 from, u32 *ext_ftr_ptr); extern int rio_mport_chk_dev_access(struct rio_mport *mport, u16 destid, u8 hopcount); extern int rio_create_sysfs_dev_files(struct rio_dev *rdev); -extern int rio_enum_mport(struct rio_mport *mport); -extern int rio_disc_mport(struct rio_mport *mport); +extern void rio_remove_sysfs_dev_files(struct rio_dev *rdev); +extern int rio_sysfs_init(struct rio_mport *mport); extern int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, u16 table, u16 route_destid, u8 route_port); @@ -37,16 +59,27 @@ extern int rio_std_route_get_entry(struct rio_mport *mport, u16 destid, u8 *route_port); extern int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, u16 table); -extern int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock); -extern struct rio_dev *rio_get_comptag(u32 comp_tag, struct rio_dev *from); +extern struct rio_dev *rio_get_comptag(struct rio_mport *mport, u32 comp_tag); +extern int rio_clr_err_stopped(struct rio_dev *rdev, u32 pnum, + u32 err_status, int *success); +extern struct rio_net *rio_get_mport_net(struct rio_mport *port); +/* extern int rio_setup_event(struct rio_dev *rdev, int portnum, int event);*/ /* Structures internal to the RIO core code */ +extern struct list_head rio_switches; +extern struct list_head rio_mports; extern struct device_attribute rio_dev_attrs[]; extern spinlock_t rio_global_list_lock; extern struct rio_switch_ops __start_rio_switch_ops[]; extern struct rio_switch_ops __end_rio_switch_ops[]; +extern struct rio_dev_fixup __start_rio_dev_fixup_early[]; +extern struct rio_dev_fixup __end_rio_dev_fixup_early[]; +extern struct rio_dev_fixup __start_rio_dev_fixup_enable[]; +extern struct rio_dev_fixup __end_rio_dev_fixup_enable[]; + + /* Helpers internal to the RIO core code */ #define DECLARE_RIO_SWITCH_SECTION(section, name, vid, did, init_hook) \ static const struct rio_switch_ops __rio_switch_##name __used \ @@ -68,5 +101,7 @@ extern struct rio_switch_ops __end_rio_switch_ops[]; DECLARE_RIO_SWITCH_SECTION(.rio_switch_ops, vid##did, \ vid, did, init_hook) -#define RIO_GET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x00ff0000) >> 16)) -#define RIO_SET_DID(size, x) (size ? (x & 0xffff) : ((x & 0x000000ff) << 16)) +#define RIO_GET_DID(size, x) (size ? (x & 0xffff) : \ + ((x & 0x00ff0000) >> 16)) +#define RIO_SET_DID(size, x) (size ? (x & 0xffff) : \ + ((x & 0x000000ff) << 16)) -- 1.7.9.5 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto