This patch introduces APIs to allocate and free IMS interrupts.

Cc: Jacob Pan <jacob.jun....@linux.intel.com>
Signed-off-by: Sanjay Kumar <sanjay.k.ku...@intel.com>
Signed-off-by: Megha Dey <megha....@linux.intel.com>
---
 drivers/base/ims-msi.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/msi.h    |   2 +
 2 files changed, 218 insertions(+)

diff --git a/drivers/base/ims-msi.c b/drivers/base/ims-msi.c
index df28ee2..3e579c9 100644
--- a/drivers/base/ims-msi.c
+++ b/drivers/base/ims-msi.c
@@ -7,9 +7,12 @@
 
 #include <linux/device.h>
 #include <linux/export.h>
+#include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/msi.h>
 
+#define DEVIMS_ID_SHIFT        21
+
 struct dev_ims_priv_data {
        struct device           *dev;
        msi_alloc_info_t        arg;
@@ -17,6 +20,8 @@ struct dev_ims_priv_data {
        struct dev_ims_ops      *ims_ops;
 };
 
+static DEFINE_IDA(dev_ims_devid_ida);
+
 u32 __dev_ims_desc_mask_irq(struct msi_desc *desc, u32 flag)
 {
        u32 mask_bits = desc->dev_ims.masked;
@@ -126,3 +131,214 @@ void dev_ims_restore_irqs(struct device *dev)
                if (entry->tag == IRQ_MSI_TAG_IMS)
                        dev_ims_restore_irq(dev, entry->irq);
 }
+
+static void dev_ims_free_descs(struct device *dev)
+{
+       struct msi_desc *desc, *tmp;
+
+       for_each_msi_entry(desc, dev)
+               if (desc->irq && desc->tag == IRQ_MSI_TAG_IMS)
+                       BUG_ON(irq_has_action(desc->irq));
+
+       dev_ims_teardown_irqs(dev);
+
+       list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
+               if (desc->tag == IRQ_MSI_TAG_IMS) {
+                       list_del(&desc->list);
+                       free_msi_entry(desc);
+               }
+       }
+}
+
+static int dev_ims_setup_msi_irqs(struct device *dev, int nvec)
+{
+       struct irq_domain *domain;
+
+       domain = dev_get_msi_domain(dev);
+       if (domain && irq_domain_is_hierarchy(domain))
+               return msi_domain_alloc_irqs(domain, dev, nvec);
+
+       return arch_setup_ims_irqs(dev, nvec);
+}
+
+static struct dev_ims_priv_data *
+dev_ims_alloc_priv_data(struct device *dev, unsigned int nvec,
+                       struct dev_ims_ops *ops)
+{
+       struct dev_ims_priv_data *datap;
+       int ret;
+
+       /*
+        * Currently there is no limit to the number of IRQs a device can
+        * allocate.
+        */
+       if (!nvec)
+               return ERR_PTR(-EINVAL);
+
+       datap = kzalloc(sizeof(*datap), GFP_KERNEL);
+       if (!datap)
+               return ERR_PTR(-ENOMEM);
+
+       ret = ida_simple_get(&dev_ims_devid_ida,
+                               0, 1 << DEVIMS_ID_SHIFT, GFP_KERNEL);
+
+       if (ret < 0) {
+               kfree(datap);
+               return ERR_PTR(ret);
+       }
+
+       datap->devid = ret;
+       datap->ims_ops = ops;
+       datap->dev = dev;
+
+       return datap;
+}
+
+static int dev_ims_alloc_descs(struct device *dev,
+                              int nvec, struct dev_ims_priv_data *data,
+                              struct irq_affinity *affd)
+{
+       struct irq_affinity_desc *curmsk, *masks = NULL;
+       struct msi_desc *desc;
+       int i, base = 0;
+
+       if (!list_empty(dev_to_msi_list(dev))) {
+               desc = list_last_entry(dev_to_msi_list(dev),
+                                       struct msi_desc, list);
+               base = desc->dev_ims.ims_index + 1;
+       }
+
+       if (affd) {
+               masks = irq_create_affinity_masks(nvec, affd);
+               if (!masks)
+                       dev_err(dev, "Unable to allocate affinity masks, 
ignoring\n");
+       }
+
+       for (i = 0, curmsk = masks; i < nvec; i++) {
+               desc = alloc_msi_entry(dev, 1, NULL);
+               if (!desc)
+                       break;
+
+               desc->dev_ims.priv = data;
+               desc->tag = IRQ_MSI_TAG_IMS;
+               desc->dev_ims.ims_index = base + i;
+
+               list_add_tail(&desc->list, dev_to_msi_list(dev));
+
+               if (masks)
+                       curmsk++;
+       }
+
+       kfree(masks);
+
+       if (i != nvec) {
+               /* Clean up the mess */
+               dev_ims_free_descs(dev);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void dev_ims_free_priv_data(struct dev_ims_priv_data *data)
+{
+       ida_simple_remove(&dev_ims_devid_ida, data->devid);
+       kfree(data);
+}
+
+/**
+ * dev_ims_enable_irqs - Allocate IMS interrupts for @dev
+ * @dev:               The device for which to allocate interrupts
+ * @nvec:              The number of interrupts to allocate
+ * @ops:               IMS device operations
+ * @affd:              optional description of the affinity requirements
+ *
+ * Returns:
+ * Zero for success, or an error code in case of failure
+ */
+int dev_ims_enable_irqs(struct device *dev, unsigned int nvec,
+                       struct dev_ims_ops *ops,
+                       struct irq_affinity *affd)
+{
+       struct dev_ims_priv_data *priv_data;
+       int err;
+
+       priv_data = dev_ims_alloc_priv_data(dev, nvec, ops);
+       if (IS_ERR(priv_data))
+               return PTR_ERR(priv_data);
+
+       err = dev_ims_alloc_descs(dev, nvec, priv_data, affd);
+       if (err)
+               goto out_free_priv_data;
+
+       err = dev_ims_setup_msi_irqs(dev, nvec);
+       if (err)
+               goto out_free_desc;
+
+       return 0;
+
+out_free_desc:
+       dev_ims_free_descs(dev);
+out_free_priv_data:
+       dev_ims_free_priv_data(priv_data);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(dev_ims_enable_irqs);
+
+int __dev_ims_alloc_irqs(struct device *dev, int nvec,
+                        struct dev_ims_ops *ops,
+                        struct irq_affinity *affd)
+{
+       int nvec1;
+       int rc;
+
+       for (;;) {
+               if (affd) {
+                       nvec1 = irq_calc_affinity_vectors(nvec, nvec, affd);
+                       if (nvec < nvec1)
+                               return -ENOSPC;
+               }
+
+               rc = dev_ims_enable_irqs(dev, nvec, ops, affd);
+               if (rc == 0)
+                       return nvec;
+
+               if (rc < 0)
+                       return rc;
+               if (rc < nvec)
+                       return -ENOSPC;
+
+               nvec = rc;
+       }
+}
+
+/**
+ * dev_ims_alloc_irqs - Alloc IMS interrupts for @dev
+ * @dev:       The device for which to allocate interrupts
+ * @nvec:      The number of interrupts to allocate
+ * @ops:       IMS device operations
+ */
+int dev_ims_alloc_irqs(struct device *dev, int nvec, struct dev_ims_ops *ops)
+{
+       return __dev_ims_alloc_irqs(dev, nvec, ops, NULL);
+}
+EXPORT_SYMBOL_GPL(dev_ims_alloc_irqs);
+
+/**
+ * dev_ims_domain_free_irqs - Free IMS interrupts for @dev
+ * @dev:       The device for which to free interrupts
+ */
+void dev_ims_free_irqs(struct device *dev)
+{
+       struct msi_desc *desc;
+
+       for_each_msi_entry(desc, dev)
+               if (desc->tag == IRQ_MSI_TAG_IMS) {
+                       dev_ims_free_priv_data(desc->dev_ims.priv);
+                       break;
+       }
+
+       dev_ims_free_descs(dev);
+}
+EXPORT_SYMBOL_GPL(dev_ims_free_irqs);
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 37070bf..4543bbf 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -235,6 +235,8 @@ void dev_ims_mask_irq(struct irq_data *data);
 void dev_ims_write_msg(struct irq_data *data, struct msi_msg *msg);
 void dev_ims_teardown_irqs(struct device *dev);
 void dev_ims_restore_irqs(struct device *dev);
+int dev_ims_alloc_irqs(struct device *dev, int nvec, struct dev_ims_ops *ops);
+void dev_ims_free_irqs(struct device *dev);
 
 /*
  * The arch hooks to setup up msi irqs. Those functions are
-- 
2.7.4

Reply via email to