All the IRQs for DPAA2 objects in the same DPRC must use
the ICID of that DPRC, as their device Id in the GIC-ITS.
Thus, all these IRQs must share the same ITT table in GIC. As
a result, a pool of IRQs with the same device Id must be
preallocated per DPRC (fsl-mc bus instance). So, the fsl-mc
bus object allocator is extended to also provide services
to allocate IRQs to DPAA2 devices, from their parent fsl-mc bus
IRQ pool.

Also, the interrupt handler for DPRC IRQs is added. DPRC IRQs are
generated for hot plug events related to DPAA2 objects in a given
DPRC. These events include, creating/destroying DPAA2 objects in
the DPRC, changing the "plugged" state of DPAA2 objects and moving
objects between DPRCs.

Signed-off-by: J. German Rivera <german.riv...@freescale.com>
Reviewed-by: Stuart Yoder <stuart.yo...@freescale.com>
---
Changes in v4:
- Addressed comments from Dan Carpenter and Greg Kroah-Hartman:
  * Removed all #ifdefed-out code
- Fixed new checkpatch "check" warnings

Changes in v3:
- Addressed comments from Dan Carpenter:
  * Removed nested redundant #ifdef GIC_ITS_MC_SUPPORT

Changes in v2:
- Addressed comments from Dan Carpenter and Scott Wood:
  * Removed unnecessary lines from commit messages
  * Replaced single error cleanup label with multiple labels, one for
    each error case and return directly when no cleanup is needed.
  * Fixed conditional alignment
  * Removed error messages for devm_kzalloc() errors
  * Removed WARN_ON in dprc_irq0_handler_thread()
  * Removed devm_kfree() calls

 drivers/staging/fsl-mc/bus/dprc-driver.c    | 353 ++++++++++++++++++++++++----
 drivers/staging/fsl-mc/bus/mc-allocator.c   | 102 +++++++-
 drivers/staging/fsl-mc/bus/mc-bus.c         | 156 +++++++++++-
 drivers/staging/fsl-mc/include/mc-private.h |  26 +-
 drivers/staging/fsl-mc/include/mc.h         |  29 ++-
 5 files changed, 599 insertions(+), 67 deletions(-)

diff --git a/drivers/staging/fsl-mc/bus/dprc-driver.c 
b/drivers/staging/fsl-mc/bus/dprc-driver.c
index 35c06cf..5351170 100644
--- a/drivers/staging/fsl-mc/bus/dprc-driver.c
+++ b/drivers/staging/fsl-mc/bus/dprc-driver.c
@@ -13,6 +13,7 @@
 #include "../include/mc-sys.h"
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/interrupt.h>
 #include "dprc-cmd.h"

 struct dprc_child_objs {
@@ -126,7 +127,7 @@ static void check_plugged_state_change(struct fsl_mc_device 
*mc_dev,
                                       struct dprc_obj_desc *obj_desc)
 {
        int error;
-       uint32_t plugged_flag_at_mc =
+       u32 plugged_flag_at_mc =
                        (obj_desc->state & DPRC_OBJ_STATE_PLUGGED);

        if (plugged_flag_at_mc !=
@@ -206,41 +207,11 @@ static void dprc_init_all_resource_pools(struct 
fsl_mc_device *mc_bus_dev)
        }
 }

-static void dprc_cleanup_resource_pool(struct fsl_mc_device *mc_bus_dev,
-                                      enum fsl_mc_pool_type pool_type)
-{
-       struct fsl_mc_resource *resource;
-       struct fsl_mc_resource *next;
-       struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
-       struct fsl_mc_resource_pool *res_pool =
-                                       &mc_bus->resource_pools[pool_type];
-       int free_count = 0;
-
-       WARN_ON(res_pool->type != pool_type);
-       WARN_ON(res_pool->free_count != res_pool->max_count);
-
-       list_for_each_entry_safe(resource, next, &res_pool->free_list, node) {
-               free_count++;
-               WARN_ON(resource->type != res_pool->type);
-               WARN_ON(resource->parent_pool != res_pool);
-               devm_kfree(&mc_bus_dev->dev, resource);
-       }
-
-       WARN_ON(free_count != res_pool->free_count);
-}
-
-static void dprc_cleanup_all_resource_pools(struct fsl_mc_device *mc_bus_dev)
-{
-       int pool_type;
-
-       for (pool_type = 0; pool_type < FSL_MC_NUM_POOL_TYPES; pool_type++)
-               dprc_cleanup_resource_pool(mc_bus_dev, pool_type);
-}
-
 /**
  * dprc_scan_objects - Discover objects in a DPRC
  *
  * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
+ * @total_irq_count: total number of IRQs needed by objects in the DPRC.
  *
  * Detects objects added and removed from a DPRC and synchronizes the
  * state of the Linux bus driver, MC by adding and removing
@@ -254,11 +225,13 @@ static void dprc_cleanup_all_resource_pools(struct 
fsl_mc_device *mc_bus_dev)
  * populated before they can get allocation requests from probe callbacks
  * of the device drivers for the non-allocatable devices.
  */
-int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev)
+int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
+                     unsigned int *total_irq_count)
 {
        int num_child_objects;
        int dprc_get_obj_failures;
        int error;
+       unsigned int irq_count = mc_bus_dev->obj_desc.irq_count;
        struct dprc_obj_desc *child_obj_desc_array = NULL;

        error = dprc_get_obj_count(mc_bus_dev->mc_io,
@@ -274,9 +247,9 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev)
                int i;

                child_obj_desc_array =
-                   devm_kmalloc_array(&mc_bus_dev->dev, num_child_objects,
-                                      sizeof(*child_obj_desc_array),
-                                      GFP_KERNEL);
+                   kmalloc_array(num_child_objects,
+                                 sizeof(*child_obj_desc_array),
+                                 GFP_KERNEL);
                if (!child_obj_desc_array)
                        return -ENOMEM;

@@ -305,6 +278,7 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev)
                                continue;
                        }

