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