2 * Cell Internal Interrupt Controller
4 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
6 * Author: Arnd Bergmann <arndb@de.ibm.com>
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)
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.
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.
23 #include <linux/config.h>
24 #include <linux/interrupt.h>
25 #include <linux/irq.h>
26 #include <linux/module.h>
27 #include <linux/percpu.h>
28 #include <linux/types.h>
31 #include <asm/pgtable.h>
33 #include <asm/ptrace.h>
35 #include "interrupt.h"
37 struct iic_pending_bits
{
45 enum iic_pending_flags
{
51 struct iic_pending_bits pending
;
52 struct iic_pending_bits pending_destr
;
58 struct iic_regs __iomem
*regs
;
62 static DEFINE_PER_CPU(struct iic
, iic
);
64 void iic_local_enable(void)
66 out_be64(&__get_cpu_var(iic
).regs
->prio
, 0xff);
69 void iic_local_disable(void)
71 out_be64(&__get_cpu_var(iic
).regs
->prio
, 0x0);
74 static unsigned int iic_startup(unsigned int irq
)
79 static void iic_enable(unsigned int irq
)
84 static void iic_disable(unsigned int irq
)
88 static void iic_end(unsigned int irq
)
93 static struct hw_interrupt_type iic_pic
= {
94 .typename
= " CELL-IIC ",
95 .startup
= iic_startup
,
97 .disable
= iic_disable
,
101 static int iic_external_get_irq(struct iic_pending_bits pending
)
104 unsigned char node
, unit
;
106 node
= pending
.source
>> 4;
107 unit
= pending
.source
& 0xf;
111 * This mapping is specific to the Cell Broadband
112 * Engine. We might need to get the numbers
113 * from the device tree to support future CPUs.
119 * One of these units can be connected
120 * to an external interrupt controller.
122 if (pending
.prio
> 0x3f ||
126 + spider_get_irq(pending
.prio
+ node
* IIC_NODE_STRIDE
)
127 + node
* IIC_NODE_STRIDE
;
132 * These units are connected to the SPEs
134 if (pending
.class > 2)
137 + pending
.class * IIC_CLASS_STRIDE
138 + node
* IIC_NODE_STRIDE
143 printk(KERN_WARNING
"Unexpected interrupt class %02x, "
144 "source %02x, prio %02x, cpu %02x\n", pending
.class,
145 pending
.source
, pending
.prio
, smp_processor_id());
149 /* Get an IRQ number from the pending state register of the IIC */
150 int iic_get_irq(struct pt_regs
*regs
)
154 struct iic_pending_bits pending
;
156 iic
= &__get_cpu_var(iic
);
157 *(unsigned long *) &pending
=
158 in_be64((unsigned long __iomem
*) &iic
->regs
->pending_destr
);
161 if (pending
.flags
& IIC_VALID
) {
162 if (pending
.flags
& IIC_IPI
) {
163 irq
= IIC_IPI_OFFSET
+ (pending
.prio
>> 4);
166 printk(KERN_WARNING "Unexpected IPI prio %02x"
167 "on CPU %02x\n", pending.prio,
171 irq
= iic_external_get_irq(pending
);
177 static int setup_iic(int cpu
, struct iic
*iic
)
179 struct device_node
*np
;
180 int nodeid
= cpu
/ 2;
183 for (np
= of_find_node_by_type(NULL
, "cpu");
185 np
= of_find_node_by_type(np
, "cpu")) {
186 if (nodeid
== *(int *)get_property(np
, "node-id", NULL
))
191 printk(KERN_WARNING
"IIC: CPU %d not found\n", cpu
);
193 iic
->target_id
= 0xff;
197 regs
= *(long *)get_property(np
, "iic", NULL
);
199 /* hack until we have decided on the devtree info */
204 printk(KERN_DEBUG
"IIC for CPU %d at %lx\n", cpu
, regs
);
205 iic
->regs
= __ioremap(regs
, sizeof(struct iic_regs
),
207 iic
->target_id
= (nodeid
<< 4) + ((cpu
& 1) ? 0xf : 0xe);
213 /* Use the highest interrupt priorities for IPI */
214 static inline int iic_ipi_to_irq(int ipi
)
216 return IIC_IPI_OFFSET
+ IIC_NUM_IPIS
- 1 - ipi
;
219 static inline int iic_irq_to_ipi(int irq
)
221 return IIC_NUM_IPIS
- 1 - (irq
- IIC_IPI_OFFSET
);
224 void iic_setup_cpu(void)
226 out_be64(&__get_cpu_var(iic
).regs
->prio
, 0xff);
229 void iic_cause_IPI(int cpu
, int mesg
)
231 out_be64(&per_cpu(iic
, cpu
).regs
->generate
, (IIC_NUM_IPIS
- 1 - mesg
) << 4);
234 u8
iic_get_target_id(int cpu
)
236 return per_cpu(iic
, cpu
).target_id
;
238 EXPORT_SYMBOL_GPL(iic_get_target_id
);
240 static irqreturn_t
iic_ipi_action(int irq
, void *dev_id
, struct pt_regs
*regs
)
242 smp_message_recv(iic_irq_to_ipi(irq
), regs
);
246 static void iic_request_ipi(int ipi
, const char *name
)
250 irq
= iic_ipi_to_irq(ipi
);
251 /* IPIs are marked SA_INTERRUPT as they must run with irqs
253 get_irq_desc(irq
)->handler
= &iic_pic
;
254 get_irq_desc(irq
)->status
|= IRQ_PER_CPU
;
255 request_irq(irq
, iic_ipi_action
, SA_INTERRUPT
, name
, NULL
);
258 void iic_request_IPIs(void)
260 iic_request_ipi(PPC_MSG_CALL_FUNCTION
, "IPI-call");
261 iic_request_ipi(PPC_MSG_RESCHEDULE
, "IPI-resched");
262 #ifdef CONFIG_DEBUGGER
263 iic_request_ipi(PPC_MSG_DEBUGGER_BREAK
, "IPI-debug");
264 #endif /* CONFIG_DEBUGGER */
266 #endif /* CONFIG_SMP */
268 static void iic_setup_spe_handlers(void)
272 /* Assume two threads per BE are present */
273 for (be
=0; be
< num_present_cpus() / 2; be
++) {
274 for (isrc
= 0; isrc
< IIC_CLASS_STRIDE
* 3; isrc
++) {
275 int irq
= IIC_NODE_STRIDE
* be
+ IIC_SPE_OFFSET
+ isrc
;
276 get_irq_desc(irq
)->handler
= &iic_pic
;
281 void iic_init_IRQ(void)
288 iic
= &per_cpu(iic
, cpu
);
291 out_be64(&iic
->regs
->prio
, 0xff);
293 iic_setup_spe_handlers();