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