aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Petazzoni <thomas.petazzoni@free-electrons.com>2013-08-09 16:27:11 -0400
committerJason Cooper <jason@lakedaemon.net>2013-09-30 10:58:12 -0400
commit31f614edb726fcc4d5aa0f2895fbdec9b04a3ca4 (patch)
treeeedc5350cb3c435039d435fbfef39e32873ad508
parent627dfcc249e2eae07982272808ad560592e730e0 (diff)
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> Signed-off-by: Jason Cooper <jason@lakedaemon.net>
-rw-r--r--Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt3
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c182
2 files changed, 184 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
index 61df564c0d23..d74091a8a3bf 100644
--- 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 Interrupt Controller
4Required properties: 4Required properties:
5- compatible: Should be "marvell,mpic" 5- compatible: Should be "marvell,mpic"
6- interrupt-controller: Identifies the node as an interrupt controller. 6- interrupt-controller: Identifies the node as an interrupt controller.
7- msi-controller: Identifies the node as an PCI Message Signaled
8 Interrupt controller.
7- #interrupt-cells: The number of cells to define the interrupts. Should be 1. 9- #interrupt-cells: The number of cells to define the interrupts. Should be 1.
8 The cell is the IRQ number 10 The cell is the IRQ number
9 11
@@ -24,6 +26,7 @@ Example:
24 #address-cells = <1>; 26 #address-cells = <1>;
25 #size-cells = <1>; 27 #size-cells = <1>;
26 interrupt-controller; 28 interrupt-controller;
29 msi-controller;
27 reg = <0xd0020a00 0x1d0>, 30 reg = <0xd0020a00 0x1d0>,
28 <0xd0021070 0x58>; 31 <0xd0021070 0x58>;
29 }; 32 };
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 26adc741f764..433cc8568dec 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -21,7 +21,10 @@
21#include <linux/io.h> 21#include <linux/io.h>
22#include <linux/of_address.h> 22#include <linux/of_address.h>
23#include <linux/of_irq.h> 23#include <linux/of_irq.h>
24#include <linux/of_pci.h>
24#include <linux/irqdomain.h> 25#include <linux/irqdomain.h>
26#include <linux/slab.h>
27#include <linux/msi.h>
25#include <asm/mach/arch.h> 28#include <asm/mach/arch.h>
26#include <asm/exception.h> 29#include <asm/exception.h>
27#include <asm/smp_plat.h> 30#include <asm/smp_plat.h>
@@ -51,12 +54,22 @@
51#define IPI_DOORBELL_START (0) 54#define IPI_DOORBELL_START (0)
52#define IPI_DOORBELL_END (8) 55#define IPI_DOORBELL_END (8)
53#define IPI_DOORBELL_MASK 0xFF 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
54 61
55static DEFINE_RAW_SPINLOCK(irq_controller_lock); 62static DEFINE_RAW_SPINLOCK(irq_controller_lock);
56 63
57static void __iomem *per_cpu_int_base; 64static void __iomem *per_cpu_int_base;
58static void __iomem *main_int_base; 65static void __iomem *main_int_base;
59static struct irq_domain *armada_370_xp_mpic_domain; 66static struct irq_domain *armada_370_xp_mpic_domain;
67#ifdef CONFIG_PCI_MSI
68static struct irq_domain *armada_370_xp_msi_domain;
69static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
70static DEFINE_MUTEX(msi_used_lock);
71static phys_addr_t msi_doorbell_addr;
72#endif
60 73
61/* 74/*
62 * In SMP mode: 75 * In SMP mode:
@@ -87,6 +100,144 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
87 ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 100 ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
88} 101}
89 102
103#ifdef CONFIG_PCI_MSI
104
105static int armada_370_xp_alloc_msi(void)
106{
107 int hwirq;
108
109 mutex_lock(&msi_used_lock);
110 hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
111 if (hwirq >= PCI_MSI_DOORBELL_NR)
112 hwirq = -ENOSPC;
113 else
114 set_bit(hwirq, msi_used);
115 mutex_unlock(&msi_used_lock);
116
117 return hwirq;
118}
119
120static void armada_370_xp_free_msi(int hwirq)
121{
122 mutex_lock(&msi_used_lock);
123 if (!test_bit(hwirq, msi_used))
124 pr_err("trying to free unused MSI#%d\n", hwirq);
125 else
126 clear_bit(hwirq, msi_used);
127 mutex_unlock(&msi_used_lock);
128}
129
130static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
131 struct pci_dev *pdev,
132 struct msi_desc *desc)
133{
134 struct msi_msg msg;
135 irq_hw_number_t hwirq;
136 int virq;
137
138 hwirq = armada_370_xp_alloc_msi();
139 if (hwirq < 0)
140 return hwirq;
141
142 virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
143 if (!virq) {
144 armada_370_xp_free_msi(hwirq);
145 return -EINVAL;
146 }
147
148 irq_set_msi_desc(virq, desc);
149
150 msg.address_lo = msi_doorbell_addr;
151 msg.address_hi = 0;
152 msg.data = 0xf00 | (hwirq + 16);
153
154 write_msi_msg(virq, &msg);
155 return 0;
156}
157
158static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
159 unsigned int irq)
160{
161 struct irq_data *d = irq_get_irq_data(irq);
162 irq_dispose_mapping(irq);
163 armada_370_xp_free_msi(d->hwirq);
164}
165
166static struct irq_chip armada_370_xp_msi_irq_chip = {
167 .name = "armada_370_xp_msi_irq",
168 .irq_enable = unmask_msi_irq,
169 .irq_disable = mask_msi_irq,
170 .irq_mask = mask_msi_irq,
171 .irq_unmask = unmask_msi_irq,
172};
173
174static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
175 irq_hw_number_t hw)
176{
177 irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
178 handle_simple_irq);
179 set_irq_flags(virq, IRQF_VALID);
180
181 return 0;
182}
183
184static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
185 .map = armada_370_xp_msi_map,
186};
187
188static int armada_370_xp_msi_init(struct device_node *node,
189 phys_addr_t main_int_phys_base)
190{
191 struct msi_chip *msi_chip;
192 u32 reg;
193 int ret;
194
195 msi_doorbell_addr = main_int_phys_base +
196 ARMADA_370_XP_SW_TRIG_INT_OFFS;
197
198 msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL);
199 if (!msi_chip)
200 return -ENOMEM;
201
202 msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
203 msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
204 msi_chip->of_node = node;
205
206 armada_370_xp_msi_domain =
207 irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
208 &armada_370_xp_msi_irq_ops,
209 NULL);
210 if (!armada_370_xp_msi_domain) {
211 kfree(msi_chip);
212 return -ENOMEM;
213 }
214
215 ret = of_pci_msi_chip_add(msi_chip);
216 if (ret < 0) {
217 irq_domain_remove(armada_370_xp_msi_domain);
218 kfree(msi_chip);
219 return ret;
220 }
221
222 reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
223 | PCI_MSI_DOORBELL_MASK;
224
225 writel(reg, per_cpu_int_base +
226 ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
227
228 /* Unmask IPI interrupt */
229 writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
230
231 return 0;
232}
233#else
234static inline int armada_370_xp_msi_init(struct device_node *node,
235 phys_addr_t main_int_phys_base)
236{
237 return 0;
238}
239#endif
240
90#ifdef CONFIG_SMP 241#ifdef CONFIG_SMP
91static int armada_xp_set_affinity(struct irq_data *d, 242static int armada_xp_set_affinity(struct irq_data *d,
92 const struct cpumask *mask_val, bool force) 243 const struct cpumask *mask_val, bool force)
@@ -214,12 +365,39 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
214 if (irqnr > 1022) 365 if (irqnr > 1022)
215 break; 366 break;
216 367
217 if (irqnr > 0) { 368 if (irqnr > 1) {
218 irqnr = irq_find_mapping(armada_370_xp_mpic_domain, 369 irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
219 irqnr); 370 irqnr);
220 handle_IRQ(irqnr, regs); 371 handle_IRQ(irqnr, regs);
221 continue; 372 continue;
222 } 373 }
374
375#ifdef CONFIG_PCI_MSI
376 /* MSI handling */
377 if (irqnr == 1) {
378 u32 msimask, msinr;
379
380 msimask = readl_relaxed(per_cpu_int_base +
381 ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
382 & PCI_MSI_DOORBELL_MASK;
383
384 writel(~PCI_MSI_DOORBELL_MASK, per_cpu_int_base +
385 ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
386
387 for (msinr = PCI_MSI_DOORBELL_START;
388 msinr < PCI_MSI_DOORBELL_END; msinr++) {
389 int irq;
390
391 if (!(msimask & BIT(msinr)))
392 continue;
393
394 irq = irq_find_mapping(armada_370_xp_msi_domain,
395 msinr - 16);
396 handle_IRQ(irq, regs);
397 }
398 }
399#endif
400
223#ifdef CONFIG_SMP 401#ifdef CONFIG_SMP
224 /* IPI Handling */ 402 /* IPI Handling */
225 if (irqnr == 0) { 403 if (irqnr == 0) {
@@ -292,6 +470,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
292 470
293#endif 471#endif
294 472
473 armada_370_xp_msi_init(node, main_int_res.start);
474
295 set_handle_irq(armada_370_xp_handle_irq); 475 set_handle_irq(armada_370_xp_handle_irq);
296 476
297 return 0; 477 return 0;