2 * BPA 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/percpu.h>
27 #include <linux/types.h>
30 #include <asm/pgtable.h>
32 #include <asm/ptrace.h>
36 struct iic_pending_bits
{
44 enum iic_pending_flags
{
50 struct iic_pending_bits pending
;
51 struct iic_pending_bits pending_destr
;
57 struct iic_regs __iomem
*regs
;
60 static DEFINE_PER_CPU(struct iic
, iic
);
62 void iic_local_enable(void)
64 out_be64(&__get_cpu_var(iic
).regs
->prio
, 0xff);
67 void iic_local_disable(void)
69 out_be64(&__get_cpu_var(iic
).regs
->prio
, 0x0);
72 static unsigned int iic_startup(unsigned int irq
)
77 static void iic_enable(unsigned int irq
)
82 static void iic_disable(unsigned int irq
)
86 static void iic_end(unsigned int irq
)
91 static struct hw_interrupt_type iic_pic
= {
92 .typename
= " BPA-IIC ",
93 .startup
= iic_startup
,
95 .disable
= iic_disable
,
99 static int iic_external_get_irq(struct iic_pending_bits pending
)
102 unsigned char node
, unit
;
104 node
= pending
.source
>> 4;
105 unit
= pending
.source
& 0xf;
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.
117 * One of these units can be connected
118 * to an external interrupt controller.
120 if (pending
.prio
> 0x3f ||
124 + spider_get_irq(pending
.prio
+ node
* IIC_NODE_STRIDE
)
125 + node
* IIC_NODE_STRIDE
;
130 * These units are connected to the SPEs
132 if (pending
.class > 2)
135 + pending
.class * IIC_CLASS_STRIDE
136 + node
* IIC_NODE_STRIDE
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());
147 /* Get an IRQ number from the pending state register of the IIC */
148 int iic_get_irq(struct pt_regs
*regs
)
152 struct iic_pending_bits pending
;
154 iic
= &__get_cpu_var(iic
);
155 *(unsigned long *) &pending
=
156 in_be64((unsigned long __iomem
*) &iic
->regs
->pending_destr
);
159 if (pending
.flags
& IIC_VALID
) {
160 if (pending
.flags
& IIC_IPI
) {
161 irq
= IIC_IPI_OFFSET
+ (pending
.prio
>> 4);
164 printk(KERN_WARNING "Unexpected IPI prio %02x"
165 "on CPU %02x\n", pending.prio,
169 irq
= iic_external_get_irq(pending
);
175 static struct iic_regs __iomem
*find_iic(int cpu
)
177 struct device_node
*np
;
178 int nodeid
= cpu
/ 2;
180 struct iic_regs __iomem
*iic_regs
;
182 for (np
= of_find_node_by_type(NULL
, "cpu");
184 np
= of_find_node_by_type(np
, "cpu")) {
185 if (nodeid
== *(int *)get_property(np
, "node-id", NULL
))
190 printk(KERN_WARNING
"IIC: CPU %d not found\n", cpu
);
193 regs
= *(long *)get_property(np
, "iic", NULL
);
195 /* hack until we have decided on the devtree info */
200 printk(KERN_DEBUG
"IIC for CPU %d at %lx\n", cpu
, regs
);
201 iic_regs
= __ioremap(regs
, sizeof(struct iic_regs
),
208 void iic_setup_cpu(void)
210 out_be64(&__get_cpu_var(iic
).regs
->prio
, 0xff);
213 void iic_cause_IPI(int cpu
, int mesg
)
215 out_be64(&per_cpu(iic
, cpu
).regs
->generate
, mesg
);
218 static irqreturn_t
iic_ipi_action(int irq
, void *dev_id
, struct pt_regs
*regs
)
221 smp_message_recv(irq
- IIC_IPI_OFFSET
, regs
);
225 static void iic_request_ipi(int irq
, const char *name
)
227 /* IPIs are marked SA_INTERRUPT as they must run with irqs
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
);
234 void iic_request_IPIs(void)
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 */
242 #endif /* CONFIG_SMP */
244 static void iic_setup_spe_handlers(void)
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
;
257 void iic_init_IRQ(void)
264 iic
= &per_cpu(iic
, cpu
);
265 iic
->regs
= find_iic(cpu
);
267 out_be64(&iic
->regs
->prio
, 0xff);
269 iic_setup_spe_handlers();