diff mbox

[2/2,v5] irqchip/Layerscape: Add SCFG MSI controller support

Message ID 1457321782-3245-2-git-send-email-Minghuan.Lian@nxp.com (mailing list archive)
State New, archived
Headers show

Commit Message

M.h. Lian March 7, 2016, 3:36 a.m. UTC
Some kind of NXP Layerscape SoC provides a MSI
implementation which uses two SCFG registers MSIIR and
MSIR to support 32 MSI interrupts for each PCIe controller.
The patch is to support it.

Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>
---
Change log
v5: 
1. drop nr_irqs from struct ls_scfg_msi
v4: 
1. do not register irq_enable irq_disable
2. shorten the chip name to "SCFG"
v3:
1. call of_node_to_fwnode()
v2:
1. rename ls1-msi to ls-scfg-msi
2. remove reg-names MSIIR MSIR 
3. remove calling set_irq_flags()

 drivers/irqchip/Kconfig           |   5 +
 drivers/irqchip/Makefile          |   1 +
 drivers/irqchip/irq-ls-scfg-msi.c | 240 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 246 insertions(+)
 create mode 100644 drivers/irqchip/irq-ls-scfg-msi.c

Comments

Marc Zyngier March 7, 2016, 9:50 a.m. UTC | #1
On Mon, 7 Mar 2016 11:36:22 +0800
Minghuan Lian <Minghuan.Lian@nxp.com> wrote:

> Some kind of NXP Layerscape SoC provides a MSI
> implementation which uses two SCFG registers MSIIR and
> MSIR to support 32 MSI interrupts for each PCIe controller.
> The patch is to support it.
> 
> Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>

Acked-by: Marc Zyngier <marc.zyngier@arm.com>

