diff mbox

[v2,4/5] irqchip:create irq domain for each mbigen device

Message ID 1455604648-20668-5-git-send-email-majun258@huawei.com (mailing list archive)
State New, archived
Headers show

Commit Message

majun (F) Feb. 16, 2016, 6:37 a.m. UTC
From: Ma Jun <majun258@huawei.com>

For peripheral devices which connect to mbigen,mbigen is a interrupt
controller. So, we create irq domain for each mbigen device and add
mbigen irq domain into irq hierarchy structure.

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

Comments

Marc Zyngier Feb. 16, 2016, 8:50 a.m. UTC | #1
On Tue, 16 Feb 2016 14:37:27 +0800
MaJun <majun258@huawei.com> wrote:

> From: Ma Jun <majun258@huawei.com>
> 
> For peripheral devices which connect to mbigen,mbigen is a interrupt
> controller. So, we create irq domain for each mbigen device and add
> mbigen irq domain into irq hierarchy structure.
> 
> Signed-off-by: Ma Jun <majun258@huawei.com>
> ---
>  drivers/irqchip/irq-mbigen-v1.c |  136 +++++++++++++++++++++++++++++++++++++++
>  1 files changed, 136 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-mbigen-v1.c b/drivers/irqchip/irq-mbigen-v1.c
> index 9445658..61e7ad0 100644
> --- a/drivers/irqchip/irq-mbigen-v1.c
> +++ b/drivers/irqchip/irq-mbigen-v1.c
> @@ -16,11 +16,30 @@
>   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
>   */
>  
> +#include <linux/interrupt.h>
> +#include <linux/irqchip.h>
>  #include <linux/module.h>
> +#include <linux/msi.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
>  #include <linux/of_platform.h>
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
>  
> +/* The maximum IRQ pin number of mbigen chip(start from 0) */
> +#define MAXIMUM_IRQ_PIN_NUM		640
> +
> +/**
> + * In mbigen vector register
> + * bit[31:16]:	device id
> + * bit[15:0]:	event id value
> + */
> +#define IRQ_EVENT_ID_MASK		0xffff
> +
> +/* offset of vector register in mbigen node */
> +#define REG_MBIGEN_VEC_OFFSET		0x300
> +#define REG_MBIGEN_EXT_VEC_OFFSET		0x320
> +
>  /**
>   * struct mbigen_device - holds the information of mbigen device.
>   *
> @@ -32,10 +51,112 @@ struct mbigen_device {
>  	void __iomem		*base;
>  };
>  
> +static int get_mbigen_nid(unsigned int offset)
> +{
> +	int nid = 0;
> +
> +	if (offset < 256)
> +		nid = offset / 64;
> +	else if (offset < 384)
> +		nid = 4;
> +	else if (offset < 640)
> +		nid = 5;
> +
> +	return nid;
> +}
> +
> +static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq)
> +{
> +	unsigned int nid;
> +
> +	nid = get_mbigen_nid(hwirq);
> +
> +	if (nid < 4)
> +		return (nid * 4) + REG_MBIGEN_VEC_OFFSET;
> +	else
> +		return (nid - 4) * 4 + REG_MBIGEN_EXT_VEC_OFFSET;
> +}
> +
> +static struct irq_chip mbigen_irq_chip = {
> +	.name =			"mbigen-v1",
> +};
> +
> +static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
> +{
> +	/* The address of doorbell is encoded in mbigen register by default
> +	 * So,we don't need to program the doorbell address at here
> +	 * Besides, the event ID is decided by the hardware pin number,
> +	 * we can't change it in software.So, we don't need to encode the
> +	 * event ID in mbigen register.
> +	 */

Really? What if tomorrow I decide to change the EventID allocation
policy in the ITS driver? Have your HW engineers really baked the
behaviour of the Linux driver into the device?

I'm puzzled.

	M.
