2015-06-12 02:50:24

by majun (Euler7)

[permalink] [raw]
Subject: [PATCH v2 0/3] IRQ/Gic-V3:Support Mbigen interrupt controller

This patch set is applied to supprot the mbigen device.

Mbigen means message based interrupt generator.
It locate in ITS or out side of ITS.

In fact, mbigen is a kind of interrupt controller collects
the irq form non-PCI devices and generate msi interrupt.

Hisilicon designed mbigen to reduce the wired interrupt
number connected to GIC.

In these patches, the interrupts from non-PCI devices are
named as MBI to distiguish between legacy MSI interrupt from
PCI devices.

As a kind of MSI interrupt controller, the mbigen is used as a child
domain of ITS domain just like PCI devices.

So,in order to support the mbigen interrupt controller, the
its-gic-v3-its.c and other related files are changed.

Change in v2
---Compared to v1, mbigen domain is used as a child domain of ITS accroding
to Marc's advice.So, all changed files related with MSI in v1 are removed.
Now, only its domain related files are changed to support this new interrupt
controller.



Ma Jun (3):
Add mbigen driver to support mbigen interrupt controller
Change arm-gic-its to support the Mbigen interrupt
dt-binding:Documents the mbigen bindings

Documentation/devicetree/bindings/arm/mbigen.txt | 59 +++
drivers/irqchip/Kconfig | 7 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-gic-v3-its.c | 66 +++-
drivers/irqchip/irq-mbigen.c | 579 ++++++++++++++++++++++
include/linux/irq.h | 5 +
include/linux/mbi.h | 114 +++++
kernel/irq/chip.c | 40 ++-
8 files changed, 866 insertions(+), 5 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/mbigen.txt
create mode 100644 drivers/irqchip/irq-mbigen.c
create mode 100644 include/linux/mbi.h


2015-06-12 02:50:50

by majun (Euler7)

[permalink] [raw]
Subject: [PATCH v2 1/3] IRQ/Gic-V3: Add mbigen driver to support mbigen interrupt controller

This patch contains the mbigen device driver.

To support Mbigen device, irq-mbigen.c and mbi.h are added.

As a kind of MSI interrupt controller, the mbigen is used as a child
domain of ITS domain just like PCI devices does.

Change log:
---irq-mbigen.c: the driver of mbigen device.The mbigen irq domain is created here
as child of ITS domain.


Signed-off-by: Ma Jun <[email protected]>
---
drivers/irqchip/Kconfig | 7 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-mbigen.c | 579 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mbi.h | 114 +++++++++
4 files changed, 701 insertions(+), 0 deletions(-)
create mode 100644 drivers/irqchip/irq-mbigen.c
create mode 100644 include/linux/mbi.h

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 6de62a9..f6cc4a7 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -27,6 +27,13 @@ config ARM_GIC_V3_ITS
bool
select PCI_MSI_IRQ_DOMAIN