The DT binding still needs an Ack from the DT maintainers though (cc'd).

	M.
Alexander Stein March 23, 2016, 9:18 a.m. UTC | #2
On Monday 07 March 2016 11:36:22, Minghuan Lian wrote:
> Some kind of NXP Layerscape SoC provides a MSI
> implementation which uses two SCFG registers MSIIR and
> MSIR to support 32 MSI interrupts for each PCIe controller.
> The patch is to support it.
> 
> Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>

Tested-by: Alexander Stein <alexander.stein@systec-electronic.com>

Using an intel e1000e card which uses 3 MSIs. But the IRQ numbers are a bit strange though:
> grep eth3 /proc/interrupts
> 
>  63:         49          0       MSI 134742016 Edge      eth3-rx-0
>  64:          3          0       MSI 134742017 Edge      eth3-tx-0
>  65:          4          0       MSI 134742018 Edge      eth3

Best regards,
Alexander
M.h. Lian March 23, 2016, 10:50 a.m. UTC | #3
Hi Alexander,

Thanks for your test.

Number 134742016 is calculated by the following code

/**
 * pci_msi_domain_calc_hwirq - Generate a unique ID for an MSI source
 * @dev:	Pointer to the PCI device
 * @desc:	Pointer to the msi descriptor
 *
 * The ID number is only used within the irqdomain.
 */
irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev,
					  struct msi_desc *desc)
{
	return (irq_hw_number_t)desc->msi_attrib.entry_nr |
		PCI_DEVID(dev->bus->number, dev->devfn) << 11 |
		(pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 27;
}

And this value is assigned to the hwirq in the function:
static void pci_msi_domain_set_desc(msi_alloc_info_t *arg,
				    struct msi_desc *desc)
{
	arg->desc = desc;
	arg->hwirq = pci_msi_domain_calc_hwirq(msi_desc_to_pci_dev(desc),
					       desc);
}


Thanks,
Minghuan

> -----Original Message-----
> From: Alexander Stein [mailto:alexander.stein@systec-electronic.com]
> Sent: Wednesday, March 23, 2016 5:18 PM
> To: linux-kernel@vger.kernel.org
> Cc: Minghuan Lian <minghuan.lian@nxp.com>;
> linux-arm-kernel@lists.infradead.org; Marc Zyngier <marc.zyngier@arm.com>;
> Thomas Gleixner <tglx@linutronix.de>; Jason Cooper
> <jason@lakedaemon.net>; Roy Zang <roy.zang@nxp.com>; Mingkai Hu
> <mingkai.hu@nxp.com>; Stuart Yoder <stuart.yoder@nxp.com>; Yang-Leo Li
> <leoyang.li@nxp.com>
> Subject: Re: [PATCH 2/2 v5] irqchip/Layerscape: Add SCFG MSI controller
> support
> 
> On Monday 07 March 2016 11:36:22, Minghuan Lian wrote:
> > Some kind of NXP Layerscape SoC provides a MSI
> > implementation which uses two SCFG registers MSIIR and
> > MSIR to support 32 MSI interrupts for each PCIe controller.
> > The patch is to support it.
> >
> > Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>
> 
> Tested-by: Alexander Stein <alexander.stein@systec-electronic.com>
> 
> Using an intel e1000e card which uses 3 MSIs. But the IRQ numbers are a bit
> strange though:
> > grep eth3 /proc/interrupts
> >
> >  63:         49          0       MSI 134742016 Edge
> eth3-rx-0
> >  64:          3          0       MSI 134742017 Edge
> eth3-tx-0
> >  65:          4          0       MSI 134742018 Edge      eth3
> 
> Best regards,
> Alexander
Alexander Stein March 23, 2016, 11:19 a.m. UTC | #4
On Wednesday 23 March 2016 11:08:04, Marc Zyngier wrote:
> > Using an intel e1000e card which uses 3 MSIs. But the IRQ numbers are a bit strange though:
> >> grep eth3 /proc/interrupts
> >>
> >>  63:         49          0       MSI 134742016 Edge      eth3-rx-0
> >>  64:          3          0       MSI 134742017 Edge      eth3-tx-0
> >>  65:          4          0       MSI 134742018 Edge      eth3
> 
> This is a virtual interrupt number (despite being displayed as a hwirq),
> computed from the PCI requester ID and the MSI index. You shouldn't
> infer anything from it.

Why show it anyway then if you can't infer anything?

Best regards,
Alexander
Yang Li April 22, 2016, 5:33 a.m. UTC | #5
On Mon, Mar 7, 2016 at 3:50 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> On Mon, 7 Mar 2016 11:36:22 +0800
> Minghuan Lian <Minghuan.Lian@nxp.com> wrote:
>
>> Some kind of NXP Layerscape SoC provides a MSI
>> implementation which uses two SCFG registers MSIIR and
>> MSIR to support 32 MSI interrupts for each PCIe controller.
>> The patch is to support it.
>>
>> Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>
>
> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
>
> The DT binding still needs an Ack from the DT maintainers though (cc'd).

Marc,

Who will be responsible to pick this driver?  I see you are also one
of the maintainers for irqchip.  Can you pick up the driver?  The
binding has already gotten ACKed by the device tree maintainer.

Regards,
Leo
Marc Zyngier April 22, 2016, 7:43 a.m. UTC | #6
On 22/04/16 06:33, Leo Li wrote:
> On Mon, Mar 7, 2016 at 3:50 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> On Mon, 7 Mar 2016 11:36:22 +0800
>> Minghuan Lian <Minghuan.Lian@nxp.com> wrote:
>>
>>> Some kind of NXP Layerscape SoC provides a MSI
>>> implementation which uses two SCFG registers MSIIR and
>>> MSIR to support 32 MSI interrupts for each PCIe controller.
>>> The patch is to support it.
>>>
>>> Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>
>>
>> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
>>
>> The DT binding still needs an Ack from the DT maintainers though (cc'd).
> 
> Marc,
> 
> Who will be responsible to pick this driver?  I see you are also one
> of the maintainers for irqchip.  Can you pick up the driver?  The
> binding has already gotten ACKed by the device tree maintainer.

Can you point me to this Ack? I can't see any trace of it in my Inbox.

Thanks,

	M.
M.h. Lian April 22, 2016, 7:53 a.m. UTC | #7
Hi Marc,

Please see the link:
https://patchwork.kernel.org/patch/8649241/

Rob Herring has given the ACK.

I have submitted the v6 patch:  https://patchwork.kernel.org/patch/8649251/
Please apply the latest the patch after you review.

Thank you very much.


Regard,
Minghuan


> -----Original Message-----
> From: Marc Zyngier [mailto:marc.zyngier@arm.com]
> Sent: Friday, April 22, 2016 3:43 PM
> To: Leo Li <pku.leo@gmail.com>
> Cc: Minghuan Lian <minghuan.lian@nxp.com>;
> linux-arm-kernel@lists.infradead.org; lkml <linux-kernel@vger.kernel.org>;
> Thomas Gleixner <tglx@linutronix.de>; Jason Cooper
> <jason@lakedaemon.net>; Roy Zang <roy.zang@nxp.com>; Mingkai Hu
> <mingkai.hu@nxp.com>; Stuart Yoder <stuart.yoder@nxp.com>; Yang-Leo Li
> <leoyang.li@nxp.com>; Rob Herring <robh+dt@kernel.org>; Mark Rutland
> <Mark.Rutland@arm.com>
> Subject: Re: [PATCH 2/2 v5] irqchip/Layerscape: Add SCFG MSI controller
> support
> 
> On 22/04/16 06:33, Leo Li wrote:
> > On Mon, Mar 7, 2016 at 3:50 AM, Marc Zyngier <marc.zyngier@arm.com>
> wrote:
> >> On Mon, 7 Mar 2016 11:36:22 +0800
> >> Minghuan Lian <Minghuan.Lian@nxp.com> wrote:
> >>
> >>> Some kind of NXP Layerscape SoC provides a MSI
> >>> implementation which uses two SCFG registers MSIIR and
> >>> MSIR to support 32 MSI interrupts for each PCIe controller.
> >>> The patch is to support it.
> >>>
> >>> Signed-off-by: Minghuan Lian <Minghuan.Lian@nxp.com>
> >>
> >> Acked-by: Marc Zyngier <marc.zyngier@arm.com>
> >>
> >> The DT binding still needs an Ack from the DT maintainers though (cc'd).
> >
> > Marc,
> >
> > Who will be responsible to pick this driver?  I see you are also one
> > of the maintainers for irqchip.  Can you pick up the driver?  The
> > binding has already gotten ACKed by the device tree maintainer.
> 
> Can you point me to this Ack? I can't see any trace of it in my Inbox.
> 
> Thanks,
> 
> 	M.
> --
> Jazz is not dead. It just smells funny...
Marc Zyngier April 22, 2016, 7:58 a.m. UTC | #8
On 22/04/16 08:53, Minghuan Lian wrote:
> Hi Marc,
> 
> Please see the link:
> https://patchwork.kernel.org/patch/8649241/
> 
> Rob Herring has given the ACK.
> 
> I have submitted the v6 patch:  https://patchwork.kernel.org/patch/8649251/
> Please apply the latest the patch after you review.

Thanks. I'll queue that for 4.7 together with Rob's ack.

Thanks,

	M.
diff mbox

Patch

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index fb50911..0f2a3c3 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -218,3 +218,8 @@  config IRQ_MXS
 	def_bool y if MACH_ASM9260 || ARCH_MXS
 	select IRQ_DOMAIN
 	select STMP_DEVICE
+
+config LS_SCFG_MSI
+	def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE
+	depends on PCI && PCI_MSI
+	select PCI_MSI_IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 18caacb..37e12de 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -59,3 +59,4 @@  obj-$(CONFIG_ARCH_SA1100)		+= irq-sa11x0.o
 obj-$(CONFIG_INGENIC_IRQ)		+= irq-ingenic.o
 obj-$(CONFIG_IMX_GPCV2)			+= irq-imx-gpcv2.o
 obj-$(CONFIG_PIC32_EVIC)		+= irq-pic32-evic.o
+obj-$(CONFIG_LS_SCFG_MSI)		+= irq-ls-scfg-msi.o
diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c
new file mode 100644
index 0000000..0314bc8
--- /dev/null
+++ b/drivers/irqchip/irq-ls-scfg-msi.c
@@ -0,0 +1,240 @@ 
+/*
+ * NXP SCFG MSI(-X) support
+ *
+ * Copyright (C) 2016 NXP Semiconductor.
+ *
+ * Author: Minghuan Lian <Minghuan.Lian@nxp.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock.h>
+
+#define MSI_MAX_IRQS	32
+#define MSI_IBS_SHIFT	3
+#define MSIR		4
+
+struct ls_scfg_msi {
+	spinlock_t		lock;
+	struct platform_device	*pdev;
+	struct irq_domain	*parent;
+	struct irq_domain	*msi_domain;
+	void __iomem		*regs;
+	phys_addr_t		msiir_addr;
+	int			irq;
+	DECLARE_BITMAP(used, MSI_MAX_IRQS);
+};
+
+static struct irq_chip ls_scfg_msi_irq_chip = {
+	.name = "MSI",
+	.irq_mask	= pci_msi_mask_irq,
+	.irq_unmask	= pci_msi_unmask_irq,
+};
+
+static struct msi_domain_info ls_scfg_msi_domain_info = {
+	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS |
+		   MSI_FLAG_USE_DEF_CHIP_OPS |
+		   MSI_FLAG_PCI_MSIX),
+	.chip	= &ls_scfg_msi_irq_chip,
+};
+
+static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
+{
+	struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data);
+
+	msg->address_hi = upper_32_bits(msi_data->msiir_addr);
+	msg->address_lo = lower_32_bits(msi_data->msiir_addr);
+	msg->data = data->hwirq << MSI_IBS_SHIFT;
+}
+
+static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
+				    const struct cpumask *mask, bool force)
+{
+	return -EINVAL;
+}
+
+static struct irq_chip ls_scfg_msi_parent_chip = {
+	.name			= "SCFG",
+	.irq_compose_msi_msg	= ls_scfg_msi_compose_msg,
+	.irq_set_affinity	= ls_scfg_msi_set_affinity,
+};
+
+static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
+					unsigned int virq,
+					unsigned int nr_irqs,
+					void *args)
+{
+	struct ls_scfg_msi *msi_data = domain->host_data;
+	int pos, err = 0;
+
+	WARN_ON(nr_irqs != 1);
+
+	spin_lock(&msi_data->lock);
+	pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS);
+	if (pos < MSI_MAX_IRQS)
+		__set_bit(pos, msi_data->used);
+	else
+		err = -ENOSPC;
+	spin_unlock(&msi_data->lock);
+
+	if (err)
+		return err;
+
+	irq_domain_set_info(domain, virq, pos,
+			    &ls_scfg_msi_parent_chip, msi_data,
+			    handle_simple_irq, NULL, NULL);
+
+	return 0;
+}
+
+static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
+				   unsigned int virq, unsigned int nr_irqs)
+{
+	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+	struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d);
+	int pos;
+
+	pos = d->hwirq;
+	if (pos < 0 || pos >= MSI_MAX_IRQS) {
+		pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
+		return;
+	}
+
+	spin_lock(&msi_data->lock);
+	__clear_bit(pos, msi_data->used);
+	spin_unlock(&msi_data->lock);
+}
+
+static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
+	.alloc	= ls_scfg_msi_domain_irq_alloc,
+	.free	= ls_scfg_msi_domain_irq_free,
+};
+
+static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
+{
+	struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc);
+	unsigned long val;
+	int pos, virq;
+
+	chained_irq_enter(irq_desc_get_chip(desc), desc);
+
+	val = ioread32be(msi_data->regs + MSIR);
+	for_each_set_bit(pos, &val, MSI_MAX_IRQS) {
+		virq = irq_find_mapping(msi_data->parent, (31 - pos));
+		if (virq)
+			generic_handle_irq(virq);
+	}
+
+	chained_irq_exit(irq_desc_get_chip(desc), desc);
+}
+
+static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
+{
+	/* Initialize MSI domain parent */
+	msi_data->parent = irq_domain_add_linear(NULL,
+						 MSI_MAX_IRQS,
+						 &ls_scfg_msi_domain_ops,
+						 msi_data);
+	if (!msi_data->parent) {
+		dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n");
+		return -ENOMEM;
+	}
+
+	msi_data->msi_domain = pci_msi_create_irq_domain(
+				of_node_to_fwnode(msi_data->pdev->dev.of_node),
+				&ls_scfg_msi_domain_info,
+				msi_data->parent);
+	if (!msi_data->msi_domain) {
+		dev_err(&msi_data->pdev->dev, "failed to create MSI domain\n");
+		irq_domain_remove(msi_data->parent);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int ls_scfg_msi_probe(struct platform_device *pdev)
+{
+	struct ls_scfg_msi *msi_data;
+	struct resource *res;
+	int ret;
+
+	msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
+	if (!msi_data)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	msi_data->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(msi_data->regs)) {
+		dev_err(&pdev->dev, "failed to initialize 'regs'\n");
+		return PTR_ERR(msi_data->regs);
+	}
+	msi_data->msiir_addr = res->start;
+
+	msi_data->irq = platform_get_irq(pdev, 0);
+	if (msi_data->irq <= 0) {
+		dev_err(&pdev->dev, "failed to get MSI irq\n");
+		return -ENODEV;
+	}
+
+	msi_data->pdev = pdev;
+	spin_lock_init(&msi_data->lock);
+
+	ret = ls_scfg_msi_domains_init(msi_data);
+	if (ret)
+		return ret;
+
+	irq_set_chained_handler_and_data(msi_data->irq,
+					 ls_scfg_msi_irq_handler,
+					 msi_data);
+
+	platform_set_drvdata(pdev, msi_data);
+
+	return 0;
+}
+
+static int ls_scfg_msi_remove(struct platform_device *pdev)
+{
+	struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
+
+	irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL);
+
+	irq_domain_remove(msi_data->msi_domain);
+	irq_domain_remove(msi_data->parent);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id ls_scfg_msi_id[] = {
+	{ .compatible = "fsl,1s1021a-msi", },
+	{ .compatible = "fsl,1s1043a-msi", },
+	{},
+};
+
+static struct platform_driver ls_scfg_msi_driver = {
+	.driver = {
+		.name = "ls-scfg-msi",
+		.of_match_table = ls_scfg_msi_id,
+	},
+	.probe = ls_scfg_msi_probe,
+	.remove = ls_scfg_msi_remove,
+};
+
+module_platform_driver(ls_scfg_msi_driver);
+
+MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>");
+MODULE_DESCRIPTION("NXP Layerscape SCFG MSI controller driver");
+MODULE_LICENSE("GPL v2");