majun (F) Feb. 17, 2016, 4:18 a.m. UTC | #2
? 2016/2/16 16:50, Marc Zyngier ??:
> On Tue, 16 Feb 2016 14:37:27 +0800
> MaJun <majun258@huawei.com> wrote:
> 
>> From: Ma Jun <majun258@huawei.com>
[...]
>> +	unsigned int nid;
>> +
>> +	nid = get_mbigen_nid(hwirq);
>> +
>> +	if (nid < 4)
>> +		return (nid * 4) + REG_MBIGEN_VEC_OFFSET;
>> +	else
>> +		return (nid - 4) * 4 + REG_MBIGEN_EXT_VEC_OFFSET;
>> +}
>> +
>> +static struct irq_chip mbigen_irq_chip = {
>> +	.name =			"mbigen-v1",
>> +};
>> +
>> +static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
>> +{
>> +	/* The address of doorbell is encoded in mbigen register by default
>> +	 * So,we don't need to program the doorbell address at here
>> +	 * Besides, the event ID is decided by the hardware pin number,
>> +	 * we can't change it in software.So, we don't need to encode the
>> +	 * event ID in mbigen register.
>> +	 */
> 
> Really? What if tomorrow I decide to change the EventID allocation
> policy in the ITS driver? Have your HW engineers really baked the
> behaviour of the Linux driver into the device?
> 

Yes.
If we really need to support this chip,is there
any possible solution for this problem?

Thanks!
MaJun

> I'm puzzled.
> 
> 	M.
>
Marc Zyngier Feb. 17, 2016, 7:47 a.m. UTC | #3
On Wed, 17 Feb 2016 12:18:52 +0800
"majun (F)" <majun258@huawei.com> wrote:

> 
> 
> ? 2016/2/16 16:50, Marc Zyngier ??:
> > On Tue, 16 Feb 2016 14:37:27 +0800
> > MaJun <majun258@huawei.com> wrote:
> > 
> >> From: Ma Jun <majun258@huawei.com>
> [...]
> >> +	unsigned int nid;
> >> +
> >> +	nid = get_mbigen_nid(hwirq);
> >> +
> >> +	if (nid < 4)
> >> +		return (nid * 4) + REG_MBIGEN_VEC_OFFSET;
> >> +	else
> >> +		return (nid - 4) * 4 + REG_MBIGEN_EXT_VEC_OFFSET;
> >> +}
> >> +
> >> +static struct irq_chip mbigen_irq_chip = {
> >> +	.name =			"mbigen-v1",
> >> +};
> >> +
> >> +static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
> >> +{
> >> +	/* The address of doorbell is encoded in mbigen register by default
> >> +	 * So,we don't need to program the doorbell address at here
> >> +	 * Besides, the event ID is decided by the hardware pin number,
> >> +	 * we can't change it in software.So, we don't need to encode the
> >> +	 * event ID in mbigen register.
> >> +	 */
> > 
> > Really? What if tomorrow I decide to change the EventID allocation
> > policy in the ITS driver? Have your HW engineers really baked the
> > behaviour of the Linux driver into the device?
> > 
> 
> Yes.
> If we really need to support this chip,is there
> any possible solution for this problem?

You would have to provide some sort of lookup table from the
device-tree, or find a way to pass this information down the ITS code.

The real question is: do we take this as it is and fix it once it
breaks? or do we mandate a proper solution before this has a remote
chance of getting in?

At the moment, I don't know, because the idea of hardcoded MSIs is so
wrong and so against the way the whole stack works that I just want to
say no to this and run away.

I need to think.

	M.
diff mbox

Patch

diff --git a/drivers/irqchip/irq-mbigen-v1.c b/drivers/irqchip/irq-mbigen-v1.c
index 9445658..61e7ad0 100644
--- a/drivers/irqchip/irq-mbigen-v1.c
+++ b/drivers/irqchip/irq-mbigen-v1.c
@@ -16,11 +16,30 @@ 
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
 #include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
+/* The maximum IRQ pin number of mbigen chip(start from 0) */
+#define MAXIMUM_IRQ_PIN_NUM		640
+
+/**
+ * In mbigen vector register
+ * bit[31:16]:	device id
+ * bit[15:0]:	event id value
+ */
+#define IRQ_EVENT_ID_MASK		0xffff
+
+/* offset of vector register in mbigen node */
+#define REG_MBIGEN_VEC_OFFSET		0x300
+#define REG_MBIGEN_EXT_VEC_OFFSET		0x320
+
 /**
  * struct mbigen_device - holds the information of mbigen device.
  *
@@ -32,10 +51,112 @@  struct mbigen_device {
 	void __iomem		*base;
 };
 
+static int get_mbigen_nid(unsigned int offset)
+{
+	int nid = 0;
+
+	if (offset < 256)
+		nid = offset / 64;
+	else if (offset < 384)
+		nid = 4;
+	else if (offset < 640)
+		nid = 5;
+
+	return nid;
+}
+
+static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq)
+{
+	unsigned int nid;
+
+	nid = get_mbigen_nid(hwirq);
+
+	if (nid < 4)
+		return (nid * 4) + REG_MBIGEN_VEC_OFFSET;
+	else
+		return (nid - 4) * 4 + REG_MBIGEN_EXT_VEC_OFFSET;
+}
+
+static struct irq_chip mbigen_irq_chip = {
+	.name =			"mbigen-v1",
+};
+
+static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+	/* The address of doorbell is encoded in mbigen register by default
+	 * So,we don't need to program the doorbell address at here
+	 * Besides, the event ID is decided by the hardware pin number,
+	 * we can't change it in software.So, we don't need to encode the
+	 * event ID in mbigen register.
+	 */
+}
+
+static int mbigen_domain_translate(struct irq_domain *d,
+				    struct irq_fwspec *fwspec,
+				    unsigned long *hwirq,
+				    unsigned int *type)
+{
+	if (is_of_node(fwspec->fwnode)) {
+		if (fwspec->param_count != 2)
+			return -EINVAL;
+
+		if (fwspec->param[0] > MAXIMUM_IRQ_PIN_NUM)
+			return -EINVAL;
+
+		*hwirq = fwspec->param[0];
+
+		/* If there is no valid irq type, just use the default type */
+		if ((fwspec->param[1] == IRQ_TYPE_EDGE_RISING) ||
+			(fwspec->param[1] == IRQ_TYPE_LEVEL_HIGH))
+			*type = fwspec->param[1];
+		else
+			return -EINVAL;
+
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int mbigen_irq_domain_alloc(struct irq_domain *domain,
+					unsigned int virq,
+					unsigned int nr_irqs,
+					void *args)
+{
+	struct irq_fwspec *fwspec = args;
+	irq_hw_number_t hwirq;
+	unsigned int type;
+	struct mbigen_device *mgn_chip;
+	int i, err;
+
+	err = mbigen_domain_translate(domain, fwspec, &hwirq, &type);
+	if (err)
+		return err;
+
+	err = platform_msi_domain_alloc(domain, virq, nr_irqs);
+	if (err)
+		return err;
+
+	mgn_chip = platform_msi_get_host_data(domain);
+
+	for (i = 0; i < nr_irqs; i++)
+		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+				      &mbigen_irq_chip, mgn_chip->base);
+
+	return 0;
+}
+
+static struct irq_domain_ops mbigen_domain_ops = {
+	.translate	= mbigen_domain_translate,
+	.alloc		= mbigen_irq_domain_alloc,
+	.free		= irq_domain_free_irqs_common,
+};
+
 static int mbigen_device_probe(struct platform_device *pdev)
 {
 	struct mbigen_device *mgn_chip;
 	struct resource *res;
+	struct irq_domain *domain;
+	u32 num_pins;
 
 	mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL);
 	if (!mgn_chip)
@@ -48,8 +169,23 @@  static int mbigen_device_probe(struct platform_device *pdev)
 	if (IS_ERR(mgn_chip->base))
 		return PTR_ERR(mgn_chip->base);
 
+	if (of_property_read_u32(pdev->dev.of_node, "num-pins", &num_pins) < 0) {
+		dev_err(&pdev->dev, "No num-pins property\n");
+		return -EINVAL;
+	}
+
+	domain = platform_msi_create_device_domain(&pdev->dev, num_pins,
+							mbigen_write_msg,
+							&mbigen_domain_ops,
+							mgn_chip);
+
+	if (!domain)
+		return -ENOMEM;
+
 	platform_set_drvdata(pdev, mgn_chip);
 
+	dev_info(&pdev->dev, "Allocated %d MSIs\n", num_pins);
+
 	return 0;
 }