+config MBIGEN_IRQ_DOMAIN
+ bool "Support mbigen interrupt controller"
+ default y
+ help
+ Enable the mbigen interrupt controller used on
+ Hisillicon platform.
+
config ARM_NVIC
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index dda4927..23571c1 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o
obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o
obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o
obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o
+obj-$(CONFIG_MBIGEN_IRQ_DOMAIN) += irq-mbigen.o
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
obj-$(CONFIG_ARM_VIC) += irq-vic.o
obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o
diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c
new file mode 100644
index 0000000..6a0f532
--- /dev/null
+++ b/drivers/irqchip/irq-mbigen.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2014 Hisilicon Limited, All Rights Reserved.
+ * Author: Yun Wu <[email protected]>
+ * Author: Jun Ma <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/mbi.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include "irqchip.h"
+
+/* Register offsets */
+#define MG_IRQ_TYPE 0x0
+#define MG_IRQ_CLEAR 0x100
+#define MG_IRQ_STATUS 0x200
+#define MG_MSG_DATA 0x300
+
+/* The gap between normal and extended pin region */
+#define MG_EXT_OFST 0x10
+
+/* Max number of interrupts supported */
+#define MG_NR_IRQS 640
+
+/* Number of mbigen node supported in one mbigen chip */
+#define MG_NR 6
+
+/*
+ * refer to the mbigen node in a mbigen chip
+ * usually, a mbigen chip includes 4 mbigen nodes,
+ * but some some mbigen chip include 5 or 6 nodes.
+ * The node number depends on the hardware.
+ */
+struct mbigen_node {
+ raw_spinlock_t lock;
+ struct list_head entry;
+ struct mbigen_chip *chip;
+ unsigned int nid;
+ unsigned int irqs_used;
+ struct list_head nodes;
+};
+
+/* refer to the devices connected to mbigen node */
+struct mbigen_device {
+ struct list_head entry;
+ struct mbigen_node *mgn_node;
+ struct device_node *source;
+ unsigned int irq;
+ unsigned int nr_irqs;
+ unsigned int offset;
+};
+
+static LIST_HEAD(mbigen_chip_list);
+static DEFINE_SPINLOCK(mbigen_lock);
+
+int get_mbi_offset(int virq)
+{
+ struct irq_data *data;
+ struct mbigen_device *mgn_dev;
+ int offset = 0;
+
+ data = irq_get_irq_data(virq);
+ mgn_dev = data->chip_data;
+
+ offset = mgn_dev->offset;
+ return offset;
+}
+
+/*
+ * Get the irq number that the mbigen node can supported
+ * nid: the mbigen node id.
+ * nid = 0 ~ 3: 64 irq supported
+ * nid = 4: 128 irqs supported
+ * nid = 5: 256 irqs supported
+*/
+static unsigned int mbigen_get_nr_irqs(unsigned int nid)
+{
+ return 1 << (max_t(unsigned int, nid, 3) + 3);
+}
+
+static unsigned int mbigen_get_nid(unsigned long hwirq)
+{
+ unsigned int nid = min_t(unsigned long, hwirq >> 6, 3);
+
+ if (hwirq >> 8)
+ nid += min_t(unsigned long, hwirq >> 7, 3) - 1;
+
+ return nid;
+}
+
+static unsigned long mbigen_get_hwirq_base(unsigned int nid)
+{
+ return (nid < 5) ? (nid << 6) : ((nid + 1) << 6);
+}
+
+static void mbigen_free_dev(struct mbigen_device *mgn_dev)
+{
+ raw_spin_lock(&mgn_dev->mgn_node->lock);
+ list_del(&mgn_dev->entry);
+ raw_spin_unlock(&mgn_dev->mgn_node->lock);
+ kfree(mgn_dev);
+}
+
+static struct mbigen_device *mbigen_create_device(struct mbigen_node *mbigen,
+ struct device_node *node,
+ unsigned int virq,
+ unsigned int nr_irqs,
+ unsigned int offset)
+{
+ struct mbigen_device *mgn_dev;
+
+ mgn_dev = kzalloc(sizeof(*mgn_dev), GFP_KERNEL);
+ if (!mgn_dev)
+ return NULL;
+
+ INIT_LIST_HEAD(&mgn_dev->entry);
+ mgn_dev->mgn_node = mbigen;
+ mgn_dev->source = node;
+ mgn_dev->irq = virq;
+ mgn_dev->nr_irqs = nr_irqs;
+ mgn_dev->offset = offset;
+
+ raw_spin_lock(&mbigen->lock);
+ list_add(&mgn_dev->entry, &mbigen->nodes);
+ raw_spin_unlock(&mbigen->lock);
+ return mgn_dev;
+}
+
+static void mbigen_free(struct mbigen_node *mbigen)
+{
+ struct mbigen_device *mgn_dev, *tmp;
+
+ list_for_each_entry_safe(mgn_dev, tmp, &mbigen->nodes, entry)
+ mbigen_free_dev(mgn_dev);
+
+ kfree(mbigen);
+}
+
+static struct mbigen_node *get_mbigen_node(struct mbigen_chip *chip,
+ unsigned int nid)
+{
+ struct mbigen_node *tmp, *mbigen;
+ bool found = false;
+
+ if (nid >= MG_NR) {
+ pr_warn("MBIGEN: Device ID exceeds max number!\n");
+ return NULL;
+ }
+
+ list_for_each_entry(mbigen, &chip->nodes, entry) {
+ if (mbigen->nid == nid) {
+ found = true;
+ return mbigen;
+ }
+ }
+
+ /*
+ * Stop working if no memory available, even if we could
+ * get what we want.
+ */
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ return NULL;
+
+ raw_spin_lock(&chip->lock);
+
+ tmp->chip = chip;
+ tmp->nid = nid;
+ raw_spin_lock_init(&tmp->lock);
+ INIT_LIST_HEAD(&tmp->entry);
+ INIT_LIST_HEAD(&tmp->nodes);
+
+ list_add(&tmp->entry, &chip->nodes);
+ mbigen = tmp;
+ raw_spin_unlock(&chip->lock);
+
+ return mbigen;
+}
+
+
+void mbigen_write_msg(struct irq_data *irq_data, struct mbigen_msg *msg)
+{
+
+ struct irq_data *data;
+ struct mbigen_device *mgn_dev;
+ struct mbigen_node *mbigen;
+ void __iomem *addr;
+
+
+ data = irq_get_irq_data(irq_data->irq);
+
+ mgn_dev = data->chip_data;
+ mbigen = mgn_dev->mgn_node;
+
+ addr = mbigen->chip->base + MG_MSG_DATA + mbigen->nid * 4;
+ if (mbigen->nid > 3)
+ addr += MG_EXT_OFST;
+
+ writel_relaxed(msg->data & ~0xffff, addr);
+}
+
+static struct mbigen_chip *get_mbigen_chip(struct irq_domain *domain)
+{
+ struct mbigen_chip *chip = NULL;
+
+ spin_lock(&mbigen_lock);
+ list_for_each_entry(chip, &mbigen_chip_list, entry) {
+ if (chip->node == domain->of_node)
+ break;
+ }
+ spin_unlock(&mbigen_lock);
+
+ if (!chip)
+ return NULL;
+
+ return chip;
+}
+
+/*
+ * Interrupt controller operations
+ */
+
+static void mbigen_ack_irq(struct irq_data *d)
+{
+ struct mbigen_chip *chip;
+ u32 ofst = d->hwirq / 32 * 4;
+ u32 mask = 1 << (d->hwirq % 32);
+
+ chip = get_mbigen_chip(d->domain);
+ if (!chip)
+ pr_warn("NO Mbigen chip found\n");
+
+ writel_relaxed(mask, chip->base + MG_IRQ_CLEAR + ofst);
+}
+
+static int mbigen_set_type(struct irq_data *d, unsigned int type)
+{
+ struct mbigen_chip *chip;
+ u32 ofst = d->hwirq / 32 * 4;
+ u32 mask = 1 << (d->hwirq % 32);
+ u32 val;
+
+ if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
+ return -EINVAL;
+
+ chip = get_mbigen_chip(d->domain);
+ if (!chip)
+ pr_warn("NO Mbigen chip found\n");
+
+ raw_spin_lock(&chip->lock);
+ val = readl_relaxed(chip->base + MG_IRQ_TYPE + ofst);
+
+ if (type == IRQ_TYPE_LEVEL_HIGH)
+ val |= mask;
+ else if (type == IRQ_TYPE_EDGE_RISING)
+ val &= ~mask;
+
+ writel_relaxed(val, chip->base + MG_IRQ_TYPE + ofst);
+ raw_spin_unlock(&chip->lock);
+
+ return 0;
+}
+
+void mbigen_mask_irq(struct irq_data *data)
+{
+ irq_chip_mask_parent(data);
+}
+
+void mbigen_unmask_irq(struct irq_data *data)
+{
+ irq_chip_unmask_parent(data);
+}
+
+static struct irq_chip mbigen_irq_chip = {
+ .name = "MBIGEN",
+ .irq_mask = mbigen_mask_irq,
+ .irq_unmask = mbigen_unmask_irq,
+ .irq_ack = mbigen_ack_irq,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_set_type = mbigen_set_type,
+ .irq_write_mbigen_msg = mbigen_write_msg,
+};
+
+static inline void irq_chip_write_mbigen_msg(struct irq_data *data,
+ struct mbigen_msg *msg)
+{
+ data->chip->irq_write_mbigen_msg(data, msg);
+}
+
+/**
+ * mbi_alloc_desc() - allocate an MBI descriptor
+ * @dev: the device owned the MBI
+ * @ops: config operations of @dev
+ * @msg_id: identifier of the message group
+ * @lines: max number of interrupts supported by the message register
+ * @offset: hardware pin offset of @irq
+ * @data: message specific data
+ */
+struct mbi_desc *mbi_alloc_desc(struct device *dev, int msg_id,
+ unsigned int lines,
+ unsigned int offset, void *data)
+{
+ struct mbi_desc *desc;
+
+ desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return NULL;
+
+ desc->dev = dev;
+ desc->msg_id = msg_id;
+ desc->lines = lines;
+ desc->offset = offset;
+ desc->data = data;
+
+ return desc;
+}
+
+/*
+ * Interrupt domain operations
+ */
+
+static int mbigen_domain_xlate(struct irq_domain *d,
+ struct device_node *controller,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq,
+ unsigned int *out_type)
+{
+ if (d->of_node != controller)
+ return -EINVAL;
+
+ if (intsize < 2)
+ return -EINVAL;
+
+ *out_hwirq = intspec[0];
+ *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+
+ return 0;
+}
+
+static int mbigen_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ struct of_phandle_args *irq_data = arg;
+ struct mbigen_domain_info *info = domain->host_data;
+ struct mbigen_domain_ops *ops = info->ops;
+ struct mbigen_chip *chip;
+ irq_hw_number_t hwirq = irq_data->args[0];
+ unsigned int nid = mbigen_get_nid(hwirq);
+ struct mbigen_node *mbigen;
+ struct mbigen_device *mgn_dev;
+ struct mbi_desc *mbi;
+ struct mbi_alloc_info out_arg;
+ int ret = 0;
+ unsigned int offset = 0;
+
+ /* OF style allocation, one interrupt at a time */
+ WARN_ON(nr_irqs != 1);
+
+ chip = get_mbigen_chip(domain);
+ if (!chip)
+ pr_warn("NO Mbigen chip found\n");
+
+ mbigen = get_mbigen_node(chip, nid);
+ if (!mbigen)
+ return -ENODEV;
+
+ offset = hwirq - mbigen_get_hwirq_base(nid);
+
+ mgn_dev = mbigen_create_device(mbigen, irq_data->np, virq, nr_irqs, offset);
+ if (!mgn_dev)
+ return -ENOMEM;
+
+ mbi = mbi_alloc_desc(chip->dev, nid, mbigen_get_nr_irqs(nid),
+ offset, mgn_dev);
+ if (!mbi) {
+ mbigen_free_dev(mgn_dev);
+ return -ENOMEM;
+ }
+
+ ret = ops->mbigen_prepare(domain, mbi, hwirq, &out_arg);
+ if (ret)
+ return ret;
+
+ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &out_arg);
+ if (ret < 0) {
+ irq_domain_free_irqs_top(domain, virq, nr_irqs);
+ return ret;
+ }
+
+ irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
+ &mbigen_irq_chip, mgn_dev);
+
+ return ret;
+}
+
+static void mbigen_domain_free(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs)
+{
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+ struct mbigen_device *mgn_dev = irq_data_get_irq_chip_data(d);
+
+ WARN_ON(virq != mgn_dev->irq);
+ WARN_ON(nr_irqs != mgn_dev->nr_irqs);
+ mbigen_free_dev(mgn_dev);
+ irq_domain_free_irqs_parent(domain, virq, nr_irqs);
+}
+static void mbigen_domain_activate(struct irq_domain *domain,
+ struct irq_data *irq_data)
+{
+ struct mbigen_msg msg;
+
+ BUG_ON(irq_chip_compose_mbigen_msg(irq_data, &msg));
+ irq_chip_write_mbigen_msg(irq_data, &msg);
+}
+
+static void mbigen_domain_deactivate(struct irq_domain *domain,
+ struct irq_data *irq_data)
+{
+ struct mbigen_msg msg;
+
+ memset(&msg, 0, sizeof(msg));
+ irq_chip_write_mbigen_msg(irq_data, &msg);
+}
+
+static struct irq_domain_ops mbigen_domain_ops = {
+ .xlate = mbigen_domain_xlate,
+ .alloc = mbigen_domain_alloc,
+ .free = mbigen_domain_free,
+ .activate = mbigen_domain_activate,
+ .deactivate = mbigen_domain_deactivate,
+};
+
+static const struct of_device_id mbigen_of_match[] = {
+ { .compatible = "hisilicon,mbi-gen" },
+ { /* END */ }
+};
+MODULE_DEVICE_TABLE(of, mbigen_of_match);
+
+struct irq_domain *its_mbigen_create_irq_domain(struct device_node *node,
+ struct mbigen_domain_info *info,
+ struct irq_domain *parent_domain)
+{
+ struct device_node *np;
+ struct device_node *parent_node = NULL;
+ struct mbigen_chip *chip;
+
+ for_each_matching_node(np, mbigen_of_match) {
+
+ parent_node = of_parse_phandle(np, "msi-parent", 0);
+ if (parent_node == node)
+ break;
+ }
+
+ if (!parent_node) {
+ pr_warn("MBIGEN: %s-No mbigen detected\n", node->full_name);
+ return NULL;
+ }
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return NULL;
+
+ chip->node = np;
+
+ chip->base = of_iomap(np, 0);
+ if (!chip->base) {
+ pr_err("%s: Registers not found.\n", node->full_name);
+ goto free_chip;
+ }
+
+
+ info->chip = &mbigen_irq_chip;
+
+ chip->domain = irq_domain_add_hierarchy(parent_domain,
+ 0, MG_NR_IRQS, np,
+ &mbigen_domain_ops, info);
+
+ if (!chip->domain)
+ goto unmap_reg;
+
+ raw_spin_lock_init(&chip->lock);
+ INIT_LIST_HEAD(&chip->entry);
+ INIT_LIST_HEAD(&chip->nodes);
+ spin_lock(&mbigen_lock);
+ list_add(&chip->entry, &mbigen_chip_list);
+ spin_unlock(&mbigen_lock);
+ return chip->domain;
+
+unmap_reg:
+ iounmap(chip->base);
+free_chip:
+ kfree(chip);
+ return NULL;
+}
+
+/*
+ * Late initialization as a platform device
+ */
+
+static int mbigen_probe(struct platform_device *pdev)
+{
+ struct mbigen_chip *tmp, *chip = NULL;
+ struct device *dev = &pdev->dev;
+
+ spin_lock(&mbigen_lock);
+ list_for_each_entry(tmp, &mbigen_chip_list, entry) {
+ if (tmp->node == dev->of_node) {
+ chip = tmp;
+ break;
+ }
+ }
+ spin_unlock(&mbigen_lock);
+
+ if (!chip)
+ return -ENODEV;
+
+ chip->dev = dev;
+ platform_set_drvdata(pdev, chip);
+
+ return 0;
+}
+
+static int mbigen_remove(struct platform_device *pdev)
+{
+ struct mbigen_chip *chip = platform_get_drvdata(pdev);
+ struct mbigen_node *mbigen, *tmp;
+
+ spin_lock(&mbigen_lock);
+ list_del(&chip->entry);
+ spin_unlock(&mbigen_lock);
+
+ list_for_each_entry_safe(mbigen, tmp, &chip->nodes, entry) {
+ list_del(&mbigen->entry);
+ mbigen_free(mbigen);
+ }
+
+ irq_domain_remove(chip->domain);
+ iounmap(chip->base);
+ kfree(chip);
+
+ return 0;
+}
+
+static struct platform_driver mbigen_platform_driver = {
+ .driver = {
+ .name = "Hisilicon MBIGEN",
+ .owner = THIS_MODULE,
+ .of_match_table = mbigen_of_match,
+ },
+ .probe = mbigen_probe,
+ .remove = mbigen_remove,
+};
+
+module_platform_driver(mbigen_platform_driver);
+
+MODULE_AUTHOR("Jun Ma <[email protected]>");
+MODULE_AUTHOR("Yun Wu <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Hisilicon MBI Generator driver");
diff --git a/include/linux/mbi.h b/include/linux/mbi.h
new file mode 100644
index 0000000..e53e42b
--- /dev/null
+++ b/include/linux/mbi.h
@@ -0,0 +1,114 @@
+#ifndef _LINUX_MBI_H
+#define _LINUX_MBI_H
+
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/msi.h>
+
+struct mbi_ops;
+struct mbi_desc;
+
+/**
+ * struct mbigen_msg - MBI message descriptor
+ *
+ * @address_lo: lower 32bit value of MBI address register
+ * @address_hi: higher 32bit value of MBI address register
+ * @data: data value of MBI data register
+ */
+struct mbigen_msg {
+ u32 address_lo;
+ u32 address_hi;
+ u32 data;
+};
+
+/**
+ * struct mbi_desc - Message Based Interrupt (MBI) descriptor
+ *
+ * @dev: the device owned the MBI
+ * @msg_id: identifier of the message group
+ * @lines: max number of interrupts supported by the message register
+ * @irq: base linux interrupt number of the MBI
+ * @nvec: number of interrupts controlled by the MBI
+ * @offset: hardware pin offset of @irq
+ * @data: message specific data
+ */
+struct mbi_desc {
+ /* MBI-related device information */
+ struct device *dev;
+ int msg_id;
+ unsigned int lines;
+ /* Message properties */
+ unsigned int irq;
+ unsigned int nvec;
+ int offset;
+ void *data;
+};
+/**
+ * strutct mbigen_chip - mbigen chip structure descriptor
+ */
+struct mbigen_chip {
+ raw_spinlock_t lock;
+ struct list_head entry;
+ struct device *dev;
+ struct device_node *node;
+ struct device_node *of_node;
+ void __iomem *base;
+ struct irq_domain *domain;
+ struct list_head nodes;
+};
+
+/**
+ * struct mbi_alloc_info - Default structure for MBI interrupt allocation.
+ * @desc: Pointer to mbi descriptor
+ * @hwirq: Associated hw interrupt number in the domain
+ * @scratchpad: Storage for implementation specific scratch data
+ *
+ */
+struct mbi_alloc_info {
+ struct mbi_desc *desc;
+ irq_hw_number_t hwirq;
+ union {
+ unsigned long ul;
+ void *ptr;
+ } scratchpad[2];
+};
+
+/**
+ * struct mbi_ops - MBI functions of MBI-capable device
+ *
+ * @write_msg: write message registers for an MBI
+ * @mask_irq: mask an MBI interrupt
+ * @unmask_irq: unmask an MBI interrupt
+ */
+struct mbi_ops {
+ void (*write_msg)(struct mbi_desc *desc, struct mbigen_msg *msg);
+ void (*mask_irq)(struct mbi_desc *desc);
+ void (*unmask_irq)(struct mbi_desc *desc);
+};
+
+struct mbigen_domain_ops {
+ int (*mbigen_prepare)(struct irq_domain *domain,
+ struct mbi_desc *desc,
+ int hwirq,
+ struct mbi_alloc_info *arg);
+};
+
+struct mbigen_domain_info {
+ u32 flags;
+ struct mbigen_domain_ops *ops;
+ struct irq_chip *chip;
+};
+/* Functions to allocate an MBI descriptor */
+struct mbi_desc *mbi_alloc_desc(struct device *dev, int msg_id,
+ unsigned int lines,
+ unsigned int offset, void *data);
+
+/* Function to parse and map message interrupts */
+void mbigen_write_msg(struct irq_data *data, struct mbigen_msg *msg);
+int get_mbi_offset(int virq);
+struct irq_domain *its_mbigen_create_irq_domain(struct device_node *node,
+ struct mbigen_domain_info *info,
+ struct irq_domain *parent_domain);
+
+#endif /* _LINUX_MBI_H */
--
1.7.1

