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 | * | |
fd43fe19 | 7 | * Copyright (C) 2002 - 2006 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> | |
21 | ||
22 | #include <asm/uaccess.h> | |
23 | #include <asm/platform.h> | |
24 | ||
5a0015d6 CZ |
25 | static unsigned int cached_irq_mask; |
26 | ||
27 | atomic_t irq_err_count; | |
28 | ||
5a0015d6 CZ |
29 | /* |
30 | * do_IRQ handles all normal device IRQ's (the special | |
31 | * SMP cross-CPU interrupts have their own specific | |
32 | * handlers). | |
33 | */ | |
34 | ||
fd43fe19 | 35 | asmlinkage void do_IRQ(int irq, struct pt_regs *regs) |
5a0015d6 | 36 | { |
fd43fe19 | 37 | struct pt_regs *old_regs = set_irq_regs(regs); |
fd43fe19 CZ |
38 | |
39 | if (irq >= NR_IRQS) { | |
40 | printk(KERN_EMERG "%s: cannot handle IRQ %d\n", | |
1b532c6c | 41 | __func__, irq); |
fd43fe19 CZ |
42 | } |
43 | ||
5a0015d6 CZ |
44 | irq_enter(); |
45 | ||
46 | #ifdef CONFIG_DEBUG_STACKOVERFLOW | |
47 | /* Debugging check for stack overflow: is there less than 1KB free? */ | |
48 | { | |
49 | unsigned long sp; | |
50 | ||
51 | __asm__ __volatile__ ("mov %0, a1\n" : "=a" (sp)); | |
52 | sp &= THREAD_SIZE - 1; | |
53 | ||
54 | if (unlikely(sp < (sizeof(thread_info) + 1024))) | |
55 | printk("Stack overflow in do_IRQ: %ld\n", | |
56 | sp - sizeof(struct thread_info)); | |
57 | } | |
58 | #endif | |
495e0c79 | 59 | generic_handle_irq(irq); |
5a0015d6 CZ |
60 | |
61 | irq_exit(); | |
fd43fe19 | 62 | set_irq_regs(old_regs); |
5a0015d6 CZ |
63 | } |
64 | ||
65 | /* | |
66 | * Generic, controller-independent functions: | |
67 | */ | |
68 | ||
69 | int show_interrupts(struct seq_file *p, void *v) | |
70 | { | |
71 | int i = *(loff_t *) v, j; | |
72 | struct irqaction * action; | |
73 | unsigned long flags; | |
74 | ||
75 | if (i == 0) { | |
76 | seq_printf(p, " "); | |
394e3902 AM |
77 | for_each_online_cpu(j) |
78 | seq_printf(p, "CPU%d ",j); | |
5a0015d6 CZ |
79 | seq_putc(p, '\n'); |
80 | } | |
81 | ||
82 | if (i < NR_IRQS) { | |
239007b8 | 83 | raw_spin_lock_irqsave(&irq_desc[i].lock, flags); |
5a0015d6 CZ |
84 | action = irq_desc[i].action; |
85 | if (!action) | |
86 | goto skip; | |
87 | seq_printf(p, "%3d: ",i); | |
88 | #ifndef CONFIG_SMP | |
89 | seq_printf(p, "%10u ", kstat_irqs(i)); | |
90 | #else | |
394e3902 | 91 | for_each_online_cpu(j) |
dee4102a | 92 | seq_printf(p, "%10u ", kstat_irqs_cpu(i, j)); |
5a0015d6 | 93 | #endif |
d1ea13c6 | 94 | seq_printf(p, " %14s", irq_desc[i].chip->name); |
5a0015d6 CZ |
95 | seq_printf(p, " %s", action->name); |
96 | ||
97 | for (action=action->next; action; action = action->next) | |
98 | seq_printf(p, ", %s", action->name); | |
99 | ||
100 | seq_putc(p, '\n'); | |
101 | skip: | |
239007b8 | 102 | raw_spin_unlock_irqrestore(&irq_desc[i].lock, flags); |
5a0015d6 CZ |
103 | } else if (i == NR_IRQS) { |
104 | seq_printf(p, "NMI: "); | |
394e3902 AM |
105 | for_each_online_cpu(j) |
106 | seq_printf(p, "%10u ", nmi_count(j)); | |
5a0015d6 CZ |
107 | seq_putc(p, '\n'); |
108 | seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); | |
109 | } | |
110 | return 0; | |
111 | } | |
5a0015d6 | 112 | |
495e0c79 | 113 | static void xtensa_irq_mask(struct irq_chip *d) |
5a0015d6 | 114 | { |
495e0c79 | 115 | cached_irq_mask &= ~(1 << d->irq); |
5a0015d6 CZ |
116 | set_sr (cached_irq_mask, INTENABLE); |
117 | } | |
118 | ||
495e0c79 | 119 | static void xtensa_irq_unmask(struct irq_chip *d) |
5a0015d6 | 120 | { |
495e0c79 | 121 | cached_irq_mask |= 1 << d->irq; |
5a0015d6 CZ |
122 | set_sr (cached_irq_mask, INTENABLE); |
123 | } | |
124 | ||
495e0c79 | 125 | static void xtensa_irq_enable(struct irq_chip *d) |
4c0d2141 | 126 | { |
495e0c79 TG |
127 | variant_irq_enable(d->irq); |
128 | xtensa_irq_unmask(d->irq); | |
4c0d2141 JW |
129 | } |
130 | ||
495e0c79 | 131 | static void xtensa_irq_disable(struct irq_chip *d) |
4c0d2141 | 132 | { |
495e0c79 TG |
133 | xtensa_irq_mask(d->irq); |
134 | variant_irq_disable(d->irq); | |
4c0d2141 JW |
135 | } |
136 | ||
495e0c79 | 137 | static void xtensa_irq_ack(struct irq_chip *d) |
5a0015d6 | 138 | { |
495e0c79 | 139 | set_sr(1 << d->irq, INTCLEAR); |
5a0015d6 CZ |
140 | } |
141 | ||
495e0c79 | 142 | static int xtensa_irq_retrigger(struct irq_chip *d) |
5a0015d6 | 143 | { |
495e0c79 | 144 | set_sr (1 << d->irq, INTSET); |
fd43fe19 | 145 | return 1; |
5a0015d6 CZ |
146 | } |
147 | ||
5a0015d6 | 148 | |
fd43fe19 CZ |
149 | static struct irq_chip xtensa_irq_chip = { |
150 | .name = "xtensa", | |
495e0c79 TG |
151 | .irq_enable = xtensa_irq_enable, |
152 | .irq_disable = xtensa_irq_disable, | |
153 | .irq_mask = xtensa_irq_mask, | |
154 | .irq_unmask = xtensa_irq_unmask, | |
155 | .irq_ack = xtensa_irq_ack, | |
156 | .irq_retrigger = xtensa_irq_retrigger, | |
fd43fe19 | 157 | }; |
5a0015d6 CZ |
158 | |
159 | void __init init_IRQ(void) | |
160 | { | |
fd43fe19 | 161 | int index; |
5a0015d6 | 162 | |
fd43fe19 CZ |
163 | for (index = 0; index < XTENSA_NR_IRQS; index++) { |
164 | int mask = 1 << index; | |
5a0015d6 | 165 | |
fd43fe19 | 166 | if (mask & XCHAL_INTTYPE_MASK_SOFTWARE) |
610e1756 | 167 | irq_set_chip_and_handler(index, &xtensa_irq_chip, |
fd43fe19 | 168 | handle_simple_irq); |
5a0015d6 | 169 | |
fd43fe19 | 170 | else if (mask & XCHAL_INTTYPE_MASK_EXTERN_EDGE) |
610e1756 | 171 | irq_set_chip_and_handler(index, &xtensa_irq_chip, |
fd43fe19 CZ |
172 | handle_edge_irq); |
173 | ||
174 | else if (mask & XCHAL_INTTYPE_MASK_EXTERN_LEVEL) | |
610e1756 | 175 | irq_set_chip_and_handler(index, &xtensa_irq_chip, |
fd43fe19 CZ |
176 | handle_level_irq); |
177 | ||
178 | else if (mask & XCHAL_INTTYPE_MASK_TIMER) | |
610e1756 | 179 | irq_set_chip_and_handler(index, &xtensa_irq_chip, |
fd43fe19 CZ |
180 | handle_edge_irq); |
181 | ||
182 | else /* XCHAL_INTTYPE_MASK_WRITE_ERROR */ | |
183 | /* XCHAL_INTTYPE_MASK_NMI */ | |
184 | ||
610e1756 | 185 | irq_set_chip_and_handler(index, &xtensa_irq_chip, |
fd43fe19 CZ |
186 | handle_level_irq); |
187 | } | |
188 | ||
189 | cached_irq_mask = 0; | |
1beee210 DG |
190 | |
191 | variant_init_irq(); | |
5a0015d6 | 192 | } |