Commit | Line | Data |
---|---|---|
cebf589c AB |
1 | /* |
2 | * BPA Internal Interrupt Controller | |
3 | * | |
4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | |
5 | * | |
6 | * Author: Arnd Bergmann <arndb@de.ibm.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2, or (at your option) | |
11 | * any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | */ | |
22 | ||
23 | #include <linux/config.h> | |
24 | #include <linux/interrupt.h> | |
25 | #include <linux/irq.h> | |
26 | #include <linux/percpu.h> | |
27 | #include <linux/types.h> | |
28 | ||
29 | #include <asm/io.h> | |
30 | #include <asm/pgtable.h> | |
31 | #include <asm/prom.h> | |
32 | #include <asm/ptrace.h> | |
33 | ||
34 | #include "bpa_iic.h" | |
35 | ||
36 | struct iic_pending_bits { | |
37 | u32 data; | |
38 | u8 flags; | |
39 | u8 class; | |
40 | u8 source; | |
41 | u8 prio; | |
42 | }; | |
43 | ||
44 | enum iic_pending_flags { | |
45 | IIC_VALID = 0x80, | |
46 | IIC_IPI = 0x40, | |
47 | }; | |
48 | ||
49 | struct iic_regs { | |
50 | struct iic_pending_bits pending; | |
51 | struct iic_pending_bits pending_destr; | |
52 | u64 generate; | |
53 | u64 prio; | |
54 | }; | |
55 | ||
56 | struct iic { | |
57 | struct iic_regs __iomem *regs; | |
58 | }; | |
59 | ||
60 | static DEFINE_PER_CPU(struct iic, iic); | |
61 | ||
62 | void iic_local_enable(void) | |
63 | { | |
64 | out_be64(&__get_cpu_var(iic).regs->prio, 0xff); | |
65 | } | |
66 | ||
67 | void iic_local_disable(void) | |
68 | { | |
69 | out_be64(&__get_cpu_var(iic).regs->prio, 0x0); | |
70 | } | |
71 | ||
72 | static unsigned int iic_startup(unsigned int irq) | |
73 | { | |
74 | return 0; | |
75 | } | |
76 | ||
77 | static void iic_enable(unsigned int irq) | |
78 | { | |
79 | iic_local_enable(); | |
80 | } | |
81 | ||
82 | static void iic_disable(unsigned int irq) | |
83 | { | |
84 | } | |
85 | ||
86 | static void iic_end(unsigned int irq) | |
87 | { | |
88 | iic_local_enable(); | |
89 | } | |
90 | ||
91 | static struct hw_interrupt_type iic_pic = { | |
92 | .typename = " BPA-IIC ", | |
93 | .startup = iic_startup, | |
94 | .enable = iic_enable, | |
95 | .disable = iic_disable, | |
96 | .end = iic_end, | |
97 | }; | |
98 | ||
99 | static int iic_external_get_irq(struct iic_pending_bits pending) | |
100 | { | |
101 | int irq; | |
102 | unsigned char node, unit; | |
103 | ||
104 | node = pending.source >> 4; | |
105 | unit = pending.source & 0xf; | |
106 | irq = -1; | |
107 | ||
108 | /* | |
109 | * This mapping is specific to the Broadband | |
110 | * Engine. We might need to get the numbers | |
111 | * from the device tree to support future CPUs. | |
112 | */ | |
113 | switch (unit) { | |
114 | case 0x00: | |
115 | case 0x0b: | |
116 | /* | |
117 | * One of these units can be connected | |
118 | * to an external interrupt controller. | |
119 | */ | |
120 | if (pending.prio > 0x3f || | |
121 | pending.class != 2) | |
122 | break; | |
123 | irq = IIC_EXT_OFFSET | |
124 | + spider_get_irq(pending.prio + node * IIC_NODE_STRIDE) | |
125 | + node * IIC_NODE_STRIDE; | |
126 | break; | |
127 | case 0x01 ... 0x04: | |
128 | case 0x07 ... 0x0a: | |
129 | /* | |
130 | * These units are connected to the SPEs | |
131 | */ | |
132 | if (pending.class > 2) | |
133 | break; | |
134 | irq = IIC_SPE_OFFSET | |
135 | + pending.class * IIC_CLASS_STRIDE | |
136 | + node * IIC_NODE_STRIDE | |
137 | + unit; | |
138 | break; | |
139 | } | |
140 | if (irq == -1) | |
141 | printk(KERN_WARNING "Unexpected interrupt class %02x, " | |
142 | "source %02x, prio %02x, cpu %02x\n", pending.class, | |
143 | pending.source, pending.prio, smp_processor_id()); | |
144 | return irq; | |
145 | } | |
146 | ||
147 | /* Get an IRQ number from the pending state register of the IIC */ | |
148 | int iic_get_irq(struct pt_regs *regs) | |
149 | { | |
150 | struct iic *iic; | |
151 | int irq; | |
152 | struct iic_pending_bits pending; | |
153 | ||
154 | iic = &__get_cpu_var(iic); | |
155 | *(unsigned long *) &pending = | |
156 | in_be64((unsigned long __iomem *) &iic->regs->pending_destr); | |
157 | ||
158 | irq = -1; | |
159 | if (pending.flags & IIC_VALID) { | |
160 | if (pending.flags & IIC_IPI) { | |
161 | irq = IIC_IPI_OFFSET + (pending.prio >> 4); | |
162 | /* | |
163 | if (irq > 0x80) | |
164 | printk(KERN_WARNING "Unexpected IPI prio %02x" | |
165 | "on CPU %02x\n", pending.prio, | |
166 | smp_processor_id()); | |
167 | */ | |
168 | } else { | |
169 | irq = iic_external_get_irq(pending); | |
170 | } | |
171 | } | |
172 | return irq; | |
173 | } | |
174 | ||
175 | static struct iic_regs __iomem *find_iic(int cpu) | |
176 | { | |
177 | struct device_node *np; | |
178 | int nodeid = cpu / 2; | |
179 | unsigned long regs; | |
180 | struct iic_regs __iomem *iic_regs; | |
181 | ||
182 | for (np = of_find_node_by_type(NULL, "cpu"); | |
183 | np; | |
184 | np = of_find_node_by_type(np, "cpu")) { | |
185 | if (nodeid == *(int *)get_property(np, "node-id", NULL)) | |
186 | break; | |
187 | } | |
188 | ||
189 | if (!np) { | |
190 | printk(KERN_WARNING "IIC: CPU %d not found\n", cpu); | |
191 | iic_regs = NULL; | |
192 | } else { | |
193 | regs = *(long *)get_property(np, "iic", NULL); | |
194 | ||
195 | /* hack until we have decided on the devtree info */ | |
196 | regs += 0x400; | |
197 | if (cpu & 1) | |
198 | regs += 0x20; | |
199 | ||
200 | printk(KERN_DEBUG "IIC for CPU %d at %lx\n", cpu, regs); | |
201 | iic_regs = __ioremap(regs, sizeof(struct iic_regs), | |
202 | _PAGE_NO_CACHE); | |
203 | } | |
204 | return iic_regs; | |
205 | } | |
206 | ||
207 | #ifdef CONFIG_SMP | |
208 | void iic_setup_cpu(void) | |
209 | { | |
210 | out_be64(&__get_cpu_var(iic).regs->prio, 0xff); | |
211 | } | |
212 | ||
213 | void iic_cause_IPI(int cpu, int mesg) | |
214 | { | |
215 | out_be64(&per_cpu(iic, cpu).regs->generate, mesg); | |
216 | } | |
217 | ||
218 | static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs) | |
219 | { | |
220 | ||
221 | smp_message_recv(irq - IIC_IPI_OFFSET, regs); | |
222 | return IRQ_HANDLED; | |
223 | } | |
224 | ||
225 | static void iic_request_ipi(int irq, const char *name) | |
226 | { | |
227 | /* IPIs are marked SA_INTERRUPT as they must run with irqs | |
228 | * disabled */ | |
229 | get_irq_desc(irq)->handler = &iic_pic; | |
230 | get_irq_desc(irq)->status |= IRQ_PER_CPU; | |
231 | request_irq(irq, iic_ipi_action, SA_INTERRUPT, name, NULL); | |
232 | } | |
233 | ||
234 | void iic_request_IPIs(void) | |
235 | { | |
236 | iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_CALL_FUNCTION, "IPI-call"); | |
237 | iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_RESCHEDULE, "IPI-resched"); | |
238 | #ifdef CONFIG_DEBUGGER | |
239 | iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_DEBUGGER_BREAK, "IPI-debug"); | |
240 | #endif /* CONFIG_DEBUGGER */ | |
241 | } | |
242 | #endif /* CONFIG_SMP */ | |
243 | ||
244 | static void iic_setup_spe_handlers(void) | |
245 | { | |
246 | int be, isrc; | |
247 | ||
248 | /* Assume two threads per BE are present */ | |
249 | for (be=0; be < num_present_cpus() / 2; be++) { | |
250 | for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) { | |
251 | int irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc; | |
252 | get_irq_desc(irq)->handler = &iic_pic; | |
253 | } | |
254 | } | |
255 | } | |
256 | ||
257 | void iic_init_IRQ(void) | |
258 | { | |
259 | int cpu, irq_offset; | |
260 | struct iic *iic; | |
261 | ||
262 | irq_offset = 0; | |
263 | for_each_cpu(cpu) { | |
264 | iic = &per_cpu(iic, cpu); | |
265 | iic->regs = find_iic(cpu); | |
266 | if (iic->regs) | |
267 | out_be64(&iic->regs->prio, 0xff); | |
268 | } | |
269 | iic_setup_spe_handlers(); | |
270 | } |