2015-06-12 02:50:35

by majun (Euler7)

[permalink] [raw]
Subject: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt

This patch is applied to support the mbigen interrupt.

As a kind of MSI interrupt controller, the mbigen is used as a child
domain of ITS domain just like PCI devices.
So the arm-gic-v3-its and related files are changed.

The chip.c is also changed to check irq_ach before it called.

Change log:
---arm-gic-v3-its.c: [1]Create the mbigen irq domain in its_init;
[2] add the irq_compose_mbigen_msg and irq_write_mbigen_msg
for mbigen using.[3]add its_mbigen_prepare function.
---irq.h: Add 'irq_compose_mbigen_msg' and 'irq_write_mbigen_msg' in
struct irq_chip for mbigen using.
---chip.c: Before the irq_ack callback, check the irq_ack first(chip.c)


Signed-off-by: Ma Jun <[email protected]>
---
drivers/irqchip/irq-gic-v3-its.c | 66 +++++++++++++++++++++++++++++++++++++-
include/linux/irq.h | 5 +++
kernel/irq/chip.c | 40 ++++++++++++++++++++--
3 files changed, 106 insertions(+), 5 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 9687f8a..2651f7c 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -22,6 +22,7 @@
#include <linux/log2.h>
#include <linux/mm.h>
#include <linux/msi.h>
+#include <linux/mbi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -61,6 +62,7 @@ struct its_node {
raw_spinlock_t lock;
struct list_head entry;
struct msi_controller msi_chip;
+ struct mbigen_chip mbi_chip;
struct irq_domain *domain;
void __iomem *base;
unsigned long phys_base;
@@ -599,7 +601,21 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)