+                       irq_count += obj_desc->irq_count;
                        dev_dbg(&mc_bus_dev->dev,
                                "Discovered object: type %s, id %d\n",
                                obj_desc->type, obj_desc->id);
@@ -317,15 +291,14 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev)
                }
        }

+       *total_irq_count = irq_count;
        dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
                            num_child_objects);

        dprc_add_new_devices(mc_bus_dev, child_obj_desc_array,
                             num_child_objects);

-       if (child_obj_desc_array)
-               devm_kfree(&mc_bus_dev->dev, child_obj_desc_array);
-
+       kfree(child_obj_desc_array);
        return 0;
 }
 EXPORT_SYMBOL_GPL(dprc_scan_objects);
@@ -339,9 +312,10 @@ EXPORT_SYMBOL_GPL(dprc_scan_objects);
  * bus driver with the actual state of the MC by adding and removing
  * devices as appropriate.
  */
-int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
+static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
 {
        int error;
+       unsigned int irq_count;
        struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);

        dprc_init_all_resource_pools(mc_bus_dev);
@@ -350,17 +324,280 @@ int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
         * Discover objects in the DPRC:
         */
        mutex_lock(&mc_bus->scan_mutex);
-       error = dprc_scan_objects(mc_bus_dev);
+       error = dprc_scan_objects(mc_bus_dev, &irq_count);
        mutex_unlock(&mc_bus->scan_mutex);
        if (error < 0)
