18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/arch/ia64/kernel/irq_ia64.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1998-2001 Hewlett-Packard Co
68c2ecf20Sopenharmony_ci *	Stephane Eranian <eranian@hpl.hp.com>
78c2ecf20Sopenharmony_ci *	David Mosberger-Tang <davidm@hpl.hp.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  6/10/99: Updated to bring in sync with x86 version to facilitate
108c2ecf20Sopenharmony_ci *	     support for SMP and different interrupt controllers.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * 09/15/00 Goutham Rao <goutham.rao@intel.com> Implemented pci_irq_to_vector
138c2ecf20Sopenharmony_ci *                      PCI to vector allocation routine.
148c2ecf20Sopenharmony_ci * 04/14/2004 Ashok Raj <ashok.raj@intel.com>
158c2ecf20Sopenharmony_ci *						Added CPU Hotplug handling for IPF.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
228c2ecf20Sopenharmony_ci#include <linux/errno.h>
238c2ecf20Sopenharmony_ci#include <linux/init.h>
248c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
258c2ecf20Sopenharmony_ci#include <linux/ioport.h>
268c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h>
278c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
288c2ecf20Sopenharmony_ci#include <linux/signal.h>
298c2ecf20Sopenharmony_ci#include <linux/smp.h>
308c2ecf20Sopenharmony_ci#include <linux/threads.h>
318c2ecf20Sopenharmony_ci#include <linux/bitops.h>
328c2ecf20Sopenharmony_ci#include <linux/irq.h>
338c2ecf20Sopenharmony_ci#include <linux/ratelimit.h>
348c2ecf20Sopenharmony_ci#include <linux/acpi.h>
358c2ecf20Sopenharmony_ci#include <linux/sched.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <asm/delay.h>
388c2ecf20Sopenharmony_ci#include <asm/intrinsics.h>
398c2ecf20Sopenharmony_ci#include <asm/io.h>
408c2ecf20Sopenharmony_ci#include <asm/hw_irq.h>
418c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define IRQ_DEBUG	0
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define IRQ_VECTOR_UNASSIGNED	(0)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define IRQ_UNUSED		(0)
488c2ecf20Sopenharmony_ci#define IRQ_USED		(1)
498c2ecf20Sopenharmony_ci#define IRQ_RSVD		(2)
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ciint ia64_first_device_vector = IA64_DEF_FIRST_DEVICE_VECTOR;
528c2ecf20Sopenharmony_ciint ia64_last_device_vector = IA64_DEF_LAST_DEVICE_VECTOR;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* default base addr of IPI table */
558c2ecf20Sopenharmony_civoid __iomem *ipi_base_addr = ((void __iomem *)
568c2ecf20Sopenharmony_ci			       (__IA64_UNCACHED_OFFSET | IA64_IPI_DEFAULT_BASE_ADDR));
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic cpumask_t vector_allocation_domain(int cpu);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/*
618c2ecf20Sopenharmony_ci * Legacy IRQ to IA-64 vector translation table.
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_ci__u8 isa_irq_to_vector_map[16] = {
648c2ecf20Sopenharmony_ci	/* 8259 IRQ translation, first 16 entries */
658c2ecf20Sopenharmony_ci	0x2f, 0x20, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
668c2ecf20Sopenharmony_ci	0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(isa_irq_to_vector_map);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(vector_lock);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct irq_cfg irq_cfg[NR_IRQS] __read_mostly = {
738c2ecf20Sopenharmony_ci	[0 ... NR_IRQS - 1] = {
748c2ecf20Sopenharmony_ci		.vector = IRQ_VECTOR_UNASSIGNED,
758c2ecf20Sopenharmony_ci		.domain = CPU_MASK_NONE
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciDEFINE_PER_CPU(int[IA64_NUM_VECTORS], vector_irq) = {
808c2ecf20Sopenharmony_ci	[0 ... IA64_NUM_VECTORS - 1] = -1
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic cpumask_t vector_table[IA64_NUM_VECTORS] = {
848c2ecf20Sopenharmony_ci	[0 ... IA64_NUM_VECTORS - 1] = CPU_MASK_NONE
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int irq_status[NR_IRQS] = {
888c2ecf20Sopenharmony_ci	[0 ... NR_IRQS -1] = IRQ_UNUSED
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic inline int find_unassigned_irq(void)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	int irq;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	for (irq = IA64_FIRST_DEVICE_VECTOR; irq < NR_IRQS; irq++)
968c2ecf20Sopenharmony_ci		if (irq_status[irq] == IRQ_UNUSED)
978c2ecf20Sopenharmony_ci			return irq;
988c2ecf20Sopenharmony_ci	return -ENOSPC;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic inline int find_unassigned_vector(cpumask_t domain)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	cpumask_t mask;
1048c2ecf20Sopenharmony_ci	int pos, vector;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	cpumask_and(&mask, &domain, cpu_online_mask);
1078c2ecf20Sopenharmony_ci	if (cpumask_empty(&mask))
1088c2ecf20Sopenharmony_ci		return -EINVAL;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	for (pos = 0; pos < IA64_NUM_DEVICE_VECTORS; pos++) {
1118c2ecf20Sopenharmony_ci		vector = IA64_FIRST_DEVICE_VECTOR + pos;
1128c2ecf20Sopenharmony_ci		cpumask_and(&mask, &domain, &vector_table[vector]);
1138c2ecf20Sopenharmony_ci		if (!cpumask_empty(&mask))
1148c2ecf20Sopenharmony_ci			continue;
1158c2ecf20Sopenharmony_ci		return vector;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	return -ENOSPC;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int __bind_irq_vector(int irq, int vector, cpumask_t domain)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	cpumask_t mask;
1238c2ecf20Sopenharmony_ci	int cpu;
1248c2ecf20Sopenharmony_ci	struct irq_cfg *cfg = &irq_cfg[irq];
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	BUG_ON((unsigned)irq >= NR_IRQS);
1278c2ecf20Sopenharmony_ci	BUG_ON((unsigned)vector >= IA64_NUM_VECTORS);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	cpumask_and(&mask, &domain, cpu_online_mask);
1308c2ecf20Sopenharmony_ci	if (cpumask_empty(&mask))
1318c2ecf20Sopenharmony_ci		return -EINVAL;
1328c2ecf20Sopenharmony_ci	if ((cfg->vector == vector) && cpumask_equal(&cfg->domain, &domain))
1338c2ecf20Sopenharmony_ci		return 0;
1348c2ecf20Sopenharmony_ci	if (cfg->vector != IRQ_VECTOR_UNASSIGNED)
1358c2ecf20Sopenharmony_ci		return -EBUSY;
1368c2ecf20Sopenharmony_ci	for_each_cpu(cpu, &mask)
1378c2ecf20Sopenharmony_ci		per_cpu(vector_irq, cpu)[vector] = irq;
1388c2ecf20Sopenharmony_ci	cfg->vector = vector;
1398c2ecf20Sopenharmony_ci	cfg->domain = domain;
1408c2ecf20Sopenharmony_ci	irq_status[irq] = IRQ_USED;
1418c2ecf20Sopenharmony_ci	cpumask_or(&vector_table[vector], &vector_table[vector], &domain);
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ciint bind_irq_vector(int irq, int vector, cpumask_t domain)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	unsigned long flags;
1488c2ecf20Sopenharmony_ci	int ret;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vector_lock, flags);
1518c2ecf20Sopenharmony_ci	ret = __bind_irq_vector(irq, vector, domain);
1528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vector_lock, flags);
1538c2ecf20Sopenharmony_ci	return ret;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic void __clear_irq_vector(int irq)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	int vector, cpu;
1598c2ecf20Sopenharmony_ci	cpumask_t domain;
1608c2ecf20Sopenharmony_ci	struct irq_cfg *cfg = &irq_cfg[irq];
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	BUG_ON((unsigned)irq >= NR_IRQS);
1638c2ecf20Sopenharmony_ci	BUG_ON(cfg->vector == IRQ_VECTOR_UNASSIGNED);
1648c2ecf20Sopenharmony_ci	vector = cfg->vector;
1658c2ecf20Sopenharmony_ci	domain = cfg->domain;
1668c2ecf20Sopenharmony_ci	for_each_cpu_and(cpu, &cfg->domain, cpu_online_mask)
1678c2ecf20Sopenharmony_ci		per_cpu(vector_irq, cpu)[vector] = -1;
1688c2ecf20Sopenharmony_ci	cfg->vector = IRQ_VECTOR_UNASSIGNED;
1698c2ecf20Sopenharmony_ci	cfg->domain = CPU_MASK_NONE;
1708c2ecf20Sopenharmony_ci	irq_status[irq] = IRQ_UNUSED;
1718c2ecf20Sopenharmony_ci	cpumask_andnot(&vector_table[vector], &vector_table[vector], &domain);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic void clear_irq_vector(int irq)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	unsigned long flags;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vector_lock, flags);
1798c2ecf20Sopenharmony_ci	__clear_irq_vector(irq);
1808c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vector_lock, flags);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ciint
1848c2ecf20Sopenharmony_ciia64_native_assign_irq_vector (int irq)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	unsigned long flags;
1878c2ecf20Sopenharmony_ci	int vector, cpu;
1888c2ecf20Sopenharmony_ci	cpumask_t domain = CPU_MASK_NONE;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	vector = -ENOSPC;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vector_lock, flags);
1938c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu) {
1948c2ecf20Sopenharmony_ci		domain = vector_allocation_domain(cpu);
1958c2ecf20Sopenharmony_ci		vector = find_unassigned_vector(domain);
1968c2ecf20Sopenharmony_ci		if (vector >= 0)
1978c2ecf20Sopenharmony_ci			break;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	if (vector < 0)
2008c2ecf20Sopenharmony_ci		goto out;
2018c2ecf20Sopenharmony_ci	if (irq == AUTO_ASSIGN)
2028c2ecf20Sopenharmony_ci		irq = vector;
2038c2ecf20Sopenharmony_ci	BUG_ON(__bind_irq_vector(irq, vector, domain));
2048c2ecf20Sopenharmony_ci out:
2058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vector_lock, flags);
2068c2ecf20Sopenharmony_ci	return vector;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_civoid
2108c2ecf20Sopenharmony_ciia64_native_free_irq_vector (int vector)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	if (vector < IA64_FIRST_DEVICE_VECTOR ||
2138c2ecf20Sopenharmony_ci	    vector > IA64_LAST_DEVICE_VECTOR)
2148c2ecf20Sopenharmony_ci		return;
2158c2ecf20Sopenharmony_ci	clear_irq_vector(vector);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciint
2198c2ecf20Sopenharmony_cireserve_irq_vector (int vector)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	if (vector < IA64_FIRST_DEVICE_VECTOR ||
2228c2ecf20Sopenharmony_ci	    vector > IA64_LAST_DEVICE_VECTOR)
2238c2ecf20Sopenharmony_ci		return -EINVAL;
2248c2ecf20Sopenharmony_ci	return !!bind_irq_vector(vector, vector, CPU_MASK_ALL);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/*
2288c2ecf20Sopenharmony_ci * Initialize vector_irq on a new cpu. This function must be called
2298c2ecf20Sopenharmony_ci * with vector_lock held.
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_civoid __setup_vector_irq(int cpu)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	int irq, vector;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/* Clear vector_irq */
2368c2ecf20Sopenharmony_ci	for (vector = 0; vector < IA64_NUM_VECTORS; ++vector)
2378c2ecf20Sopenharmony_ci		per_cpu(vector_irq, cpu)[vector] = -1;
2388c2ecf20Sopenharmony_ci	/* Mark the inuse vectors */
2398c2ecf20Sopenharmony_ci	for (irq = 0; irq < NR_IRQS; ++irq) {
2408c2ecf20Sopenharmony_ci		if (!cpumask_test_cpu(cpu, &irq_cfg[irq].domain))
2418c2ecf20Sopenharmony_ci			continue;
2428c2ecf20Sopenharmony_ci		vector = irq_to_vector(irq);
2438c2ecf20Sopenharmony_ci		per_cpu(vector_irq, cpu)[vector] = irq;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic enum vector_domain_type {
2508c2ecf20Sopenharmony_ci	VECTOR_DOMAIN_NONE,
2518c2ecf20Sopenharmony_ci	VECTOR_DOMAIN_PERCPU
2528c2ecf20Sopenharmony_ci} vector_domain_type = VECTOR_DOMAIN_NONE;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic cpumask_t vector_allocation_domain(int cpu)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	if (vector_domain_type == VECTOR_DOMAIN_PERCPU)
2578c2ecf20Sopenharmony_ci		return *cpumask_of(cpu);
2588c2ecf20Sopenharmony_ci	return CPU_MASK_ALL;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int __irq_prepare_move(int irq, int cpu)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct irq_cfg *cfg = &irq_cfg[irq];
2648c2ecf20Sopenharmony_ci	int vector;
2658c2ecf20Sopenharmony_ci	cpumask_t domain;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (cfg->move_in_progress || cfg->move_cleanup_count)
2688c2ecf20Sopenharmony_ci		return -EBUSY;
2698c2ecf20Sopenharmony_ci	if (cfg->vector == IRQ_VECTOR_UNASSIGNED || !cpu_online(cpu))
2708c2ecf20Sopenharmony_ci		return -EINVAL;
2718c2ecf20Sopenharmony_ci	if (cpumask_test_cpu(cpu, &cfg->domain))
2728c2ecf20Sopenharmony_ci		return 0;
2738c2ecf20Sopenharmony_ci	domain = vector_allocation_domain(cpu);
2748c2ecf20Sopenharmony_ci	vector = find_unassigned_vector(domain);
2758c2ecf20Sopenharmony_ci	if (vector < 0)
2768c2ecf20Sopenharmony_ci		return -ENOSPC;
2778c2ecf20Sopenharmony_ci	cfg->move_in_progress = 1;
2788c2ecf20Sopenharmony_ci	cfg->old_domain = cfg->domain;
2798c2ecf20Sopenharmony_ci	cfg->vector = IRQ_VECTOR_UNASSIGNED;
2808c2ecf20Sopenharmony_ci	cfg->domain = CPU_MASK_NONE;
2818c2ecf20Sopenharmony_ci	BUG_ON(__bind_irq_vector(irq, vector, domain));
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ciint irq_prepare_move(int irq, int cpu)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	unsigned long flags;
2888c2ecf20Sopenharmony_ci	int ret;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vector_lock, flags);
2918c2ecf20Sopenharmony_ci	ret = __irq_prepare_move(irq, cpu);
2928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vector_lock, flags);
2938c2ecf20Sopenharmony_ci	return ret;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_civoid irq_complete_move(unsigned irq)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct irq_cfg *cfg = &irq_cfg[irq];
2998c2ecf20Sopenharmony_ci	cpumask_t cleanup_mask;
3008c2ecf20Sopenharmony_ci	int i;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (likely(!cfg->move_in_progress))
3038c2ecf20Sopenharmony_ci		return;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (unlikely(cpumask_test_cpu(smp_processor_id(), &cfg->old_domain)))
3068c2ecf20Sopenharmony_ci		return;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	cpumask_and(&cleanup_mask, &cfg->old_domain, cpu_online_mask);
3098c2ecf20Sopenharmony_ci	cfg->move_cleanup_count = cpumask_weight(&cleanup_mask);
3108c2ecf20Sopenharmony_ci	for_each_cpu(i, &cleanup_mask)
3118c2ecf20Sopenharmony_ci		ia64_send_ipi(i, IA64_IRQ_MOVE_VECTOR, IA64_IPI_DM_INT, 0);
3128c2ecf20Sopenharmony_ci	cfg->move_in_progress = 0;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic irqreturn_t smp_irq_move_cleanup_interrupt(int irq, void *dev_id)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	int me = smp_processor_id();
3188c2ecf20Sopenharmony_ci	ia64_vector vector;
3198c2ecf20Sopenharmony_ci	unsigned long flags;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	for (vector = IA64_FIRST_DEVICE_VECTOR;
3228c2ecf20Sopenharmony_ci	     vector < IA64_LAST_DEVICE_VECTOR; vector++) {
3238c2ecf20Sopenharmony_ci		int irq;
3248c2ecf20Sopenharmony_ci		struct irq_desc *desc;
3258c2ecf20Sopenharmony_ci		struct irq_cfg *cfg;
3268c2ecf20Sopenharmony_ci		irq = __this_cpu_read(vector_irq[vector]);
3278c2ecf20Sopenharmony_ci		if (irq < 0)
3288c2ecf20Sopenharmony_ci			continue;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		desc = irq_to_desc(irq);
3318c2ecf20Sopenharmony_ci		cfg = irq_cfg + irq;
3328c2ecf20Sopenharmony_ci		raw_spin_lock(&desc->lock);
3338c2ecf20Sopenharmony_ci		if (!cfg->move_cleanup_count)
3348c2ecf20Sopenharmony_ci			goto unlock;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		if (!cpumask_test_cpu(me, &cfg->old_domain))
3378c2ecf20Sopenharmony_ci			goto unlock;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		spin_lock_irqsave(&vector_lock, flags);
3408c2ecf20Sopenharmony_ci		__this_cpu_write(vector_irq[vector], -1);
3418c2ecf20Sopenharmony_ci		cpumask_clear_cpu(me, &vector_table[vector]);
3428c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&vector_lock, flags);
3438c2ecf20Sopenharmony_ci		cfg->move_cleanup_count--;
3448c2ecf20Sopenharmony_ci	unlock:
3458c2ecf20Sopenharmony_ci		raw_spin_unlock(&desc->lock);
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int __init parse_vector_domain(char *arg)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	if (!arg)
3538c2ecf20Sopenharmony_ci		return -EINVAL;
3548c2ecf20Sopenharmony_ci	if (!strcmp(arg, "percpu")) {
3558c2ecf20Sopenharmony_ci		vector_domain_type = VECTOR_DOMAIN_PERCPU;
3568c2ecf20Sopenharmony_ci		no_int_routing = 1;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ciearly_param("vector", parse_vector_domain);
3618c2ecf20Sopenharmony_ci#else
3628c2ecf20Sopenharmony_cistatic cpumask_t vector_allocation_domain(int cpu)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	return CPU_MASK_ALL;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci#endif
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_civoid destroy_and_reserve_irq(unsigned int irq)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	unsigned long flags;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	irq_init_desc(irq);
3748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vector_lock, flags);
3758c2ecf20Sopenharmony_ci	__clear_irq_vector(irq);
3768c2ecf20Sopenharmony_ci	irq_status[irq] = IRQ_RSVD;
3778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vector_lock, flags);
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci/*
3818c2ecf20Sopenharmony_ci * Dynamic irq allocate and deallocation for MSI
3828c2ecf20Sopenharmony_ci */
3838c2ecf20Sopenharmony_ciint create_irq(void)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	unsigned long flags;
3868c2ecf20Sopenharmony_ci	int irq, vector, cpu;
3878c2ecf20Sopenharmony_ci	cpumask_t domain = CPU_MASK_NONE;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	irq = vector = -ENOSPC;
3908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vector_lock, flags);
3918c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu) {
3928c2ecf20Sopenharmony_ci		domain = vector_allocation_domain(cpu);
3938c2ecf20Sopenharmony_ci		vector = find_unassigned_vector(domain);
3948c2ecf20Sopenharmony_ci		if (vector >= 0)
3958c2ecf20Sopenharmony_ci			break;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci	if (vector < 0)
3988c2ecf20Sopenharmony_ci		goto out;
3998c2ecf20Sopenharmony_ci	irq = find_unassigned_irq();
4008c2ecf20Sopenharmony_ci	if (irq < 0)
4018c2ecf20Sopenharmony_ci		goto out;
4028c2ecf20Sopenharmony_ci	BUG_ON(__bind_irq_vector(irq, vector, domain));
4038c2ecf20Sopenharmony_ci out:
4048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vector_lock, flags);
4058c2ecf20Sopenharmony_ci	if (irq >= 0)
4068c2ecf20Sopenharmony_ci		irq_init_desc(irq);
4078c2ecf20Sopenharmony_ci	return irq;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_civoid destroy_irq(unsigned int irq)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	irq_init_desc(irq);
4138c2ecf20Sopenharmony_ci	clear_irq_vector(irq);
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
4178c2ecf20Sopenharmony_ci#	define IS_RESCHEDULE(vec)	(vec == IA64_IPI_RESCHEDULE)
4188c2ecf20Sopenharmony_ci#	define IS_LOCAL_TLB_FLUSH(vec)	(vec == IA64_IPI_LOCAL_TLB_FLUSH)
4198c2ecf20Sopenharmony_ci#else
4208c2ecf20Sopenharmony_ci#	define IS_RESCHEDULE(vec)	(0)
4218c2ecf20Sopenharmony_ci#	define IS_LOCAL_TLB_FLUSH(vec)	(0)
4228c2ecf20Sopenharmony_ci#endif
4238c2ecf20Sopenharmony_ci/*
4248c2ecf20Sopenharmony_ci * That's where the IVT branches when we get an external
4258c2ecf20Sopenharmony_ci * interrupt. This branches to the correct hardware IRQ handler via
4268c2ecf20Sopenharmony_ci * function ptr.
4278c2ecf20Sopenharmony_ci */
4288c2ecf20Sopenharmony_civoid
4298c2ecf20Sopenharmony_ciia64_handle_irq (ia64_vector vector, struct pt_regs *regs)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	struct pt_regs *old_regs = set_irq_regs(regs);
4328c2ecf20Sopenharmony_ci	unsigned long saved_tpr;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci#if IRQ_DEBUG
4358c2ecf20Sopenharmony_ci	{
4368c2ecf20Sopenharmony_ci		unsigned long bsp, sp;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		/*
4398c2ecf20Sopenharmony_ci		 * Note: if the interrupt happened while executing in
4408c2ecf20Sopenharmony_ci		 * the context switch routine (ia64_switch_to), we may
4418c2ecf20Sopenharmony_ci		 * get a spurious stack overflow here.  This is
4428c2ecf20Sopenharmony_ci		 * because the register and the memory stack are not
4438c2ecf20Sopenharmony_ci		 * switched atomically.
4448c2ecf20Sopenharmony_ci		 */
4458c2ecf20Sopenharmony_ci		bsp = ia64_getreg(_IA64_REG_AR_BSP);
4468c2ecf20Sopenharmony_ci		sp = ia64_getreg(_IA64_REG_SP);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		if ((sp - bsp) < 1024) {
4498c2ecf20Sopenharmony_ci			static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 5);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci			if (__ratelimit(&ratelimit)) {
4528c2ecf20Sopenharmony_ci				printk("ia64_handle_irq: DANGER: less than "
4538c2ecf20Sopenharmony_ci				       "1KB of free stack space!!\n"
4548c2ecf20Sopenharmony_ci				       "(bsp=0x%lx, sp=%lx)\n", bsp, sp);
4558c2ecf20Sopenharmony_ci			}
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci#endif /* IRQ_DEBUG */
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/*
4618c2ecf20Sopenharmony_ci	 * Always set TPR to limit maximum interrupt nesting depth to
4628c2ecf20Sopenharmony_ci	 * 16 (without this, it would be ~240, which could easily lead
4638c2ecf20Sopenharmony_ci	 * to kernel stack overflows).
4648c2ecf20Sopenharmony_ci	 */
4658c2ecf20Sopenharmony_ci	irq_enter();
4668c2ecf20Sopenharmony_ci	saved_tpr = ia64_getreg(_IA64_REG_CR_TPR);
4678c2ecf20Sopenharmony_ci	ia64_srlz_d();
4688c2ecf20Sopenharmony_ci	while (vector != IA64_SPURIOUS_INT_VECTOR) {
4698c2ecf20Sopenharmony_ci		int irq = local_vector_to_irq(vector);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		if (unlikely(IS_LOCAL_TLB_FLUSH(vector))) {
4728c2ecf20Sopenharmony_ci			smp_local_flush_tlb();
4738c2ecf20Sopenharmony_ci			kstat_incr_irq_this_cpu(irq);
4748c2ecf20Sopenharmony_ci		} else if (unlikely(IS_RESCHEDULE(vector))) {
4758c2ecf20Sopenharmony_ci			scheduler_ipi();
4768c2ecf20Sopenharmony_ci			kstat_incr_irq_this_cpu(irq);
4778c2ecf20Sopenharmony_ci		} else {
4788c2ecf20Sopenharmony_ci			ia64_setreg(_IA64_REG_CR_TPR, vector);
4798c2ecf20Sopenharmony_ci			ia64_srlz_d();
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci			if (unlikely(irq < 0)) {
4828c2ecf20Sopenharmony_ci				printk(KERN_ERR "%s: Unexpected interrupt "
4838c2ecf20Sopenharmony_ci				       "vector %d on CPU %d is not mapped "
4848c2ecf20Sopenharmony_ci				       "to any IRQ!\n", __func__, vector,
4858c2ecf20Sopenharmony_ci				       smp_processor_id());
4868c2ecf20Sopenharmony_ci			} else
4878c2ecf20Sopenharmony_ci				generic_handle_irq(irq);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci			/*
4908c2ecf20Sopenharmony_ci			 * Disable interrupts and send EOI:
4918c2ecf20Sopenharmony_ci			 */
4928c2ecf20Sopenharmony_ci			local_irq_disable();
4938c2ecf20Sopenharmony_ci			ia64_setreg(_IA64_REG_CR_TPR, saved_tpr);
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci		ia64_eoi();
4968c2ecf20Sopenharmony_ci		vector = ia64_get_ivr();
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	/*
4998c2ecf20Sopenharmony_ci	 * This must be done *after* the ia64_eoi().  For example, the keyboard softirq
5008c2ecf20Sopenharmony_ci	 * handler needs to be able to wait for further keyboard interrupts, which can't
5018c2ecf20Sopenharmony_ci	 * come through until ia64_eoi() has been done.
5028c2ecf20Sopenharmony_ci	 */
5038c2ecf20Sopenharmony_ci	irq_exit();
5048c2ecf20Sopenharmony_ci	set_irq_regs(old_regs);
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
5088c2ecf20Sopenharmony_ci/*
5098c2ecf20Sopenharmony_ci * This function emulates a interrupt processing when a cpu is about to be
5108c2ecf20Sopenharmony_ci * brought down.
5118c2ecf20Sopenharmony_ci */
5128c2ecf20Sopenharmony_civoid ia64_process_pending_intr(void)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	ia64_vector vector;
5158c2ecf20Sopenharmony_ci	unsigned long saved_tpr;
5168c2ecf20Sopenharmony_ci	extern unsigned int vectors_in_migration[NR_IRQS];
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	vector = ia64_get_ivr();
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	irq_enter();
5218c2ecf20Sopenharmony_ci	saved_tpr = ia64_getreg(_IA64_REG_CR_TPR);
5228c2ecf20Sopenharmony_ci	ia64_srlz_d();
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	 /*
5258c2ecf20Sopenharmony_ci	  * Perform normal interrupt style processing
5268c2ecf20Sopenharmony_ci	  */
5278c2ecf20Sopenharmony_ci	while (vector != IA64_SPURIOUS_INT_VECTOR) {
5288c2ecf20Sopenharmony_ci		int irq = local_vector_to_irq(vector);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		if (unlikely(IS_LOCAL_TLB_FLUSH(vector))) {
5318c2ecf20Sopenharmony_ci			smp_local_flush_tlb();
5328c2ecf20Sopenharmony_ci			kstat_incr_irq_this_cpu(irq);
5338c2ecf20Sopenharmony_ci		} else if (unlikely(IS_RESCHEDULE(vector))) {
5348c2ecf20Sopenharmony_ci			kstat_incr_irq_this_cpu(irq);
5358c2ecf20Sopenharmony_ci		} else {
5368c2ecf20Sopenharmony_ci			struct pt_regs *old_regs = set_irq_regs(NULL);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci			ia64_setreg(_IA64_REG_CR_TPR, vector);
5398c2ecf20Sopenharmony_ci			ia64_srlz_d();
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci			/*
5428c2ecf20Sopenharmony_ci			 * Now try calling normal ia64_handle_irq as it would have got called
5438c2ecf20Sopenharmony_ci			 * from a real intr handler. Try passing null for pt_regs, hopefully
5448c2ecf20Sopenharmony_ci			 * it will work. I hope it works!.
5458c2ecf20Sopenharmony_ci			 * Probably could shared code.
5468c2ecf20Sopenharmony_ci			 */
5478c2ecf20Sopenharmony_ci			if (unlikely(irq < 0)) {
5488c2ecf20Sopenharmony_ci				printk(KERN_ERR "%s: Unexpected interrupt "
5498c2ecf20Sopenharmony_ci				       "vector %d on CPU %d not being mapped "
5508c2ecf20Sopenharmony_ci				       "to any IRQ!!\n", __func__, vector,
5518c2ecf20Sopenharmony_ci				       smp_processor_id());
5528c2ecf20Sopenharmony_ci			} else {
5538c2ecf20Sopenharmony_ci				vectors_in_migration[irq]=0;
5548c2ecf20Sopenharmony_ci				generic_handle_irq(irq);
5558c2ecf20Sopenharmony_ci			}
5568c2ecf20Sopenharmony_ci			set_irq_regs(old_regs);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci			/*
5598c2ecf20Sopenharmony_ci			 * Disable interrupts and send EOI
5608c2ecf20Sopenharmony_ci			 */
5618c2ecf20Sopenharmony_ci			local_irq_disable();
5628c2ecf20Sopenharmony_ci			ia64_setreg(_IA64_REG_CR_TPR, saved_tpr);
5638c2ecf20Sopenharmony_ci		}
5648c2ecf20Sopenharmony_ci		ia64_eoi();
5658c2ecf20Sopenharmony_ci		vector = ia64_get_ivr();
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci	irq_exit();
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci#endif
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_cistatic irqreturn_t dummy_handler (int irq, void *dev_id)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	BUG();
5778c2ecf20Sopenharmony_ci	return IRQ_NONE;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci/*
5818c2ecf20Sopenharmony_ci * KVM uses this interrupt to force a cpu out of guest mode
5828c2ecf20Sopenharmony_ci */
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci#endif
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_civoid
5878c2ecf20Sopenharmony_ciregister_percpu_irq(ia64_vector vec, irq_handler_t handler, unsigned long flags,
5888c2ecf20Sopenharmony_ci		    const char *name)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	unsigned int irq;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	irq = vec;
5938c2ecf20Sopenharmony_ci	BUG_ON(bind_irq_vector(irq, vec, CPU_MASK_ALL));
5948c2ecf20Sopenharmony_ci	irq_set_status_flags(irq, IRQ_PER_CPU);
5958c2ecf20Sopenharmony_ci	irq_set_chip(irq, &irq_type_ia64_lsapic);
5968c2ecf20Sopenharmony_ci	if (handler)
5978c2ecf20Sopenharmony_ci		if (request_irq(irq, handler, flags, name, NULL))
5988c2ecf20Sopenharmony_ci			pr_err("Failed to request irq %u (%s)\n", irq, name);
5998c2ecf20Sopenharmony_ci	irq_set_handler(irq, handle_percpu_irq);
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_civoid __init
6038c2ecf20Sopenharmony_ciia64_native_register_ipi(void)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
6068c2ecf20Sopenharmony_ci	register_percpu_irq(IA64_IPI_VECTOR, handle_IPI, 0, "IPI");
6078c2ecf20Sopenharmony_ci	register_percpu_irq(IA64_IPI_RESCHEDULE, dummy_handler, 0, "resched");
6088c2ecf20Sopenharmony_ci	register_percpu_irq(IA64_IPI_LOCAL_TLB_FLUSH, dummy_handler, 0,
6098c2ecf20Sopenharmony_ci			    "tlb_flush");
6108c2ecf20Sopenharmony_ci#endif
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_civoid __init
6148c2ecf20Sopenharmony_ciinit_IRQ (void)
6158c2ecf20Sopenharmony_ci{
6168c2ecf20Sopenharmony_ci	acpi_boot_init();
6178c2ecf20Sopenharmony_ci	ia64_register_ipi();
6188c2ecf20Sopenharmony_ci	register_percpu_irq(IA64_SPURIOUS_INT_VECTOR, NULL, 0, NULL);
6198c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP
6208c2ecf20Sopenharmony_ci	if (vector_domain_type != VECTOR_DOMAIN_NONE) {
6218c2ecf20Sopenharmony_ci		register_percpu_irq(IA64_IRQ_MOVE_VECTOR,
6228c2ecf20Sopenharmony_ci				    smp_irq_move_cleanup_interrupt, 0,
6238c2ecf20Sopenharmony_ci				    "irq_move");
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci#endif
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_civoid
6298c2ecf20Sopenharmony_ciia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	void __iomem *ipi_addr;
6328c2ecf20Sopenharmony_ci	unsigned long ipi_data;
6338c2ecf20Sopenharmony_ci	unsigned long phys_cpu_id;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	phys_cpu_id = cpu_physical_id(cpu);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	/*
6388c2ecf20Sopenharmony_ci	 * cpu number is in 8bit ID and 8bit EID
6398c2ecf20Sopenharmony_ci	 */
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	ipi_data = (delivery_mode << 8) | (vector & 0xff);
6428c2ecf20Sopenharmony_ci	ipi_addr = ipi_base_addr + ((phys_cpu_id << 4) | ((redirect & 1) << 3));
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	writeq(ipi_data, ipi_addr);
6458c2ecf20Sopenharmony_ci}
646