msg->address_lo = addr & ((1UL << 32) - 1);
msg->address_hi = addr >> 32;
- msg->data = its_get_event_id(d);
+ msg->data = its_get_event_id(d);
+}
+
+static void its_irq_compose_mbigen_msg(struct irq_data *d,struct mbigen_msg *msg)
+{
+ struct its_device *its_dev = irq_data_get_irq_chip_data(d);
+ struct its_node *its;
+ u64 addr;
+
+ its = its_dev->its;
+ addr = its->phys_base + GITS_TRANSLATER;
+
+ msg->address_lo = addr & ((1UL << 32) - 1);
+ msg->address_hi = addr >> 32;
+ msg->data = its_get_event_id(d);
}

static struct irq_chip its_irq_chip = {
@@ -609,6 +625,7 @@ static struct irq_chip its_irq_chip = {
.irq_eoi = its_eoi_irq,
.irq_set_affinity = its_set_affinity,
.irq_compose_msi_msg = its_irq_compose_msi_msg,
+ .irq_compose_mbigen_msg = its_irq_compose_mbigen_msg,
};

static void its_mask_msi_irq(struct irq_data *d)
@@ -1245,6 +1262,38 @@ static struct msi_domain_info its_pci_msi_domain_info = {
.chip = &its_msi_irq_chip,
};

+static int its_mbigen_prepare(struct irq_domain *domain, struct mbi_desc *desc,
+ int hwirq, struct mbi_alloc_info *arg)
+{
+ struct its_node *its = domain->parent->host_data;
+ struct its_device *its_dev;
+ u32 dev_id;
+
+ dev_id = desc->msg_id;
+
+ its_dev = its_find_device(its, dev_id);
+ if (!its_dev) {
+ its_dev = its_create_device(its, dev_id, desc->lines);
+ if (!its_dev)
+ return -ENOMEM;
+ }
+
+ arg->scratchpad[0].ptr = its_dev;
+ arg->scratchpad[1].ptr = NULL;
+
+ arg->desc = desc;
+ arg->hwirq = hwirq;
+ return 0;
+}
+
+static struct mbigen_domain_ops its_mbigen_ops = {
+ .mbigen_prepare = its_mbigen_prepare,
+};
+
+static struct mbigen_domain_info its_mbigen_domain_info = {
+ .ops = &its_mbigen_ops,
+};
+
static int its_irq_gic_domain_alloc(struct irq_domain *domain,
unsigned int virq,
irq_hw_number_t hwirq)
@@ -1489,6 +1538,18 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
err = of_pci_msi_chip_add(&its->msi_chip);
if (err)
goto out_free_domains;
+
+ if (IS_ENABLED(CONFIG_MBIGEN_IRQ_DOMAIN)) {
+ its->mbi_chip.domain = its_mbigen_create_irq_domain(node,
+ &its_mbigen_domain_info,
+ its->domain);
+
+ if (!its->mbi_chip.domain) {
+ err = -ENOMEM;
+ pr_warn_once("ITS:no mbigen chip found\n");
+ goto out_free_mbigen;
+ }
+ }
}

spin_lock(&its_lock);
@@ -1497,6 +1558,9 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)

return 0;

+out_free_mbigen:
+ if (its->mbi_chip.domain)
+ irq_domain_remove(its->mbi_chip.domain);
out_free_domains:
if (its->msi_chip.domain)
irq_domain_remove(its->msi_chip.domain);
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 62c6901..874497a 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -30,6 +30,7 @@
struct seq_file;
struct module;
struct msi_msg;
+struct mbigen_msg;
enum irqchip_irq_state;

/*
@@ -363,6 +364,9 @@ struct irq_chip {
int (*irq_request_resources)(struct irq_data *data);
void (*irq_release_resources)(struct irq_data *data);

+ void (*irq_compose_mbigen_msg)(struct irq_data *data, struct mbigen_msg *msg);
+ void (*irq_write_mbigen_msg)(struct irq_data *data, struct mbigen_msg *msg);
+
void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);

@@ -457,6 +461,7 @@ extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc);
extern void handle_nested_irq(unsigned int irq);

extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
+extern int irq_chip_compose_mbigen_msg(struct irq_data *data, struct mbigen_msg *msg);
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
extern void irq_chip_ack_parent(struct irq_data *data);
extern int irq_chip_retrigger_hierarchy(struct irq_data *data);
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index eb9a4ea..83104fd 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -12,6 +12,7 @@

#include <linux/irq.h>
#include <linux/msi.h>
+#include <linux/mbi.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
@@ -882,7 +883,8 @@ void irq_cpu_offline(void)
void irq_chip_ack_parent(struct irq_data *data)
{
data = data->parent_data;
- data->chip->irq_ack(data);
+ if (data->chip->irq_ack)
+ data->chip->irq_ack(data);
}

/**
@@ -892,7 +894,8 @@ void irq_chip_ack_parent(struct irq_data *data)
void irq_chip_mask_parent(struct irq_data *data)
{
data = data->parent_data;
- data->chip->irq_mask(data);
+ if (data->chip->irq_mask)
+ data->chip->irq_mask(data);
}

/**
@@ -902,7 +905,8 @@ void irq_chip_mask_parent(struct irq_data *data)
void irq_chip_unmask_parent(struct irq_data *data)
{
data = data->parent_data;
- data->chip->irq_unmask(data);
+ if (data->chip->irq_unmask)
+ data->chip->irq_unmask(data);
}

/**
@@ -912,7 +916,8 @@ void irq_chip_unmask_parent(struct irq_data *data)
void irq_chip_eoi_parent(struct irq_data *data)
{
data = data->parent_data;
- data->chip->irq_eoi(data);
+ if (data->chip->irq_eoi)
+ data->chip->irq_eoi(data);
}

/**
@@ -991,3 +996,30 @@ int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)

return 0;
}
+
+/**
+ * irq_chip_compose_mbigen_msg - Componse mbigen message for a mbigen irq chip
+ * @data: Pointer to interrupt specific data
+ * @msg: Pointer to the mbigen message
+ *
+ * For hierarchical domains we find the first chip in the hierarchy
+ * which implements the irq_compose_mbigen_msg callback. For non
+ * hierarchical we use the top level chip.
+ */
+
+int irq_chip_compose_mbigen_msg(struct irq_data *data, struct mbigen_msg *msg)
+{
+ struct irq_data *pos = NULL;
+
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ for (; data; data = data->parent_data)
+#endif
+ if (data->chip && data->chip->irq_compose_mbigen_msg)
+ pos = data;
+ if (!pos)
+ return -ENOSYS;
+
+ pos->chip->irq_compose_mbigen_msg(pos, msg);
+
+ return 0;
+}
--
1.7.1

