summaryrefslogtreecommitdiff
path: root/drivers/rapidio/rio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rapidio/rio.c')
-rw-r--r--drivers/rapidio/rio.c441
1 files changed, 391 insertions, 50 deletions
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index d7b87c64b7cd..0dcaa660cba1 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -30,6 +30,20 @@
#include "rio.h"
+/*
+ * struct rio_pwrite - RIO portwrite event
+ * @node: Node in list of doorbell events
+ * @pwcback: Doorbell event callback
+ * @context: Handler specific context to pass on event
+ */
+struct rio_pwrite {
+ struct list_head node;
+
+ int (*pwcback)(struct rio_mport *mport, void *context,
+ union rio_pw_msg *msg, int step);
+ void *context;
+};
+
MODULE_DESCRIPTION("RapidIO Subsystem Core");
MODULE_AUTHOR("Matt Porter <mporter@kernel.crashing.org>");
MODULE_AUTHOR("Alexandre Bounine <alexandre.bounine@idt.com>");
@@ -42,6 +56,7 @@ MODULE_PARM_DESC(hdid,
"Destination ID assignment to local RapidIO controllers");
static LIST_HEAD(rio_devices);
+static LIST_HEAD(rio_nets);
static DEFINE_SPINLOCK(rio_global_list_lock);
static LIST_HEAD(rio_mports);
@@ -68,6 +83,89 @@ u16 rio_local_get_device_id(struct rio_mport *port)
}
/**
+ * rio_query_mport - Query mport device attributes
+ * @port: mport device to query
+ * @mport_attr: mport attributes data structure
+ *
+ * Returns attributes of specified mport through the
+ * pointer to attributes data structure.
+ */
+int rio_query_mport(struct rio_mport *port,
+ struct rio_mport_attr *mport_attr)
+{
+ if (!port->ops->query_mport)
+ return -ENODATA;
+ return port->ops->query_mport(port, mport_attr);
+}
+EXPORT_SYMBOL(rio_query_mport);
+
+/**
+ * rio_alloc_net- Allocate and initialize a new RIO network data structure
+ * @mport: Master port associated with the RIO network
+ *
+ * Allocates a RIO network structure, initializes per-network
+ * list heads, and adds the associated master port to the
+ * network list of associated master ports. Returns a
+ * RIO network pointer on success or %NULL on failure.
+ */
+struct rio_net *rio_alloc_net(struct rio_mport *mport)
+{
+ struct rio_net *net;
+
+ net = kzalloc(sizeof(struct rio_net), GFP_KERNEL);
+ if (net) {
+ INIT_LIST_HEAD(&net->node);
+ INIT_LIST_HEAD(&net->devices);
+ INIT_LIST_HEAD(&net->switches);
+ INIT_LIST_HEAD(&net->mports);
+ mport->net = net;
+ }
+ return net;
+}
+EXPORT_SYMBOL_GPL(rio_alloc_net);
+
+int rio_add_net(struct rio_net *net)
+{
+ int err;
+
+ err = device_register(&net->dev);
+ if (err)
+ return err;
+ spin_lock(&rio_global_list_lock);
+ list_add_tail(&net->node, &rio_nets);
+ spin_unlock(&rio_global_list_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rio_add_net);
+
+void rio_free_net(struct rio_net *net)
+{
+ spin_lock(&rio_global_list_lock);
+ if (!list_empty(&net->node))
+ list_del(&net->node);
+ spin_unlock(&rio_global_list_lock);
+ if (net->release)
+ net->release(net);
+ device_unregister(&net->dev);
+}
+EXPORT_SYMBOL_GPL(rio_free_net);
+
+/**
+ * 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.
+ */
+void rio_local_set_device_id(struct rio_mport *port, u16 did)
+{
+ rio_local_write_config_32(port, RIO_DID_CSR,
+ RIO_SET_DID(port->sys_size, did));
+}
+EXPORT_SYMBOL_GPL(rio_local_set_device_id);
+
+/**
* rio_add_device- Adds a RIO device to the device model
* @rdev: RIO device
*
@@ -79,12 +177,19 @@ int rio_add_device(struct rio_dev *rdev)
{
int err;
- err = device_add(&rdev->dev);
+ atomic_set(&rdev->state, RIO_DEVICE_RUNNING);
+ err = device_register(&rdev->dev);
if (err)
return err;
spin_lock(&rio_global_list_lock);
list_add_tail(&rdev->global_list, &rio_devices);
+ if (rdev->net) {
+ list_add_tail(&rdev->net_list, &rdev->net->devices);
+ if (rdev->pef & RIO_PEF_SWITCH)
+ list_add_tail(&rdev->rswitch->node,
+ &rdev->net->switches);
+ }
spin_unlock(&rio_global_list_lock);
rio_create_sysfs_dev_files(rdev);
@@ -93,6 +198,33 @@ int rio_add_device(struct rio_dev *rdev)
}
EXPORT_SYMBOL_GPL(rio_add_device);
+/*
+ * rio_del_device - removes a RIO device from the device model
+ * @rdev: RIO device
+ * @state: device state to set during removal process
+ *
+ * Removes the RIO device to the kernel device list and subsystem's device list.
+ * Clears sysfs entries for the removed device.
+ */
+void rio_del_device(struct rio_dev *rdev, enum rio_device_state state)
+{
+ pr_debug("RIO: %s: removing %s\n", __func__, rio_name(rdev));
+ atomic_set(&rdev->state, state);
+ spin_lock(&rio_global_list_lock);
+ list_del(&rdev->global_list);
+ if (rdev->net) {
+ list_del(&rdev->net_list);
+ if (rdev->pef & RIO_PEF_SWITCH) {
+ list_del(&rdev->rswitch->node);
+ kfree(rdev->rswitch->route_table);
+ }
+ }
+ spin_unlock(&rio_global_list_lock);
+ rio_remove_sysfs_dev_files(rdev);
+ device_unregister(&rdev->dev);
+}
+EXPORT_SYMBOL_GPL(rio_del_device);
+
/**
* rio_request_inb_mbox - request inbound mailbox service
* @mport: RIO master port from which to allocate the mailbox resource
@@ -117,7 +249,7 @@ int rio_request_inb_mbox(struct rio_mport *mport,
if (mport->ops->open_inb_mbox == NULL)
goto out;
- res = kmalloc(sizeof(struct resource), GFP_KERNEL);
+ res = kzalloc(sizeof(struct resource), GFP_KERNEL);
if (res) {
rio_init_mbox_res(res, mbox, mbox);
@@ -185,7 +317,7 @@ int rio_request_outb_mbox(struct rio_mport *mport,
if (mport->ops->open_outb_mbox == NULL)
goto out;
- res = kmalloc(sizeof(struct resource), GFP_KERNEL);
+ res = kzalloc(sizeof(struct resource), GFP_KERNEL);
if (res) {
rio_init_mbox_res(res, mbox, mbox);
@@ -258,7 +390,9 @@ rio_setup_inb_dbell(struct rio_mport *mport, void *dev_id, struct resource *res,
dbell->dinb = dinb;
dbell->dev_id = dev_id;
+ mutex_lock(&mport->lock);
list_add_tail(&dbell->node, &mport->dbells);
+ mutex_unlock(&mport->lock);
out:
return rc;
@@ -285,7 +419,7 @@ int rio_request_inb_dbell(struct rio_mport *mport,
{
int rc = 0;
- struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
+ struct resource *res = kzalloc(sizeof(struct resource), GFP_KERNEL);
if (res) {
rio_init_dbell_res(res, start, end);
@@ -322,12 +456,15 @@ int rio_release_inb_dbell(struct rio_mport *mport, u16 start, u16 end)
int rc = 0, found = 0;
struct rio_dbell *dbell;
+ mutex_lock(&mport->lock);
list_for_each_entry(dbell, &mport->dbells, node) {
if ((dbell->res->start == start) && (dbell->res->end == end)) {
+ list_del(&dbell->node);
found = 1;
break;
}
}
+ mutex_unlock(&mport->lock);
/* If we can't find an exact match, fail */
if (!found) {
@@ -335,9 +472,6 @@ int rio_release_inb_dbell(struct rio_mport *mport, u16 start, u16 end)
goto out;
}
- /* Delete from list */
- list_del(&dbell->node);
-
/* Release the doorbell resource */
rc = release_resource(dbell->res);
@@ -360,7 +494,7 @@ int rio_release_inb_dbell(struct rio_mport *mport, u16 start, u16 end)
struct resource *rio_request_outb_dbell(struct rio_dev *rdev, u16 start,
u16 end)
{
- struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
+ struct resource *res = kzalloc(sizeof(struct resource), GFP_KERNEL);
if (res) {
rio_init_dbell_res(res, start, end);
@@ -394,7 +528,71 @@ int rio_release_outb_dbell(struct rio_dev *rdev, struct resource *res)
}
/**
- * rio_request_inb_pwrite - request inbound port-write message service
+ * rio_add_mport_pw_handler - add port-write message handler into the list
+ * of mport specific pw handlers
+ * @mport: RIO master port to bind the portwrite callback
+ * @context: Handler specific context to pass on event
+ * @pwcback: Callback to execute when portwrite is received
+ *
+ * Returns 0 if the request has been satisfied.
+ */
+int rio_add_mport_pw_handler(struct rio_mport *mport, void *context,
+ int (*pwcback)(struct rio_mport *mport,
+ void *context, union rio_pw_msg *msg, int step))
+{
+ int rc = 0;
+ struct rio_pwrite *pwrite;
+
+ pwrite = kzalloc(sizeof(struct rio_pwrite), GFP_KERNEL);
+ if (!pwrite) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ pwrite->pwcback = pwcback;
+ pwrite->context = context;
+ mutex_lock(&mport->lock);
+ list_add_tail(&pwrite->node, &mport->pwrites);
+ mutex_unlock(&mport->lock);
+out:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rio_add_mport_pw_handler);
+
+/**
+ * rio_del_mport_pw_handler - remove port-write message handler from the list
+ * of mport specific pw handlers
+ * @mport: RIO master port to bind the portwrite callback
+ * @context: Registered handler specific context to pass on event
+ * @pwcback: Registered callback function
+ *
+ * Returns 0 if the request has been satisfied.
+ */
+int rio_del_mport_pw_handler(struct rio_mport *mport, void *context,
+ int (*pwcback)(struct rio_mport *mport,
+ void *context, union rio_pw_msg *msg, int step))
+{
+ int rc = -EINVAL;
+ struct rio_pwrite *pwrite;
+
+ mutex_lock(&mport->lock);
+ list_for_each_entry(pwrite, &mport->pwrites, node) {
+ if (pwrite->pwcback == pwcback && pwrite->context == context) {
+ list_del(&pwrite->node);
+ kfree(pwrite);
+ rc = 0;
+ break;
+ }
+ }
+ mutex_unlock(&mport->lock);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rio_del_mport_pw_handler);
+
+/**
+ * rio_request_inb_pwrite - request inbound port-write message service for
+ * specific RapidIO device
* @rdev: RIO device to which register inbound port-write callback routine
* @pwcback: Callback routine to execute when port-write is received
*
@@ -419,6 +617,7 @@ EXPORT_SYMBOL_GPL(rio_request_inb_pwrite);
/**
* rio_release_inb_pwrite - release inbound port-write message service
+ * associated with specific RapidIO device
* @rdev: RIO device which registered for inbound port-write callback
*
* Removes callback from the rio_dev structure. Returns 0 if the request
@@ -440,6 +639,24 @@ int rio_release_inb_pwrite(struct rio_dev *rdev)
EXPORT_SYMBOL_GPL(rio_release_inb_pwrite);
/**
+ * rio_pw_enable - Enables/disables port-write handling by a master port
+ * @mport: Master port associated with port-write handling
+ * @enable: 1=enable, 0=disable
+ */
+void rio_pw_enable(struct rio_mport *mport, int enable)
+{
+ if (mport->ops->pwenable) {
+ mutex_lock(&mport->lock);
+
+ if ((enable && ++mport->pwe_refcnt == 1) ||
+ (!enable && mport->pwe_refcnt && --mport->pwe_refcnt == 0))
+ mport->ops->pwenable(mport, enable);
+ mutex_unlock(&mport->lock);
+ }
+}
+EXPORT_SYMBOL_GPL(rio_pw_enable);
+
+/**
* rio_map_inb_region -- Map inbound memory region.
* @mport: Master port.
* @local: physical address of memory region to be mapped
@@ -483,6 +700,56 @@ void rio_unmap_inb_region(struct rio_mport *mport, dma_addr_t lstart)
EXPORT_SYMBOL_GPL(rio_unmap_inb_region);
/**
+ * rio_map_outb_region -- Map outbound memory region.
+ * @mport: Master port.
+ * @destid: destination id window points to
+ * @rbase: RIO base address window translates to
+ * @size: Size of the memory region
+ * @rflags: Flags for mapping.
+ * @local: physical address of memory region mapped
+ *
+ * Return: 0 -- Success.
+ *
+ * This function will create the mapping from RIO space to local memory.
+ */
+int rio_map_outb_region(struct rio_mport *mport, u16 destid, u64 rbase,
+ u32 size, u32 rflags, dma_addr_t *local)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ if (!mport->ops->map_outb)
+ return -ENODEV;
+
+ spin_lock_irqsave(&rio_mmap_lock, flags);
+ rc = mport->ops->map_outb(mport, destid, rbase, size,
+ rflags, local);
+ spin_unlock_irqrestore(&rio_mmap_lock, flags);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rio_map_outb_region);
+
+/**
+ * rio_unmap_inb_region -- Unmap the inbound memory region
+ * @mport: Master port
+ * @destid: destination id mapping points to
+ * @rstart: RIO base address window translates to
+ */
+void rio_unmap_outb_region(struct rio_mport *mport, u16 destid, u64 rstart)
+{
+ unsigned long flags;
+
+ if (!mport->ops->unmap_outb)
+ return;
+
+ spin_lock_irqsave(&rio_mmap_lock, flags);
+ mport->ops->unmap_outb(mport, destid, rstart);
+ spin_unlock_irqrestore(&rio_mmap_lock, flags);
+}
+EXPORT_SYMBOL_GPL(rio_unmap_outb_region);
+
+/**
* rio_mport_get_physefb - Helper function that returns register offset
* for Physical Layer Extended Features Block.
* @port: Master port to issue transaction
@@ -864,52 +1131,66 @@ rd_err:
}
/**
- * rio_inb_pwrite_handler - process inbound port-write message
+ * rio_inb_pwrite_handler - inbound port-write message handler
+ * @mport: mport device associated with port-write
* @pw_msg: pointer to inbound port-write message
*
* 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;
-
- rdev = rio_get_comptag((pw_msg->em.comptag & RIO_CTAG_UDEVID), NULL);
- 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: Port-Write message from %s\n", rio_name(rdev));
+ struct rio_pwrite *pwrite;
#ifdef DEBUG_PW
{
- u32 i;
- for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32);) {
+ u32 i;
+
+ pr_debug("%s: PW to mport_%d:\n", __func__, mport->id);
+ for (i = 0; i < RIO_PW_MSG_SIZE / sizeof(u32); i = i + 4) {
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;
- }
+ i * 4, pw_msg->raw[i], pw_msg->raw[i + 1],
+ pw_msg->raw[i + 2], pw_msg->raw[i + 3]);
+ }
}
#endif
- /* Call an external service function (if such is registered
- * for this device). This may be the service for endpoints that send
- * device-specific port-write messages. End-point messages expected
- * to be handled completely by EP specific device driver.
+ rdev = rio_get_comptag((pw_msg->em.comptag & RIO_CTAG_UDEVID), NULL);
+ if (rdev) {
+ pr_debug("RIO: Port-Write message from %s\n", rio_name(rdev));
+ } else {
+ pr_debug("RIO: %s No matching device for CTag 0x%08x\n",
+ __func__, pw_msg->em.comptag);
+ }
+
+ /* Call a device-specific handler (if it is registered for the device).
+ * This may be the service for endpoints that send device-specific
+ * port-write messages. End-point messages expected to be handled
+ * completely by EP specific device driver.
* For switches rc==0 signals that no standard processing required.
*/
- if (rdev->pwcback != NULL) {
+ if (rdev && rdev->pwcback) {
rc = rdev->pwcback(rdev, pw_msg, 0);
if (rc == 0)
return 0;
}
+ mutex_lock(&mport->lock);
+ list_for_each_entry(pwrite, &mport->pwrites, node)
+ pwrite->pwcback(mport, pwrite->context, pw_msg, 0);
+ mutex_unlock(&mport->lock);
+
+ if (!rdev)
+ return 0;
+
+ /*
+ * FIXME: The code below stays as it was before for now until we decide
+ * how to do default PW handling in combination with per-mport callbacks
+ */
+
portnum = pw_msg->em.is_port & 0xFF;
/* Check if device and route to it are functional:
@@ -1909,32 +2190,31 @@ static int rio_get_hdid(int index)
return hdid[index];
}
-int rio_register_mport(struct rio_mport *port)
+int rio_mport_initialize(struct rio_mport *mport)
{
- struct rio_scan_node *scan = NULL;
- int res = 0;
-
if (next_portid >= RIO_MAX_MPORTS) {
pr_err("RIO: reached specified max number of mports\n");
- return 1;
+ return -ENODEV;
}
- port->id = next_portid++;
- port->host_deviceid = rio_get_hdid(port->id);
- port->nscan = NULL;
+ atomic_set(&mport->state, RIO_DEVICE_INITIALIZING);
+ mport->id = next_portid++;
+ mport->host_deviceid = rio_get_hdid(mport->id);
+ mport->nscan = NULL;
+ mutex_init(&mport->lock);
+ mport->pwe_refcnt = 0;
+ INIT_LIST_HEAD(&mport->pwrites);
- dev_set_name(&port->dev, "rapidio%d", port->id);
- port->dev.class = &rio_mport_class;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rio_mport_initialize);
- res = device_register(&port->dev);
- if (res)
- dev_err(&port->dev, "RIO: mport%d registration failed ERR=%d\n",
- port->id, res);
- else
- dev_dbg(&port->dev, "RIO: mport%d registered\n", port->id);
+int rio_register_mport(struct rio_mport *port)
+{
+ struct rio_scan_node *scan = NULL;
+ int res = 0;
mutex_lock(&rio_mport_list_lock);
- list_add_tail(&port->node, &rio_mports);
/*
* Check if there are any registered enumeration/discovery operations
@@ -1948,12 +2228,73 @@ int rio_register_mport(struct rio_mport *port)
break;
}
}
+
+ list_add_tail(&port->node, &rio_mports);
mutex_unlock(&rio_mport_list_lock);
+ dev_set_name(&port->dev, "rapidio%d", port->id);
+ port->dev.class = &rio_mport_class;
+ atomic_set(&port->state, RIO_DEVICE_RUNNING);
+
+ res = device_register(&port->dev);
+ if (res)
+ dev_err(&port->dev, "RIO: mport%d registration failed ERR=%d\n",
+ port->id, res);
+ else
+ dev_dbg(&port->dev, "RIO: registered mport%d\n", port->id);
+
+ return res;
+}
+EXPORT_SYMBOL_GPL(rio_register_mport);
+
+static int rio_mport_cleanup_callback(struct device *dev, void *data)
+{
+ struct rio_dev *rdev = to_rio_dev(dev);
+
+ if (dev->bus == &rio_bus_type)
+ rio_del_device(rdev, RIO_DEVICE_SHUTDOWN);
+ return 0;
+}
+
+static int rio_net_remove_children(struct rio_net *net)
+{
+ /*
+ * Unregister all RapidIO devices residing on this net (this will
+ * invoke notification of registered subsystem interfaces as well).
+ */
+ device_for_each_child(&net->dev, NULL, rio_mport_cleanup_callback);
+ return 0;
+}
+
+int rio_unregister_mport(struct rio_mport *port)
+{
pr_debug("RIO: %s %s id=%d\n", __func__, port->name, port->id);
+
+ /* Transition mport to the SHUTDOWN state */
+ if (atomic_cmpxchg(&port->state,
+ RIO_DEVICE_RUNNING,
+ RIO_DEVICE_SHUTDOWN) != RIO_DEVICE_RUNNING) {
+ pr_err("RIO: %s unexpected state transition for mport %s\n",
+ __func__, port->name);
+ }
+
+ if (port->net && port->net->hport == port) {
+ rio_net_remove_children(port->net);
+ rio_free_net(port->net);
+ }
+
+ /*
+ * Unregister all RapidIO devices attached to this mport (this will
+ * invoke notification of registered subsystem interfaces as well).
+ */
+ mutex_lock(&rio_mport_list_lock);
+ list_del(&port->node);
+ mutex_unlock(&rio_mport_list_lock);
+ device_unregister(&port->dev);
+
return 0;
}
-EXPORT_SYMBOL_GPL(rio_register_mport);
+EXPORT_SYMBOL_GPL(rio_unregister_mport);
EXPORT_SYMBOL_GPL(rio_local_get_device_id);
EXPORT_SYMBOL_GPL(rio_get_device);