1 From eaa70d53f6b827f147d775a3de7ff3ef27d0fae6 Mon Sep 17 00:00:00 2001
2 From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
3 Date: Thu, 6 Jun 2013 18:25:16 +0200
4 Subject: [PATCH 075/203] irqchip: armada-370-xp: implement MSI support
6 This commit introduces the support for the MSI interrupts in the
7 armada-370-xp interrupt controller driver. It registers an MSI chip to
8 the MSI chip registry, which will be used by the Marvell PCIe host
11 The MSI interrupts use the 16 high doorbells, and are therefore
12 notified using IRQ1 of the main interrupt controller.
14 Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
15 Acked-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
17 .../devicetree/bindings/arm/armada-370-xp-mpic.txt | 3 +
18 drivers/irqchip/irq-armada-370-xp.c | 182 ++++++++++++++++++++-
19 2 files changed, 184 insertions(+), 1 deletion(-)
21 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
22 +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
23 @@ -4,6 +4,8 @@ Marvell Armada 370 and Armada XP Interru
25 - compatible: Should be "marvell,mpic"
26 - interrupt-controller: Identifies the node as an interrupt controller.
27 +- msi-controller: Identifies the node as an PCI Message Signaled
28 + Interrupt controller.
29 - #interrupt-cells: The number of cells to define the interrupts. Should be 1.
30 The cell is the IRQ number
32 @@ -24,6 +26,7 @@ Example:
37 reg = <0xd0020a00 0x1d0>,
40 --- a/drivers/irqchip/irq-armada-370-xp.c
41 +++ b/drivers/irqchip/irq-armada-370-xp.c
44 #include <linux/of_address.h>
45 #include <linux/of_irq.h>
46 +#include <linux/of_pci.h>
47 #include <linux/irqdomain.h>
48 +#include <linux/slab.h>
49 +#include <linux/msi.h>
50 #include <asm/mach/arch.h>
51 #include <asm/exception.h>
52 #include <asm/smp_plat.h>
54 #define IPI_DOORBELL_START (0)
55 #define IPI_DOORBELL_END (8)
56 #define IPI_DOORBELL_MASK 0xFF
57 +#define PCI_MSI_DOORBELL_START (16)
58 +#define PCI_MSI_DOORBELL_NR (16)
59 +#define PCI_MSI_DOORBELL_END (32)
60 +#define PCI_MSI_DOORBELL_MASK 0xFFFF0000
62 static DEFINE_RAW_SPINLOCK(irq_controller_lock);
64 static void __iomem *per_cpu_int_base;
65 static void __iomem *main_int_base;
66 static struct irq_domain *armada_370_xp_mpic_domain;
67 +#ifdef CONFIG_PCI_MSI
68 +static struct irq_domain *armada_370_xp_msi_domain;
69 +static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
70 +static DEFINE_MUTEX(msi_used_lock);
71 +static phys_addr_t msi_doorbell_addr;
76 @@ -87,6 +100,144 @@ static void armada_370_xp_irq_unmask(str
77 ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
80 +#ifdef CONFIG_PCI_MSI
82 +static int armada_370_xp_alloc_msi(void)
86 + mutex_lock(&msi_used_lock);
87 + hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
88 + if (hwirq >= PCI_MSI_DOORBELL_NR)
91 + set_bit(hwirq, msi_used);
92 + mutex_unlock(&msi_used_lock);
97 +static void armada_370_xp_free_msi(int hwirq)
99 + mutex_lock(&msi_used_lock);
100 + if (!test_bit(hwirq, msi_used))
101 + pr_err("trying to free unused MSI#%d\n", hwirq);
103 + clear_bit(hwirq, msi_used);
104 + mutex_unlock(&msi_used_lock);
107 +static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
108 + struct pci_dev *pdev,
109 + struct msi_desc *desc)
111 + struct msi_msg msg;
112 + irq_hw_number_t hwirq;
115 + hwirq = armada_370_xp_alloc_msi();
119 + virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
121 + armada_370_xp_free_msi(hwirq);
125 + irq_set_msi_desc(virq, desc);
127 + msg.address_lo = msi_doorbell_addr;
128 + msg.address_hi = 0;
129 + msg.data = 0xf00 | (hwirq + 16);
131 + write_msi_msg(virq, &msg);
135 +static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
138 + struct irq_data *d = irq_get_irq_data(irq);
139 + irq_dispose_mapping(irq);
140 + armada_370_xp_free_msi(d->hwirq);
143 +static struct irq_chip armada_370_xp_msi_irq_chip = {
144 + .name = "armada_370_xp_msi_irq",
145 + .irq_enable = unmask_msi_irq,
146 + .irq_disable = mask_msi_irq,
147 + .irq_mask = mask_msi_irq,
148 + .irq_unmask = unmask_msi_irq,
151 +static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
152 + irq_hw_number_t hw)
154 + irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
155 + handle_simple_irq);
156 + set_irq_flags(virq, IRQF_VALID);
161 +static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
162 + .map = armada_370_xp_msi_map,
165 +static int armada_370_xp_msi_init(struct device_node *node,
166 + phys_addr_t main_int_phys_base)
168 + struct msi_chip *msi_chip;
172 + msi_doorbell_addr = main_int_phys_base +
173 + ARMADA_370_XP_SW_TRIG_INT_OFFS;
175 + msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL);
179 + msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
180 + msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
181 + msi_chip->of_node = node;
183 + armada_370_xp_msi_domain =
184 + irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
185 + &armada_370_xp_msi_irq_ops,
187 + if (!armada_370_xp_msi_domain) {
192 + ret = of_pci_msi_chip_add(msi_chip);
194 + irq_domain_remove(armada_370_xp_msi_domain);
199 + reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
200 + | PCI_MSI_DOORBELL_MASK;
202 + writel(reg, per_cpu_int_base +
203 + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
205 + /* Unmask IPI interrupt */
206 + writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
211 +static inline int armada_370_xp_msi_init(struct device_node *node,
212 + phys_addr_t main_int_phys_base)
219 static int armada_xp_set_affinity(struct irq_data *d,
220 const struct cpumask *mask_val, bool force)
221 @@ -214,12 +365,39 @@ armada_370_xp_handle_irq(struct pt_regs
227 irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
229 handle_IRQ(irqnr, regs);
233 +#ifdef CONFIG_PCI_MSI
236 + u32 msimask, msinr;
238 + msimask = readl_relaxed(per_cpu_int_base +
239 + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
240 + & PCI_MSI_DOORBELL_MASK;
242 + writel(~PCI_MSI_DOORBELL_MASK, per_cpu_int_base +
243 + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
245 + for (msinr = PCI_MSI_DOORBELL_START;
246 + msinr < PCI_MSI_DOORBELL_END; msinr++) {
249 + if (!(msimask & BIT(msinr)))
252 + irq = irq_find_mapping(armada_370_xp_msi_domain,
254 + handle_IRQ(irq, regs);
262 @@ -292,6 +470,8 @@ static int __init armada_370_xp_mpic_of_
266 + armada_370_xp_msi_init(node, main_int_res.start);
268 set_handle_irq(armada_370_xp_handle_irq);