2015-06-12 02:50:28

by majun (Euler7)

[permalink] [raw]
Subject: [PATCH v2 3/3] dt-binding:Documents the mbigen bindings

Add the mbigen msi interrupt controller bindings document

Signed-off-by: Ma Jun <[email protected]>
---
Documentation/devicetree/bindings/arm/mbigen.txt | 59 ++++++++++++++++++++++
1 files changed, 59 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/mbigen.txt

diff --git a/Documentation/devicetree/bindings/arm/mbigen.txt b/Documentation/devicetree/bindings/arm/mbigen.txt
new file mode 100644
index 0000000..c7c261b
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mbigen.txt
@@ -0,0 +1,59 @@
+Hisilicon mbigen device tree bindings.
+=======================================
+
+Mbigen means: message based interrupt generator.
+
+MBI is kind of msi interrupt only used on Non-PCI devices.
+
+To reduce the wired interrupt number connected to GIC,
+Hisilicon designed mbigen to collect and generate interrupt.
+
+
+Non-pci devices can connect to mbigen and gnerate the inteerrupt
+by wirtting ITS register.
+
+The mbigen and devices connect to mbigen have the following properties:
+
+
+Mbigen required properties:
+-------------------------------------------
+-compatible: Should be "hisilicon,mbi-gen"
+-msi-parent: should specified the ITS mbigen connected
+-interrupt controller: Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode an
+ interrupt source. The value is 2 for now.
+
+ The 1st cell is the interrupt number(Hwirq).This value depends on
+ the Soc design.
+
+ The 2nd cell is the interrupt trigger type, encoded as follows:
+ 1 = edge triggered
+ 4 = level triggered
+
+- reg: Specifies the base physical address and size of the ITS
+ registers.
+
+Examples:
+
+ mbigen_pa: interrupt-controller@4c030000 {
+ compatible = "hisilicon,mbi-gen";
+ msi-parent = <&its_pa>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ reg = <0x4c030000 0x10000>;
+ };
+
+Device connect to mbigen required properties:
+----------------------------------------------------
+-interrupt-parent: Specifies the mbigen node which device connected.
+-interrupts:specifies the interrupt source.The first cell is hwirq num, the
+ second number is trigger type.
+
+Examples:
+ usb0: ehci@a1000000 {
+ compatible = "generic-ehci";
+ interrupt-parent = <&mbigen_pa>;
+ reg = <0xa1000000 0x10000>;
+ interrupts = <20 4>;
+ };
+
--
1.7.1

2015-06-12 10:48:47

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt

On Fri, 12 Jun 2015, Ma Jun wrote:

> This patch is applied to support the mbigen interrupt.
>
> As a kind of MSI interrupt controller, the mbigen is used as a child
> domain of ITS domain just like PCI devices.
> So the arm-gic-v3-its and related files are changed.
>
> The chip.c is also changed to check irq_ach before it called.

This patch wants to be split into several:

1) Changes to the core code

2) New functionality in the core code

2) Changes to gic-v3-its

And all patches require proper changelogs which explain WHY these
changes are necessary.

We can see which files are changed from the diffstat and the patch
ourself. So no point to mention this in the changelog.

But we cannot figure out from looking at the code WHY you think that
your approach to solve the problem is the right one.

> void irq_chip_ack_parent(struct irq_data *data)
> {
> data = data->parent_data;
> - data->chip->irq_ack(data);
> + if (data->chip->irq_ack)
> + data->chip->irq_ack(data);

Why is this required? Just because? Again, you fail to provide a
rationale for the changes to the irq_chip*parent() functions.

Why would you call irq_chip_ack_parent() if that parent does not
provide the required functionality in the first place?

> /*
> @@ -363,6 +364,9 @@ struct irq_chip {
> int (*irq_request_resources)(struct irq_data *data);
> void (*irq_release_resources)(struct irq_data *data);
>
> + void (*irq_compose_mbigen_msg)(struct irq_data *data, struct mbigen_msg *msg);
> + void (*irq_write_mbigen_msg)(struct irq_data *data, struct mbigen_msg *msg);
> +

What's so special about mbigen to justify extra callbacks which just
bloat the data structure for everyone. Why are the msi callbacks not
sufficient?

MBI is just another variant of MSI, right?

struct mbigen_msg {
u32 address_lo;
u32 address_hi;
u32 data;
};

struct mbigen_msg is just a mindless copy of struct msi_msg:

struct msi_msg {
u32 address_lo; /* low 32 bits of msi message address */
u32 address_hi; /* high 32 bits of msi message address */
u32 data; /* 16 bits of msi message data */
};

So what's the point of this?

> void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
> void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
>

> +
> +/**
> + * irq_chip_compose_mbigen_msg - Componse mbigen message for a mbigen irq chip
> + * @data: Pointer to interrupt specific data
> + * @msg: Pointer to the mbigen message
> + *
> + * For hierarchical domains we find the first chip in the hierarchy
> + * which implements the irq_compose_mbigen_msg callback. For non
> + * hierarchical we use the top level chip.
> + */
> +
> +int irq_chip_compose_mbigen_msg(struct irq_data *data, struct mbigen_msg *msg)
> +{
> + struct irq_data *pos = NULL;
> +
> +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
> + for (; data; data = data->parent_data)
> +#endif
> + if (data->chip && data->chip->irq_compose_mbigen_msg)
> + pos = data;
> + if (!pos)
> + return -ENOSYS;
> +
> + pos->chip->irq_compose_mbigen_msg(pos, msg);
> +
> + return 0;
> +}

Again, this is a completely useless copy of irq_chip_compose_msi_msg().
Why can't you just use the existing callbacks and use struct msi_msg
for your special chip?

And w/o looking at the mbigen code in detail, I bet it's nothing else
than MSI for non PCI devices and contains tons of redundant and copied
code, right?

Can you please provide a proper description of this mbigen chip and
explain WHY you think that it needs all this special hackery?

Thanks,

tglx

2015-06-15 07:07:52

by majun (Euler7)

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt



