1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
3f30c2269SUwe Zeisberger * linux/arch/ia64/kernel/irq_ia64.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1998-2001 Hewlett-Packard Co
61da177e4SLinus Torvalds * Stephane Eranian <eranian@hpl.hp.com>
71da177e4SLinus Torvalds * David Mosberger-Tang <davidm@hpl.hp.com>
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * 6/10/99: Updated to bring in sync with x86 version to facilitate
101da177e4SLinus Torvalds * support for SMP and different interrupt controllers.
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds * 09/15/00 Goutham Rao <goutham.rao@intel.com> Implemented pci_irq_to_vector
131da177e4SLinus Torvalds * PCI to vector allocation routine.
141da177e4SLinus Torvalds * 04/14/2004 Ashok Raj <ashok.raj@intel.com>
151da177e4SLinus Torvalds * Added CPU Hotplug handling for IPF.
161da177e4SLinus Torvalds */
171da177e4SLinus Torvalds
181da177e4SLinus Torvalds #include <linux/module.h>
1965fddcfcSMike Rapoport #include <linux/pgtable.h>
201da177e4SLinus Torvalds
211da177e4SLinus Torvalds #include <linux/jiffies.h>
221da177e4SLinus Torvalds #include <linux/errno.h>
231da177e4SLinus Torvalds #include <linux/init.h>
241da177e4SLinus Torvalds #include <linux/interrupt.h>
251da177e4SLinus Torvalds #include <linux/ioport.h>
261da177e4SLinus Torvalds #include <linux/kernel_stat.h>
271da177e4SLinus Torvalds #include <linux/ptrace.h>
281da177e4SLinus Torvalds #include <linux/signal.h>
291da177e4SLinus Torvalds #include <linux/smp.h>
301da177e4SLinus Torvalds #include <linux/threads.h>
311da177e4SLinus Torvalds #include <linux/bitops.h>
32b6cf2583SEric W. Biederman #include <linux/irq.h>
337683a3f9SAkinobu Mita #include <linux/ratelimit.h>
344de0a759STony Luck #include <linux/acpi.h>
35184748ccSPeter Zijlstra #include <linux/sched.h>
361da177e4SLinus Torvalds
371da177e4SLinus Torvalds #include <asm/delay.h>
381da177e4SLinus Torvalds #include <asm/intrinsics.h>
391da177e4SLinus Torvalds #include <asm/io.h>
401da177e4SLinus Torvalds #include <asm/hw_irq.h>
413be44b9cSJack Steiner #include <asm/tlbflush.h>
421da177e4SLinus Torvalds
431da177e4SLinus Torvalds #define IRQ_DEBUG 0
441da177e4SLinus Torvalds
45e1b30a39SYasuaki Ishimatsu #define IRQ_VECTOR_UNASSIGNED (0)
46e1b30a39SYasuaki Ishimatsu
47e1b30a39SYasuaki Ishimatsu #define IRQ_UNUSED (0)
48e1b30a39SYasuaki Ishimatsu #define IRQ_USED (1)
49e1b30a39SYasuaki Ishimatsu #define IRQ_RSVD (2)
50e1b30a39SYasuaki Ishimatsu
5110083072SMark Maule int ia64_first_device_vector = IA64_DEF_FIRST_DEVICE_VECTOR;
5210083072SMark Maule int ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR;
5310083072SMark Maule
541da177e4SLinus Torvalds /* default base addr of IPI table */
551da177e4SLinus Torvalds void __iomem *ipi_base_addr = ((void __iomem *)
561da177e4SLinus Torvalds (__IA64_UNCACHED_OFFSET | IA64_IPI_DEFAULT_BASE_ADDR));
571da177e4SLinus Torvalds
584994be1bSYasuaki Ishimatsu static cpumask_t vector_allocation_domain(int cpu);
594994be1bSYasuaki Ishimatsu
601da177e4SLinus Torvalds /*
611da177e4SLinus Torvalds * Legacy IRQ to IA-64 vector translation table.
621da177e4SLinus Torvalds */
631da177e4SLinus Torvalds __u8 isa_irq_to_vector_map[16] = {
641da177e4SLinus Torvalds /* 8259 IRQ translation, first 16 entries */
651da177e4SLinus Torvalds 0x2f, 0x20, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
661da177e4SLinus Torvalds 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21
671da177e4SLinus Torvalds };
681da177e4SLinus Torvalds EXPORT_SYMBOL(isa_irq_to_vector_map);
691da177e4SLinus Torvalds
70e1b30a39SYasuaki Ishimatsu DEFINE_SPINLOCK(vector_lock);
71e1b30a39SYasuaki Ishimatsu
72e1b30a39SYasuaki Ishimatsu struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = {
734994be1bSYasuaki Ishimatsu [0 ... NR_IRQS - 1] = {
744994be1bSYasuaki Ishimatsu .vector = IRQ_VECTOR_UNASSIGNED,
754994be1bSYasuaki Ishimatsu .domain = CPU_MASK_NONE
764994be1bSYasuaki Ishimatsu }
77e1b30a39SYasuaki Ishimatsu };
78e1b30a39SYasuaki Ishimatsu
79e1b30a39SYasuaki Ishimatsu DEFINE_PER_CPU(int[IA64_NUM_VECTORS], vector_irq) = {
8017764d24SKenji Kaneshige [0 ... IA64_NUM_VECTORS - 1] = -1
81e1b30a39SYasuaki Ishimatsu };
82e1b30a39SYasuaki Ishimatsu
836ffbc823SKenji Kaneshige static cpumask_t vector_table[IA64_NUM_VECTORS] = {
846ffbc823SKenji Kaneshige [0 ... IA64_NUM_VECTORS - 1] = CPU_MASK_NONE
854994be1bSYasuaki Ishimatsu };
864994be1bSYasuaki Ishimatsu
87e1b30a39SYasuaki Ishimatsu static int irq_status[NR_IRQS] = {
88e1b30a39SYasuaki Ishimatsu [0 ... NR_IRQS -1] = IRQ_UNUSED
89e1b30a39SYasuaki Ishimatsu };
90e1b30a39SYasuaki Ishimatsu
find_unassigned_irq(void)91e1b30a39SYasuaki Ishimatsu static inline int find_unassigned_irq(void)
92e1b30a39SYasuaki Ishimatsu {
93e1b30a39SYasuaki Ishimatsu int irq;
94e1b30a39SYasuaki Ishimatsu
95e1b30a39SYasuaki Ishimatsu for (irq = IA64_FIRST_DEVICE_VECTOR; irq < NR_IRQS; irq++)
96e1b30a39SYasuaki Ishimatsu if (irq_status[irq] == IRQ_UNUSED)
97e1b30a39SYasuaki Ishimatsu return irq;
98e1b30a39SYasuaki Ishimatsu return -ENOSPC;
99e1b30a39SYasuaki Ishimatsu }
100e1b30a39SYasuaki Ishimatsu
find_unassigned_vector(cpumask_t domain)1014994be1bSYasuaki Ishimatsu static inline int find_unassigned_vector(cpumask_t domain)
102e1b30a39SYasuaki Ishimatsu {
1034994be1bSYasuaki Ishimatsu cpumask_t mask;
1046ffbc823SKenji Kaneshige int pos, vector;
105e1b30a39SYasuaki Ishimatsu
1067d7f9848SSrivatsa S. Bhat cpumask_and(&mask, &domain, cpu_online_mask);
1075d2068daSRusty Russell if (cpumask_empty(&mask))
1084994be1bSYasuaki Ishimatsu return -EINVAL;
1094994be1bSYasuaki Ishimatsu
1104994be1bSYasuaki Ishimatsu for (pos = 0; pos < IA64_NUM_DEVICE_VECTORS; pos++) {
1116ffbc823SKenji Kaneshige vector = IA64_FIRST_DEVICE_VECTOR + pos;
1125d2068daSRusty Russell cpumask_and(&mask, &domain, &vector_table[vector]);
1135d2068daSRusty Russell if (!cpumask_empty(&mask))
1144994be1bSYasuaki Ishimatsu continue;
1156ffbc823SKenji Kaneshige return vector;
1164994be1bSYasuaki Ishimatsu }
117e1b30a39SYasuaki Ishimatsu return -ENOSPC;
118e1b30a39SYasuaki Ishimatsu }
119e1b30a39SYasuaki Ishimatsu
__bind_irq_vector(int irq,int vector,cpumask_t domain)1204994be1bSYasuaki Ishimatsu static int __bind_irq_vector(int irq, int vector, cpumask_t domain)
121e1b30a39SYasuaki Ishimatsu {
1224994be1bSYasuaki Ishimatsu cpumask_t mask;
1236ffbc823SKenji Kaneshige int cpu;
1244994be1bSYasuaki Ishimatsu struct irq_cfg *cfg = &irq_cfg[irq];
125e1b30a39SYasuaki Ishimatsu
1266bde71ecSKenji Kaneshige BUG_ON((unsigned)irq >= NR_IRQS);
1276bde71ecSKenji Kaneshige BUG_ON((unsigned)vector >= IA64_NUM_VECTORS);
1286bde71ecSKenji Kaneshige
1297d7f9848SSrivatsa S. Bhat cpumask_and(&mask, &domain, cpu_online_mask);
1305d2068daSRusty Russell if (cpumask_empty(&mask))
1314994be1bSYasuaki Ishimatsu return -EINVAL;
1325d2068daSRusty Russell if ((cfg->vector == vector) && cpumask_equal(&cfg->domain, &domain))
133e1b30a39SYasuaki Ishimatsu return 0;
1344994be1bSYasuaki Ishimatsu if (cfg->vector != IRQ_VECTOR_UNASSIGNED)
135e1b30a39SYasuaki Ishimatsu return -EBUSY;
1365d2068daSRusty Russell for_each_cpu(cpu, &mask)
137e1b30a39SYasuaki Ishimatsu per_cpu(vector_irq, cpu)[vector] = irq;
1384994be1bSYasuaki Ishimatsu cfg->vector = vector;
1394994be1bSYasuaki Ishimatsu cfg->domain = domain;
140e1b30a39SYasuaki Ishimatsu irq_status[irq] = IRQ_USED;
1415d2068daSRusty Russell cpumask_or(&vector_table[vector], &vector_table[vector], &domain);
142e1b30a39SYasuaki Ishimatsu return 0;
143e1b30a39SYasuaki Ishimatsu }
144e1b30a39SYasuaki Ishimatsu
bind_irq_vector(int irq,int vector,cpumask_t domain)1454994be1bSYasuaki Ishimatsu int bind_irq_vector(int irq, int vector, cpumask_t domain)
146e1b30a39SYasuaki Ishimatsu {
147e1b30a39SYasuaki Ishimatsu unsigned long flags;
148e1b30a39SYasuaki Ishimatsu int ret;
149e1b30a39SYasuaki Ishimatsu
150e1b30a39SYasuaki Ishimatsu spin_lock_irqsave(&vector_lock, flags);
1514994be1bSYasuaki Ishimatsu ret = __bind_irq_vector(irq, vector, domain);
152e1b30a39SYasuaki Ishimatsu spin_unlock_irqrestore(&vector_lock, flags);
153e1b30a39SYasuaki Ishimatsu return ret;
154e1b30a39SYasuaki Ishimatsu }
155e1b30a39SYasuaki Ishimatsu
__clear_irq_vector(int irq)156cd378f18SYasuaki Ishimatsu static void __clear_irq_vector(int irq)
157e1b30a39SYasuaki Ishimatsu {
1586ffbc823SKenji Kaneshige int vector, cpu;
1594994be1bSYasuaki Ishimatsu cpumask_t domain;
1604994be1bSYasuaki Ishimatsu struct irq_cfg *cfg = &irq_cfg[irq];
161e1b30a39SYasuaki Ishimatsu
162e1b30a39SYasuaki Ishimatsu BUG_ON((unsigned)irq >= NR_IRQS);
1634994be1bSYasuaki Ishimatsu BUG_ON(cfg->vector == IRQ_VECTOR_UNASSIGNED);
1644994be1bSYasuaki Ishimatsu vector = cfg->vector;
1654994be1bSYasuaki Ishimatsu domain = cfg->domain;
16651f7bd85SRusty Russell for_each_cpu_and(cpu, &cfg->domain, cpu_online_mask)
16717764d24SKenji Kaneshige per_cpu(vector_irq, cpu)[vector] = -1;
1684994be1bSYasuaki Ishimatsu cfg->vector = IRQ_VECTOR_UNASSIGNED;
1694994be1bSYasuaki Ishimatsu cfg->domain = CPU_MASK_NONE;
170e1b30a39SYasuaki Ishimatsu irq_status[irq] = IRQ_UNUSED;
1716a4bd8d1SRusty Russell cpumask_andnot(&vector_table[vector], &vector_table[vector], &domain);
172cd378f18SYasuaki Ishimatsu }
173cd378f18SYasuaki Ishimatsu
clear_irq_vector(int irq)174cd378f18SYasuaki Ishimatsu static void clear_irq_vector(int irq)
175cd378f18SYasuaki Ishimatsu {
176cd378f18SYasuaki Ishimatsu unsigned long flags;
177cd378f18SYasuaki Ishimatsu
178cd378f18SYasuaki Ishimatsu spin_lock_irqsave(&vector_lock, flags);
179cd378f18SYasuaki Ishimatsu __clear_irq_vector(irq);
180e1b30a39SYasuaki Ishimatsu spin_unlock_irqrestore(&vector_lock, flags);
181e1b30a39SYasuaki Ishimatsu }
1821da177e4SLinus Torvalds
1831da177e4SLinus Torvalds int
ia64_native_assign_irq_vector(int irq)18485cbc503SIsaku Yamahata ia64_native_assign_irq_vector (int irq)
1851da177e4SLinus Torvalds {
186e1b30a39SYasuaki Ishimatsu unsigned long flags;
1874994be1bSYasuaki Ishimatsu int vector, cpu;
188373167e8SKenji Kaneshige cpumask_t domain = CPU_MASK_NONE;
189e1b30a39SYasuaki Ishimatsu
1904994be1bSYasuaki Ishimatsu vector = -ENOSPC;
1914994be1bSYasuaki Ishimatsu
1924994be1bSYasuaki Ishimatsu spin_lock_irqsave(&vector_lock, flags);
1934994be1bSYasuaki Ishimatsu for_each_online_cpu(cpu) {
1944994be1bSYasuaki Ishimatsu domain = vector_allocation_domain(cpu);
1954994be1bSYasuaki Ishimatsu vector = find_unassigned_vector(domain);
1964994be1bSYasuaki Ishimatsu if (vector >= 0)
1974994be1bSYasuaki Ishimatsu break;
1984994be1bSYasuaki Ishimatsu }
199e1b30a39SYasuaki Ishimatsu if (vector < 0)
200e1b30a39SYasuaki Ishimatsu goto out;
2018f5ad1a8SYasuaki Ishimatsu if (irq == AUTO_ASSIGN)
2028f5ad1a8SYasuaki Ishimatsu irq = vector;
2034994be1bSYasuaki Ishimatsu BUG_ON(__bind_irq_vector(irq, vector, domain));
204e1b30a39SYasuaki Ishimatsu out:
2054994be1bSYasuaki Ishimatsu spin_unlock_irqrestore(&vector_lock, flags);
2061da177e4SLinus Torvalds return vector;
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds
2091da177e4SLinus Torvalds void
ia64_native_free_irq_vector(int vector)21085cbc503SIsaku Yamahata ia64_native_free_irq_vector (int vector)
2111da177e4SLinus Torvalds {
212e1b30a39SYasuaki Ishimatsu if (vector < IA64_FIRST_DEVICE_VECTOR ||
213e1b30a39SYasuaki Ishimatsu vector > IA64_LAST_DEVICE_VECTOR)
2141da177e4SLinus Torvalds return;
215e1b30a39SYasuaki Ishimatsu clear_irq_vector(vector);
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds
21810083072SMark Maule int
reserve_irq_vector(int vector)21910083072SMark Maule reserve_irq_vector (int vector)
22010083072SMark Maule {
22110083072SMark Maule if (vector < IA64_FIRST_DEVICE_VECTOR ||
22210083072SMark Maule vector > IA64_LAST_DEVICE_VECTOR)
22310083072SMark Maule return -EINVAL;
2244994be1bSYasuaki Ishimatsu return !!bind_irq_vector(vector, vector, CPU_MASK_ALL);
225e1b30a39SYasuaki Ishimatsu }
22610083072SMark Maule
227e1b30a39SYasuaki Ishimatsu /*
228e1b30a39SYasuaki Ishimatsu * Initialize vector_irq on a new cpu. This function must be called
229e1b30a39SYasuaki Ishimatsu * with vector_lock held.
230e1b30a39SYasuaki Ishimatsu */
__setup_vector_irq(int cpu)231e1b30a39SYasuaki Ishimatsu void __setup_vector_irq(int cpu)
232e1b30a39SYasuaki Ishimatsu {
233e1b30a39SYasuaki Ishimatsu int irq, vector;
234e1b30a39SYasuaki Ishimatsu
235e1b30a39SYasuaki Ishimatsu /* Clear vector_irq */
236e1b30a39SYasuaki Ishimatsu for (vector = 0; vector < IA64_NUM_VECTORS; ++vector)
23717764d24SKenji Kaneshige per_cpu(vector_irq, cpu)[vector] = -1;
238e1b30a39SYasuaki Ishimatsu /* Mark the inuse vectors */
239e1b30a39SYasuaki Ishimatsu for (irq = 0; irq < NR_IRQS; ++irq) {
2405d2068daSRusty Russell if (!cpumask_test_cpu(cpu, &irq_cfg[irq].domain))
2414994be1bSYasuaki Ishimatsu continue;
2424994be1bSYasuaki Ishimatsu vector = irq_to_vector(irq);
243e1b30a39SYasuaki Ishimatsu per_cpu(vector_irq, cpu)[vector] = irq;
244e1b30a39SYasuaki Ishimatsu }
245e1b30a39SYasuaki Ishimatsu }
246e1b30a39SYasuaki Ishimatsu
247df41017eSChristoph Hellwig #ifdef CONFIG_SMP
248a6cd6322SKenji Kaneshige
249d080d397SYasuaki Ishimatsu static enum vector_domain_type {
250d080d397SYasuaki Ishimatsu VECTOR_DOMAIN_NONE,
251d080d397SYasuaki Ishimatsu VECTOR_DOMAIN_PERCPU
252d080d397SYasuaki Ishimatsu } vector_domain_type = VECTOR_DOMAIN_NONE;
253d080d397SYasuaki Ishimatsu
vector_allocation_domain(int cpu)254d080d397SYasuaki Ishimatsu static cpumask_t vector_allocation_domain(int cpu)
255d080d397SYasuaki Ishimatsu {
256d080d397SYasuaki Ishimatsu if (vector_domain_type == VECTOR_DOMAIN_PERCPU)
2576a4bd8d1SRusty Russell return *cpumask_of(cpu);
258d080d397SYasuaki Ishimatsu return CPU_MASK_ALL;
259d080d397SYasuaki Ishimatsu }
260d080d397SYasuaki Ishimatsu
__irq_prepare_move(int irq,int cpu)261a6cd6322SKenji Kaneshige static int __irq_prepare_move(int irq, int cpu)
262a6cd6322SKenji Kaneshige {
263a6cd6322SKenji Kaneshige struct irq_cfg *cfg = &irq_cfg[irq];
264a6cd6322SKenji Kaneshige int vector;
265a6cd6322SKenji Kaneshige cpumask_t domain;
266a6cd6322SKenji Kaneshige
267a6cd6322SKenji Kaneshige if (cfg->move_in_progress || cfg->move_cleanup_count)
268a6cd6322SKenji Kaneshige return -EBUSY;
269a6cd6322SKenji Kaneshige if (cfg->vector == IRQ_VECTOR_UNASSIGNED || !cpu_online(cpu))
270a6cd6322SKenji Kaneshige return -EINVAL;
2715d2068daSRusty Russell if (cpumask_test_cpu(cpu, &cfg->domain))
272a6cd6322SKenji Kaneshige return 0;
273a6cd6322SKenji Kaneshige domain = vector_allocation_domain(cpu);
274a6cd6322SKenji Kaneshige vector = find_unassigned_vector(domain);
275a6cd6322SKenji Kaneshige if (vector < 0)
276a6cd6322SKenji Kaneshige return -ENOSPC;
277a6cd6322SKenji Kaneshige cfg->move_in_progress = 1;
278a6cd6322SKenji Kaneshige cfg->old_domain = cfg->domain;
279a6cd6322SKenji Kaneshige cfg->vector = IRQ_VECTOR_UNASSIGNED;
280a6cd6322SKenji Kaneshige cfg->domain = CPU_MASK_NONE;
281a6cd6322SKenji Kaneshige BUG_ON(__bind_irq_vector(irq, vector, domain));
282a6cd6322SKenji Kaneshige return 0;
283a6cd6322SKenji Kaneshige }
284a6cd6322SKenji Kaneshige
irq_prepare_move(int irq,int cpu)285a6cd6322SKenji Kaneshige int irq_prepare_move(int irq, int cpu)
286a6cd6322SKenji Kaneshige {
287a6cd6322SKenji Kaneshige unsigned long flags;
288a6cd6322SKenji Kaneshige int ret;
289a6cd6322SKenji Kaneshige
290a6cd6322SKenji Kaneshige spin_lock_irqsave(&vector_lock, flags);
291a6cd6322SKenji Kaneshige ret = __irq_prepare_move(irq, cpu);
292a6cd6322SKenji Kaneshige spin_unlock_irqrestore(&vector_lock, flags);
293a6cd6322SKenji Kaneshige return ret;
294a6cd6322SKenji Kaneshige }
295a6cd6322SKenji Kaneshige
irq_complete_move(unsigned irq)296a6cd6322SKenji Kaneshige void irq_complete_move(unsigned irq)
297a6cd6322SKenji Kaneshige {
298a6cd6322SKenji Kaneshige struct irq_cfg *cfg = &irq_cfg[irq];
299a6cd6322SKenji Kaneshige cpumask_t cleanup_mask;
300a6cd6322SKenji Kaneshige int i;
301a6cd6322SKenji Kaneshige
302a6cd6322SKenji Kaneshige if (likely(!cfg->move_in_progress))
303a6cd6322SKenji Kaneshige return;
304a6cd6322SKenji Kaneshige
3055d2068daSRusty Russell if (unlikely(cpumask_test_cpu(smp_processor_id(), &cfg->old_domain)))
306a6cd6322SKenji Kaneshige return;
307a6cd6322SKenji Kaneshige
3087d7f9848SSrivatsa S. Bhat cpumask_and(&cleanup_mask, &cfg->old_domain, cpu_online_mask);
3095d2068daSRusty Russell cfg->move_cleanup_count = cpumask_weight(&cleanup_mask);
3105d2068daSRusty Russell for_each_cpu(i, &cleanup_mask)
31105933aacSChristoph Hellwig ia64_send_ipi(i, IA64_IRQ_MOVE_VECTOR, IA64_IPI_DM_INT, 0);
312a6cd6322SKenji Kaneshige cfg->move_in_progress = 0;
313a6cd6322SKenji Kaneshige }
314a6cd6322SKenji Kaneshige
smp_irq_move_cleanup_interrupt(int irq,void * dev_id)315a6cd6322SKenji Kaneshige static irqreturn_t smp_irq_move_cleanup_interrupt(int irq, void *dev_id)
316a6cd6322SKenji Kaneshige {
317a6cd6322SKenji Kaneshige int me = smp_processor_id();
318a6cd6322SKenji Kaneshige ia64_vector vector;
319a6cd6322SKenji Kaneshige unsigned long flags;
320a6cd6322SKenji Kaneshige
321a6cd6322SKenji Kaneshige for (vector = IA64_FIRST_DEVICE_VECTOR;
322a6cd6322SKenji Kaneshige vector < IA64_LAST_DEVICE_VECTOR; vector++) {
323a6cd6322SKenji Kaneshige int irq;
324a6cd6322SKenji Kaneshige struct irq_desc *desc;
325a6cd6322SKenji Kaneshige struct irq_cfg *cfg;
3266065a244SChristoph Lameter irq = __this_cpu_read(vector_irq[vector]);
327a6cd6322SKenji Kaneshige if (irq < 0)
328a6cd6322SKenji Kaneshige continue;
329a6cd6322SKenji Kaneshige
330a2178334SThomas Gleixner desc = irq_to_desc(irq);
331a6cd6322SKenji Kaneshige cfg = irq_cfg + irq;
332239007b8SThomas Gleixner raw_spin_lock(&desc->lock);
333a6cd6322SKenji Kaneshige if (!cfg->move_cleanup_count)
334a6cd6322SKenji Kaneshige goto unlock;
335a6cd6322SKenji Kaneshige
3365d2068daSRusty Russell if (!cpumask_test_cpu(me, &cfg->old_domain))
337a6cd6322SKenji Kaneshige goto unlock;
338a6cd6322SKenji Kaneshige
339a6cd6322SKenji Kaneshige spin_lock_irqsave(&vector_lock, flags);
3406065a244SChristoph Lameter __this_cpu_write(vector_irq[vector], -1);
3415d2068daSRusty Russell cpumask_clear_cpu(me, &vector_table[vector]);
342a6cd6322SKenji Kaneshige spin_unlock_irqrestore(&vector_lock, flags);
343a6cd6322SKenji Kaneshige cfg->move_cleanup_count--;
344a6cd6322SKenji Kaneshige unlock:
345239007b8SThomas Gleixner raw_spin_unlock(&desc->lock);
346a6cd6322SKenji Kaneshige }
347a6cd6322SKenji Kaneshige return IRQ_HANDLED;
348a6cd6322SKenji Kaneshige }
349a6cd6322SKenji Kaneshige
parse_vector_domain(char * arg)350d080d397SYasuaki Ishimatsu static int __init parse_vector_domain(char *arg)
351d080d397SYasuaki Ishimatsu {
352d080d397SYasuaki Ishimatsu if (!arg)
353d080d397SYasuaki Ishimatsu return -EINVAL;
354d080d397SYasuaki Ishimatsu if (!strcmp(arg, "percpu")) {
355d080d397SYasuaki Ishimatsu vector_domain_type = VECTOR_DOMAIN_PERCPU;
356d080d397SYasuaki Ishimatsu no_int_routing = 1;
357d080d397SYasuaki Ishimatsu }
358074ff856SKenji Kaneshige return 0;
359d080d397SYasuaki Ishimatsu }
360d080d397SYasuaki Ishimatsu early_param("vector", parse_vector_domain);
361d080d397SYasuaki Ishimatsu #else
vector_allocation_domain(int cpu)3624994be1bSYasuaki Ishimatsu static cpumask_t vector_allocation_domain(int cpu)
3634994be1bSYasuaki Ishimatsu {
3644994be1bSYasuaki Ishimatsu return CPU_MASK_ALL;
3654994be1bSYasuaki Ishimatsu }
366d080d397SYasuaki Ishimatsu #endif
3674994be1bSYasuaki Ishimatsu
3684994be1bSYasuaki Ishimatsu
destroy_and_reserve_irq(unsigned int irq)369e1b30a39SYasuaki Ishimatsu void destroy_and_reserve_irq(unsigned int irq)
370e1b30a39SYasuaki Ishimatsu {
371216fcd29SKenji Kaneshige unsigned long flags;
372216fcd29SKenji Kaneshige
3734debd723SThomas Gleixner irq_init_desc(irq);
374216fcd29SKenji Kaneshige spin_lock_irqsave(&vector_lock, flags);
375216fcd29SKenji Kaneshige __clear_irq_vector(irq);
376216fcd29SKenji Kaneshige irq_status[irq] = IRQ_RSVD;
377216fcd29SKenji Kaneshige spin_unlock_irqrestore(&vector_lock, flags);
37810083072SMark Maule }
37910083072SMark Maule
380b6cf2583SEric W. Biederman /*
381b6cf2583SEric W. Biederman * Dynamic irq allocate and deallocation for MSI
382b6cf2583SEric W. Biederman */
create_irq(void)383b6cf2583SEric W. Biederman int create_irq(void)
384b6cf2583SEric W. Biederman {
385e1b30a39SYasuaki Ishimatsu unsigned long flags;
3864994be1bSYasuaki Ishimatsu int irq, vector, cpu;
387373167e8SKenji Kaneshige cpumask_t domain = CPU_MASK_NONE;
388b6cf2583SEric W. Biederman
3894994be1bSYasuaki Ishimatsu irq = vector = -ENOSPC;
390e1b30a39SYasuaki Ishimatsu spin_lock_irqsave(&vector_lock, flags);
3914994be1bSYasuaki Ishimatsu for_each_online_cpu(cpu) {
3924994be1bSYasuaki Ishimatsu domain = vector_allocation_domain(cpu);
3934994be1bSYasuaki Ishimatsu vector = find_unassigned_vector(domain);
3944994be1bSYasuaki Ishimatsu if (vector >= 0)
3954994be1bSYasuaki Ishimatsu break;
3964994be1bSYasuaki Ishimatsu }
397e1b30a39SYasuaki Ishimatsu if (vector < 0)
398e1b30a39SYasuaki Ishimatsu goto out;
399e1b30a39SYasuaki Ishimatsu irq = find_unassigned_irq();
400e1b30a39SYasuaki Ishimatsu if (irq < 0)
401e1b30a39SYasuaki Ishimatsu goto out;
4024994be1bSYasuaki Ishimatsu BUG_ON(__bind_irq_vector(irq, vector, domain));
403e1b30a39SYasuaki Ishimatsu out:
404e1b30a39SYasuaki Ishimatsu spin_unlock_irqrestore(&vector_lock, flags);
405e1b30a39SYasuaki Ishimatsu if (irq >= 0)
4064debd723SThomas Gleixner irq_init_desc(irq);
407e1b30a39SYasuaki Ishimatsu return irq;
408b6cf2583SEric W. Biederman }
409b6cf2583SEric W. Biederman
destroy_irq(unsigned int irq)410b6cf2583SEric W. Biederman void destroy_irq(unsigned int irq)
411b6cf2583SEric W. Biederman {
4124debd723SThomas Gleixner irq_init_desc(irq);
413e1b30a39SYasuaki Ishimatsu clear_irq_vector(irq);
414b6cf2583SEric W. Biederman }
415b6cf2583SEric W. Biederman
4161da177e4SLinus Torvalds #ifdef CONFIG_SMP
4171da177e4SLinus Torvalds # define IS_RESCHEDULE(vec) (vec == IA64_IPI_RESCHEDULE)
4183be44b9cSJack Steiner # define IS_LOCAL_TLB_FLUSH(vec) (vec == IA64_IPI_LOCAL_TLB_FLUSH)
4191da177e4SLinus Torvalds #else
4201da177e4SLinus Torvalds # define IS_RESCHEDULE(vec) (0)
4213be44b9cSJack Steiner # define IS_LOCAL_TLB_FLUSH(vec) (0)
4221da177e4SLinus Torvalds #endif
4231da177e4SLinus Torvalds /*
4241da177e4SLinus Torvalds * That's where the IVT branches when we get an external
4251da177e4SLinus Torvalds * interrupt. This branches to the correct hardware IRQ handler via
4261da177e4SLinus Torvalds * function ptr.
4271da177e4SLinus Torvalds */
4281da177e4SLinus Torvalds void
ia64_handle_irq(ia64_vector vector,struct pt_regs * regs)4291da177e4SLinus Torvalds ia64_handle_irq (ia64_vector vector, struct pt_regs *regs)
4301da177e4SLinus Torvalds {
4317d12e780SDavid Howells struct pt_regs *old_regs = set_irq_regs(regs);
4321da177e4SLinus Torvalds unsigned long saved_tpr;
4331da177e4SLinus Torvalds
4341da177e4SLinus Torvalds #if IRQ_DEBUG
4351da177e4SLinus Torvalds {
4361da177e4SLinus Torvalds unsigned long bsp, sp;
4371da177e4SLinus Torvalds
4381da177e4SLinus Torvalds /*
4391da177e4SLinus Torvalds * Note: if the interrupt happened while executing in
4401da177e4SLinus Torvalds * the context switch routine (ia64_switch_to), we may
4411da177e4SLinus Torvalds * get a spurious stack overflow here. This is
4421da177e4SLinus Torvalds * because the register and the memory stack are not
4431da177e4SLinus Torvalds * switched atomically.
4441da177e4SLinus Torvalds */
4451da177e4SLinus Torvalds bsp = ia64_getreg(_IA64_REG_AR_BSP);
4461da177e4SLinus Torvalds sp = ia64_getreg(_IA64_REG_SP);
4471da177e4SLinus Torvalds
4481da177e4SLinus Torvalds if ((sp - bsp) < 1024) {
4497683a3f9SAkinobu Mita static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
4501da177e4SLinus Torvalds
4517683a3f9SAkinobu Mita if (__ratelimit(&ratelimit)) {
4521da177e4SLinus Torvalds printk("ia64_handle_irq: DANGER: less than "
4531da177e4SLinus Torvalds "1KB of free stack space!!\n"
4541da177e4SLinus Torvalds "(bsp=0x%lx, sp=%lx)\n", bsp, sp);
4551da177e4SLinus Torvalds }
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds }
4581da177e4SLinus Torvalds #endif /* IRQ_DEBUG */
4591da177e4SLinus Torvalds
4601da177e4SLinus Torvalds /*
4611da177e4SLinus Torvalds * Always set TPR to limit maximum interrupt nesting depth to
4621da177e4SLinus Torvalds * 16 (without this, it would be ~240, which could easily lead
4631da177e4SLinus Torvalds * to kernel stack overflows).
4641da177e4SLinus Torvalds */
4651da177e4SLinus Torvalds irq_enter();
4661da177e4SLinus Torvalds saved_tpr = ia64_getreg(_IA64_REG_CR_TPR);
4671da177e4SLinus Torvalds ia64_srlz_d();
4681da177e4SLinus Torvalds while (vector != IA64_SPURIOUS_INT_VECTOR) {
46917764d24SKenji Kaneshige int irq = local_vector_to_irq(vector);
47017764d24SKenji Kaneshige
47166f3e6afSJes Sorensen if (unlikely(IS_LOCAL_TLB_FLUSH(vector))) {
47266f3e6afSJes Sorensen smp_local_flush_tlb();
4733611587aSThomas Gleixner kstat_incr_irq_this_cpu(irq);
4747c730ccdSLinus Torvalds } else if (unlikely(IS_RESCHEDULE(vector))) {
475184748ccSPeter Zijlstra scheduler_ipi();
4763611587aSThomas Gleixner kstat_incr_irq_this_cpu(irq);
4777c730ccdSLinus Torvalds } else {
4781da177e4SLinus Torvalds ia64_setreg(_IA64_REG_CR_TPR, vector);
4791da177e4SLinus Torvalds ia64_srlz_d();
4801da177e4SLinus Torvalds
48117764d24SKenji Kaneshige if (unlikely(irq < 0)) {
48217764d24SKenji Kaneshige printk(KERN_ERR "%s: Unexpected interrupt "
48317764d24SKenji Kaneshige "vector %d on CPU %d is not mapped "
484d4ed8084SHarvey Harrison "to any IRQ!\n", __func__, vector,
48517764d24SKenji Kaneshige smp_processor_id());
48617764d24SKenji Kaneshige } else
48717764d24SKenji Kaneshige generic_handle_irq(irq);
4881da177e4SLinus Torvalds
4891da177e4SLinus Torvalds /*
4901da177e4SLinus Torvalds * Disable interrupts and send EOI:
4911da177e4SLinus Torvalds */
4921da177e4SLinus Torvalds local_irq_disable();
4931da177e4SLinus Torvalds ia64_setreg(_IA64_REG_CR_TPR, saved_tpr);
4941da177e4SLinus Torvalds }
4951da177e4SLinus Torvalds ia64_eoi();
4961da177e4SLinus Torvalds vector = ia64_get_ivr();
4971da177e4SLinus Torvalds }
4981da177e4SLinus Torvalds /*
4991da177e4SLinus Torvalds * This must be done *after* the ia64_eoi(). For example, the keyboard softirq
5001da177e4SLinus Torvalds * handler needs to be able to wait for further keyboard interrupts, which can't
5011da177e4SLinus Torvalds * come through until ia64_eoi() has been done.
5021da177e4SLinus Torvalds */
5031da177e4SLinus Torvalds irq_exit();
5047d12e780SDavid Howells set_irq_regs(old_regs);
5051da177e4SLinus Torvalds }
5061da177e4SLinus Torvalds
5071da177e4SLinus Torvalds #ifdef CONFIG_HOTPLUG_CPU
5081da177e4SLinus Torvalds /*
5091da177e4SLinus Torvalds * This function emulates a interrupt processing when a cpu is about to be
5101da177e4SLinus Torvalds * brought down.
5111da177e4SLinus Torvalds */
ia64_process_pending_intr(void)5121da177e4SLinus Torvalds void ia64_process_pending_intr(void)
5131da177e4SLinus Torvalds {
5141da177e4SLinus Torvalds ia64_vector vector;
5151da177e4SLinus Torvalds unsigned long saved_tpr;
5161da177e4SLinus Torvalds extern unsigned int vectors_in_migration[NR_IRQS];
5171da177e4SLinus Torvalds
5181da177e4SLinus Torvalds vector = ia64_get_ivr();
5191da177e4SLinus Torvalds
5201da177e4SLinus Torvalds irq_enter();
5211da177e4SLinus Torvalds saved_tpr = ia64_getreg(_IA64_REG_CR_TPR);
5221da177e4SLinus Torvalds ia64_srlz_d();
5231da177e4SLinus Torvalds
5241da177e4SLinus Torvalds /*
5251da177e4SLinus Torvalds * Perform normal interrupt style processing
5261da177e4SLinus Torvalds */
5271da177e4SLinus Torvalds while (vector != IA64_SPURIOUS_INT_VECTOR) {
52866f3e6afSJes Sorensen int irq = local_vector_to_irq(vector);
52966f3e6afSJes Sorensen
5303be44b9cSJack Steiner if (unlikely(IS_LOCAL_TLB_FLUSH(vector))) {
5313be44b9cSJack Steiner smp_local_flush_tlb();
5323611587aSThomas Gleixner kstat_incr_irq_this_cpu(irq);
5337c730ccdSLinus Torvalds } else if (unlikely(IS_RESCHEDULE(vector))) {
5343611587aSThomas Gleixner kstat_incr_irq_this_cpu(irq);
5357c730ccdSLinus Torvalds } else {
5368c1addbcSTony Luck struct pt_regs *old_regs = set_irq_regs(NULL);
5378c1addbcSTony Luck
5381da177e4SLinus Torvalds ia64_setreg(_IA64_REG_CR_TPR, vector);
5391da177e4SLinus Torvalds ia64_srlz_d();
5401da177e4SLinus Torvalds
5411da177e4SLinus Torvalds /*
5421da177e4SLinus Torvalds * Now try calling normal ia64_handle_irq as it would have got called
5431da177e4SLinus Torvalds * from a real intr handler. Try passing null for pt_regs, hopefully
5441da177e4SLinus Torvalds * it will work. I hope it works!.
5451da177e4SLinus Torvalds * Probably could shared code.
5461da177e4SLinus Torvalds */
54717764d24SKenji Kaneshige if (unlikely(irq < 0)) {
54817764d24SKenji Kaneshige printk(KERN_ERR "%s: Unexpected interrupt "
54917764d24SKenji Kaneshige "vector %d on CPU %d not being mapped "
550d4ed8084SHarvey Harrison "to any IRQ!!\n", __func__, vector,
55117764d24SKenji Kaneshige smp_processor_id());
55217764d24SKenji Kaneshige } else {
55317764d24SKenji Kaneshige vectors_in_migration[irq]=0;
55417764d24SKenji Kaneshige generic_handle_irq(irq);
55517764d24SKenji Kaneshige }
5568c1addbcSTony Luck set_irq_regs(old_regs);
5571da177e4SLinus Torvalds
5581da177e4SLinus Torvalds /*
5591da177e4SLinus Torvalds * Disable interrupts and send EOI
5601da177e4SLinus Torvalds */
5611da177e4SLinus Torvalds local_irq_disable();
5621da177e4SLinus Torvalds ia64_setreg(_IA64_REG_CR_TPR, saved_tpr);
5631da177e4SLinus Torvalds }
5641da177e4SLinus Torvalds ia64_eoi();
5651da177e4SLinus Torvalds vector = ia64_get_ivr();
5661da177e4SLinus Torvalds }
5671da177e4SLinus Torvalds irq_exit();
5681da177e4SLinus Torvalds }
5691da177e4SLinus Torvalds #endif
5701da177e4SLinus Torvalds
5711da177e4SLinus Torvalds
5721da177e4SLinus Torvalds #ifdef CONFIG_SMP
5731da177e4SLinus Torvalds
dummy_handler(int irq,void * dev_id)5749b3377f9SJack Steiner static irqreturn_t dummy_handler (int irq, void *dev_id)
5759b3377f9SJack Steiner {
5769b3377f9SJack Steiner BUG();
577722e6f50STony Luck return IRQ_NONE;
5789b3377f9SJack Steiner }
5799b3377f9SJack Steiner
58032f88400SMarcelo Tosatti /*
58132f88400SMarcelo Tosatti * KVM uses this interrupt to force a cpu out of guest mode
58232f88400SMarcelo Tosatti */
5833be44b9cSJack Steiner
5841da177e4SLinus Torvalds #endif
5851da177e4SLinus Torvalds
5861da177e4SLinus Torvalds void
register_percpu_irq(ia64_vector vec,irq_handler_t handler,unsigned long flags,const char * name)58790341cd8Safzal mohammed register_percpu_irq(ia64_vector vec, irq_handler_t handler, unsigned long flags,
58890341cd8Safzal mohammed const char *name)
5891da177e4SLinus Torvalds {
5901da177e4SLinus Torvalds unsigned int irq;
5911da177e4SLinus Torvalds
592e1b30a39SYasuaki Ishimatsu irq = vec;
5934994be1bSYasuaki Ishimatsu BUG_ON(bind_irq_vector(irq, vec, CPU_MASK_ALL));
594a2178334SThomas Gleixner irq_set_status_flags(irq, IRQ_PER_CPU);
59553c909c9SThomas Gleixner irq_set_chip(irq, &irq_type_ia64_lsapic);
59690341cd8Safzal mohammed if (handler)
59790341cd8Safzal mohammed if (request_irq(irq, handler, flags, name, NULL))
59890341cd8Safzal mohammed pr_err("Failed to request irq %u (%s)\n", irq, name);
59953c909c9SThomas Gleixner irq_set_handler(irq, handle_percpu_irq);
6001da177e4SLinus Torvalds }
6011da177e4SLinus Torvalds
6021da177e4SLinus Torvalds void __init
ia64_native_register_ipi(void)60385cbc503SIsaku Yamahata ia64_native_register_ipi(void)
6041da177e4SLinus Torvalds {
6051da177e4SLinus Torvalds #ifdef CONFIG_SMP
60690341cd8Safzal mohammed register_percpu_irq(IA64_IPI_VECTOR, handle_IPI, 0, "IPI");
60790341cd8Safzal mohammed register_percpu_irq(IA64_IPI_RESCHEDULE, dummy_handler, 0, "resched");
60890341cd8Safzal mohammed register_percpu_irq(IA64_IPI_LOCAL_TLB_FLUSH, dummy_handler, 0,
60990341cd8Safzal mohammed "tlb_flush");
61085cbc503SIsaku Yamahata #endif
61185cbc503SIsaku Yamahata }
61285cbc503SIsaku Yamahata
61385cbc503SIsaku Yamahata void __init
init_IRQ(void)61485cbc503SIsaku Yamahata init_IRQ (void)
61585cbc503SIsaku Yamahata {
6164de0a759STony Luck acpi_boot_init();
61785cbc503SIsaku Yamahata ia64_register_ipi();
61890341cd8Safzal mohammed register_percpu_irq(IA64_SPURIOUS_INT_VECTOR, NULL, 0, NULL);
61985cbc503SIsaku Yamahata #ifdef CONFIG_SMP
62090341cd8Safzal mohammed if (vector_domain_type != VECTOR_DOMAIN_NONE) {
62190341cd8Safzal mohammed register_percpu_irq(IA64_IRQ_MOVE_VECTOR,
62290341cd8Safzal mohammed smp_irq_move_cleanup_interrupt, 0,
62390341cd8Safzal mohammed "irq_move");
62490341cd8Safzal mohammed }
625a6cd6322SKenji Kaneshige #endif
6261da177e4SLinus Torvalds }
6271da177e4SLinus Torvalds
6281da177e4SLinus Torvalds void
ia64_send_ipi(int cpu,int vector,int delivery_mode,int redirect)6291da177e4SLinus Torvalds ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect)
6301da177e4SLinus Torvalds {
6311da177e4SLinus Torvalds void __iomem *ipi_addr;
6321da177e4SLinus Torvalds unsigned long ipi_data;
6331da177e4SLinus Torvalds unsigned long phys_cpu_id;
6341da177e4SLinus Torvalds
6351da177e4SLinus Torvalds phys_cpu_id = cpu_physical_id(cpu);
6361da177e4SLinus Torvalds
6371da177e4SLinus Torvalds /*
6381da177e4SLinus Torvalds * cpu number is in 8bit ID and 8bit EID
6391da177e4SLinus Torvalds */
6401da177e4SLinus Torvalds
6411da177e4SLinus Torvalds ipi_data = (delivery_mode << 8) | (vector & 0xff);
6421da177e4SLinus Torvalds ipi_addr = ipi_base_addr + ((phys_cpu_id << 4) | ((redirect & 1) << 3));
6431da177e4SLinus Torvalds
6441da177e4SLinus Torvalds writeq(ipi_data, ipi_addr);
6451da177e4SLinus Torvalds }
646