xref: /openbmc/linux/arch/powerpc/platforms/ps3/interrupt.c (revision 2832a81df7f3cb7e7f912a256c156ddbd3450265)
1*2832a81dSGeoff Levand /*
2*2832a81dSGeoff Levand  *  PS3 interrupt routines.
3*2832a81dSGeoff Levand  *
4*2832a81dSGeoff Levand  *  Copyright (C) 2006 Sony Computer Entertainment Inc.
5*2832a81dSGeoff Levand  *  Copyright 2006 Sony Corp.
6*2832a81dSGeoff Levand  *
7*2832a81dSGeoff Levand  *  This program is free software; you can redistribute it and/or modify
8*2832a81dSGeoff Levand  *  it under the terms of the GNU General Public License as published by
9*2832a81dSGeoff Levand  *  the Free Software Foundation; version 2 of the License.
10*2832a81dSGeoff Levand  *
11*2832a81dSGeoff Levand  *  This program is distributed in the hope that it will be useful,
12*2832a81dSGeoff Levand  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13*2832a81dSGeoff Levand  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*2832a81dSGeoff Levand  *  GNU General Public License for more details.
15*2832a81dSGeoff Levand  *
16*2832a81dSGeoff Levand  *  You should have received a copy of the GNU General Public License
17*2832a81dSGeoff Levand  *  along with this program; if not, write to the Free Software
18*2832a81dSGeoff Levand  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19*2832a81dSGeoff Levand  */
20*2832a81dSGeoff Levand 
21*2832a81dSGeoff Levand #include <linux/kernel.h>
22*2832a81dSGeoff Levand #include <linux/module.h>
23*2832a81dSGeoff Levand #include <linux/irq.h>
24*2832a81dSGeoff Levand 
25*2832a81dSGeoff Levand #include <asm/machdep.h>
26*2832a81dSGeoff Levand #include <asm/udbg.h>
27*2832a81dSGeoff Levand #include <asm/ps3.h>
28*2832a81dSGeoff Levand #include <asm/lv1call.h>
29*2832a81dSGeoff Levand 
30*2832a81dSGeoff Levand #include "platform.h"
31*2832a81dSGeoff Levand 
32*2832a81dSGeoff Levand #if defined(DEBUG)
33*2832a81dSGeoff Levand #define DBG(fmt...) udbg_printf(fmt)
34*2832a81dSGeoff Levand #else
35*2832a81dSGeoff Levand #define DBG(fmt...) do{if(0)printk(fmt);}while(0)
36*2832a81dSGeoff Levand #endif
37*2832a81dSGeoff Levand 
38*2832a81dSGeoff Levand /**
39*2832a81dSGeoff Levand  * ps3_alloc_io_irq - Assign a virq to a system bus device.
40*2832a81dSGeoff Levand  * interrupt_id: The device interrupt id read from the system repository.
41*2832a81dSGeoff Levand  * @virq: The assigned Linux virq.
42*2832a81dSGeoff Levand  *
43*2832a81dSGeoff Levand  * An io irq represents a non-virtualized device interrupt.  interrupt_id
44*2832a81dSGeoff Levand  * coresponds to the interrupt number of the interrupt controller.
45*2832a81dSGeoff Levand  */
46*2832a81dSGeoff Levand 
47*2832a81dSGeoff Levand int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq)
48*2832a81dSGeoff Levand {
49*2832a81dSGeoff Levand 	int result;
50*2832a81dSGeoff Levand 	unsigned long outlet;
51*2832a81dSGeoff Levand 
52*2832a81dSGeoff Levand 	result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);
53*2832a81dSGeoff Levand 
54*2832a81dSGeoff Levand 	if (result) {
55*2832a81dSGeoff Levand 		pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
56*2832a81dSGeoff Levand 			__func__, __LINE__, ps3_result(result));
57*2832a81dSGeoff Levand 		return result;
58*2832a81dSGeoff Levand 	}
59*2832a81dSGeoff Levand 
60*2832a81dSGeoff Levand 	*virq = irq_create_mapping(NULL, outlet);
61*2832a81dSGeoff Levand 
62*2832a81dSGeoff Levand 	pr_debug("%s:%d: interrupt_id %u => outlet %lu, virq %u\n",
63*2832a81dSGeoff Levand 		__func__, __LINE__, interrupt_id, outlet, *virq);
64*2832a81dSGeoff Levand 
65*2832a81dSGeoff Levand 	return 0;
66*2832a81dSGeoff Levand }
67*2832a81dSGeoff Levand 
68*2832a81dSGeoff Levand int ps3_free_io_irq(unsigned int virq)
69*2832a81dSGeoff Levand {
70*2832a81dSGeoff Levand 	int result;
71*2832a81dSGeoff Levand 
72*2832a81dSGeoff Levand 	result = lv1_destruct_io_irq_outlet(virq_to_hw(virq));
73*2832a81dSGeoff Levand 
74*2832a81dSGeoff Levand 	if (!result)
75*2832a81dSGeoff Levand 		pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
76*2832a81dSGeoff Levand 			__func__, __LINE__, ps3_result(result));
77*2832a81dSGeoff Levand 
78*2832a81dSGeoff Levand 	irq_dispose_mapping(virq);
79*2832a81dSGeoff Levand 
80*2832a81dSGeoff Levand 	return result;
81*2832a81dSGeoff Levand }
82*2832a81dSGeoff Levand 
83*2832a81dSGeoff Levand /**
84*2832a81dSGeoff Levand  * ps3_alloc_event_irq - Allocate a virq for use with a system event.
85*2832a81dSGeoff Levand  * @virq: The assigned Linux virq.
86*2832a81dSGeoff Levand  *
87*2832a81dSGeoff Levand  * The virq can be used with lv1_connect_interrupt_event_receive_port() to
88*2832a81dSGeoff Levand  * arrange to receive events, or with ps3_send_event_locally() to signal
89*2832a81dSGeoff Levand  * events.
90*2832a81dSGeoff Levand  */
91*2832a81dSGeoff Levand 
92*2832a81dSGeoff Levand int ps3_alloc_event_irq(unsigned int *virq)
93*2832a81dSGeoff Levand {
94*2832a81dSGeoff Levand 	int result;
95*2832a81dSGeoff Levand 	unsigned long outlet;
96*2832a81dSGeoff Levand 
97*2832a81dSGeoff Levand 	result = lv1_construct_event_receive_port(&outlet);
98*2832a81dSGeoff Levand 
99*2832a81dSGeoff Levand 	if (result) {
100*2832a81dSGeoff Levand 		pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n",
101*2832a81dSGeoff Levand 			__func__, __LINE__, ps3_result(result));
102*2832a81dSGeoff Levand 		*virq = NO_IRQ;
103*2832a81dSGeoff Levand 		return result;
104*2832a81dSGeoff Levand 	}
105*2832a81dSGeoff Levand 
106*2832a81dSGeoff Levand 	*virq = irq_create_mapping(NULL, outlet);
107*2832a81dSGeoff Levand 
108*2832a81dSGeoff Levand 	pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, outlet,
109*2832a81dSGeoff Levand 		*virq);
110*2832a81dSGeoff Levand 
111*2832a81dSGeoff Levand 	return 0;
112*2832a81dSGeoff Levand }
113*2832a81dSGeoff Levand 
114*2832a81dSGeoff Levand int ps3_free_event_irq(unsigned int virq)
115*2832a81dSGeoff Levand {
116*2832a81dSGeoff Levand 	int result;
117*2832a81dSGeoff Levand 
118*2832a81dSGeoff Levand 	pr_debug(" -> %s:%d\n", __func__, __LINE__);
119*2832a81dSGeoff Levand 
120*2832a81dSGeoff Levand 	result = lv1_destruct_event_receive_port(virq_to_hw(virq));
121*2832a81dSGeoff Levand 
122*2832a81dSGeoff Levand 	if (result)
123*2832a81dSGeoff Levand 		pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
124*2832a81dSGeoff Levand 			__func__, __LINE__, ps3_result(result));
125*2832a81dSGeoff Levand 
126*2832a81dSGeoff Levand 	irq_dispose_mapping(virq);
127*2832a81dSGeoff Levand 
128*2832a81dSGeoff Levand 	pr_debug(" <- %s:%d\n", __func__, __LINE__);
129*2832a81dSGeoff Levand 	return result;
130*2832a81dSGeoff Levand }
131*2832a81dSGeoff Levand 
132*2832a81dSGeoff Levand int ps3_send_event_locally(unsigned int virq)
133*2832a81dSGeoff Levand {
134*2832a81dSGeoff Levand 	return lv1_send_event_locally(virq_to_hw(virq));
135*2832a81dSGeoff Levand }
136*2832a81dSGeoff Levand 
137*2832a81dSGeoff Levand /**
138*2832a81dSGeoff Levand  * ps3_connect_event_irq - Assign a virq to a system bus device.
139*2832a81dSGeoff Levand  * @did: The HV device identifier read from the system repository.
140*2832a81dSGeoff Levand  * @interrupt_id: The device interrupt id read from the system repository.
141*2832a81dSGeoff Levand  * @virq: The assigned Linux virq.
142*2832a81dSGeoff Levand  *
143*2832a81dSGeoff Levand  * An event irq represents a virtual device interrupt.  The interrupt_id
144*2832a81dSGeoff Levand  * coresponds to the software interrupt number.
145*2832a81dSGeoff Levand  */
146*2832a81dSGeoff Levand 
147*2832a81dSGeoff Levand int ps3_connect_event_irq(const struct ps3_device_id *did,
148*2832a81dSGeoff Levand 	unsigned int interrupt_id, unsigned int *virq)
149*2832a81dSGeoff Levand {
150*2832a81dSGeoff Levand 	int result;
151*2832a81dSGeoff Levand 
152*2832a81dSGeoff Levand 	result = ps3_alloc_event_irq(virq);
153*2832a81dSGeoff Levand 
154*2832a81dSGeoff Levand 	if (result)
155*2832a81dSGeoff Levand 		return result;
156*2832a81dSGeoff Levand 
157*2832a81dSGeoff Levand 	result = lv1_connect_interrupt_event_receive_port(did->bus_id,
158*2832a81dSGeoff Levand 		did->dev_id, virq_to_hw(*virq), interrupt_id);
159*2832a81dSGeoff Levand 
160*2832a81dSGeoff Levand 	if (result) {
161*2832a81dSGeoff Levand 		pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port"
162*2832a81dSGeoff Levand 			" failed: %s\n", __func__, __LINE__,
163*2832a81dSGeoff Levand 			ps3_result(result));
164*2832a81dSGeoff Levand 		ps3_free_event_irq(*virq);
165*2832a81dSGeoff Levand 		*virq = NO_IRQ;
166*2832a81dSGeoff Levand 		return result;
167*2832a81dSGeoff Levand 	}
168*2832a81dSGeoff Levand 
169*2832a81dSGeoff Levand 	pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
170*2832a81dSGeoff Levand 		interrupt_id, *virq);
171*2832a81dSGeoff Levand 
172*2832a81dSGeoff Levand 	return 0;
173*2832a81dSGeoff Levand }
174*2832a81dSGeoff Levand 
175*2832a81dSGeoff Levand int ps3_disconnect_event_irq(const struct ps3_device_id *did,
176*2832a81dSGeoff Levand 	unsigned int interrupt_id, unsigned int virq)
177*2832a81dSGeoff Levand {
178*2832a81dSGeoff Levand 	int result;
179*2832a81dSGeoff Levand 
180*2832a81dSGeoff Levand 	pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
181*2832a81dSGeoff Levand 		interrupt_id, virq);
182*2832a81dSGeoff Levand 
183*2832a81dSGeoff Levand 	result = lv1_disconnect_interrupt_event_receive_port(did->bus_id,
184*2832a81dSGeoff Levand 		did->dev_id, virq_to_hw(virq), interrupt_id);
185*2832a81dSGeoff Levand 
186*2832a81dSGeoff Levand 	if (result)
187*2832a81dSGeoff Levand 		pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port"
188*2832a81dSGeoff Levand 			" failed: %s\n", __func__, __LINE__,
189*2832a81dSGeoff Levand 			ps3_result(result));
190*2832a81dSGeoff Levand 
191*2832a81dSGeoff Levand 	ps3_free_event_irq(virq);
192*2832a81dSGeoff Levand 
193*2832a81dSGeoff Levand 	pr_debug(" <- %s:%d\n", __func__, __LINE__);
194*2832a81dSGeoff Levand 	return result;
195*2832a81dSGeoff Levand }
196*2832a81dSGeoff Levand 
197*2832a81dSGeoff Levand /**
198*2832a81dSGeoff Levand  * ps3_alloc_vuart_irq - Configure the system virtual uart virq.
199*2832a81dSGeoff Levand  * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
200*2832a81dSGeoff Levand  * @virq: The assigned Linux virq.
201*2832a81dSGeoff Levand  *
202*2832a81dSGeoff Levand  * The system supports only a single virtual uart, so multiple calls without
203*2832a81dSGeoff Levand  * freeing the interrupt will return a wrong state error.
204*2832a81dSGeoff Levand  */
205*2832a81dSGeoff Levand 
206*2832a81dSGeoff Levand int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq)
207*2832a81dSGeoff Levand {
208*2832a81dSGeoff Levand 	int result;
209*2832a81dSGeoff Levand 	unsigned long outlet;
210*2832a81dSGeoff Levand 	unsigned long lpar_addr;
211*2832a81dSGeoff Levand 
212*2832a81dSGeoff Levand 	BUG_ON(!is_kernel_addr((unsigned long)virt_addr_bmp));
213*2832a81dSGeoff Levand 
214*2832a81dSGeoff Levand 	lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp));
215*2832a81dSGeoff Levand 
216*2832a81dSGeoff Levand 	result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet);
217*2832a81dSGeoff Levand 
218*2832a81dSGeoff Levand 	if (result) {
219*2832a81dSGeoff Levand 		pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
220*2832a81dSGeoff Levand 			__func__, __LINE__, ps3_result(result));
221*2832a81dSGeoff Levand 		return result;
222*2832a81dSGeoff Levand 	}
223*2832a81dSGeoff Levand 
224*2832a81dSGeoff Levand 	*virq = irq_create_mapping(NULL, outlet);
225*2832a81dSGeoff Levand 
226*2832a81dSGeoff Levand 	pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__,
227*2832a81dSGeoff Levand 		outlet, *virq);
228*2832a81dSGeoff Levand 
229*2832a81dSGeoff Levand 	return 0;
230*2832a81dSGeoff Levand }
231*2832a81dSGeoff Levand 
232*2832a81dSGeoff Levand int ps3_free_vuart_irq(unsigned int virq)
233*2832a81dSGeoff Levand {
234*2832a81dSGeoff Levand 	int result;
235*2832a81dSGeoff Levand 
236*2832a81dSGeoff Levand 	result = lv1_deconfigure_virtual_uart_irq();
237*2832a81dSGeoff Levand 
238*2832a81dSGeoff Levand 	if (result) {
239*2832a81dSGeoff Levand 		pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
240*2832a81dSGeoff Levand 			__func__, __LINE__, ps3_result(result));
241*2832a81dSGeoff Levand 		return result;
242*2832a81dSGeoff Levand 	}
243*2832a81dSGeoff Levand 
244*2832a81dSGeoff Levand 	irq_dispose_mapping(virq);
245*2832a81dSGeoff Levand 
246*2832a81dSGeoff Levand 	return result;
247*2832a81dSGeoff Levand }
248*2832a81dSGeoff Levand 
249*2832a81dSGeoff Levand /**
250*2832a81dSGeoff Levand  * ps3_alloc_spe_irq - Configure an spe virq.
251*2832a81dSGeoff Levand  * @spe_id: The spe_id returned from lv1_construct_logical_spe().
252*2832a81dSGeoff Levand  * @class: The spe interrupt class {0,1,2}.
253*2832a81dSGeoff Levand  * @virq: The assigned Linux virq.
254*2832a81dSGeoff Levand  *
255*2832a81dSGeoff Levand  */
256*2832a81dSGeoff Levand 
257*2832a81dSGeoff Levand int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class,
258*2832a81dSGeoff Levand 	unsigned int *virq)
259*2832a81dSGeoff Levand {
260*2832a81dSGeoff Levand 	int result;
261*2832a81dSGeoff Levand 	unsigned long outlet;
262*2832a81dSGeoff Levand 
263*2832a81dSGeoff Levand 	BUG_ON(class > 2);
264*2832a81dSGeoff Levand 
265*2832a81dSGeoff Levand 	result = lv1_get_spe_irq_outlet(spe_id, class, &outlet);
266*2832a81dSGeoff Levand 
267*2832a81dSGeoff Levand 	if (result) {
268*2832a81dSGeoff Levand 		pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n",
269*2832a81dSGeoff Levand 			__func__, __LINE__, ps3_result(result));
270*2832a81dSGeoff Levand 		return result;
271*2832a81dSGeoff Levand 	}
272*2832a81dSGeoff Levand 
273*2832a81dSGeoff Levand 	*virq = irq_create_mapping(NULL, outlet);
274*2832a81dSGeoff Levand 
275*2832a81dSGeoff Levand 	pr_debug("%s:%d: spe_id %lu, class %u, outlet %lu, virq %u\n",
276*2832a81dSGeoff Levand 		__func__, __LINE__, spe_id, class, outlet, *virq);
277*2832a81dSGeoff Levand 
278*2832a81dSGeoff Levand 	return 0;
279*2832a81dSGeoff Levand }
280*2832a81dSGeoff Levand 
281*2832a81dSGeoff Levand int ps3_free_spe_irq(unsigned int virq)
282*2832a81dSGeoff Levand {
283*2832a81dSGeoff Levand 	irq_dispose_mapping(virq);
284*2832a81dSGeoff Levand 	return 0;
285*2832a81dSGeoff Levand }
286*2832a81dSGeoff Levand 
287*2832a81dSGeoff Levand #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
288*2832a81dSGeoff Levand #define PS3_PLUG_MAX 63
289*2832a81dSGeoff Levand 
290*2832a81dSGeoff Levand /**
291*2832a81dSGeoff Levand  * struct bmp - a per cpu irq status and mask bitmap structure
292*2832a81dSGeoff Levand  * @status: 256 bit status bitmap indexed by plug
293*2832a81dSGeoff Levand  * @unused_1:
294*2832a81dSGeoff Levand  * @mask: 256 bit mask bitmap indexed by plug
295*2832a81dSGeoff Levand  * @unused_2:
296*2832a81dSGeoff Levand  * @lock:
297*2832a81dSGeoff Levand  * @ipi_debug_brk_mask:
298*2832a81dSGeoff Levand  *
299*2832a81dSGeoff Levand  * The HV mantains per SMT thread mappings of HV outlet to HV plug on
300*2832a81dSGeoff Levand  * behalf of the guest.  These mappings are implemented as 256 bit guest
301*2832a81dSGeoff Levand  * supplied bitmaps indexed by plug number.  The address of the bitmaps are
302*2832a81dSGeoff Levand  * registered with the HV through lv1_configure_irq_state_bitmap().
303*2832a81dSGeoff Levand  *
304*2832a81dSGeoff Levand  * The HV supports 256 plugs per thread, assigned as {0..255}, for a total
305*2832a81dSGeoff Levand  * of 512 plugs supported on a processor.  To simplify the logic this
306*2832a81dSGeoff Levand  * implementation equates HV plug value to linux virq value, constrains each
307*2832a81dSGeoff Levand  * interrupt to have a system wide unique plug number, and limits the range
308*2832a81dSGeoff Levand  * of the plug values to map into the first dword of the bitmaps.  This
309*2832a81dSGeoff Levand  * gives a usable range of plug values of  {NUM_ISA_INTERRUPTS..63}.  Note
310*2832a81dSGeoff Levand  * that there is no constraint on how many in this set an individual thread
311*2832a81dSGeoff Levand  * can aquire.
312*2832a81dSGeoff Levand  */
313*2832a81dSGeoff Levand 
314*2832a81dSGeoff Levand struct bmp {
315*2832a81dSGeoff Levand 	struct {
316*2832a81dSGeoff Levand 		unsigned long status;
317*2832a81dSGeoff Levand 		unsigned long unused_1[3];
318*2832a81dSGeoff Levand 		unsigned long mask;
319*2832a81dSGeoff Levand 		unsigned long unused_2[3];
320*2832a81dSGeoff Levand 	} __attribute__ ((packed));
321*2832a81dSGeoff Levand 	spinlock_t lock;
322*2832a81dSGeoff Levand 	unsigned long ipi_debug_brk_mask;
323*2832a81dSGeoff Levand };
324*2832a81dSGeoff Levand 
325*2832a81dSGeoff Levand /**
326*2832a81dSGeoff Levand  * struct private - a per cpu data structure
327*2832a81dSGeoff Levand  * @node: HV node id
328*2832a81dSGeoff Levand  * @cpu: HV thread id
329*2832a81dSGeoff Levand  * @bmp: an HV bmp structure
330*2832a81dSGeoff Levand  */
331*2832a81dSGeoff Levand 
332*2832a81dSGeoff Levand struct private {
333*2832a81dSGeoff Levand 	unsigned long node;
334*2832a81dSGeoff Levand 	unsigned int cpu;
335*2832a81dSGeoff Levand 	struct bmp bmp;
336*2832a81dSGeoff Levand };
337*2832a81dSGeoff Levand 
338*2832a81dSGeoff Levand #if defined(DEBUG)
339*2832a81dSGeoff Levand static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cpu,
340*2832a81dSGeoff Levand 	const char* func, int line)
341*2832a81dSGeoff Levand {
342*2832a81dSGeoff Levand 	pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n",
343*2832a81dSGeoff Levand 		func, line, header, cpu,
344*2832a81dSGeoff Levand 		*p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff,
345*2832a81dSGeoff Levand 		*p & 0xffff);
346*2832a81dSGeoff Levand }
347*2832a81dSGeoff Levand 
348*2832a81dSGeoff Levand static void __attribute__ ((unused)) _dump_256_bmp(const char *header,
349*2832a81dSGeoff Levand 	const unsigned long *p, unsigned cpu, const char* func, int line)
350*2832a81dSGeoff Levand {
351*2832a81dSGeoff Levand 	pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n",
352*2832a81dSGeoff Levand 		func, line, header, cpu, p[0], p[1], p[2], p[3]);
353*2832a81dSGeoff Levand }
354*2832a81dSGeoff Levand 
355*2832a81dSGeoff Levand #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__)
356*2832a81dSGeoff Levand static void _dump_bmp(struct private* pd, const char* func, int line)
357*2832a81dSGeoff Levand {
358*2832a81dSGeoff Levand 	unsigned long flags;
359*2832a81dSGeoff Levand 
360*2832a81dSGeoff Levand 	spin_lock_irqsave(&pd->bmp.lock, flags);
361*2832a81dSGeoff Levand 	_dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line);
362*2832a81dSGeoff Levand 	_dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line);
363*2832a81dSGeoff Levand 	spin_unlock_irqrestore(&pd->bmp.lock, flags);
364*2832a81dSGeoff Levand }
365*2832a81dSGeoff Levand 
366*2832a81dSGeoff Levand #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
367*2832a81dSGeoff Levand static void __attribute__ ((unused)) _dump_mask(struct private* pd,
368*2832a81dSGeoff Levand 	const char* func, int line)
369*2832a81dSGeoff Levand {
370*2832a81dSGeoff Levand 	unsigned long flags;
371*2832a81dSGeoff Levand 
372*2832a81dSGeoff Levand 	spin_lock_irqsave(&pd->bmp.lock, flags);
373*2832a81dSGeoff Levand 	_dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line);
374*2832a81dSGeoff Levand 	spin_unlock_irqrestore(&pd->bmp.lock, flags);
375*2832a81dSGeoff Levand }
376*2832a81dSGeoff Levand #else
377*2832a81dSGeoff Levand static void dump_bmp(struct private* pd) {};
378*2832a81dSGeoff Levand #endif /* defined(DEBUG) */
379*2832a81dSGeoff Levand 
380*2832a81dSGeoff Levand static void chip_mask(unsigned int virq)
381*2832a81dSGeoff Levand {
382*2832a81dSGeoff Levand 	unsigned long flags;
383*2832a81dSGeoff Levand 	struct private *pd = get_irq_chip_data(virq);
384*2832a81dSGeoff Levand 
385*2832a81dSGeoff Levand 	pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
386*2832a81dSGeoff Levand 
387*2832a81dSGeoff Levand 	BUG_ON(virq < NUM_ISA_INTERRUPTS);
388*2832a81dSGeoff Levand 	BUG_ON(virq > PS3_PLUG_MAX);
389*2832a81dSGeoff Levand 
390*2832a81dSGeoff Levand 	spin_lock_irqsave(&pd->bmp.lock, flags);
391*2832a81dSGeoff Levand 	pd->bmp.mask &= ~(0x8000000000000000UL >> virq);
392*2832a81dSGeoff Levand 	spin_unlock_irqrestore(&pd->bmp.lock, flags);
393*2832a81dSGeoff Levand 
394*2832a81dSGeoff Levand 	lv1_did_update_interrupt_mask(pd->node, pd->cpu);
395*2832a81dSGeoff Levand }
396*2832a81dSGeoff Levand 
397*2832a81dSGeoff Levand static void chip_unmask(unsigned int virq)
398*2832a81dSGeoff Levand {
399*2832a81dSGeoff Levand 	unsigned long flags;
400*2832a81dSGeoff Levand 	struct private *pd = get_irq_chip_data(virq);
401*2832a81dSGeoff Levand 
402*2832a81dSGeoff Levand 	pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
403*2832a81dSGeoff Levand 
404*2832a81dSGeoff Levand 	BUG_ON(virq < NUM_ISA_INTERRUPTS);
405*2832a81dSGeoff Levand 	BUG_ON(virq > PS3_PLUG_MAX);
406*2832a81dSGeoff Levand 
407*2832a81dSGeoff Levand 	spin_lock_irqsave(&pd->bmp.lock, flags);
408*2832a81dSGeoff Levand 	pd->bmp.mask |= (0x8000000000000000UL >> virq);
409*2832a81dSGeoff Levand 	spin_unlock_irqrestore(&pd->bmp.lock, flags);
410*2832a81dSGeoff Levand 
411*2832a81dSGeoff Levand 	lv1_did_update_interrupt_mask(pd->node, pd->cpu);
412*2832a81dSGeoff Levand }
413*2832a81dSGeoff Levand 
414*2832a81dSGeoff Levand static void chip_eoi(unsigned int virq)
415*2832a81dSGeoff Levand {
416*2832a81dSGeoff Levand 	lv1_end_of_interrupt(virq);
417*2832a81dSGeoff Levand }
418*2832a81dSGeoff Levand 
419*2832a81dSGeoff Levand static struct irq_chip irq_chip = {
420*2832a81dSGeoff Levand 	.typename = "ps3",
421*2832a81dSGeoff Levand 	.mask = chip_mask,
422*2832a81dSGeoff Levand 	.unmask = chip_unmask,
423*2832a81dSGeoff Levand 	.eoi = chip_eoi,
424*2832a81dSGeoff Levand };
425*2832a81dSGeoff Levand 
426*2832a81dSGeoff Levand static void host_unmap(struct irq_host *h, unsigned int virq)
427*2832a81dSGeoff Levand {
428*2832a81dSGeoff Levand 	int result;
429*2832a81dSGeoff Levand 
430*2832a81dSGeoff Levand 	pr_debug("%s:%d: virq %d\n", __func__, __LINE__, virq);
431*2832a81dSGeoff Levand 
432*2832a81dSGeoff Levand 	lv1_disconnect_irq_plug(virq);
433*2832a81dSGeoff Levand 
434*2832a81dSGeoff Levand 	result = set_irq_chip_data(virq, NULL);
435*2832a81dSGeoff Levand 	BUG_ON(result);
436*2832a81dSGeoff Levand }
437*2832a81dSGeoff Levand 
438*2832a81dSGeoff Levand static DEFINE_PER_CPU(struct private, private);
439*2832a81dSGeoff Levand 
440*2832a81dSGeoff Levand static int host_map(struct irq_host *h, unsigned int virq,
441*2832a81dSGeoff Levand 	irq_hw_number_t hwirq)
442*2832a81dSGeoff Levand {
443*2832a81dSGeoff Levand 	int result;
444*2832a81dSGeoff Levand 	unsigned int cpu;
445*2832a81dSGeoff Levand 
446*2832a81dSGeoff Levand 	pr_debug(" -> %s:%d\n", __func__, __LINE__);
447*2832a81dSGeoff Levand 	pr_debug("%s:%d: hwirq %lu => virq %u\n", __func__, __LINE__, hwirq,
448*2832a81dSGeoff Levand 		virq);
449*2832a81dSGeoff Levand 
450*2832a81dSGeoff Levand 	/* bind this virq to a cpu */
451*2832a81dSGeoff Levand 
452*2832a81dSGeoff Levand 	preempt_disable();
453*2832a81dSGeoff Levand 	cpu = smp_processor_id();
454*2832a81dSGeoff Levand 	result = lv1_connect_irq_plug(virq, hwirq);
455*2832a81dSGeoff Levand 	preempt_enable();
456*2832a81dSGeoff Levand 
457*2832a81dSGeoff Levand 	if (result) {
458*2832a81dSGeoff Levand 		pr_info("%s:%d: lv1_connect_irq_plug failed:"
459*2832a81dSGeoff Levand 			" %s\n", __func__, __LINE__, ps3_result(result));
460*2832a81dSGeoff Levand 		return -EPERM;
461*2832a81dSGeoff Levand 	}
462*2832a81dSGeoff Levand 
463*2832a81dSGeoff Levand 	result = set_irq_chip_data(virq, &per_cpu(private, cpu));
464*2832a81dSGeoff Levand 	BUG_ON(result);
465*2832a81dSGeoff Levand 
466*2832a81dSGeoff Levand 	set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq);
467*2832a81dSGeoff Levand 
468*2832a81dSGeoff Levand 	pr_debug(" <- %s:%d\n", __func__, __LINE__);
469*2832a81dSGeoff Levand 	return result;
470*2832a81dSGeoff Levand }
471*2832a81dSGeoff Levand 
472*2832a81dSGeoff Levand static struct irq_host_ops host_ops = {
473*2832a81dSGeoff Levand 	.map = host_map,
474*2832a81dSGeoff Levand 	.unmap = host_unmap,
475*2832a81dSGeoff Levand };
476*2832a81dSGeoff Levand 
477*2832a81dSGeoff Levand void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
478*2832a81dSGeoff Levand {
479*2832a81dSGeoff Levand 	struct private *pd = &per_cpu(private, cpu);
480*2832a81dSGeoff Levand 
481*2832a81dSGeoff Levand 	pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq;
482*2832a81dSGeoff Levand 
483*2832a81dSGeoff Levand 	pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__,
484*2832a81dSGeoff Levand 		cpu, virq, pd->bmp.ipi_debug_brk_mask);
485*2832a81dSGeoff Levand }
486*2832a81dSGeoff Levand 
487*2832a81dSGeoff Levand static int bmp_get_and_clear_status_bit(struct bmp *m)
488*2832a81dSGeoff Levand {
489*2832a81dSGeoff Levand 	unsigned long flags;
490*2832a81dSGeoff Levand 	unsigned int bit;
491*2832a81dSGeoff Levand 	unsigned long x;
492*2832a81dSGeoff Levand 
493*2832a81dSGeoff Levand 	spin_lock_irqsave(&m->lock, flags);
494*2832a81dSGeoff Levand 
495*2832a81dSGeoff Levand 	/* check for ipi break first to stop this cpu ASAP */
496*2832a81dSGeoff Levand 
497*2832a81dSGeoff Levand 	if (m->status & m->ipi_debug_brk_mask) {
498*2832a81dSGeoff Levand 		m->status &= ~m->ipi_debug_brk_mask;
499*2832a81dSGeoff Levand 		spin_unlock_irqrestore(&m->lock, flags);
500*2832a81dSGeoff Levand 		return __ilog2(m->ipi_debug_brk_mask);
501*2832a81dSGeoff Levand 	}
502*2832a81dSGeoff Levand 
503*2832a81dSGeoff Levand 	x = (m->status & m->mask);
504*2832a81dSGeoff Levand 
505*2832a81dSGeoff Levand 	for (bit = NUM_ISA_INTERRUPTS, x <<= bit; x; bit++, x <<= 1)
506*2832a81dSGeoff Levand 		if (x & 0x8000000000000000UL) {
507*2832a81dSGeoff Levand 			m->status &= ~(0x8000000000000000UL >> bit);
508*2832a81dSGeoff Levand 			spin_unlock_irqrestore(&m->lock, flags);
509*2832a81dSGeoff Levand 			return bit;
510*2832a81dSGeoff Levand 		}
511*2832a81dSGeoff Levand 
512*2832a81dSGeoff Levand 	spin_unlock_irqrestore(&m->lock, flags);
513*2832a81dSGeoff Levand 
514*2832a81dSGeoff Levand 	pr_debug("%s:%d: not found\n", __func__, __LINE__);
515*2832a81dSGeoff Levand 	return -1;
516*2832a81dSGeoff Levand }
517*2832a81dSGeoff Levand 
518*2832a81dSGeoff Levand unsigned int ps3_get_irq(void)
519*2832a81dSGeoff Levand {
520*2832a81dSGeoff Levand 	int plug;
521*2832a81dSGeoff Levand 
522*2832a81dSGeoff Levand 	struct private *pd = &__get_cpu_var(private);
523*2832a81dSGeoff Levand 
524*2832a81dSGeoff Levand 	plug = bmp_get_and_clear_status_bit(&pd->bmp);
525*2832a81dSGeoff Levand 
526*2832a81dSGeoff Levand 	if (plug < 1) {
527*2832a81dSGeoff Levand 		pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__,
528*2832a81dSGeoff Levand 			pd->cpu);
529*2832a81dSGeoff Levand 		dump_bmp(&per_cpu(private, 0));
530*2832a81dSGeoff Levand 		dump_bmp(&per_cpu(private, 1));
531*2832a81dSGeoff Levand 		return NO_IRQ;
532*2832a81dSGeoff Levand 	}
533*2832a81dSGeoff Levand 
534*2832a81dSGeoff Levand #if defined(DEBUG)
535*2832a81dSGeoff Levand 	if (plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX) {
536*2832a81dSGeoff Levand 		dump_bmp(&per_cpu(private, 0));
537*2832a81dSGeoff Levand 		dump_bmp(&per_cpu(private, 1));
538*2832a81dSGeoff Levand 		BUG();
539*2832a81dSGeoff Levand 	}
540*2832a81dSGeoff Levand #endif
541*2832a81dSGeoff Levand 	return plug;
542*2832a81dSGeoff Levand }
543*2832a81dSGeoff Levand 
544*2832a81dSGeoff Levand void __init ps3_init_IRQ(void)
545*2832a81dSGeoff Levand {
546*2832a81dSGeoff Levand 	int result;
547*2832a81dSGeoff Levand 	unsigned long node;
548*2832a81dSGeoff Levand 	unsigned cpu;
549*2832a81dSGeoff Levand 	struct irq_host *host;
550*2832a81dSGeoff Levand 
551*2832a81dSGeoff Levand 	lv1_get_logical_ppe_id(&node);
552*2832a81dSGeoff Levand 
553*2832a81dSGeoff Levand 	host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &host_ops,
554*2832a81dSGeoff Levand 		PS3_INVALID_OUTLET);
555*2832a81dSGeoff Levand 	irq_set_default_host(host);
556*2832a81dSGeoff Levand 	irq_set_virq_count(PS3_PLUG_MAX + 1);
557*2832a81dSGeoff Levand 
558*2832a81dSGeoff Levand 	for_each_possible_cpu(cpu) {
559*2832a81dSGeoff Levand 		struct private *pd = &per_cpu(private, cpu);
560*2832a81dSGeoff Levand 
561*2832a81dSGeoff Levand 		pd->node = node;
562*2832a81dSGeoff Levand 		pd->cpu = cpu;
563*2832a81dSGeoff Levand 		spin_lock_init(&pd->bmp.lock);
564*2832a81dSGeoff Levand 
565*2832a81dSGeoff Levand 		result = lv1_configure_irq_state_bitmap(node, cpu,
566*2832a81dSGeoff Levand 			ps3_mm_phys_to_lpar(__pa(&pd->bmp.status)));
567*2832a81dSGeoff Levand 
568*2832a81dSGeoff Levand 		if (result)
569*2832a81dSGeoff Levand 			pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:"
570*2832a81dSGeoff Levand 				" %s\n", __func__, __LINE__,
571*2832a81dSGeoff Levand 				ps3_result(result));
572*2832a81dSGeoff Levand 	}
573*2832a81dSGeoff Levand 
574*2832a81dSGeoff Levand 	ppc_md.get_irq = ps3_get_irq;
575*2832a81dSGeoff Levand }
576