diff options
Diffstat (limited to 'drivers/irqchip/irq-armada-370-xp.c')
-rw-r--r-- | drivers/irqchip/irq-armada-370-xp.c | 202 |
1 files changed, 197 insertions, 5 deletions
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index bb328a366122..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 | ||
55 | static DEFINE_RAW_SPINLOCK(irq_controller_lock); | 62 | static DEFINE_RAW_SPINLOCK(irq_controller_lock); |
56 | 63 | ||
57 | static void __iomem *per_cpu_int_base; | 64 | static void __iomem *per_cpu_int_base; |
58 | static void __iomem *main_int_base; | 65 | static void __iomem *main_int_base; |
59 | static struct irq_domain *armada_370_xp_mpic_domain; | 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; | ||
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 | |||
105 | static 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 | |||
120 | static 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 | |||
130 | static 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 | |||
158 | static 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 | |||
166 | static 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 | |||
174 | static 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 | |||
184 | static const struct irq_domain_ops armada_370_xp_msi_irq_ops = { | ||
185 | .map = armada_370_xp_msi_map, | ||
186 | }; | ||
187 | |||
188 | static 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 | ||
234 | static 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 |
91 | static int armada_xp_set_affinity(struct irq_data *d, | 242 | static 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) { |
@@ -248,12 +426,25 @@ armada_370_xp_handle_irq(struct pt_regs *regs) | |||
248 | static int __init armada_370_xp_mpic_of_init(struct device_node *node, | 426 | static int __init armada_370_xp_mpic_of_init(struct device_node *node, |
249 | struct device_node *parent) | 427 | struct device_node *parent) |
250 | { | 428 | { |
429 | struct resource main_int_res, per_cpu_int_res; | ||
251 | u32 control; | 430 | u32 control; |
252 | 431 | ||
253 | main_int_base = of_iomap(node, 0); | 432 | BUG_ON(of_address_to_resource(node, 0, &main_int_res)); |
254 | per_cpu_int_base = of_iomap(node, 1); | 433 | BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res)); |
434 | |||
435 | BUG_ON(!request_mem_region(main_int_res.start, | ||
436 | resource_size(&main_int_res), | ||
437 | node->full_name)); | ||
438 | BUG_ON(!request_mem_region(per_cpu_int_res.start, | ||
439 | resource_size(&per_cpu_int_res), | ||
440 | node->full_name)); | ||
255 | 441 | ||
442 | main_int_base = ioremap(main_int_res.start, | ||
443 | resource_size(&main_int_res)); | ||
256 | BUG_ON(!main_int_base); | 444 | BUG_ON(!main_int_base); |
445 | |||
446 | per_cpu_int_base = ioremap(per_cpu_int_res.start, | ||
447 | resource_size(&per_cpu_int_res)); | ||
257 | BUG_ON(!per_cpu_int_base); | 448 | BUG_ON(!per_cpu_int_base); |
258 | 449 | ||
259 | control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); | 450 | control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); |
@@ -262,8 +453,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, | |||
262 | irq_domain_add_linear(node, (control >> 2) & 0x3ff, | 453 | irq_domain_add_linear(node, (control >> 2) & 0x3ff, |
263 | &armada_370_xp_mpic_irq_ops, NULL); | 454 | &armada_370_xp_mpic_irq_ops, NULL); |
264 | 455 | ||
265 | if (!armada_370_xp_mpic_domain) | 456 | BUG_ON(!armada_370_xp_mpic_domain); |
266 | panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n"); | ||
267 | 457 | ||
268 | irq_set_default_host(armada_370_xp_mpic_domain); | 458 | irq_set_default_host(armada_370_xp_mpic_domain); |
269 | 459 | ||
@@ -280,6 +470,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, | |||
280 | 470 | ||
281 | #endif | 471 | #endif |
282 | 472 | ||
473 | armada_370_xp_msi_init(node, main_int_res.start); | ||
474 | |||
283 | set_handle_irq(armada_370_xp_handle_irq); | 475 | set_handle_irq(armada_370_xp_handle_irq); |
284 | 476 | ||
285 | return 0; | 477 | return 0; |