aboutsummaryrefslogtreecommitdiffstats
path: root/arch/tile/kernel/irq.c
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@tilera.com>2010-05-28 23:09:12 -0400
committerChris Metcalf <cmetcalf@tilera.com>2010-06-04 17:11:18 -0400
commit867e359b97c970a60626d5d76bbe2a8fadbf38fb (patch)
treec5ccbb7f5172e8555977119608ecb1eee3cc37e3 /arch/tile/kernel/irq.c
parent5360bd776f73d0a7da571d72a09a03f237e99900 (diff)
arch/tile: core support for Tilera 32-bit chips.
This change is the core kernel support for TILEPro and TILE64 chips. No driver support (except the console driver) is included yet. This includes the relevant Linux headers in asm/; the low-level low-level "Tile architecture" headers in arch/, which are shared with the hypervisor, etc., and are build-system agnostic; and the relevant hypervisor headers in hv/. Signed-off-by: Chris Metcalf <cmetcalf@tilera.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> Reviewed-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/tile/kernel/irq.c')
-rw-r--r--arch/tile/kernel/irq.c227
1 files changed, 227 insertions, 0 deletions
diff --git a/arch/tile/kernel/irq.c b/arch/tile/kernel/irq.c
new file mode 100644
index 000000000000..24cc6b2abc2c
--- /dev/null
+++ b/arch/tile/kernel/irq.c
@@ -0,0 +1,227 @@
1/*
2 * Copyright 2010 Tilera Corporation. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation, version 2.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11 * NON INFRINGEMENT. See the GNU General Public License for
12 * more details.
13 */
14
15#include <linux/module.h>
16#include <linux/seq_file.h>
17#include <linux/interrupt.h>
18#include <linux/irq.h>
19#include <linux/kernel_stat.h>
20#include <linux/uaccess.h>
21#include <hv/drv_pcie_rc_intf.h>
22
23/*
24 * The set of interrupts we enable for raw_local_irq_enable().
25 * This is initialized to have just a single interrupt that the kernel
26 * doesn't actually use as a sentinel. During kernel init,
27 * interrupts are added as the kernel gets prepared to support them.
28 * NOTE: we could probably initialize them all statically up front.
29 */
30DEFINE_PER_CPU(unsigned long long, interrupts_enabled_mask) =
31 INITIAL_INTERRUPTS_ENABLED;
32EXPORT_PER_CPU_SYMBOL(interrupts_enabled_mask);
33
34/* Define per-tile device interrupt state */
35DEFINE_PER_CPU(HV_IntrState, dev_intr_state);
36
37DEFINE_PER_CPU(irq_cpustat_t, irq_stat) ____cacheline_internodealigned_in_smp;
38EXPORT_PER_CPU_SYMBOL(irq_stat);
39
40
41
42/*
43 * Interrupt dispatcher, invoked upon a hypervisor device interrupt downcall
44 */
45void tile_dev_intr(struct pt_regs *regs, int intnum)
46{
47 int irq;
48
49 /*
50 * Get the device interrupt pending mask from where the hypervisor
51 * has tucked it away for us.
52 */
53 unsigned long pending_dev_intr_mask = __insn_mfspr(SPR_SYSTEM_SAVE_1_3);
54
55
56 /* Track time spent here in an interrupt context. */
57 struct pt_regs *old_regs = set_irq_regs(regs);
58 irq_enter();
59
60#ifdef CONFIG_DEBUG_STACKOVERFLOW
61 /* Debugging check for stack overflow: less than 1/8th stack free? */
62 {
63 long sp = stack_pointer - (long) current_thread_info();
64 if (unlikely(sp < (sizeof(struct thread_info) + STACK_WARN))) {
65 printk(KERN_EMERG "tile_dev_intr: "
66 "stack overflow: %ld\n",
67 sp - sizeof(struct thread_info));
68 dump_stack();
69 }
70 }
71#endif
72
73 for (irq = 0; pending_dev_intr_mask; ++irq) {
74 if (pending_dev_intr_mask & 0x1) {
75 generic_handle_irq(irq);
76
77 /* Count device irqs; IPIs are counted elsewhere. */
78 if (irq > HV_MAX_IPI_INTERRUPT)
79 __get_cpu_var(irq_stat).irq_dev_intr_count++;
80 }
81 pending_dev_intr_mask >>= 1;
82 }
83
84 /*
85 * Track time spent against the current process again and
86 * process any softirqs if they are waiting.
87 */
88 irq_exit();
89 set_irq_regs(old_regs);
90}
91
92
93/* Mask an interrupt. */
94static void hv_dev_irq_mask(unsigned int irq)
95{
96 HV_IntrState *p_intr_state = &__get_cpu_var(dev_intr_state);
97 hv_disable_intr(p_intr_state, 1 << irq);
98}
99
100/* Unmask an interrupt. */
101static void hv_dev_irq_unmask(unsigned int irq)
102{
103 /* Re-enable the hypervisor to generate interrupts. */
104 HV_IntrState *p_intr_state = &__get_cpu_var(dev_intr_state);
105 hv_enable_intr(p_intr_state, 1 << irq);
106}
107
108/*
109 * The HV doesn't latch incoming interrupts while an interrupt is
110 * disabled, so we need to reenable interrupts before running the
111 * handler.
112 *
113 * ISSUE: Enabling the interrupt this early avoids any race conditions
114 * but introduces the possibility of nested interrupt stack overflow.
115 * An imminent change to the HV IRQ model will fix this.
116 */
117static void hv_dev_irq_ack(unsigned int irq)
118{
119 hv_dev_irq_unmask(irq);
120}
121
122/*
123 * Since ack() reenables interrupts, there's nothing to do at eoi().
124 */
125static void hv_dev_irq_eoi(unsigned int irq)
126{
127}
128
129static struct irq_chip hv_dev_irq_chip = {
130 .typename = "hv_dev_irq_chip",
131 .ack = hv_dev_irq_ack,
132 .mask = hv_dev_irq_mask,
133 .unmask = hv_dev_irq_unmask,
134 .eoi = hv_dev_irq_eoi,
135};
136
137static struct irqaction resched_action = {
138 .handler = handle_reschedule_ipi,
139 .name = "resched",
140 .dev_id = handle_reschedule_ipi /* unique token */,
141};
142
143void __init init_IRQ(void)
144{
145 /* Bind IPI irqs. Does this belong somewhere else in init? */
146 tile_irq_activate(IRQ_RESCHEDULE);
147 BUG_ON(setup_irq(IRQ_RESCHEDULE, &resched_action));
148}
149
150void __cpuinit init_per_tile_IRQs(void)
151{
152 int rc;
153
154 /* Set the pointer to the per-tile device interrupt state. */
155 HV_IntrState *sv_ptr = &__get_cpu_var(dev_intr_state);
156 rc = hv_dev_register_intr_state(sv_ptr);
157 if (rc != HV_OK)
158 panic("hv_dev_register_intr_state: error %d", rc);
159
160}
161
162void tile_irq_activate(unsigned int irq)
163{
164 /*
165 * Paravirtualized drivers can call up to the HV to find out
166 * which irq they're associated with. The HV interface
167 * doesn't provide a generic call for discovering all valid
168 * IRQs, so drivers must call this method to initialize newly
169 * discovered IRQs.
170 *
171 * We could also just initialize all 32 IRQs at startup, but
172 * doing so would lead to a kernel fault if an unexpected
173 * interrupt fires and jumps to a NULL action. By defering
174 * the set_irq_chip_and_handler() call, unexpected IRQs are
175 * handled properly by handle_bad_irq().
176 */
177 hv_dev_irq_mask(irq);
178 set_irq_chip_and_handler(irq, &hv_dev_irq_chip, handle_percpu_irq);
179}
180
181void ack_bad_irq(unsigned int irq)
182{
183 printk(KERN_ERR "unexpected IRQ trap at vector %02x\n", irq);
184}
185
186/*
187 * Generic, controller-independent functions:
188 */
189
190int show_interrupts(struct seq_file *p, void *v)
191{
192 int i = *(loff_t *) v, j;
193 struct irqaction *action;
194 unsigned long flags;
195
196 if (i == 0) {
197 seq_printf(p, " ");
198 for (j = 0; j < NR_CPUS; j++)
199 if (cpu_online(j))
200 seq_printf(p, "CPU%-8d", j);
201 seq_putc(p, '\n');
202 }
203
204 if (i < NR_IRQS) {
205 raw_spin_lock_irqsave(&irq_desc[i].lock, flags);
206 action = irq_desc[i].action;
207 if (!action)
208 goto skip;
209 seq_printf(p, "%3d: ", i);
210#ifndef CONFIG_SMP
211 seq_printf(p, "%10u ", kstat_irqs(i));
212#else
213 for_each_online_cpu(j)
214 seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));
215#endif
216 seq_printf(p, " %14s", irq_desc[i].chip->typename);
217 seq_printf(p, " %s", action->name);
218
219 for (action = action->next; action; action = action->next)
220 seq_printf(p, ", %s", action->name);
221
222 seq_putc(p, '\n');
223skip:
224 raw_spin_unlock_irqrestore(&irq_desc[i].lock, flags);
225 }
226 return 0;
227}