-               goto error;
+               return error;
+
+       if (!mc_bus->irq_resources) {
+               irq_count += FSL_MC_IRQ_POOL_MAX_EXTRA_IRQS;
+               error = fsl_mc_populate_irq_pool(mc_bus, irq_count);
+               if (error < 0)
+                       return error;
+       }
+
+       return 0;
+}
+
+/**
+ * dprc_irq0_handler - Regular ISR for DPRC interrupt 0
+ *
+ * @irq: IRQ number of the interrupt being handled
+ * @arg: Pointer to device structure
+ */
+static irqreturn_t dprc_irq0_handler(int irq_num, void *arg)
+{
+       return IRQ_WAKE_THREAD;
+}
+
+/**
+ * dprc_irq0_handler_thread - Handler thread function for DPRC interrupt 0
+ *
+ * @irq: IRQ number of the interrupt being handled
+ * @arg: Pointer to device structure
+ */
+static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg)
+{
+       int error;
+       u32 status;
+       struct device *dev = (struct device *)arg;
+       struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+       struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
+       struct fsl_mc_io *mc_io = mc_dev->mc_io;
+       int irq_index = 0;
+
+       dev_dbg(dev, "DPRC IRQ %d\n", irq_num);
+       if (WARN_ON(!(mc_dev->flags & FSL_MC_IS_DPRC)))
+               return IRQ_HANDLED;
+
+       mutex_lock(&mc_bus->scan_mutex);
+       if (WARN_ON(mc_dev->irqs[irq_index]->irq_number != (u32)irq_num))
+               goto out;
+
+       error = dprc_get_irq_status(mc_io, mc_dev->mc_handle, irq_index,
+                                   &status);
+       if (error < 0) {
+               dev_err(dev,
+                       "dprc_get_irq_status() failed: %d\n", error);
+               goto out;
+       }
+
+       error = dprc_clear_irq_status(mc_io, mc_dev->mc_handle, irq_index,
+                                     status);
+       if (error < 0) {
+               dev_err(dev,
+                       "dprc_clear_irq_status() failed: %d\n", error);
+               goto out;
+       }
+
+       if (status & (DPRC_IRQ_EVENT_OBJ_ADDED |
+                     DPRC_IRQ_EVENT_OBJ_REMOVED |
+                     DPRC_IRQ_EVENT_CONTAINER_DESTROYED |
+                     DPRC_IRQ_EVENT_OBJ_DESTROYED |
+                     DPRC_IRQ_EVENT_OBJ_CREATED)) {
+               unsigned int irq_count;
+
+               error = dprc_scan_objects(mc_dev, &irq_count);
+               if (error < 0) {
+                       dev_err(dev, "dprc_scan_objects() failed: %d\n", error);
+                       goto out;
+               }
+
+               if (irq_count >
+                   mc_bus->resource_pools[FSL_MC_POOL_IRQ].max_count) {
+                       dev_warn(dev,
+                                "IRQs needed (%u) exceed IRQs preallocated 
(%u)\n",
+                                irq_count,
+                                mc_bus->resource_pools[FSL_MC_POOL_IRQ].
+                                                               max_count);
+               }
+       }
+
+out:
+       mutex_unlock(&mc_bus->scan_mutex);
+       return IRQ_HANDLED;
+}
+
+/*
+ * Disable and clear interrupts for a given DPRC object
+ */
+static int disable_dprc_irqs(struct fsl_mc_device *mc_dev)
+{
+       int i;
+       int error;
+       struct fsl_mc_io *mc_io = mc_dev->mc_io;
+       int irq_count = mc_dev->obj_desc.irq_count;
+
+       if (WARN_ON(irq_count == 0))
+               return -EINVAL;
+
+       for (i = 0; i < irq_count; i++) {
+               /*
+                * Disable generation of interrupt i, while we configure it:
+                */
+               error = dprc_set_irq_enable(mc_io, mc_dev->mc_handle, i, 0);
+               if (error < 0) {
+                       dev_err(&mc_dev->dev,
+                               "dprc_set_irq_enable() failed: %d\n", error);
+
+                       return error;
+               }
+
+               /*
+                * Disable all interrupt causes for interrupt i:
+                */
+               error = dprc_set_irq_mask(mc_io, mc_dev->mc_handle, i, 0x0);
+               if (error < 0) {
+                       dev_err(&mc_dev->dev,
+                               "dprc_set_irq_mask() failed: %d\n", error);
+
+                       return error;
+               }
+
+               /*
+                * Clear any leftover interrupt i:
+                */
+               error = dprc_clear_irq_status(mc_io, mc_dev->mc_handle, i,
+                                             ~0x0U);
+               if (error < 0) {
+                       dev_err(&mc_dev->dev,
+                               "dprc_clear_irq_status() failed: %d\n",
+                               error);
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
+static int register_dprc_irq_handlers(struct fsl_mc_device *mc_dev)
+{
+       static const struct irq_handler {
+               irq_handler_t irq_handler;
+               irq_handler_t irq_handler_thread;
+               const char *irq_name;
+       } irq_handlers[] = {
+               [0] = {
+                       .irq_handler = dprc_irq0_handler,
+                       .irq_handler_thread = dprc_irq0_handler_thread,
+                       .irq_name = "FSL MC DPRC irq0",
+               },
+       };
+
+       unsigned int i;
+       int error;
+       struct fsl_mc_device_irq *irq;
+       int irq_count = mc_dev->obj_desc.irq_count;
+
+       if (WARN_ON(irq_count != ARRAY_SIZE(irq_handlers)))
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(irq_handlers); i++) {
+               irq = mc_dev->irqs[i];
+               error = devm_request_threaded_irq(&mc_dev->dev,
+                                                 irq->irq_number,
+                                                 irq_handlers[i].irq_handler,
+                                                 irq_handlers[i].
+                                                       irq_handler_thread,
+                                                 IRQF_NO_SUSPEND |
+                                                       IRQF_ONESHOT,
+                                                 irq_handlers[i].irq_name,
+                                                 &mc_dev->dev);
+               if (error < 0) {
+                       dev_err(&mc_dev->dev,
+                               "devm_request_threaded_irq() failed: %d\n",
+                               error);
+                       return error;
+               }
+
+               /*
+                * Program the MSI (paddr, value) pair in the device:
+                *
+                * TODO: This needs to be moved to mc_bus_msi_domain_write_msg()
+                * when the MC object-independent dprc_set_irq() flib API
+                * becomes available
+                */
+               error = dprc_set_irq(mc_dev->mc_io, mc_dev->mc_handle,
+                                    i, irq->msi_paddr,
+                                    irq->msi_value,
+                                    irq->irq_number);
+               if (error < 0) {
+                       dev_err(&mc_dev->dev,
+                               "mc_set_irq() failed: %d\n", error);
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
+static int enable_dprc_irqs(struct fsl_mc_device *mc_dev)
+{
+       int i;
+       int error;
+       int irq_count = mc_dev->obj_desc.irq_count;
+
+       for (i = 0; i < irq_count; i++) {
+               /*
+                * Enable all interrupt causes for the interrupt:
+                */
+               error = dprc_set_irq_mask(mc_dev->mc_io,
+                                         mc_dev->mc_handle,
+                                         i,
+                                         ~0x0u);
+               if (error < 0) {
+                       dev_err(&mc_dev->dev,
+                               "dprc_set_irq_mask() failed: %d\n", error);
+
+                       return error;
+               }
+
+               /*
+                * Enable generation of the interrupt:
+                */
+               error = dprc_set_irq_enable(mc_dev->mc_io,
+                                           mc_dev->mc_handle,
+                                           i, 1);
+               if (error < 0) {
+                       dev_err(&mc_dev->dev,
+                               "dprc_set_irq_enable() failed: %d\n", error);
+
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * Setup interrupts for a given DPRC device
+ */
+static int dprc_setup_irqs(struct fsl_mc_device *mc_dev)
+{
+       int error;
+
+       error = fsl_mc_allocate_irqs(mc_dev);
+       if (error < 0)
+               return error;
+
+       error = disable_dprc_irqs(mc_dev);
+       if (error < 0)
+               goto error_free_irqs;
+
+       error = register_dprc_irq_handlers(mc_dev);
+       if (error < 0)
+               goto error_free_irqs;
+
+       error = enable_dprc_irqs(mc_dev);
+       if (error < 0)
+               goto error_free_irqs;

        return 0;
-error:
-       dprc_cleanup_all_resource_pools(mc_bus_dev);
+
+error_free_irqs:
+       fsl_mc_free_irqs(mc_dev);
        return error;
 }
-EXPORT_SYMBOL_GPL(dprc_scan_container);

 /**
  * dprc_probe - callback invoked when a DPRC is being bound to this driver
@@ -415,10 +652,20 @@ static int dprc_probe(struct fsl_mc_device *mc_dev)
        if (error < 0)
                goto error_cleanup_open;

+       /*
+        * Configure interrupts for the DPRC object associated with this MC bus:
+        */
+       error = dprc_setup_irqs(mc_dev);
+       if (error < 0)
+               goto error_cleanup_open;
+
        dev_info(&mc_dev->dev, "DPRC device bound to driver");
        return 0;

 error_cleanup_open:
+       if (mc_bus->irq_resources)
+               fsl_mc_cleanup_irq_pool(mc_bus);
+
        (void)dprc_close(mc_dev->mc_io, mc_dev->mc_handle);

 error_cleanup_mc_io:
@@ -426,6 +673,15 @@ error_cleanup_mc_io:
        return error;
 }

+/*
+ * Tear down interrupts for a given DPRC object
+ */
+static void dprc_teardown_irqs(struct fsl_mc_device *mc_dev)
+{
+       (void)disable_dprc_irqs(mc_dev);
+       fsl_mc_free_irqs(mc_dev);
+}
+
 /**
  * dprc_remove - callback invoked when a DPRC is being unbound from this driver
  *
@@ -439,18 +695,23 @@ error_cleanup_mc_io:
 static int dprc_remove(struct fsl_mc_device *mc_dev)
 {
        int error;
+       struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);

        if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
                return -EINVAL;
        if (WARN_ON(!mc_dev->mc_io))
                return -EINVAL;

+       if (WARN_ON(!mc_bus->irq_resources))
+               return -EINVAL;
+
+       dprc_teardown_irqs(mc_dev);
        device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
-       dprc_cleanup_all_resource_pools(mc_dev);
        error = dprc_close(mc_dev->mc_io, mc_dev->mc_handle);
        if (error < 0)
                dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);

+       fsl_mc_cleanup_irq_pool(mc_bus);
        dev_info(&mc_dev->dev, "DPRC device unbound from driver");
        return 0;
 }
diff --git a/drivers/staging/fsl-mc/bus/mc-allocator.c 
b/drivers/staging/fsl-mc/bus/mc-allocator.c
index e36235d..aa8280a 100644
--- a/drivers/staging/fsl-mc/bus/mc-allocator.c
+++ b/drivers/staging/fsl-mc/bus/mc-allocator.c
@@ -66,8 +66,6 @@ static int __must_check 
fsl_mc_resource_pool_add_device(struct fsl_mc_bus
                                GFP_KERNEL);
        if (!resource) {
                error = -ENOMEM;
-               dev_err(&mc_bus_dev->dev,
-                       "Failed to allocate memory for fsl_mc_resource\n");
                goto out;
        }

@@ -145,8 +143,6 @@ static int __must_check 
fsl_mc_resource_pool_remove_device(struct fsl_mc_device
        INIT_LIST_HEAD(&resource->node);
        res_pool->free_count--;
        res_pool->max_count--;
-
-       devm_kfree(&mc_bus_dev->dev, resource);
        mc_dev->resource = NULL;
        error = 0;
 out:
@@ -160,6 +156,7 @@ static const char *const fsl_mc_pool_type_strings[] = {
        [FSL_MC_POOL_DPMCP] = "dpmcp",
        [FSL_MC_POOL_DPBP] = "dpbp",
        [FSL_MC_POOL_DPCON] = "dpcon",
+       [FSL_MC_POOL_IRQ] = "irq",
 };

 static int __must_check object_type_to_pool_type(const char *object_type,
@@ -473,6 +470,103 @@ void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
 EXPORT_SYMBOL_GPL(fsl_mc_object_free);

 /**
+ * It allocates the IRQs required by a given MC object device. The
+ * IRQs are allocated from the interrupt pool associated with the
+ * MC bus that contains the device, if the device is not a DPRC device.
+ * Otherwise, the IRQs are allocated from the interrupt pool associated
+ * with the MC bus that represents the DPRC device itself.
+ */
+int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev)
+{
+       int i;
+       int irq_count;
+       int res_allocated_count = 0;
+       int error = -EINVAL;
+       struct fsl_mc_device_irq **irqs = NULL;
+       struct fsl_mc_bus *mc_bus;
+       struct fsl_mc_resource_pool *res_pool;
+
+       if (WARN_ON(mc_dev->irqs))
+               return -EINVAL;
+
+       irq_count = mc_dev->obj_desc.irq_count;
+       if (WARN_ON(irq_count == 0))
+               return -EINVAL;
+
+       if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
+               mc_bus = to_fsl_mc_bus(mc_dev);
+       else
+               mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
+
+       if (WARN_ON(!mc_bus->irq_resources))
+               return -EINVAL;
+
+       res_pool = &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
+       if (res_pool->free_count < irq_count) {
+               dev_err(&mc_dev->dev,
+                       "Not able to allocate %u irqs for device\n", irq_count);
+               return -ENOSPC;
+       }
+
+       irqs = devm_kzalloc(&mc_dev->dev, irq_count * sizeof(irqs[0]),
+                           GFP_KERNEL);
+       if (!irqs)
+               return -ENOMEM;
+
+       for (i = 0; i < irq_count; i++) {
+               struct fsl_mc_resource *resource;
+
+               error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_IRQ,
+                                                &resource);
+               if (error < 0)
+                       goto error_resource_alloc;
+
+               irqs[i] = to_fsl_mc_irq(resource);
+               res_allocated_count++;
+       }
+
+       mc_dev->irqs = irqs;
+       return 0;
+
+error_resource_alloc:
+       for (i = 0; i < res_allocated_count; i++)
+               fsl_mc_resource_free(&irqs[i]->resource);
+
+       return error;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs);
+
+/*
+ * It frees the IRQs that were allocated for a MC object device, by
+ * returning them to the corresponding interrupt pool.
+ */
+void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev)
+{
+       int i;
+       int irq_count;
+       struct fsl_mc_bus *mc_bus;
+
+       if (WARN_ON(!mc_dev->irqs))
+               return;
+
+       irq_count = mc_dev->obj_desc.irq_count;
+
+       if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
+               mc_bus = to_fsl_mc_bus(mc_dev);
+       else
+               mc_bus = to_fsl_mc_bus(to_fsl_mc_device(mc_dev->dev.parent));
+
+       if (WARN_ON(!mc_bus->irq_resources))
+               return;
+
+       for (i = 0; i < irq_count; i++)
+               fsl_mc_resource_free(&mc_dev->irqs[i]->resource);
+
+       mc_dev->irqs = NULL;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_free_irqs);
+
+/**
  * fsl_mc_allocator_probe - callback invoked when an allocatable device is
  * being added to the system
  */
diff --git a/drivers/staging/fsl-mc/bus/mc-bus.c 
b/drivers/staging/fsl-mc/bus/mc-bus.c
index 766a659..9d98828 100644
--- a/drivers/staging/fsl-mc/bus/mc-bus.c
+++ b/drivers/staging/fsl-mc/bus/mc-bus.c
@@ -15,11 +15,27 @@
 #include <linux/of_address.h>
 #include <linux/ioport.h>
 #include <linux/slab.h>
+#include <linux/irqchip/arm-gic-v3.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/limits.h>
+#include <linux/bitops.h>
 #include "../include/dpmng.h"
 #include "../include/mc-sys.h"
 #include "dprc-cmd.h"

+/*
+ * IOMMU stream ID flags
+ */
+#define STREAM_ID_PL_MASK   BIT(9)         /* privilege level */
+#define STREAM_ID_BMT_MASK  BIT(8)         /* bypass memory translation */
+#define STREAM_ID_VA_MASK   BIT(7)         /* virtual address translation
+                                            * (two-stage translation) */
+#define STREAM_ID_ICID_MASK (BIT(7) - 1)    /* isolation context ID
+                                            * (translation context) */
+
+#define MAX_STREAM_ID_ICID  STREAM_ID_ICID_MASK
+
 static struct kmem_cache *mc_dev_cache;

 /**
@@ -295,8 +311,9 @@ static int fsl_mc_device_get_mmio_regions(struct 
fsl_mc_device *mc_dev,
                                          &regions[i].start);
                if (error < 0) {
                        dev_err(parent_dev,
-                               "Invalid MC address: %#llx\n",
-                               region_desc.base_paddr);
+                               "Invalid MC address: %#llx (for %s.%d\'s region 
%d)\n",
+                               region_desc.base_paddr,
+                               obj_desc->type, obj_desc->id, i);
                        goto error_cleanup_regions;
                }

@@ -335,7 +352,7 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc,
                /*
                 * Allocate an MC bus device object:
                 */
-               mc_bus = devm_kzalloc(parent_dev, sizeof(*mc_bus), GFP_KERNEL);
+               mc_bus = kzalloc(sizeof(*mc_bus), GFP_KERNEL);
                if (!mc_bus)
                        return -ENOMEM;

@@ -437,10 +454,10 @@ int fsl_mc_device_add(struct dprc_obj_desc *obj_desc,

 error_cleanup_dev:
        kfree(mc_dev->regions);
-       if (mc_bus)
-               devm_kfree(parent_dev, mc_bus);
-       else
+       if (!mc_bus)
                kmem_cache_free(mc_dev_cache, mc_dev);
+       else
+               kfree(mc_bus);

        return error;
 }
@@ -475,13 +492,120 @@ void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
                        fsl_mc_bus_type.dev_root = NULL;
        }

-       if (mc_bus)
-               devm_kfree(mc_dev->dev.parent, mc_bus);
-       else
+       if (!mc_bus)
                kmem_cache_free(mc_dev_cache, mc_dev);
+       else
+               kfree(mc_bus);
 }
 EXPORT_SYMBOL_GPL(fsl_mc_device_remove);

+static int create_mc_irq_domain(struct platform_device *mc_pdev,
+                               struct irq_domain **new_irq_domain)
+{
+       int error;
+       struct device_node *its_of_node;
+       struct irq_domain *its_domain;
+       struct device_node *mc_of_node = mc_pdev->dev.of_node;
+
+       its_of_node = of_parse_phandle(mc_of_node, "msi-parent", 0);
+       if (!its_of_node) {
+               dev_err(&mc_pdev->dev,
+                       "msi-parent phandle missing for %s\n",
+                       mc_of_node->full_name);
+               return -ENOENT;
+       }
+
+       /*
+        * Extract MSI parent node:
+        */
+       its_domain = irq_find_host(its_of_node);
+       if (!its_domain) {
+               dev_err(&mc_pdev->dev, "Unable to find parent domain\n");
+               error = -ENOENT;
+               goto cleanup_its_of_node;
+       }
+
+       /*
+        * TODO: Call msi_create_irq_domain() to create IRQ domain for MC MSIs
+        * when complete GIC-ITS support * for generic MSIs is upstream. On
+        * success, set mc->gic_supported to true.
+        */
+       *new_irq_domain = NULL;
+       dev_err(&mc_pdev->dev, "Cannot create MSI domain\n");
+       error = -ENOTSUPP;
+
+cleanup_its_of_node:
+       of_node_put(its_of_node);
+       return error;
+}
+
+/*
+ * Initialize the interrupt pool associated with a MC bus.
+ * It allocates a block of IRQs from the GIC-ITS
+ */
+int __must_check fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
+                                         unsigned int irq_count)
+{
+       unsigned int i;
+       struct fsl_mc_device_irq *irq_resources;
+       struct fsl_mc_device_irq *irq_res;
+       struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
+       struct fsl_mc_resource_pool *res_pool =
+                       &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
+
+       if (WARN_ON(irq_count == 0 ||
+                   irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS))
+               return -EINVAL;
+
+       irq_resources =
+               devm_kzalloc(&mc_bus_dev->dev,
+                            sizeof(*irq_resources) * irq_count,
+                            GFP_KERNEL);
+       if (!irq_resources)
+               return -ENOMEM;
+
+       for (i = 0; i < irq_count; i++) {
+               irq_res = &irq_resources[i];
+               /*
+                * TODO: set irq_res->msi_paddr and irq_res->irq_number with
+                * values obtained from the GIC-ITS.
+                * Also, set irq_res->resource.id to irq_number.
+                */
+               irq_res->resource.type = res_pool->type;
+               irq_res->resource.data = irq_res;
+               irq_res->resource.parent_pool = res_pool;
+               INIT_LIST_HEAD(&irq_res->resource.node);
+               list_add_tail(&irq_res->resource.node, &res_pool->free_list);
+       }
+
+       res_pool->max_count = irq_count;
+       res_pool->free_count = irq_count;
+       mc_bus->irq_resources = irq_resources;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool);
+
+/**
+ * Teardown the interrupt pool associated with an MC bus.
+ * It frees the IRQs that were allocated to the pool, back to the GIC-ITS.
+ */
+void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus)
+{
+       struct fsl_mc_resource_pool *res_pool =
+                       &mc_bus->resource_pools[FSL_MC_POOL_IRQ];
+
+       if (WARN_ON(res_pool->max_count == 0))
+               return;
+
+       if (WARN_ON(res_pool->free_count != res_pool->max_count))
+               return;
+
+       res_pool->max_count = 0;
+       res_pool->free_count = 0;
+       mc_bus->irq_resources = NULL;
+}
+EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool);
+
 static int parse_mc_ranges(struct device *dev,
                           int *paddr_cells,
                           int *mc_addr_cells,
@@ -600,7 +724,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
        struct fsl_mc_io *mc_io = NULL;
        int container_id;
        phys_addr_t mc_portal_phys_addr;
-       uint32_t mc_portal_size;
+       u32 mc_portal_size;
        struct mc_version mc_version;
        struct resource res;

@@ -611,6 +735,9 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
                return -ENOMEM;

        platform_set_drvdata(pdev, mc);
+       error = create_mc_irq_domain(pdev, &mc->irq_domain);
+       if (error < 0)
+               return error;

        /*
         * Get physical address of MC portal for the root DPRC:
@@ -620,7 +747,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
                dev_err(&pdev->dev,
                        "of_address_to_resource() failed for %s\n",
                        pdev->dev.of_node->full_name);
-               return error;
+               goto error_cleanup_irq_domain;
        }

        mc_portal_phys_addr = res.start;
@@ -628,7 +755,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
        error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr,
                                 mc_portal_size, NULL, 0, &mc_io);
        if (error < 0)
-               return error;
+               goto error_cleanup_irq_domain;

        error = mc_get_version(mc_io, &mc_version);
        if (error != 0) {
@@ -673,6 +800,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
        obj_desc.id = container_id;
        obj_desc.ver_major = DPRC_VER_MAJOR;
        obj_desc.ver_minor = DPRC_VER_MINOR;
+       obj_desc.irq_count = 1;
        obj_desc.region_count = 0;

        error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev);
@@ -684,6 +812,9 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)

 error_cleanup_mc_io:
        fsl_destroy_mc_io(mc_io);
+
+error_cleanup_irq_domain:
+       irq_domain_remove(mc->irq_domain);
        return error;
 }

@@ -698,6 +829,7 @@ static int fsl_mc_bus_remove(struct platform_device *pdev)
        if (WARN_ON(&mc->root_mc_bus_dev->dev != fsl_mc_bus_type.dev_root))
                return -EINVAL;

+       irq_domain_remove(mc->irq_domain);
        fsl_mc_device_remove(mc->root_mc_bus_dev);
        dev_info(&pdev->dev, "Root MC bus device removed");
        return 0;
diff --git a/drivers/staging/fsl-mc/include/mc-private.h 
b/drivers/staging/fsl-mc/include/mc-private.h
index c045f49..6e33942 100644
--- a/drivers/staging/fsl-mc/include/mc-private.h
+++ b/drivers/staging/fsl-mc/include/mc-private.h
@@ -27,12 +27,26 @@
         strcmp(_obj_type, "dpcon") == 0)

 /**
+ * Maximum number of total IRQs that can be pre-allocated for an MC bus'
+ * IRQ pool
+ */
+#define FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS 256
+
+/**
+ * Maximum number of extra IRQs pre-reallocated for an MC bus' IRQ pool,
+ * to be used by dynamically created MC objects
+ */
+#define FSL_MC_IRQ_POOL_MAX_EXTRA_IRQS 64
+
+/**
  * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device
  * @root_mc_bus_dev: MC object device representing the root DPRC
+ * @irq_domain: IRQ domain for the fsl-mc bus type
  * @addr_translation_ranges: array of bus to system address translation ranges
  */
 struct fsl_mc {
        struct fsl_mc_device *root_mc_bus_dev;
+       struct irq_domain *irq_domain;
        uint8_t num_translation_ranges;
        struct fsl_mc_addr_translation_range *translation_ranges;
 };
@@ -76,11 +90,13 @@ struct fsl_mc_resource_pool {
  * @resource_pools: array of resource pools (one pool per resource type)
  * for this MC bus. These resources represent allocatable entities
  * from the physical DPRC.
+ * @irq_resources: Pointer to array of IRQ objects for the IRQ pool.
  * @scan_mutex: Serializes bus scanning
  */
 struct fsl_mc_bus {
        struct fsl_mc_device mc_dev;
        struct fsl_mc_resource_pool resource_pools[FSL_MC_NUM_POOL_TYPES];
+       struct fsl_mc_device_irq *irq_resources;
        struct mutex scan_mutex;    /* serializes bus scanning */
 };

@@ -94,9 +110,8 @@ int __must_check fsl_mc_device_add(struct dprc_obj_desc 
*obj_desc,

 void fsl_mc_device_remove(struct fsl_mc_device *mc_dev);

-int dprc_scan_container(struct fsl_mc_device *mc_bus_dev);
-
-int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev);
+int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
+                     unsigned int *total_irq_count);

 int __init dprc_driver_init(void);

@@ -113,4 +128,9 @@ int __must_check fsl_mc_resource_allocate(struct fsl_mc_bus 
*mc_bus,

 void fsl_mc_resource_free(struct fsl_mc_resource *resource);

+int __must_check fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
+                                         unsigned int irq_count);
+
+void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus);
+
 #endif /* _FSL_MC_PRIVATE_H_ */
