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