1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * linux/arch/xtensa/kernel/irq.c |
4 | * |
5 | * Xtensa built-in interrupt controller and some generic functions copied |
6 | * from i386. |
7 | * |
8 | * Copyright (C) 2002 - 2013 Tensilica, Inc. |
9 | * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar |
10 | * |
11 | * |
12 | * Chris Zankel <chris@zankel.net> |
13 | * Kevin Chea |
14 | * |
15 | */ |
16 | |
17 | #include <linux/module.h> |
18 | #include <linux/seq_file.h> |
19 | #include <linux/interrupt.h> |
20 | #include <linux/irq.h> |
21 | #include <linux/kernel_stat.h> |
22 | #include <linux/irqchip.h> |
23 | #include <linux/irqchip/xtensa-mx.h> |
24 | #include <linux/irqchip/xtensa-pic.h> |
25 | #include <linux/irqdomain.h> |
26 | #include <linux/of.h> |
27 | |
28 | #include <asm/mxregs.h> |
29 | #include <linux/uaccess.h> |
30 | #include <asm/platform.h> |
31 | #include <asm/traps.h> |
32 | |
33 | DECLARE_PER_CPU(unsigned long, nmi_count); |
34 | |
35 | asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs) |
36 | { |
37 | #ifdef CONFIG_DEBUG_STACKOVERFLOW |
38 | /* Debugging check for stack overflow: is there less than 1KB free? */ |
39 | { |
40 | unsigned long sp = current_stack_pointer; |
41 | |
42 | sp &= THREAD_SIZE - 1; |
43 | |
44 | if (unlikely(sp < (sizeof(thread_info) + 1024))) |
45 | printk("Stack overflow in do_IRQ: %ld\n" , |
46 | sp - sizeof(struct thread_info)); |
47 | } |
48 | #endif |
49 | generic_handle_domain_irq(NULL, hwirq); |
50 | } |
51 | |
52 | int arch_show_interrupts(struct seq_file *p, int prec) |
53 | { |
54 | unsigned cpu __maybe_unused; |
55 | #ifdef CONFIG_SMP |
56 | show_ipi_list(p, prec); |
57 | #endif |
58 | #if XTENSA_FAKE_NMI |
59 | seq_printf(p, "%*s:" , prec, "NMI" ); |
60 | for_each_online_cpu(cpu) |
61 | seq_printf(p, " %10lu" , per_cpu(nmi_count, cpu)); |
62 | seq_puts(p, " Non-maskable interrupts\n" ); |
63 | #endif |
64 | return 0; |
65 | } |
66 | |
67 | int xtensa_irq_domain_xlate(const u32 *intspec, unsigned int intsize, |
68 | unsigned long int_irq, unsigned long ext_irq, |
69 | unsigned long *out_hwirq, unsigned int *out_type) |
70 | { |
71 | if (WARN_ON(intsize < 1 || intsize > 2)) |
72 | return -EINVAL; |
73 | if (intsize == 2 && intspec[1] == 1) { |
74 | int_irq = xtensa_map_ext_irq(ext_irq); |
75 | if (int_irq < XCHAL_NUM_INTERRUPTS) |
76 | *out_hwirq = int_irq; |
77 | else |
78 | return -EINVAL; |
79 | } else { |
80 | *out_hwirq = int_irq; |
81 | } |
82 | *out_type = IRQ_TYPE_NONE; |
83 | return 0; |
84 | } |
85 | |
86 | int xtensa_irq_map(struct irq_domain *d, unsigned int irq, |
87 | irq_hw_number_t hw) |
88 | { |
89 | struct irq_chip *irq_chip = d->host_data; |
90 | u32 mask = 1 << hw; |
91 | |
92 | if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) { |
93 | irq_set_chip_and_handler_name(irq, chip: irq_chip, |
94 | handle: handle_simple_irq, name: "level" ); |
95 | irq_set_status_flags(irq, set: IRQ_LEVEL); |
96 | } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) { |
97 | irq_set_chip_and_handler_name(irq, chip: irq_chip, |
98 | handle: handle_edge_irq, name: "edge" ); |
99 | irq_clear_status_flags(irq, clr: IRQ_LEVEL); |
100 | } else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) { |
101 | irq_set_chip_and_handler_name(irq, chip: irq_chip, |
102 | handle: handle_level_irq, name: "level" ); |
103 | irq_set_status_flags(irq, set: IRQ_LEVEL); |
104 | } else if (mask & XCHAL_INTTYPE_MASK_TIMER) { |
105 | irq_set_chip_and_handler_name(irq, chip: irq_chip, |
106 | handle: handle_percpu_irq, name: "timer" ); |
107 | irq_clear_status_flags(irq, clr: IRQ_LEVEL); |
108 | #ifdef XCHAL_INTTYPE_MASK_PROFILING |
109 | } else if (mask & XCHAL_INTTYPE_MASK_PROFILING) { |
110 | irq_set_chip_and_handler_name(irq, irq_chip, |
111 | handle_percpu_irq, "profiling" ); |
112 | irq_set_status_flags(irq, IRQ_LEVEL); |
113 | #endif |
114 | } else {/* XCHAL_INTTYPE_MASK_WRITE_ERROR */ |
115 | /* XCHAL_INTTYPE_MASK_NMI */ |
116 | irq_set_chip_and_handler_name(irq, chip: irq_chip, |
117 | handle: handle_level_irq, name: "level" ); |
118 | irq_set_status_flags(irq, set: IRQ_LEVEL); |
119 | } |
120 | return 0; |
121 | } |
122 | |
123 | unsigned xtensa_map_ext_irq(unsigned ext_irq) |
124 | { |
125 | unsigned mask = XCHAL_INTTYPE_MASK_EXTERN_EDGE | |
126 | XCHAL_INTTYPE_MASK_EXTERN_LEVEL; |
127 | unsigned i; |
128 | |
129 | for (i = 0; mask; ++i, mask >>= 1) { |
130 | if ((mask & 1) && ext_irq-- == 0) |
131 | return i; |
132 | } |
133 | return XCHAL_NUM_INTERRUPTS; |
134 | } |
135 | |
136 | unsigned xtensa_get_ext_irq_no(unsigned irq) |
137 | { |
138 | unsigned mask = (XCHAL_INTTYPE_MASK_EXTERN_EDGE | |
139 | XCHAL_INTTYPE_MASK_EXTERN_LEVEL) & |
140 | ((1u << irq) - 1); |
141 | return hweight32(mask); |
142 | } |
143 | |
144 | void __init init_IRQ(void) |
145 | { |
146 | #ifdef CONFIG_USE_OF |
147 | irqchip_init(); |
148 | #else |
149 | #ifdef CONFIG_HAVE_SMP |
150 | xtensa_mx_init_legacy(NULL); |
151 | #else |
152 | xtensa_pic_init_legacy(NULL); |
153 | #endif |
154 | #endif |
155 | |
156 | #ifdef CONFIG_SMP |
157 | ipi_init(); |
158 | #endif |
159 | } |
160 | |
161 | #ifdef CONFIG_HOTPLUG_CPU |
162 | /* |
163 | * The CPU has been marked offline. Migrate IRQs off this CPU. If |
164 | * the affinity settings do not allow other CPUs, force them onto any |
165 | * available CPU. |
166 | */ |
167 | void migrate_irqs(void) |
168 | { |
169 | unsigned int i, cpu = smp_processor_id(); |
170 | |
171 | for_each_active_irq(i) { |
172 | struct irq_data *data = irq_get_irq_data(irq: i); |
173 | const struct cpumask *mask; |
174 | unsigned int newcpu; |
175 | |
176 | if (irqd_is_per_cpu(d: data)) |
177 | continue; |
178 | |
179 | mask = irq_data_get_affinity_mask(d: data); |
180 | if (!cpumask_test_cpu(cpu, cpumask: mask)) |
181 | continue; |
182 | |
183 | newcpu = cpumask_any_and(mask, cpu_online_mask); |
184 | |
185 | if (newcpu >= nr_cpu_ids) { |
186 | pr_info_ratelimited("IRQ%u no longer affine to CPU%u\n" , |
187 | i, cpu); |
188 | |
189 | irq_set_affinity(irq: i, cpu_all_mask); |
190 | } else { |
191 | irq_set_affinity(irq: i, cpumask: mask); |
192 | } |
193 | } |
194 | } |
195 | #endif /* CONFIG_HOTPLUG_CPU */ |
196 | |