162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/arm/kernel/irq.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1992 Linus Torvalds 662306a36Sopenharmony_ci * Modifications for ARM processor Copyright (C) 1995-2000 Russell King. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Support for Dynamic Tick Timer Copyright (C) 2004-2005 Nokia Corporation. 962306a36Sopenharmony_ci * Dynamic Tick Timer written by Tony Lindgren <tony@atomide.com> and 1062306a36Sopenharmony_ci * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This file contains the code used by various IRQ handling routines: 1362306a36Sopenharmony_ci * asking for different IRQ's should be done through these routines 1462306a36Sopenharmony_ci * instead of just grabbing them. Thus setups with different IRQ numbers 1562306a36Sopenharmony_ci * shouldn't result in any weird surprises, and installing new handlers 1662306a36Sopenharmony_ci * should be easier. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * IRQ's are in fact implemented a bit like signal handlers for the kernel. 1962306a36Sopenharmony_ci * Naturally it's not a 1:1 relation, but there are similarities. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#include <linux/signal.h> 2262306a36Sopenharmony_ci#include <linux/ioport.h> 2362306a36Sopenharmony_ci#include <linux/interrupt.h> 2462306a36Sopenharmony_ci#include <linux/irq.h> 2562306a36Sopenharmony_ci#include <linux/irqchip.h> 2662306a36Sopenharmony_ci#include <linux/random.h> 2762306a36Sopenharmony_ci#include <linux/smp.h> 2862306a36Sopenharmony_ci#include <linux/init.h> 2962306a36Sopenharmony_ci#include <linux/seq_file.h> 3062306a36Sopenharmony_ci#include <linux/errno.h> 3162306a36Sopenharmony_ci#include <linux/list.h> 3262306a36Sopenharmony_ci#include <linux/kallsyms.h> 3362306a36Sopenharmony_ci#include <linux/proc_fs.h> 3462306a36Sopenharmony_ci#include <linux/export.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include <asm/hardware/cache-l2x0.h> 3762306a36Sopenharmony_ci#include <asm/hardware/cache-uniphier.h> 3862306a36Sopenharmony_ci#include <asm/outercache.h> 3962306a36Sopenharmony_ci#include <asm/softirq_stack.h> 4062306a36Sopenharmony_ci#include <asm/exception.h> 4162306a36Sopenharmony_ci#include <asm/mach/arch.h> 4262306a36Sopenharmony_ci#include <asm/mach/irq.h> 4362306a36Sopenharmony_ci#include <asm/mach/time.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include "reboot.h" 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciunsigned long irq_err_count; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#ifdef CONFIG_IRQSTACKS 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciasmlinkage DEFINE_PER_CPU_READ_MOSTLY(u8 *, irq_stack_ptr); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void __init init_irq_stacks(void) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci u8 *stack; 5662306a36Sopenharmony_ci int cpu; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 5962306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_VMAP_STACK)) 6062306a36Sopenharmony_ci stack = (u8 *)__get_free_pages(GFP_KERNEL, 6162306a36Sopenharmony_ci THREAD_SIZE_ORDER); 6262306a36Sopenharmony_ci else 6362306a36Sopenharmony_ci stack = __vmalloc_node(THREAD_SIZE, THREAD_ALIGN, 6462306a36Sopenharmony_ci THREADINFO_GFP, NUMA_NO_NODE, 6562306a36Sopenharmony_ci __builtin_return_address(0)); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (WARN_ON(!stack)) 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci per_cpu(irq_stack_ptr, cpu) = &stack[THREAD_SIZE]; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK 7462306a36Sopenharmony_cistatic void ____do_softirq(void *arg) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci __do_softirq(); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid do_softirq_own_stack(void) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci call_with_stack(____do_softirq, NULL, 8262306a36Sopenharmony_ci __this_cpu_read(irq_stack_ptr)); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci#endif 8562306a36Sopenharmony_ci#endif 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciint arch_show_interrupts(struct seq_file *p, int prec) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci#ifdef CONFIG_FIQ 9062306a36Sopenharmony_ci show_fiq_list(p, prec); 9162306a36Sopenharmony_ci#endif 9262306a36Sopenharmony_ci#ifdef CONFIG_SMP 9362306a36Sopenharmony_ci show_ipi_list(p, prec); 9462306a36Sopenharmony_ci#endif 9562306a36Sopenharmony_ci seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count); 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * handle_IRQ handles all hardware IRQ's. Decoded IRQs should 10162306a36Sopenharmony_ci * not come via this function. Instead, they should provide their 10262306a36Sopenharmony_ci * own 'handler'. Used by platform code implementing C-based 1st 10362306a36Sopenharmony_ci * level decoding. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_civoid handle_IRQ(unsigned int irq, struct pt_regs *regs) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct irq_desc *desc; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * Some hardware gives randomly wrong interrupts. Rather 11162306a36Sopenharmony_ci * than crashing, do something sensible. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci if (unlikely(!irq || irq >= nr_irqs)) 11462306a36Sopenharmony_ci desc = NULL; 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci desc = irq_to_desc(irq); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (likely(desc)) 11962306a36Sopenharmony_ci handle_irq_desc(desc); 12062306a36Sopenharmony_ci else 12162306a36Sopenharmony_ci ack_bad_irq(irq); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_civoid __init init_IRQ(void) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#ifdef CONFIG_IRQSTACKS 12962306a36Sopenharmony_ci init_irq_stacks(); 13062306a36Sopenharmony_ci#endif 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq) 13362306a36Sopenharmony_ci irqchip_init(); 13462306a36Sopenharmony_ci else 13562306a36Sopenharmony_ci machine_desc->init_irq(); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) && 13862306a36Sopenharmony_ci (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) { 13962306a36Sopenharmony_ci if (!outer_cache.write_sec) 14062306a36Sopenharmony_ci outer_cache.write_sec = machine_desc->l2c_write_sec; 14162306a36Sopenharmony_ci ret = l2x0_of_init(machine_desc->l2c_aux_val, 14262306a36Sopenharmony_ci machine_desc->l2c_aux_mask); 14362306a36Sopenharmony_ci if (ret && ret != -ENODEV) 14462306a36Sopenharmony_ci pr_err("L2C: failed to init: %d\n", ret); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci uniphier_cache_init(); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci#ifdef CONFIG_SPARSE_IRQ 15162306a36Sopenharmony_ciint __init arch_probe_nr_irqs(void) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS; 15462306a36Sopenharmony_ci return nr_irqs; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci#endif 157