[RFCv1 07/11] irqchip: armada-370-xp: add MSI support to interrupt controller driver
Thomas Petazzoni
thomas.petazzoni at free-electrons.com
Wed Mar 27 03:52:22 EST 2013
This commit introduces the support for the MSI interrupts in the
armada-370-xp interrupt controller driver. It registers an IRQ domain
associated with the 'msi' node in the Device Tree, which the PCI
controller will refer to in a followup commit.
The MSI interrupts use the 16 high doorbells, and are therefore
notified using IRQ1 of the main interrupt controller.
The Device Tree binding documentation for the armada-370-xp driver is
also updated in the process.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
---
.../devicetree/bindings/arm/armada-370-xp-mpic.txt | 39 +++++++--
arch/arm/boot/dts/armada-370-xp.dtsi | 6 ++
arch/arm/mach-mvebu/Kconfig | 1 +
drivers/irqchip/irq-armada-370-xp.c | 91 +++++++++++++++++++-
4 files changed, 130 insertions(+), 7 deletions(-)
diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
index 61df564..2233d20 100644
--- a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
+++ b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
@@ -4,8 +4,6 @@ Marvell Armada 370 and Armada XP Interrupt Controller
Required properties:
- compatible: Should be "marvell,mpic"
- interrupt-controller: Identifies the node as an interrupt controller.
-- #interrupt-cells: The number of cells to define the interrupts. Should be 1.
- The cell is the IRQ number
- reg: Should contain PMIC registers location and length. First pair
for the main interrupt registers, second pair for the per-CPU
@@ -14,16 +12,45 @@ Required properties:
automatically map to the interrupt controller registers of the
current CPU)
+The node should have two sub-nodes, each describing one aspect of the
+interrupt controller:
+ * A first node named main-intc that describes the main interrupt
+ controller itself, receiving interrupts from all devices.
+
+ This sub-node should have the following properties:
+ - interrupt-controller: identifies the code as an interrupt
+ controller.
+ - #interrupt-cells: The number of cells to define the
+ interrupts. Should be 1. The cell is the IRQ number
+
+ * A second node named msi-intc that is used for Message Signaled
+ Interrupts of the PCIe bus.
+
+ This sub-node should have the following properties:
+ - interrupt-controller: identifies the code as an interrupt
+ controller.
+ - #interrupt-cells: The number of cells to define the
+ interrupts. Should be 1. The cell is the IRQ number
+ - marvell,doorbell: Gives the physical address at which PCIe
+ devices should write to signal an MSI interrupt.
Example:
- mpic: interrupt-controller at d0020000 {
+ interrupt-controller at d0020000 {
compatible = "marvell,mpic";
- #interrupt-cells = <1>;
- #address-cells = <1>;
- #size-cells = <1>;
interrupt-controller;
reg = <0xd0020a00 0x1d0>,
<0xd0021070 0x58>;
+
+ mpic: main-intc at d0020000 {
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ };
+
+ msi: msi-intc at d0020000 {
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ marvell,doorbell = <0xd0020a04>;
+ };
};
diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi
index de054ed..de6569cd 100644
--- a/arch/arm/boot/dts/armada-370-xp.dtsi
+++ b/arch/arm/boot/dts/armada-370-xp.dtsi
@@ -36,6 +36,12 @@
#interrupt-cells = <1>;
interrupt-controller;
};
+
+ msi: msi-intc at d0020000 {
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ marvell,doorbell = <0xd0020a04>;
+ };
};
coherency-fabric at d0020200 {
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig
index d353249..3254b77 100644
--- a/arch/arm/mach-mvebu/Kconfig
+++ b/arch/arm/mach-mvebu/Kconfig
@@ -16,6 +16,7 @@ config ARCH_MVEBU
select MVEBU_MBUS
select MIGHT_HAVE_PCI
select PCI_QUIRKS if PCI
+ select ARCH_SUPPORTS_MSI if PCI
if ARCH_MVEBU
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 4b2a6d7..3180797 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -22,6 +22,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/irqdomain.h>
+#include <linux/msi.h>
#include <asm/mach/arch.h>
#include <asm/exception.h>
#include <asm/smp_plat.h>
@@ -51,6 +52,10 @@
#define IPI_DOORBELL_START (0)
#define IPI_DOORBELL_END (8)
#define IPI_DOORBELL_MASK 0xFF
+#define PCI_MSI_DOORBELL_START (16)
+#define PCI_MSI_DOORBELL_NR (16)
+#define PCI_MSI_DOORBELL_END (32)
+#define PCI_MSI_DOORBELL_MASK 0xFFFF0000
static DEFINE_RAW_SPINLOCK(irq_controller_lock);
@@ -58,6 +63,10 @@ static void __iomem *per_cpu_int_base;
static void __iomem *main_int_base;
static struct irq_domain *armada_370_xp_mpic_domain;
+#ifdef CONFIG_PCI_MSI
+static struct irq_domain *armada_370_xp_msi_domain;
+#endif
+
/*
* In SMP mode:
* For shared global interrupts, mask/unmask global enable bit
@@ -167,6 +176,57 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
return 0;
}
+#ifdef CONFIG_PCI_MSI
+static struct irq_chip armada_370_xp_msi_irq_chip = {
+ .name = "armada_370_xp_msi_irq",
+ .irq_enable = unmask_msi_irq,
+ .irq_disable = mask_msi_irq,
+ .irq_mask = mask_msi_irq,
+ .irq_unmask = unmask_msi_irq,
+};
+
+static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
+ handle_simple_irq);
+ set_irq_flags(virq, IRQF_VALID);
+
+ return 0;
+}
+
+static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
+ .map = armada_370_xp_msi_map,
+};
+
+void armada_370_xp_msi_init(struct device_node *node)
+{
+ struct device_node *msi_node;
+ u32 reg;
+
+ msi_node = of_get_child_by_name(node, "msi-intc");
+ if (!msi_node)
+ return;
+
+ armada_370_xp_msi_domain =
+ irq_domain_add_linear(msi_node, PCI_MSI_DOORBELL_NR,
+ &armada_370_xp_msi_irq_ops, NULL);
+ if (!armada_370_xp_msi_domain)
+ panic("Unable to add Armada 370/XP MSI irq domain\n");
+
+ reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
+ | PCI_MSI_DOORBELL_MASK;
+
+ writel(reg, per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+
+ /* Unmask IPI interrupt */
+ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+}
+#else
+static void armada_370_xp_msi_init(struct device_node *node) { }
+#endif /* CONFIG_PCI_MSI */
+
#ifdef CONFIG_SMP
void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq)
{
@@ -220,12 +280,39 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
if (irqnr > 1022)
break;
- if (irqnr > 0) {
+ if (irqnr > 1) {
irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
irqnr);
handle_IRQ(irqnr, regs);
continue;
}
+
+#ifdef CONFIG_PCI_MSI
+ /* MSI handling */
+ if (irqnr == 1) {
+ u32 msimask, msinr;
+
+ msimask = readl_relaxed(per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
+ & PCI_MSI_DOORBELL_MASK;
+
+ writel(~PCI_MSI_DOORBELL_MASK, per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
+
+ for (msinr = PCI_MSI_DOORBELL_START;
+ msinr < PCI_MSI_DOORBELL_END; msinr++) {
+ int irq;
+
+ if (!(msimask & BIT(msinr)))
+ continue;
+
+ irq = irq_find_mapping(armada_370_xp_msi_domain,
+ msinr - 16);
+ handle_IRQ(irq, regs);
+ }
+ }
+#endif
+
#ifdef CONFIG_SMP
/* IPI Handling */
if (irqnr == 0) {
@@ -291,6 +378,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
#endif
+ armada_370_xp_msi_init(node);
+
set_handle_irq(armada_370_xp_handle_irq);
return 0;
--
1.7.9.5
More information about the devicetree-discuss
mailing list