From: Ma Jun <majun...@huawei.com>

After initializing mbigen device as interrupt controller,
this patch is used to probe mbigen chip and initial mbigen
device as a platform device so as to allocate msi-irqs 
within ITS-pMSI domain.

Signed-off-by: Ma Jun <majun...@huawei.com>
---
 drivers/irqchip/irq-mbigen.c |  203 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 203 insertions(+), 0 deletions(-)

diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
index e05a0ed..5951180 100644
--- a/drivers/irqchip/irq-mbigen.c
+++ b/drivers/irqchip/irq-mbigen.c
@@ -82,22 +82,45 @@
 #define MBIGEN_NODE_ADDR_BASE(nid)     ((nid) * MBIGEN_NODE_OFFSET)
 
 /*
+ * struct mbigen_chip - holds the information of mbigen
+ * chip.
+ * @lock: spin lock protecting mbigen device list
+ * @pdev: pointer to the platform device structure of mbigen chip.
+ * @local_mgn_dev_list: list of devices connected to this mbigen chip.
+ * @base: mapped address of this mbigen chip.
+ */
+struct mbigen_chip {
+       raw_spinlock_t          lock;
+       struct platform_device *pdev;
+       struct list_head        local_mgn_dev_list;
+       void __iomem            *base;
+};
+
+/*
  * struct mbigen_device--Holds the  information of devices connected
  * to mbigen chip
+ * @lock: spin lock protecting mbigen node list
  * @domain: irq domain of this mbigen device.
+ * @local_entry: node in mbigen chip's mbigen_device_list
  * @global_entry: node in a global mbigen device list.
  * @node: represents the mbigen device node defined in device tree.
  * @mgn_data: pointer to mbigen_irq_data
  * @nr_irqs: the total interrupt lines of this device
  * @base: mapped address of mbigen chip which this mbigen device connected.
+ * @chip: pointer to mbigen chip
+ * @pdev: pointer to platform device structure of this mbigen device.
 */
 struct mbigen_device {
+       raw_spinlock_t          lock;
        struct irq_domain       *domain;
+       struct list_head        local_entry;
        struct list_head        global_entry;
        struct device_node      *node;
        struct mbigen_irq_data  *mgn_data;
        unsigned int            nr_irqs;
        void __iomem            *base;
+       struct mbigen_chip      *chip;
+       struct platform_device *pdev;
 };
 
 /*
@@ -340,6 +363,186 @@ out_free_dev:
 }
 IRQCHIP_DECLARE(hisi_mbigen, "hisilicon,mbigen-intc-v2", mbigen_intc_of_init);
 
+static struct mbigen_device *find_mbigen_device(struct device_node *node)
+{
+       struct mbigen_device *dev = NULL, *tmp;
+
+       spin_lock(&mbigen_device_lock);
+
+       list_for_each_entry(tmp, &mbigen_device_list, global_entry) {
+               if (tmp->node == node) {
+                       dev = tmp;
+                       break;
+               }
+       }
+       spin_unlock(&mbigen_device_lock);
+
+       return dev;
+}
+
+static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+       struct mbigen_irq_data *mgn_irq_data = irq_get_handler_data(desc->irq);
+       struct mbigen_device *mgn_dev = mgn_irq_data->dev;
+       struct irq_priv_info *info = &mgn_irq_data->info;
+       u32 val;
+
+       val = readl_relaxed(info->reg_offset + mgn_dev->base);
+
+       val &= ~(IRQ_EVENT_ID_MASK << IRQ_EVENT_ID_SHIFT);
+       val |= (msg->data << IRQ_EVENT_ID_SHIFT);
+
+       writel_relaxed(val, info->reg_offset + mgn_dev->base);
+}
+
+static void mbigen_device_free(struct mbigen_device *mgn_dev)
+{
+       struct msi_desc *desc;
+
+       for_each_msi_entry(desc, &mgn_dev->pdev->dev) {
+               free_irq(desc->irq, mgn_dev);
+       }
+
+       platform_msi_domain_free_irqs(&mgn_dev->pdev->dev);
+
+       /* delete mgn_dev from global mbigen device list*/
+       spin_lock(&mbigen_device_lock);
+       list_del(&mgn_dev->global_entry);
+       spin_unlock(&mbigen_device_lock);
+
+       irq_domain_remove(mgn_dev->domain);
+       kfree(mgn_dev->mgn_data);
+       kfree(mgn_dev);
+}
+
+static void mbigen_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
+{
+       struct mbigen_irq_data *mgn_irq_data = irq_get_handler_data(irq);
+
+       if (unlikely(!mgn_irq_data->dev_irq))
+               handle_bad_irq(mgn_irq_data->dev_irq, desc);
+       else
+               generic_handle_irq(mgn_irq_data->dev_irq);
+}
+
+
+static void mbigen_set_irq_handler_data(struct msi_desc *desc,
+                               struct mbigen_device *mgn_dev)
+{
+       struct mbigen_irq_data *mgn_irq_data;
+
+       mgn_irq_data = &mgn_dev->mgn_data[desc->platform.msi_index];
+
+       mgn_irq_data->dev = mgn_dev;
+       mgn_irq_data->msi_irq = desc->irq;
+
+       irq_set_handler_data(desc->irq, mgn_irq_data);
+}
+
+/*
+ * Initial mbigen chip and mbigen device.
+ */
+static int mbigen_chip_probe(struct platform_device *pdev)
+{
+       struct mbigen_chip *mgn_chip;
+       struct device *dev = &pdev->dev;
+       struct device_node *root = dev->of_node, *child;
+       struct platform_device *mgn_pdev;
+       struct mbigen_device *mgn_dev;
+       struct msi_desc *desc;
+       void __iomem *base;
+       int ret;
+
+       mgn_chip = kzalloc(sizeof(*mgn_chip), GFP_KERNEL);
+       if (!mgn_chip)
+               return -ENOMEM;
+
+       mgn_chip->pdev = pdev;
+
+       base = of_iomap(root, 0);
+       mgn_chip->base = base;
+
+       of_platform_populate(root, NULL, NULL, &pdev->dev);
+
+       for_each_child_of_node(root, child) {
+               mgn_dev = find_mbigen_device(child);
+               mgn_pdev = of_find_device_by_node(child);
+
+               mgn_dev->base = base;
+               mgn_dev->pdev = mgn_pdev;
+               mgn_dev->chip = mgn_chip;
+
+               /* go to allocate msi-irqs from ITS-pMSI domain */
+               ret = platform_msi_domain_alloc_irqs(&mgn_pdev->dev,
+                               mgn_dev->nr_irqs, mbigen_write_msg);
+               if (ret) {
+                       pr_warn("mbigen-v2:failed to allocate msi irqs\n");
+                       continue;
+               }
+
+               for_each_msi_entry(desc, &mgn_pdev->dev) {
+                       mbigen_set_irq_handler_data(desc, mgn_dev);
+                       irq_set_chained_handler(desc->irq, 
mbigen_handle_cascade_irq);
+               }
+
+               /* add this mbigen device into local mbigen device list
+               *which belong to mbigen chip
+               */
+               INIT_LIST_HEAD(&mgn_dev->local_entry);
+               INIT_LIST_HEAD(&mgn_chip->local_mgn_dev_list);
+               raw_spin_lock_init(&mgn_chip->lock);
+
+               raw_spin_lock(&mgn_chip->lock);
+               list_add(&mgn_dev->local_entry, &mgn_chip->local_mgn_dev_list);
+               raw_spin_unlock(&mgn_chip->lock);
+       }
+
+       platform_set_drvdata(pdev, mgn_chip);
+
+       return 0;
+}
+
+static int mbigen_chip_remove(struct platform_device *pdev)
+{
+       struct mbigen_chip *mgn_chip = platform_get_drvdata(pdev);
+       struct mbigen_device *mgn_dev, *tmp;
+
+       raw_spin_lock(&mgn_chip->lock);
+       list_for_each_entry_safe(mgn_dev, tmp, &mgn_chip->local_mgn_dev_list,
+                                                       local_entry) {
+               list_del(&mgn_dev->local_entry);
+               mbigen_device_free(mgn_dev);
+       }
+       raw_spin_lock(&mgn_chip->lock);
+
+       iounmap(mgn_chip->base);
+       kfree(mgn_chip);
+
+       return 0;
+}
+
+static const struct of_device_id mbigen_of_match[] = {
+       { .compatible = "hisilicon,mbigen-v2" },
+       { /* END */ }
+};
+MODULE_DEVICE_TABLE(of, mbigen_of_match);
+
+static struct platform_driver mbigen_platform_driver = {
+       .driver = {
+               .name           = "Hisilicon MBIGEN-V2",
+               .owner          = THIS_MODULE,
+               .of_match_table = mbigen_of_match,
+       },
+       .probe                  = mbigen_chip_probe,
+       .remove                 = mbigen_chip_remove,
+};
+
+static int __init mbigen_chip_init(void)
+{
+       return platform_driver_register(&mbigen_platform_driver);
+}
+core_initcall(mbigen_chip_init);
+
 MODULE_AUTHOR("Jun Ma <majun...@huawei.com>");
 MODULE_AUTHOR("Yun Wu <wuyun...@huawei.com>");
 MODULE_LICENSE("GPL");
-- 
1.7.1


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to