在 2015/6/12 18:48, Thomas Gleixner 写道:
> On Fri, 12 Jun 2015, Ma Jun wrote:
>
>> This patch is applied to support the mbigen interrupt.
>>
>> As a kind of MSI interrupt controller, the mbigen is used as a child
>> domain of ITS domain just like PCI devices.
>> So the arm-gic-v3-its and related files are changed.
>>
>> The chip.c is also changed to check irq_ach before it called.
>
> This patch wants to be split into several:
>
> 1) Changes to the core code
>
> 2) New functionality in the core code
>
> 2) Changes to gic-v3-its
>
> And all patches require proper changelogs which explain WHY these
> changes are necessary.
>
> We can see which files are changed from the diffstat and the patch
> ourself. So no point to mention this in the changelog.
>
> But we cannot figure out from looking at the code WHY you think that
> your approach to solve the problem is the right one.
>
>> void irq_chip_ack_parent(struct irq_data *data)
>> {
>> data = data->parent_data;
>> - data->chip->irq_ack(data);
>> + if (data->chip->irq_ack)
>> + data->chip->irq_ack(data);
>
> Why is this required? Just because? Again, you fail to provide a
> rationale for the changes to the irq_chip*parent() functions.
>
> Why would you call irq_chip_ack_parent() if that parent does not
> provide the required functionality in the first place?
>

Yes, this is not a necessary callback. I will remove this callback
from mbigen driver.

>> /*
>> @@ -363,6 +364,9 @@ struct irq_chip {
>> int (*irq_request_resources)(struct irq_data *data);
>> void (*irq_release_resources)(struct irq_data *data);
>>
>> + void (*irq_compose_mbigen_msg)(struct irq_data *data, struct mbigen_msg *msg);
>> + void (*irq_write_mbigen_msg)(struct irq_data *data, struct mbigen_msg *msg);
>> +
>
> What's so special about mbigen to justify extra callbacks which just
> bloat the data structure for everyone. Why are the msi callbacks not
> sufficient?
>
> MBI is just another variant of MSI, right?
>
yes,MBI is a kind of MSI which used for non-pci devices.

According to Marc's advice, the irq hierachy structure
in my patch likes below:
non-pci devices-->mbigen-->its-->gic
pci devices -->msi __/

Eventhough the function *irq_compose_mbigen_msg does
the same thing as *irq_chip_compose_msi_msg, I still
added this function. Because I don't want mix the code
used by msi(pci devices) with the code used by mbigen.

> struct mbigen_msg {
> u32 address_lo;
> u32 address_hi;
> u32 data;
> };
>
> struct mbigen_msg is just a mindless copy of struct msi_msg:
>
> struct msi_msg {
> u32 address_lo; /* low 32 bits of msi message address */
> u32 address_hi; /* high 32 bits of msi message address */
> u32 data; /* 16 bits of msi message data */
> };
>
> So what's the point of this?
>

Based on the same reason, I also added structure mbigen_msg for
mbigen using.

>> void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
>> void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
>>
>
>> +
>> +/**
>> + * irq_chip_compose_mbigen_msg - Componse mbigen message for a mbigen irq chip
>> + * @data: Pointer to interrupt specific data
>> + * @msg: Pointer to the mbigen message
>> + *
>> + * For hierarchical domains we find the first chip in the hierarchy
>> + * which implements the irq_compose_mbigen_msg callback. For non
>> + * hierarchical we use the top level chip.
>> + */
>> +
>> +int irq_chip_compose_mbigen_msg(struct irq_data *data, struct mbigen_msg *msg)
>> +{
>> + struct irq_data *pos = NULL;
>> +
>> +#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
>> + for (; data; data = data->parent_data)
>> +#endif
>> + if (data->chip && data->chip->irq_compose_mbigen_msg)
>> + pos = data;
>> + if (!pos)
>> + return -ENOSYS;
>> +
>> + pos->chip->irq_compose_mbigen_msg(pos, msg);
>> +
>> + return 0;
>> +}
>
> Again, this is a completely useless copy of irq_chip_compose_msi_msg().
> Why can't you just use the existing callbacks and use struct msi_msg
> for your special chip?
>
As mentioned before, to avoid using the code of msi, i added this
function.Because they are different domain.

If you don't mind, I can use the irq_chip_compose_msi_msg function in
mbigen driver instead of irq_chip_compose_mbigen_msg.

> And w/o looking at the mbigen code in detail, I bet it's nothing else
> than MSI for non PCI devices and contains tons of redundant and copied
> code, right?
>
> Can you please provide a proper description of this mbigen chip and
> explain WHY you think that it needs all this special hackery?
>
> Thanks,
>
> tglx
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>
> .
>

2015-06-18 23:52:21

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt

On Mon, 15 Jun 2015, majun (F) wrote:
> 在 2015/6/12 18:48, Thomas Gleixner 写道:
> > Can you please provide a proper description of this mbigen chip and
> > explain WHY you think that it needs all this special hackery?

You carefully avoided to provide a proper description of this mbigen
chip and how it needs to be integrated into the GIC/ITS whatever
scenario.

Thanks,

tglx

2015-06-23 09:08:09

by majun (Euler7)

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt



在 2015/6/19 7:52, Thomas Gleixner 写道:
> On Mon, 15 Jun 2015, majun (F) wrote:
>> 在 2015/6/12 18:48, Thomas Gleixner 写道:
>>> Can you please provide a proper description of this mbigen chip and
>>> explain WHY you think that it needs all this special hackery?
>
> You carefully avoided to provide a proper description of this mbigen
> chip and how it needs to be integrated into the GIC/ITS whatever
> scenario.
>
Mbigen means Message Based Interrupt Generator.
Its a kind of interrupt controller collects
the interrupts from external devices and generate msi interrupt.

Mbigen is applied to reduce the number of wire connected interrupts.

As the peripherals increasing, the interrupts lines needed is increasing
much, especially on the Arm64 server soc.

Therefore, the interrupt pin in gic is not enought for so many perpherals.

Mbigen is designed to fix this problem.

Mbigen chip locates in ITS or outside of ITS.

The working flow of Mbigen shows as below:

external devices ------> MBIGEN ------->ITS

The devices connect to Mbigen chip through wire connecting way.
Mbigen detects and collectes the interrupts from the these devices.

Then, Mbigen generats the MBI interrupts by writting the ITS
Translator register.






2015-06-23 09:29:22

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt

On Tue, 23 Jun 2015, majun (F) wrote:
> 在 2015/6/19 7:52, Thomas Gleixner 写道:
> > On Mon, 15 Jun 2015, majun (F) wrote:
> >> 在 2015/6/12 18:48, Thomas Gleixner 写道:
> >>> Can you please provide a proper description of this mbigen chip and
> >>> explain WHY you think that it needs all this special hackery?
> >
> > You carefully avoided to provide a proper description of this mbigen
> > chip and how it needs to be integrated into the GIC/ITS whatever
> > scenario.
> >
> Mbigen means Message Based Interrupt Generator.
> Its a kind of interrupt controller collects
> the interrupts from external devices and generate msi interrupt.
>
> Mbigen is applied to reduce the number of wire connected interrupts.
>
> As the peripherals increasing, the interrupts lines needed is increasing
> much, especially on the Arm64 server soc.
>
> Therefore, the interrupt pin in gic is not enought for so many perpherals.
>
> Mbigen is designed to fix this problem.
>
> Mbigen chip locates in ITS or outside of ITS.
>
> The working flow of Mbigen shows as below:
>
> external devices ------> MBIGEN ------->ITS
>
> The devices connect to Mbigen chip through wire connecting way.
> Mbigen detects and collectes the interrupts from the these devices.
>
> Then, Mbigen generats the MBI interrupts by writting the ITS
> Translator register.

So it's nothing else than a non PCI based MSI implementation which
means it can simply use the generic MSI infrastructure and implement a
interrupt domain/chip which implements the MBI specific parts and has
the ITS as its parent domain.

No hackery in ITS and no extra functionality in the core irq code. It
just can use the existing infrastructure. The only extra you need is a
proper way to retrieve the pointer to the ITS domain. Everything else
just falls in place.

Thanks,

tglx

2015-06-26 06:33:37

by majun (Euler7)

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt

Hi Thomas:

?? 2015/6/12 10:49, Ma Jun д??:

