summaryrefslogtreecommitdiff
path: root/target/linux/mvebu/patches-3.10/0075-irqchip-armada-370-xp-implement-MSI-support.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/mvebu/patches-3.10/0075-irqchip-armada-370-xp-implement-MSI-support.patch')
-rw-r--r--target/linux/mvebu/patches-3.10/0075-irqchip-armada-370-xp-implement-MSI-support.patch270
1 files changed, 270 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-3.10/0075-irqchip-armada-370-xp-implement-MSI-support.patch b/target/linux/mvebu/patches-3.10/0075-irqchip-armada-370-xp-implement-MSI-support.patch
new file mode 100644
index 0000000..999b493
--- /dev/null
+++ b/target/linux/mvebu/patches-3.10/0075-irqchip-armada-370-xp-implement-MSI-support.patch
@@ -0,0 +1,270 @@
+From eaa70d53f6b827f147d775a3de7ff3ef27d0fae6 Mon Sep 17 00:00:00 2001
+From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+Date: Thu, 6 Jun 2013 18:25:16 +0200
+Subject: [PATCH 075/203] irqchip: armada-370-xp: implement MSI support
+
+This commit introduces the support for the MSI interrupts in the
+armada-370-xp interrupt controller driver. It registers an MSI chip to
+the MSI chip registry, which will be used by the Marvell PCIe host
+controller driver.
+
+The MSI interrupts use the 16 high doorbells, and are therefore
+notified using IRQ1 of the main interrupt controller.
+
+Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+Acked-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
+---
+ .../devicetree/bindings/arm/armada-370-xp-mpic.txt | 3 +
+ drivers/irqchip/irq-armada-370-xp.c | 182 ++++++++++++++++++++-
+ 2 files changed, 184 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
++++ b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
+@@ -4,6 +4,8 @@ Marvell Armada 370 and Armada XP Interru
+ Required properties:
+ - compatible: Should be "marvell,mpic"
+ - interrupt-controller: Identifies the node as an interrupt controller.
++- msi-controller: Identifies the node as an PCI Message Signaled
++ Interrupt controller.
+ - #interrupt-cells: The number of cells to define the interrupts. Should be 1.
+ The cell is the IRQ number
+
+@@ -24,6 +26,7 @@ Example:
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-controller;
++ msi-controller;
+ reg = <0xd0020a00 0x1d0>,
+ <0xd0021070 0x58>;
+ };
+--- a/drivers/irqchip/irq-armada-370-xp.c
++++ b/drivers/irqchip/irq-armada-370-xp.c
+@@ -21,7 +21,10 @@
+ #include <linux/io.h>
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
++#include <linux/of_pci.h>
+ #include <linux/irqdomain.h>
++#include <linux/slab.h>
++#include <linux/msi.h>
+ #include <asm/mach/arch.h>
+ #include <asm/exception.h>
+ #include <asm/smp_plat.h>
+@@ -51,12 +54,22 @@
+ #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);
+
+ 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;
++static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
++static DEFINE_MUTEX(msi_used_lock);
++static phys_addr_t msi_doorbell_addr;
++#endif
+
+ /*
+ * In SMP mode:
+@@ -87,6 +100,144 @@ static void armada_370_xp_irq_unmask(str
+ ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+ }
+
++#ifdef CONFIG_PCI_MSI
++
++static int armada_370_xp_alloc_msi(void)
++{
++ int hwirq;
++
++ mutex_lock(&msi_used_lock);
++ hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
++ if (hwirq >= PCI_MSI_DOORBELL_NR)
++ hwirq = -ENOSPC;
++ else
++ set_bit(hwirq, msi_used);
++ mutex_unlock(&msi_used_lock);
++
++ return hwirq;
++}
++
++static void armada_370_xp_free_msi(int hwirq)
++{
++ mutex_lock(&msi_used_lock);
++ if (!test_bit(hwirq, msi_used))
++ pr_err("trying to free unused MSI#%d\n", hwirq);
++ else
++ clear_bit(hwirq, msi_used);
++ mutex_unlock(&msi_used_lock);
++}
++
++static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
++ struct pci_dev *pdev,
++ struct msi_desc *desc)
++{
++ struct msi_msg msg;
++ irq_hw_number_t hwirq;
++ int virq;
++
++ hwirq = armada_370_xp_alloc_msi();
++ if (hwirq < 0)
++ return hwirq;
++
++ virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
++ if (!virq) {
++ armada_370_xp_free_msi(hwirq);
++ return -EINVAL;
++ }
++
++ irq_set_msi_desc(virq, desc);
++
++ msg.address_lo = msi_doorbell_addr;
++ msg.address_hi = 0;
++ msg.data = 0xf00 | (hwirq + 16);
++
++ write_msi_msg(virq, &msg);
++ return 0;
++}
++
++static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
++ unsigned int irq)
++{
++ struct irq_data *d = irq_get_irq_data(irq);
++ irq_dispose_mapping(irq);
++ armada_370_xp_free_msi(d->hwirq);
++}
++
++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,
++};
++
++static int armada_370_xp_msi_init(struct device_node *node,
++ phys_addr_t main_int_phys_base)
++{
++ struct msi_chip *msi_chip;
++ u32 reg;
++ int ret;
++
++ msi_doorbell_addr = main_int_phys_base +
++ ARMADA_370_XP_SW_TRIG_INT_OFFS;
++
++ msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL);
++ if (!msi_chip)
++ return -ENOMEM;
++
++ msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
++ msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
++ msi_chip->of_node = node;
++
++ armada_370_xp_msi_domain =
++ irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
++ &armada_370_xp_msi_irq_ops,
++ NULL);
++ if (!armada_370_xp_msi_domain) {
++ kfree(msi_chip);
++ return -ENOMEM;
++ }
++
++ ret = of_pci_msi_chip_add(msi_chip);
++ if (ret < 0) {
++ irq_domain_remove(armada_370_xp_msi_domain);
++ kfree(msi_chip);
++ return ret;
++ }
++
++ 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);
++
++ return 0;
++}
++#else
++static inline int armada_370_xp_msi_init(struct device_node *node,
++ phys_addr_t main_int_phys_base)
++{
++ return 0;
++}
++#endif
++
+ #ifdef CONFIG_SMP
+ static int armada_xp_set_affinity(struct irq_data *d,
+ const struct cpumask *mask_val, bool force)
+@@ -214,12 +365,39 @@ armada_370_xp_handle_irq(struct pt_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) {
+@@ -292,6 +470,8 @@ static int __init armada_370_xp_mpic_of_
+
+ #endif
+
++ armada_370_xp_msi_init(node, main_int_res.start);
++
+ set_handle_irq(armada_370_xp_handle_irq);
+
+ return 0;