diff --git a/drivers/staging/fsl-mc/include/mc.h 
b/drivers/staging/fsl-mc/include/mc.h
index fa02ef0..c543a84 100644
--- a/drivers/staging/fsl-mc/include/mc.h
+++ b/drivers/staging/fsl-mc/include/mc.h
@@ -14,6 +14,7 @@
 #include <linux/device.h>
 #include <linux/mod_devicetable.h>
 #include <linux/list.h>
+#include <linux/interrupt.h>
 #include "../include/dprc.h"

 #define FSL_MC_VENDOR_FREESCALE        0x1957
@@ -61,8 +62,8 @@ struct fsl_mc_driver {
 struct fsl_mc_device_match_id {
        uint16_t vendor;
        const char obj_type[16];
-       uint32_t ver_major;
-       uint32_t ver_minor;
+       u32 ver_major;
+       u32 ver_minor;
 };

 /**
@@ -75,6 +76,7 @@ enum fsl_mc_pool_type {
        FSL_MC_POOL_DPMCP = 0x0,    /* corresponds to "dpmcp" in the MC */
        FSL_MC_POOL_DPBP,           /* corresponds to "dpbp" in the MC */
        FSL_MC_POOL_DPCON,          /* corresponds to "dpcon" in the MC */
+       FSL_MC_POOL_IRQ,

        /*
         * NOTE: New resource pool types must be added before this entry
@@ -104,6 +106,23 @@ struct fsl_mc_resource {
 };

 /**
+ * struct fsl_mc_device_irq - MC object device message-based interrupt
+ * @msi_paddr: message-based interrupt physical address
+ * @msi_value: message-based interrupt data value
+ * @irq_number: Linux IRQ number assigned to the interrupt
+ * @resource: MC generic resource associated with the interrupt
+ */
+struct fsl_mc_device_irq {
+       phys_addr_t msi_paddr;
+       u32 msi_value;
+       u32 irq_number;
+       struct fsl_mc_resource resource;
+};
+
+#define to_fsl_mc_irq(_mc_resource) \
+       container_of(_mc_resource, struct fsl_mc_device_irq, resource)
+
+/**
  * Bit masks for a MC object device (struct fsl_mc_device) flags
  */
 #define FSL_MC_IS_DPRC 0x0001
@@ -124,6 +143,7 @@ struct fsl_mc_resource {
  * NULL if none.
  * @obj_desc: MC description of the DPAA device
  * @regions: pointer to array of MMIO region entries
+ * @irqs: pointer to array of pointers to interrupts allocated to this device
  * @resource: generic resource associated with this MC object device, if any.
  *
  * Generic device object for MC object devices that are "attached" to a
@@ -155,6 +175,7 @@ struct fsl_mc_device {
        struct fsl_mc_io *mc_io;
        struct dprc_obj_desc obj_desc;
        struct resource *regions;
+       struct fsl_mc_device_irq **irqs;
        struct fsl_mc_resource *resource;
 };

@@ -196,6 +217,10 @@ int __must_check fsl_mc_object_allocate(struct 
fsl_mc_device *mc_dev,

 void fsl_mc_object_free(struct fsl_mc_device *mc_adev);

+int __must_check fsl_mc_allocate_irqs(struct fsl_mc_device *mc_dev);
+
+void fsl_mc_free_irqs(struct fsl_mc_device *mc_dev);
+
 extern struct bus_type fsl_mc_bus_type;

 #endif /* _FSL_MC_H_ */
--
2.3.3

_______________________________________________
devel mailing list
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to