> +static int its_mbigen_prepare(struct irq_domain *domain, struct mbi_desc *desc,
> + int hwirq, struct mbi_alloc_info *arg)
> +{
> + struct its_node *its = domain->parent->host_data;
> + struct its_device *its_dev;
> + u32 dev_id;
> +
> + dev_id = desc->msg_id;
> +
> + its_dev = its_find_device(its, dev_id);
> + if (!its_dev) {
> + its_dev = its_create_device(its, dev_id, desc->lines);
> + if (!its_dev)
> + return -ENOMEM;
> + }
> +
> + arg->scratchpad[0].ptr = its_dev;
> + arg->scratchpad[1].ptr = NULL;
> +
> + arg->desc = desc;
> + arg->hwirq = hwirq;
> + return 0;
> +}
> +
> +static struct mbigen_domain_ops its_mbigen_ops = {
> + .mbigen_prepare = its_mbigen_prepare,
> +};
> +
> +static struct mbigen_domain_info its_mbigen_domain_info = {
> + .ops = &its_mbigen_ops,
> +};
> +

what's you opinion about the function 'its_mbigen_prepare' ?
put this function in irq-gic-v3-its.c or move to mbigen driver ?

If I move this function to mbigen driver, some its fuctions
(ex. its_find_device, its_create_device) and struct data (ex its_node)
would be used in mbigen driver.

Now, all these functions and data structure are defined as static.
to use them, I have to remove the 'static' definition and put them
in a head file ?? create a new head file).

I'm not sure which way is better .



2015-06-26 08:44:27

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt

On 26/06/15 07:31, majun (F) wrote:
> Hi Thomas:
>
> ?? 2015/6/12 10:49, Ma Jun д??:
>
>> +static int its_mbigen_prepare(struct irq_domain *domain, struct mbi_desc *desc,
>> + int hwirq, struct mbi_alloc_info *arg)
>> +{
>> + struct its_node *its = domain->parent->host_data;
>> + struct its_device *its_dev;
>> + u32 dev_id;
>> +
>> + dev_id = desc->msg_id;
>> +
>> + its_dev = its_find_device(its, dev_id);
>> + if (!its_dev) {
>> + its_dev = its_create_device(its, dev_id, desc->lines);
>> + if (!its_dev)
>> + return -ENOMEM;
>> + }
>> +
>> + arg->scratchpad[0].ptr = its_dev;
>> + arg->scratchpad[1].ptr = NULL;
>> +
>> + arg->desc = desc;
>> + arg->hwirq = hwirq;
>> + return 0;
>> +}
>> +
>> +static struct mbigen_domain_ops its_mbigen_ops = {
>> + .mbigen_prepare = its_mbigen_prepare,
>> +};
>> +
>> +static struct mbigen_domain_info its_mbigen_domain_info = {
>> + .ops = &its_mbigen_ops,
>> +};
>> +
>
> what's you opinion about the function 'its_mbigen_prepare' ?
> put this function in irq-gic-v3-its.c or move to mbigen driver ?
>
> If I move this function to mbigen driver, some its fuctions
> (ex. its_find_device, its_create_device) and struct data (ex its_node)
> would be used in mbigen driver.

The prepare hook is very PCI specific so far, but could easily be turned into
something that is not. How about splitting it in two:

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 1b7e155..9a68c77 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -1200,49 +1200,54 @@ static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
return 0;
}

-static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
- int nvec, msi_alloc_info_t *info)
+int its_msi_prepare(struct irq_domain *domain, u32 dev_id,
+ int nvec, msi_alloc_info_t *info)
{
- struct pci_dev *pdev;
struct its_node *its;
struct its_device *its_dev;
- struct its_pci_alias dev_alias;

- if (!dev_is_pci(dev))
- return -EINVAL;
-
- pdev = to_pci_dev(dev);
- dev_alias.pdev = pdev;
- dev_alias.count = nvec;
-
- pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
its = domain->parent->host_data;
-
- its_dev = its_find_device(its, dev_alias.dev_id);
+ its_dev = its_find_device(its, dev_id);
if (its_dev) {
/*
* We already have seen this ID, probably through
* another alias (PCI bridge of some sort). No need to
* create the device.
*/
- dev_dbg(dev, "Reusing ITT for devID %x\n", dev_alias.dev_id);
+ pr_debug("Reusing ITT for devID %x\n", dev_id);
goto out;
}

- its_dev = its_create_device(its, dev_alias.dev_id, dev_alias.count);
+ its_dev = its_create_device(its, dev_id, nvec);
if (!its_dev)
return -ENOMEM;

- dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n",
- dev_alias.count, ilog2(dev_alias.count));
+ pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec));
out:
info->scratchpad[0].ptr = its_dev;
- info->scratchpad[1].ptr = dev;
return 0;
}

+static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
+ int nvec, msi_alloc_info_t *info)
+{
+ struct pci_dev *pdev;
+ struct its_pci_alias dev_alias;
+
+ if (!dev_is_pci(dev))
+ return -EINVAL;
+
+ pdev = to_pci_dev(dev);
+ dev_alias.pdev = pdev;
+ dev_alias.count = nvec;
+
+ pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
+
+ return its_msi_prepare(domain, dev_alias.dev_id, dev_alias.count, info);
+}
+
static struct msi_domain_ops its_pci_msi_ops = {
- .msi_prepare = its_msi_prepare,
+ .msi_prepare = its_pci_msi_prepare,
};

static struct msi_domain_info its_pci_msi_domain_info = {
@@ -1287,8 +1292,9 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,

irq_domain_set_hwirq_and_chip(domain, virq + i,
hwirq, &its_irq_chip, its_dev);
- dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n",
- (int)(hwirq - its_dev->lpi_base), (int)hwirq, virq + i);
+ pr_debug("ID:%d pID:%d vID:%d\n",
+ (int)(hwirq - its_dev->lpi_base),
+ (int)hwirq, virq + i);
}

return 0;

You can then keep your MBI stuff in a separate file, and call into
its_msi_prepare.

> Now, all these functions and data structure are defined as static.
> to use them, I have to remove the 'static' definition and put them
> in a head file ?? create a new head file).

I don't want to see these functions and structure leaking out of the
ITS code unless we're absolutely forced to do so. The above code
shows you one possible way to solve the problem.

Thanks,

M.
--
Jazz is not dead. It just smells funny...

2015-06-26 08:45:16

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt

On 23/06/15 10:29, Thomas Gleixner wrote:
> On Tue, 23 Jun 2015, majun (F) wrote:
>> 在 2015/6/19 7:52, Thomas Gleixner 写道:
>>> On Mon, 15 Jun 2015, majun (F) wrote:
>>>> 在 2015/6/12 18:48, Thomas Gleixner 写道:
>>>>> Can you please provide a proper description of this mbigen chip and
>>>>> explain WHY you think that it needs all this special hackery?
>>>
>>> You carefully avoided to provide a proper description of this mbigen
>>> chip and how it needs to be integrated into the GIC/ITS whatever
>>> scenario.
>>>
>> Mbigen means Message Based Interrupt Generator.
>> Its a kind of interrupt controller collects
>> the interrupts from external devices and generate msi interrupt.
>>
>> Mbigen is applied to reduce the number of wire connected interrupts.
>>
>> As the peripherals increasing, the interrupts lines needed is increasing
>> much, especially on the Arm64 server soc.
>>
>> Therefore, the interrupt pin in gic is not enought for so many perpherals.
>>
>> Mbigen is designed to fix this problem.
>>
>> Mbigen chip locates in ITS or outside of ITS.
>>
>> The working flow of Mbigen shows as below:
>>
>> external devices ------> MBIGEN ------->ITS
>>
>> The devices connect to Mbigen chip through wire connecting way.
>> Mbigen detects and collectes the interrupts from the these devices.
>>
>> Then, Mbigen generats the MBI interrupts by writting the ITS
>> Translator register.
>
> So it's nothing else than a non PCI based MSI implementation which
> means it can simply use the generic MSI infrastructure and implement a
> interrupt domain/chip which implements the MBI specific parts and has
> the ITS as its parent domain.
>
> No hackery in ITS and no extra functionality in the core irq code. It
> just can use the existing infrastructure. The only extra you need is a
> proper way to retrieve the pointer to the ITS domain. Everything else
> just falls in place.

I may have a proposal for that. Stay tuned.

