You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
9.4 KiB
Diff
327 lines
9.4 KiB
Diff
From 23b09c6b4162a8264b600f35d7048256a7afc0cd Mon Sep 17 00:00:00 2001
|
|
From: "J. German Rivera" <German.Rivera@freescale.com>
|
|
Date: Wed, 6 Jan 2016 16:03:23 -0600
|
|
Subject: [PATCH 147/226] staging: fsl-mc: Extended MC bus allocator to
|
|
include IRQs
|
|
|
|
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 | 199 +++++++++++++++++++++++++++
|
|
drivers/staging/fsl-mc/include/mc-private.h | 15 ++
|
|
drivers/staging/fsl-mc/include/mc.h | 9 ++
|
|
3 files changed, 223 insertions(+)
|
|
|
|
--- a/drivers/staging/fsl-mc/bus/mc-allocator.c
|
|
+++ b/drivers/staging/fsl-mc/bus/mc-allocator.c
|
|
@@ -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_typ
|
|
[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_de
|
|
}
|
|
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
|
|
--- a/drivers/staging/fsl-mc/include/mc-private.h
|
|
+++ b/drivers/staging/fsl-mc/include/mc-private.h
|
|
@@ -30,6 +30,16 @@ 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
|
|
* @num_translation_ranges: number of entries in addr_translation_ranges
|
|
@@ -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_ */
|
|
--- a/drivers/staging/fsl-mc/include/mc.h
|
|
+++ b/drivers/staging/fsl-mc/include/mc.h
|
|
@@ -14,12 +14,14 @@
|
|
#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(
|
|
|
|
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_ */
|