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