M.
--
Jazz is not dead. It just smells funny...

2015-06-26 10:28:33

by majun (Euler7)

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt


Hi Marc:

?? 2015/6/26 16:44, Marc Zyngier д??:
>
> You can then keep your MBI stuff in a separate file, and call into
> its_msi_prepare.
>
Thanks for your good suggestion!
I have two questions:

Question 1:

I found the ??its_msi_preapare ' defined without static.
So,I guess you mean I can call this fucntion directly
from mbigen driver, right?
or I need make the code likes below and leave these code in ITS?

static struct mbigen_domain_ops its_mbigen_ops = {
+ .mbigen_prepare = its_msi_prepare,
};

static struct mbigen_domain_info its_mbigen_domain_info = {
.ops = &its_mbigen_ops,
};

Question 2:

@@ -1489,6 +1538,18 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
err = of_pci_msi_chip_add(&its->msi_chip);
if (err)
goto out_free_domains;
+
+ if (IS_ENABLED(CONFIG_MBIGEN_IRQ_DOMAIN)) {
+ its->mbi_chip.domain = its_mbigen_create_irq_domain(node,
+ &its_mbigen_domain_info,
+ its->domain);
+
+ if (!its->mbi_chip.domain) {
+ err = -ENOMEM;
+ pr_warn_once("ITS:no mbigen chip found\n");
+ goto out_free_mbigen;
+ }
+ }
}

spin_lock(&its_lock);
@@ -1497,6 +1558,9 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)

return 0;

+out_free_mbigen:
+ if (its->mbi_chip.domain)
+ irq_domain_remove(its->mbi_chip.domain);
out_free_domains:
if (its->msi_chip.domain)
irq_domain_remove(its->msi_chip.domain);

What's you opinion about the code above
Leave it in ITS or create the mbi irq domain in mbigen driver?
If I have to create mbi irq domain in mbigen driver,
I need a pointer of its domain.

For this problem, I think i can solve it by using its_nodes??
in mbigen driver *if*
[1] add a member " struct device_node *node" in 'struct its_node'
[2] in 'its_probe' function , add
its->node = node;
[3] remove the static definition from 'static LIST_HEAD(its_nodes);'

How is you opinion?

Thansks again!



>> Now, all these functions and data structure are defined as static.
>> to use them, I have to remove the 'static' definition and put them
>> in a head file ?? create a new head file).
>
> I don't want to see these functions and structure leaking out of the
> ITS code unless we're absolutely forced to do so. The above code
> shows you one possible way to solve the problem.
>

2015-06-26 10:40:37

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt

On 26/06/15 11:28, majun (F) wrote:
>
> Hi Marc:
>
> ?? 2015/6/26 16:44, Marc Zyngier д??:
>>
>> You can then keep your MBI stuff in a separate file, and call into
>> its_msi_prepare.
>>
> Thanks for your good suggestion!
> I have two questions:
>
> Question 1:
>
> I found the ??its_msi_preapare ' defined without static.
> So,I guess you mean I can call this fucntion directly
> from mbigen driver, right?

Yes. You can use it as part of your own msi_prepare function.

> or I need make the code likes below and leave these code in ITS?
>
> static struct mbigen_domain_ops its_mbigen_ops = {
> + .mbigen_prepare = its_msi_prepare,
> };

This structure does not exist. Use the normal msi_domain_ops structure.

>
> static struct mbigen_domain_info its_mbigen_domain_info = {
> .ops = &its_mbigen_ops,
> };
>
> Question 2:
>
> @@ -1489,6 +1538,18 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
> err = of_pci_msi_chip_add(&its->msi_chip);
> if (err)
> goto out_free_domains;
> +
> + if (IS_ENABLED(CONFIG_MBIGEN_IRQ_DOMAIN)) {
> + its->mbi_chip.domain = its_mbigen_create_irq_domain(node,
> + &its_mbigen_domain_info,
> + its->domain);
> +
> + if (!its->mbi_chip.domain) {
> + err = -ENOMEM;
> + pr_warn_once("ITS:no mbigen chip found\n");
> + goto out_free_mbigen;
> + }
> + }
> }
>
> spin_lock(&its_lock);
> @@ -1497,6 +1558,9 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
>
> return 0;
>
> +out_free_mbigen:
> + if (its->mbi_chip.domain)
> + irq_domain_remove(its->mbi_chip.domain);
> out_free_domains:
> if (its->msi_chip.domain)
> irq_domain_remove(its->msi_chip.domain);
>
> What's you opinion about the code above
> Leave it in ITS or create the mbi irq domain in mbigen driver?
> If I have to create mbi irq domain in mbigen driver,
> I need a pointer of its domain.
>
> For this problem, I think i can solve it by using its_nodes??
> in mbigen driver *if*
> [1] add a member " struct device_node *node" in 'struct its_node'
> [2] in 'its_probe' function , add
> its->node = node;
> [3] remove the static definition from 'static LIST_HEAD(its_nodes);'
>
> How is you opinion?

My opinion is that we need to be able to lookup the domain from the core
code without any of these hacks, and this is what I'm working on at the
moment. There is no way external code will be allowed to mess with the
internals of the ITS.

For the time being, just expose the domain with a helper (you can match
it with the of_node). In the long run, you should be able to look it up
directly from the domain list.

Thanks,

M.
--
Jazz is not dead. It just smells funny...

2015-06-26 12:05:06

by majun (Euler7)

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt



?? 2015/6/26 18:40, Marc Zyngier д??:
>
> My opinion is that we need to be able to lookup the domain from the core
> code without any of these hacks, and this is what I'm working on at the
> moment. There is no way external code will be allowed to mess with the
> internals of the ITS.
>
> For the time being, just expose the domain with a helper (you can match
> it with the of_node).
Do you mean add a fucntion in ITS likes below:

struct irq_domain *get_its_domain(struct device_node *node)
{
struct its_node *its = NULL;

list_for_each_entry(its, &its_nodes, entry) {
if(its->msi_chip.of_node == node)
break;
}

return (its)?its->domain:NULL;
}

How about add a '.match ' member in its_domain_ops
just like:
.match = get_its_domain;

So, I can use the fucntion 'irq_find_host' in mbigne driver

>In the long run, you should be able to look it up
> directly from the domain list.
>
I think the domain list you said is 'irq_domain_list'
which defined in Irqdomain.c
static LIST_HEAD(irq_domain_list);

Thanks!

2015-06-26 13:14:41

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] IRQ/Gic-V3: Change arm-gic-its to support the Mbigen interrupt

On 26/06/15 13:04, majun (F) wrote:
>
>
> ?? 2015/6/26 18:40, Marc Zyngier д??:
>>
>> My opinion is that we need to be able to lookup the domain from the core
>> code without any of these hacks, and this is what I'm working on at the
>> moment. There is no way external code will be allowed to mess with the
>> internals of the ITS.
>>
>> For the time being, just expose the domain with a helper (you can match
>> it with the of_node).
> Do you mean add a fucntion in ITS likes below:
>
> struct irq_domain *get_its_domain(struct device_node *node)
> {
> struct its_node *its = NULL;
>
> list_for_each_entry(its, &its_nodes, entry) {
> if(its->msi_chip.of_node == node)
> break;
> }
>
> return (its)?its->domain:NULL;
> }

Yes.

>
> How about add a '.match ' member in its_domain_ops
> just like:
> .match = get_its_domain;
>
> So, I can use the fucntion 'irq_find_host' in mbigne driver

And that will only return the PCI/MSI domain, which is not of any help
to you.

At the moment, we register the PCI/MSI domain with the the of_node of
the ITS so that a PCI controller can match the its MSI controller, and
the ITS domain is completely anonymous (it doesn't have an of_node).

What I'm working on is a way to distinguish between several domains that
are identified by the same of_node, but cater for different bus types.
The current match function doesn't quite work for that case.

Thanks,

M.
--
Jazz is not dead. It just smells funny...