2 * Cell Internal Interrupt Controller
4 * Copyright (C) 2006 Benjamin Herrenschmidt (benh@kernel.crashing.org)
7 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
9 * Author: Arnd Bergmann <arndb@de.ibm.com>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2, or (at your option)
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <linux/interrupt.h>
27 #include <linux/irq.h>
28 #include <linux/module.h>
29 #include <linux/percpu.h>
30 #include <linux/types.h>
31 #include <linux/ioport.h>
34 #include <asm/pgtable.h>
36 #include <asm/ptrace.h>
37 #include <asm/machdep.h>
39 #include "interrupt.h"
43 struct cbe_iic_thread_regs __iomem
*regs
;
47 struct irq_host
*host
;
50 static DEFINE_PER_CPU(struct iic
, iic
);
51 #define IIC_NODE_COUNT 2
52 static struct irq_host
*iic_hosts
[IIC_NODE_COUNT
];
54 /* Convert between "pending" bits and hw irq number */
55 static irq_hw_number_t
iic_pending_to_hwnum(struct cbe_iic_pending_bits bits
)
57 unsigned char unit
= bits
.source
& 0xf;
59 if (bits
.flags
& CBE_IIC_IRQ_IPI
)
60 return IIC_IRQ_IPI0
| (bits
.prio
>> 4);
61 else if (bits
.class <= 3)
62 return (bits
.class << 4) | unit
;
64 return IIC_IRQ_INVALID
;
67 static void iic_mask(unsigned int irq
)
71 static void iic_unmask(unsigned int irq
)
75 static void iic_eoi(unsigned int irq
)
77 struct iic
*iic
= &__get_cpu_var(iic
);
78 out_be64(&iic
->regs
->prio
, iic
->eoi_stack
[--iic
->eoi_ptr
]);
79 BUG_ON(iic
->eoi_ptr
< 0);
82 static struct irq_chip iic_chip
= {
83 .typename
= " CELL-IIC ",
89 /* Get an IRQ number from the pending state register of the IIC */
90 static unsigned int iic_get_irq(struct pt_regs
*regs
)
92 struct cbe_iic_pending_bits pending
;
95 iic
= &__get_cpu_var(iic
);
96 *(unsigned long *) &pending
=
97 in_be64((unsigned long __iomem
*) &iic
->regs
->pending_destr
);
98 iic
->eoi_stack
[++iic
->eoi_ptr
] = pending
.prio
;
99 BUG_ON(iic
->eoi_ptr
> 15);
100 if (pending
.flags
& CBE_IIC_IRQ_VALID
)
101 return irq_linear_revmap(iic
->host
,
102 iic_pending_to_hwnum(pending
));
108 /* Use the highest interrupt priorities for IPI */
109 static inline int iic_ipi_to_irq(int ipi
)
111 return IIC_IRQ_IPI0
+ IIC_NUM_IPIS
- 1 - ipi
;
114 static inline int iic_irq_to_ipi(int irq
)
116 return IIC_NUM_IPIS
- 1 - (irq
- IIC_IRQ_IPI0
);
119 void iic_setup_cpu(void)
121 out_be64(&__get_cpu_var(iic
).regs
->prio
, 0xff);
124 void iic_cause_IPI(int cpu
, int mesg
)
126 out_be64(&per_cpu(iic
, cpu
).regs
->generate
, (IIC_NUM_IPIS
- 1 - mesg
) << 4);
129 u8
iic_get_target_id(int cpu
)
131 return per_cpu(iic
, cpu
).target_id
;
133 EXPORT_SYMBOL_GPL(iic_get_target_id
);
135 struct irq_host
*iic_get_irq_host(int node
)
137 if (node
< 0 || node
>= IIC_NODE_COUNT
)
139 return iic_hosts
[node
];
141 EXPORT_SYMBOL_GPL(iic_get_irq_host
);
144 static irqreturn_t
iic_ipi_action(int irq
, void *dev_id
, struct pt_regs
*regs
)
146 int ipi
= (int)(long)dev_id
;
148 smp_message_recv(ipi
, regs
);
153 static void iic_request_ipi(int ipi
, const char *name
)
157 for (node
= 0; node
< IIC_NODE_COUNT
; node
++) {
159 if (iic_hosts
[node
] == NULL
)
161 virq
= irq_create_mapping(iic_hosts
[node
],
162 iic_ipi_to_irq(ipi
));
163 if (virq
== NO_IRQ
) {
165 "iic: failed to map IPI %s on node %d\n",
169 rname
= kzalloc(strlen(name
) + 16, GFP_KERNEL
);
171 sprintf(rname
, "%s node %d", name
, node
);
173 rname
= (char *)name
;
174 if (request_irq(virq
, iic_ipi_action
, IRQF_DISABLED
,
175 rname
, (void *)(long)ipi
))
177 "iic: failed to request IPI %s on node %d\n",
182 void iic_request_IPIs(void)
184 iic_request_ipi(PPC_MSG_CALL_FUNCTION
, "IPI-call");
185 iic_request_ipi(PPC_MSG_RESCHEDULE
, "IPI-resched");
186 #ifdef CONFIG_DEBUGGER
187 iic_request_ipi(PPC_MSG_DEBUGGER_BREAK
, "IPI-debug");
188 #endif /* CONFIG_DEBUGGER */
191 #endif /* CONFIG_SMP */
194 static int iic_host_match(struct irq_host
*h
, struct device_node
*node
)
196 return h
->host_data
!= NULL
&& node
== h
->host_data
;
199 static int iic_host_map(struct irq_host
*h
, unsigned int virq
,
202 if (hw
< IIC_IRQ_IPI0
)
203 set_irq_chip_and_handler(virq
, &iic_chip
, handle_fasteoi_irq
);
205 set_irq_chip_and_handler(virq
, &iic_chip
, handle_percpu_irq
);
209 static int iic_host_xlate(struct irq_host
*h
, struct device_node
*ct
,
210 u32
*intspec
, unsigned int intsize
,
211 irq_hw_number_t
*out_hwirq
, unsigned int *out_flags
)
214 /* Currently, we don't translate anything. That needs to be fixed as
215 * we get better defined device-trees. iic interrupts have to be
216 * explicitely mapped by whoever needs them
221 static struct irq_host_ops iic_host_ops
= {
222 .match
= iic_host_match
,
224 .xlate
= iic_host_xlate
,
227 static void __init
init_one_iic(unsigned int hw_cpu
, unsigned long addr
,
228 struct irq_host
*host
)
230 /* XXX FIXME: should locate the linux CPU number from the HW cpu
231 * number properly. We are lucky for now
233 struct iic
*iic
= &per_cpu(iic
, hw_cpu
);
235 iic
->regs
= ioremap(addr
, sizeof(struct cbe_iic_thread_regs
));
236 BUG_ON(iic
->regs
== NULL
);
238 iic
->target_id
= ((hw_cpu
& 2) << 3) | ((hw_cpu
& 1) ? 0xf : 0xe);
239 iic
->eoi_stack
[0] = 0xff;
241 out_be64(&iic
->regs
->prio
, 0);
243 printk(KERN_INFO
"IIC for CPU %d at %lx mapped to %p, target id 0x%x\n",
244 hw_cpu
, addr
, iic
->regs
, iic
->target_id
);
247 static int __init
setup_iic(void)
249 struct device_node
*dn
;
250 struct resource r0
, r1
;
251 struct irq_host
*host
;
256 (dn
= of_find_node_by_name(dn
,"interrupt-controller")) != NULL
;) {
257 if (!device_is_compatible(dn
,
258 "IBM,CBEA-Internal-Interrupt-Controller"))
260 np
= (u32
*)get_property(dn
, "ibm,interrupt-server-ranges",
263 printk(KERN_WARNING
"IIC: CPU association not found\n");
267 if (of_address_to_resource(dn
, 0, &r0
) ||
268 of_address_to_resource(dn
, 1, &r1
)) {
269 printk(KERN_WARNING
"IIC: Can't resolve addresses\n");
274 if (found
< IIC_NODE_COUNT
) {
275 host
= irq_alloc_host(IRQ_HOST_MAP_LINEAR
,
279 iic_hosts
[found
] = host
;
280 BUG_ON(iic_hosts
[found
] == NULL
);
281 iic_hosts
[found
]->host_data
= of_node_get(dn
);
284 init_one_iic(np
[0], r0
.start
, host
);
285 init_one_iic(np
[1], r1
.start
, host
);
294 void __init
iic_init_IRQ(void)
296 /* Discover and initialize iics */
298 panic("IIC: Failed to initialize !\n");
300 /* Set master interrupt handling function */
301 ppc_md
.get_irq
= iic_get_irq
;
303 /* Enable on current CPU */