1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * linux/arch/arm/kernel/irq.c |
4 | * |
5 | * Copyright (C) 1992 Linus Torvalds |
6 | * Modifications for ARM processor Copyright (C) 1995-2000 Russell King. |
7 | * |
8 | * Support for Dynamic Tick Timer Copyright (C) 2004-2005 Nokia Corporation. |
9 | * Dynamic Tick Timer written by Tony Lindgren <tony@atomide.com> and |
10 | * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>. |
11 | * |
12 | * This file contains the code used by various IRQ handling routines: |
13 | * asking for different IRQ's should be done through these routines |
14 | * instead of just grabbing them. Thus setups with different IRQ numbers |
15 | * shouldn't result in any weird surprises, and installing new handlers |
16 | * should be easier. |
17 | * |
18 | * IRQ's are in fact implemented a bit like signal handlers for the kernel. |
19 | * Naturally it's not a 1:1 relation, but there are similarities. |
20 | */ |
21 | #include <linux/signal.h> |
22 | #include <linux/ioport.h> |
23 | #include <linux/interrupt.h> |
24 | #include <linux/irq.h> |
25 | #include <linux/irqchip.h> |
26 | #include <linux/random.h> |
27 | #include <linux/smp.h> |
28 | #include <linux/init.h> |
29 | #include <linux/seq_file.h> |
30 | #include <linux/errno.h> |
31 | #include <linux/list.h> |
32 | #include <linux/kallsyms.h> |
33 | #include <linux/proc_fs.h> |
34 | #include <linux/export.h> |
35 | |
36 | #include <asm/hardware/cache-l2x0.h> |
37 | #include <asm/hardware/cache-uniphier.h> |
38 | #include <asm/outercache.h> |
39 | #include <asm/softirq_stack.h> |
40 | #include <asm/exception.h> |
41 | #include <asm/mach/arch.h> |
42 | #include <asm/mach/irq.h> |
43 | #include <asm/mach/time.h> |
44 | |
45 | #include "reboot.h" |
46 | |
47 | unsigned long irq_err_count; |
48 | |
49 | #ifdef CONFIG_IRQSTACKS |
50 | |
51 | asmlinkage DEFINE_PER_CPU_READ_MOSTLY(u8 *, irq_stack_ptr); |
52 | |
53 | static void __init init_irq_stacks(void) |
54 | { |
55 | u8 *stack; |
56 | int cpu; |
57 | |
58 | for_each_possible_cpu(cpu) { |
59 | if (!IS_ENABLED(CONFIG_VMAP_STACK)) |
60 | stack = (u8 *)__get_free_pages(GFP_KERNEL, |
61 | THREAD_SIZE_ORDER); |
62 | else |
63 | stack = __vmalloc_node(THREAD_SIZE, THREAD_ALIGN, |
64 | THREADINFO_GFP, NUMA_NO_NODE, |
65 | __builtin_return_address(0)); |
66 | |
67 | if (WARN_ON(!stack)) |
68 | break; |
69 | per_cpu(irq_stack_ptr, cpu) = &stack[THREAD_SIZE]; |
70 | } |
71 | } |
72 | |
73 | #ifdef CONFIG_SOFTIRQ_ON_OWN_STACK |
74 | static void ____do_softirq(void *arg) |
75 | { |
76 | __do_softirq(); |
77 | } |
78 | |
79 | void do_softirq_own_stack(void) |
80 | { |
81 | call_with_stack(____do_softirq, NULL, |
82 | __this_cpu_read(irq_stack_ptr)); |
83 | } |
84 | #endif |
85 | #endif |
86 | |
87 | int arch_show_interrupts(struct seq_file *p, int prec) |
88 | { |
89 | #ifdef CONFIG_FIQ |
90 | show_fiq_list(p, prec); |
91 | #endif |
92 | #ifdef CONFIG_SMP |
93 | show_ipi_list(p, prec); |
94 | #endif |
95 | seq_printf(m: p, fmt: "%*s: %10lu\n" , prec, "Err" , irq_err_count); |
96 | return 0; |
97 | } |
98 | |
99 | /* |
100 | * handle_IRQ handles all hardware IRQ's. Decoded IRQs should |
101 | * not come via this function. Instead, they should provide their |
102 | * own 'handler'. Used by platform code implementing C-based 1st |
103 | * level decoding. |
104 | */ |
105 | void handle_IRQ(unsigned int irq, struct pt_regs *regs) |
106 | { |
107 | struct irq_desc *desc; |
108 | |
109 | /* |
110 | * Some hardware gives randomly wrong interrupts. Rather |
111 | * than crashing, do something sensible. |
112 | */ |
113 | if (unlikely(!irq || irq >= nr_irqs)) |
114 | desc = NULL; |
115 | else |
116 | desc = irq_to_desc(irq); |
117 | |
118 | if (likely(desc)) |
119 | handle_irq_desc(desc); |
120 | else |
121 | ack_bad_irq(irq); |
122 | } |
123 | |
124 | void __init init_IRQ(void) |
125 | { |
126 | int ret; |
127 | |
128 | #ifdef CONFIG_IRQSTACKS |
129 | init_irq_stacks(); |
130 | #endif |
131 | |
132 | if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq) |
133 | irqchip_init(); |
134 | else |
135 | machine_desc->init_irq(); |
136 | |
137 | if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) && |
138 | (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) { |
139 | if (!outer_cache.write_sec) |
140 | outer_cache.write_sec = machine_desc->l2c_write_sec; |
141 | ret = l2x0_of_init(machine_desc->l2c_aux_val, |
142 | machine_desc->l2c_aux_mask); |
143 | if (ret && ret != -ENODEV) |
144 | pr_err("L2C: failed to init: %d\n" , ret); |
145 | } |
146 | |
147 | uniphier_cache_init(); |
148 | } |
149 | |
150 | #ifdef CONFIG_SPARSE_IRQ |
151 | int __init arch_probe_nr_irqs(void) |
152 | { |
153 | nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS; |
154 | return nr_irqs; |
155 | } |
156 | #endif |
157 | |