xref: /openbmc/linux/arch/ia64/kernel/irq_ia64.c (revision ecf5b72d)
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