staging: fsl-mc: Extended MC bus allocator to include IRQs
authorJ. German Rivera <German.Rivera@freescale.com>
Wed, 6 Jan 2016 22:03:23 +0000 (16:03 -0600)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 8 Feb 2016 03:10:12 +0000 (19:10 -0800)
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 the 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.

Signed-off-by: J. German Rivera <German.Rivera@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/fsl-mc/bus/mc-allocator.c
drivers/staging/fsl-mc/include/mc-private.h
drivers/staging/fsl-mc/include/mc.h

index 88d1857521a530c07e50c626b91a67f0dc86f315..c5fa628411ec68a320ff17e80bd75953dbf5a8c0 100644 (file)
@@ -15,6 +15,7 @@
 #include "../include/dpcon-cmd.h"
 #include "dpmcp-cmd.h"
 #include "dpmcp.h"
+#include <linux/msi.h>
 
 /**
  * fsl_mc_resource_pool_add_device - add allocatable device to a resource
@@ -160,6 +161,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,
@@ -465,6 +467,203 @@ void fsl_mc_object_free(struct fsl_mc_device *mc_adev)
 }
 EXPORT_SYMBOL_GPL(fsl_mc_object_free);
 
+/*
+ * Initialize the interrupt pool associated with a MC bus.
+ * It allocates a block of IRQs from the GIC-ITS
+ */
+int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
+                            unsigned int irq_count)
+{
+       unsigned int i;
+       struct msi_desc *msi_desc;
+       struct fsl_mc_device_irq *irq_resources;
+       struct fsl_mc_device_irq *mc_dev_irq;
+       int error;
+       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;
+
+       error = fsl_mc_msi_domain_alloc_irqs(&mc_bus_dev->dev, irq_count);
+       if (error < 0)
+               return error;
+
+       irq_resources = devm_kzalloc(&mc_bus_dev->dev,
+                                    sizeof(*irq_resources) * irq_count,
+                                    GFP_KERNEL);
+       if (!irq_resources) {
+               error = -ENOMEM;
+               goto cleanup_msi_irqs;
+       }
+
+       for (i = 0; i < irq_count; i++) {
+               mc_dev_irq = &irq_resources[i];
+
+               /*
+                * NOTE: This mc_dev_irq's MSI addr/value pair will be set
+                * by the fsl_mc_msi_write_msg() callback
+                */
+               mc_dev_irq->resource.type = res_pool->type;
+               mc_dev_irq->resource.data = mc_dev_irq;
+               mc_dev_irq->resource.parent_pool = res_pool;
+               INIT_LIST_HEAD(&mc_dev_irq->resource.node);
+               list_add_tail(&mc_dev_irq->resource.node, &res_pool->free_list);
+       }
+
+       for_each_msi_entry(msi_desc, &mc_bus_dev->dev) {
+               mc_dev_irq = &irq_resources[msi_desc->fsl_mc.msi_index];
+               mc_dev_irq->msi_desc = msi_desc;
+               mc_dev_irq->resource.id = msi_desc->irq;
+       }
+
+       res_pool->max_count = irq_count;
+       res_pool->free_count = irq_count;
+       mc_bus->irq_resources = irq_resources;
+       return 0;
+
+cleanup_msi_irqs:
+       fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
+       return error;
+}
+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_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(!mc_bus->irq_resources))
+               return;
+
+       if (WARN_ON(res_pool->max_count == 0))
+               return;
+
+       if (WARN_ON(res_pool->free_count != res_pool->max_count))
+               return;
+
+       INIT_LIST_HEAD(&res_pool->free_list);
+       res_pool->max_count = 0;
+       res_pool->free_count = 0;
+       mc_bus->irq_resources = NULL;
+       fsl_mc_msi_domain_free_irqs(&mc_bus_dev->dev);
+}
+EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool);
+
+/**
+ * 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++;
+
+               WARN_ON(irqs[i]->mc_dev);
+               irqs[i]->mc_dev = mc_dev;
+               irqs[i]->dev_irq_index = i;
+       }
+
+       mc_dev->irqs = irqs;
+       return 0;
+
+error_resource_alloc:
+       for (i = 0; i < res_allocated_count; i++) {
+               irqs[i]->mc_dev = NULL;
+               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;
+       struct fsl_mc_device_irq **irqs = mc_dev->irqs;
+
+       if (WARN_ON(!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++) {
+               WARN_ON(!irqs[i]->mc_dev);
+               irqs[i]->mc_dev = NULL;
+               fsl_mc_resource_free(&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
index 28c8d322b2a31f20b6bbd007f3884d8fd5f5301a..3babe92d8abc2c14f449a70eb72d2868c904278d 100644 (file)
 struct irq_domain;
 struct msi_domain_info;
 
+/**
+ * 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
+
+struct device_node;
+struct irq_domain;
+struct msi_domain_info;
+
 /**
  * struct fsl_mc - Private data of a "fsl,qoriq-mc" platform device
  * @root_mc_bus_dev: MC object device representing the root DPRC
@@ -137,4 +147,9 @@ int __init its_fsl_mc_msi_init(void);
 
 void its_fsl_mc_msi_cleanup(void);
 
+int 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_ */
index 1c1d6ae649967c074eb0f6bc7d10442d9c60a27d..ac7c1ce68c03a5ce3dc42b389e96786b84cbc039 100644 (file)
 #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
 
 struct fsl_mc_device;
 struct fsl_mc_io;
+struct fsl_mc_bus;
 
 /**
  * struct fsl_mc_driver - MC object device driver object
@@ -75,6 +77,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
@@ -141,6 +144,7 @@ struct fsl_mc_device_irq {
  * 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
@@ -172,6 +176,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;
 };
 
@@ -215,6 +220,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_ */