1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22832a81dSGeoff Levand /*
32832a81dSGeoff Levand * PS3 interrupt routines.
42832a81dSGeoff Levand *
52832a81dSGeoff Levand * Copyright (C) 2006 Sony Computer Entertainment Inc.
62832a81dSGeoff Levand * Copyright 2006 Sony Corp.
72832a81dSGeoff Levand */
82832a81dSGeoff Levand
92832a81dSGeoff Levand #include <linux/kernel.h>
104b16f8e2SPaul Gortmaker #include <linux/export.h>
112832a81dSGeoff Levand #include <linux/irq.h>
1213a9a5d1SMarc Zyngier #include <linux/irqdomain.h>
132832a81dSGeoff Levand
142832a81dSGeoff Levand #include <asm/machdep.h>
152832a81dSGeoff Levand #include <asm/udbg.h>
162832a81dSGeoff Levand #include <asm/lv1call.h>
1742d284bcSStephen Rothwell #include <asm/smp.h>
182832a81dSGeoff Levand
192832a81dSGeoff Levand #include "platform.h"
202832a81dSGeoff Levand
212832a81dSGeoff Levand #if defined(DEBUG)
2283bb643dSGeert Uytterhoeven #define DBG udbg_printf
2332b9074bSGeoff Levand #define FAIL udbg_printf
242832a81dSGeoff Levand #else
2532b9074bSGeoff Levand #define DBG pr_devel
2632b9074bSGeoff Levand #define FAIL pr_debug
272832a81dSGeoff Levand #endif
282832a81dSGeoff Levand
292832a81dSGeoff Levand /**
30861be32cSGeoff Levand * struct ps3_bmp - a per cpu irq status and mask bitmap structure
31861be32cSGeoff Levand * @status: 256 bit status bitmap indexed by plug
3232b9074bSGeoff Levand * @unused_1: Alignment
33861be32cSGeoff Levand * @mask: 256 bit mask bitmap indexed by plug
3432b9074bSGeoff Levand * @unused_2: Alignment
35861be32cSGeoff Levand *
36b595076aSUwe Kleine-König * The HV maintains per SMT thread mappings of HV outlet to HV plug on
37861be32cSGeoff Levand * behalf of the guest. These mappings are implemented as 256 bit guest
38861be32cSGeoff Levand * supplied bitmaps indexed by plug number. The addresses of the bitmaps
39861be32cSGeoff Levand * are registered with the HV through lv1_configure_irq_state_bitmap().
4057715765SGeoff Levand * The HV requires that the 512 bits of status + mask not cross a page
4157715765SGeoff Levand * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte
4257715765SGeoff Levand * alignment.
43861be32cSGeoff Levand *
44861be32cSGeoff Levand * The HV supports 256 plugs per thread, assigned as {0..255}, for a total
45861be32cSGeoff Levand * of 512 plugs supported on a processor. To simplify the logic this
46861be32cSGeoff Levand * implementation equates HV plug value to Linux virq value, constrains each
47861be32cSGeoff Levand * interrupt to have a system wide unique plug number, and limits the range
48861be32cSGeoff Levand * of the plug values to map into the first dword of the bitmaps. This
49*7c576f4dSMarc Zyngier * gives a usable range of plug values of {NR_IRQS_LEGACY..63}. Note
50861be32cSGeoff Levand * that there is no constraint on how many in this set an individual thread
51861be32cSGeoff Levand * can acquire.
5246ca0d15SStephen Rothwell *
5346ca0d15SStephen Rothwell * The mask is declared as unsigned long so we can use set/clear_bit on it.
54861be32cSGeoff Levand */
55861be32cSGeoff Levand
5657715765SGeoff Levand #define PS3_BMP_MINALIGN 64
5757715765SGeoff Levand
58861be32cSGeoff Levand struct ps3_bmp {
59861be32cSGeoff Levand struct {
60861be32cSGeoff Levand u64 status;
61861be32cSGeoff Levand u64 unused_1[3];
6246ca0d15SStephen Rothwell unsigned long mask;
63861be32cSGeoff Levand u64 unused_2[3];
64861be32cSGeoff Levand };
65861be32cSGeoff Levand };
66861be32cSGeoff Levand
67861be32cSGeoff Levand /**
68861be32cSGeoff Levand * struct ps3_private - a per cpu data structure
69861be32cSGeoff Levand * @bmp: ps3_bmp structure
70446957baSAdam Buchbinder * @bmp_lock: Synchronize access to bmp.
7132b9074bSGeoff Levand * @ipi_debug_brk_mask: Mask for debug break IPIs
72aab83500SGeoff Levand * @ppe_id: HV logical_ppe_id
73aab83500SGeoff Levand * @thread_id: HV thread_id
7432b9074bSGeoff Levand * @ipi_mask: Mask of IPI virqs
75861be32cSGeoff Levand */
76861be32cSGeoff Levand
77861be32cSGeoff Levand struct ps3_private {
7857715765SGeoff Levand struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN)));
7932b9074bSGeoff Levand spinlock_t bmp_lock;
80aab83500SGeoff Levand u64 ppe_id;
81aab83500SGeoff Levand u64 thread_id;
8232b9074bSGeoff Levand unsigned long ipi_debug_brk_mask;
8372f3bea0SGeoff Levand unsigned long ipi_mask;
84861be32cSGeoff Levand };
85861be32cSGeoff Levand
86861be32cSGeoff Levand static DEFINE_PER_CPU(struct ps3_private, ps3_private);
87861be32cSGeoff Levand
88dc4f60c2SGeoff Levand /**
89743c1bb0SGeoff Levand * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp.
90743c1bb0SGeoff Levand * @virq: The assigned Linux virq.
91743c1bb0SGeoff Levand *
92743c1bb0SGeoff Levand * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
93743c1bb0SGeoff Levand */
94743c1bb0SGeoff Levand
ps3_chip_mask(struct irq_data * d)958126708aSLennert Buytenhek static void ps3_chip_mask(struct irq_data *d)
96743c1bb0SGeoff Levand {
978126708aSLennert Buytenhek struct ps3_private *pd = irq_data_get_irq_chip_data(d);
98743c1bb0SGeoff Levand unsigned long flags;
99743c1bb0SGeoff Levand
10032b9074bSGeoff Levand DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__,
1018126708aSLennert Buytenhek pd->thread_id, d->irq);
102743c1bb0SGeoff Levand
103743c1bb0SGeoff Levand local_irq_save(flags);
1048126708aSLennert Buytenhek clear_bit(63 - d->irq, &pd->bmp.mask);
105aab83500SGeoff Levand lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
106743c1bb0SGeoff Levand local_irq_restore(flags);
107743c1bb0SGeoff Levand }
108743c1bb0SGeoff Levand
109743c1bb0SGeoff Levand /**
110743c1bb0SGeoff Levand * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp.
111743c1bb0SGeoff Levand * @virq: The assigned Linux virq.
112743c1bb0SGeoff Levand *
113743c1bb0SGeoff Levand * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
114743c1bb0SGeoff Levand */
115743c1bb0SGeoff Levand
ps3_chip_unmask(struct irq_data * d)1168126708aSLennert Buytenhek static void ps3_chip_unmask(struct irq_data *d)
117743c1bb0SGeoff Levand {
1188126708aSLennert Buytenhek struct ps3_private *pd = irq_data_get_irq_chip_data(d);
119743c1bb0SGeoff Levand unsigned long flags;
120743c1bb0SGeoff Levand
12132b9074bSGeoff Levand DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__,
1228126708aSLennert Buytenhek pd->thread_id, d->irq);
123743c1bb0SGeoff Levand
124743c1bb0SGeoff Levand local_irq_save(flags);
1258126708aSLennert Buytenhek set_bit(63 - d->irq, &pd->bmp.mask);
126aab83500SGeoff Levand lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
127743c1bb0SGeoff Levand local_irq_restore(flags);
128743c1bb0SGeoff Levand }
129743c1bb0SGeoff Levand
130743c1bb0SGeoff Levand /**
131743c1bb0SGeoff Levand * ps3_chip_eoi - HV end-of-interrupt.
132743c1bb0SGeoff Levand * @virq: The assigned Linux virq.
133743c1bb0SGeoff Levand *
134743c1bb0SGeoff Levand * Calls lv1_end_of_interrupt_ext().
135743c1bb0SGeoff Levand */
136743c1bb0SGeoff Levand
ps3_chip_eoi(struct irq_data * d)1378126708aSLennert Buytenhek static void ps3_chip_eoi(struct irq_data *d)
138743c1bb0SGeoff Levand {
1398126708aSLennert Buytenhek const struct ps3_private *pd = irq_data_get_irq_chip_data(d);
14072f3bea0SGeoff Levand
14172f3bea0SGeoff Levand /* non-IPIs are EOIed here. */
14272f3bea0SGeoff Levand
14372f3bea0SGeoff Levand if (!test_bit(63 - d->irq, &pd->ipi_mask))
1448126708aSLennert Buytenhek lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, d->irq);
145743c1bb0SGeoff Levand }
146743c1bb0SGeoff Levand
147743c1bb0SGeoff Levand /**
148743c1bb0SGeoff Levand * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip.
149743c1bb0SGeoff Levand */
150743c1bb0SGeoff Levand
151743c1bb0SGeoff Levand static struct irq_chip ps3_irq_chip = {
152b27df672SThomas Gleixner .name = "ps3",
1538126708aSLennert Buytenhek .irq_mask = ps3_chip_mask,
1548126708aSLennert Buytenhek .irq_unmask = ps3_chip_unmask,
1558126708aSLennert Buytenhek .irq_eoi = ps3_chip_eoi,
156743c1bb0SGeoff Levand };
157743c1bb0SGeoff Levand
158743c1bb0SGeoff Levand /**
159dc4f60c2SGeoff Levand * ps3_virq_setup - virq related setup.
160dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
161dc4f60c2SGeoff Levand * serviced on.
162dc4f60c2SGeoff Levand * @outlet: The HV outlet from the various create outlet routines.
163dc4f60c2SGeoff Levand * @virq: The assigned Linux virq.
164dc4f60c2SGeoff Levand *
165dc4f60c2SGeoff Levand * Calls irq_create_mapping() to get a virq and sets the chip data to
166dc4f60c2SGeoff Levand * ps3_private data.
167dc4f60c2SGeoff Levand */
168dc4f60c2SGeoff Levand
ps3_virq_setup(enum ps3_cpu_binding cpu,unsigned long outlet,unsigned int * virq)169fdedb4caSGeert Uytterhoeven static int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
170861be32cSGeoff Levand unsigned int *virq)
171861be32cSGeoff Levand {
172861be32cSGeoff Levand int result;
173861be32cSGeoff Levand struct ps3_private *pd;
174861be32cSGeoff Levand
175861be32cSGeoff Levand /* This defines the default interrupt distribution policy. */
176861be32cSGeoff Levand
177861be32cSGeoff Levand if (cpu == PS3_BINDING_CPU_ANY)
178861be32cSGeoff Levand cpu = 0;
179861be32cSGeoff Levand
180861be32cSGeoff Levand pd = &per_cpu(ps3_private, cpu);
181861be32cSGeoff Levand
182861be32cSGeoff Levand *virq = irq_create_mapping(NULL, outlet);
183861be32cSGeoff Levand
184ef24ba70SMichael Ellerman if (!*virq) {
18532b9074bSGeoff Levand FAIL("%s:%d: irq_create_mapping failed: outlet %lu\n",
186861be32cSGeoff Levand __func__, __LINE__, outlet);
187861be32cSGeoff Levand result = -ENOMEM;
188861be32cSGeoff Levand goto fail_create;
189861be32cSGeoff Levand }
190861be32cSGeoff Levand
19132b9074bSGeoff Levand DBG("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__,
192861be32cSGeoff Levand outlet, cpu, *virq);
193861be32cSGeoff Levand
194ec775d0eSThomas Gleixner result = irq_set_chip_data(*virq, pd);
195861be32cSGeoff Levand
196861be32cSGeoff Levand if (result) {
19732b9074bSGeoff Levand FAIL("%s:%d: irq_set_chip_data failed\n",
198861be32cSGeoff Levand __func__, __LINE__);
199861be32cSGeoff Levand goto fail_set;
200861be32cSGeoff Levand }
201861be32cSGeoff Levand
2028126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(*virq));
2039263e85aSGeoff Levand
204861be32cSGeoff Levand return result;
205861be32cSGeoff Levand
206861be32cSGeoff Levand fail_set:
207861be32cSGeoff Levand irq_dispose_mapping(*virq);
208861be32cSGeoff Levand fail_create:
209861be32cSGeoff Levand return result;
210861be32cSGeoff Levand }
211861be32cSGeoff Levand
212dc4f60c2SGeoff Levand /**
213dc4f60c2SGeoff Levand * ps3_virq_destroy - virq related teardown.
214dc4f60c2SGeoff Levand * @virq: The assigned Linux virq.
215dc4f60c2SGeoff Levand *
216dc4f60c2SGeoff Levand * Clears chip data and calls irq_dispose_mapping() for the virq.
217dc4f60c2SGeoff Levand */
218dc4f60c2SGeoff Levand
ps3_virq_destroy(unsigned int virq)219fdedb4caSGeert Uytterhoeven static int ps3_virq_destroy(unsigned int virq)
220dc4f60c2SGeoff Levand {
221ec775d0eSThomas Gleixner const struct ps3_private *pd = irq_get_chip_data(virq);
222dc4f60c2SGeoff Levand
22332b9074bSGeoff Levand DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__,
224aab83500SGeoff Levand __LINE__, pd->ppe_id, pd->thread_id, virq);
225dc4f60c2SGeoff Levand
226ec775d0eSThomas Gleixner irq_set_chip_data(virq, NULL);
227dc4f60c2SGeoff Levand irq_dispose_mapping(virq);
228dc4f60c2SGeoff Levand
22932b9074bSGeoff Levand DBG("%s:%d <-\n", __func__, __LINE__);
230dc4f60c2SGeoff Levand return 0;
231dc4f60c2SGeoff Levand }
232dc4f60c2SGeoff Levand
233dc4f60c2SGeoff Levand /**
234dc4f60c2SGeoff Levand * ps3_irq_plug_setup - Generic outlet and virq related setup.
235dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
236dc4f60c2SGeoff Levand * serviced on.
237dc4f60c2SGeoff Levand * @outlet: The HV outlet from the various create outlet routines.
238dc4f60c2SGeoff Levand * @virq: The assigned Linux virq.
239dc4f60c2SGeoff Levand *
240dc4f60c2SGeoff Levand * Sets up virq and connects the irq plug.
241dc4f60c2SGeoff Levand */
242dc4f60c2SGeoff Levand
ps3_irq_plug_setup(enum ps3_cpu_binding cpu,unsigned long outlet,unsigned int * virq)243dc4f60c2SGeoff Levand int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
244dc4f60c2SGeoff Levand unsigned int *virq)
245dc4f60c2SGeoff Levand {
246dc4f60c2SGeoff Levand int result;
247dc4f60c2SGeoff Levand struct ps3_private *pd;
248dc4f60c2SGeoff Levand
249dc4f60c2SGeoff Levand result = ps3_virq_setup(cpu, outlet, virq);
250dc4f60c2SGeoff Levand
251dc4f60c2SGeoff Levand if (result) {
25232b9074bSGeoff Levand FAIL("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__);
253dc4f60c2SGeoff Levand goto fail_setup;
254dc4f60c2SGeoff Levand }
255dc4f60c2SGeoff Levand
256ec775d0eSThomas Gleixner pd = irq_get_chip_data(*virq);
257dc4f60c2SGeoff Levand
258dc4f60c2SGeoff Levand /* Binds outlet to cpu + virq. */
259dc4f60c2SGeoff Levand
260aab83500SGeoff Levand result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq,
261aab83500SGeoff Levand outlet, 0);
262dc4f60c2SGeoff Levand
263dc4f60c2SGeoff Levand if (result) {
26432b9074bSGeoff Levand FAIL("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
265dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result));
266dc4f60c2SGeoff Levand result = -EPERM;
267dc4f60c2SGeoff Levand goto fail_connect;
268dc4f60c2SGeoff Levand }
269dc4f60c2SGeoff Levand
270dc4f60c2SGeoff Levand return result;
271dc4f60c2SGeoff Levand
272dc4f60c2SGeoff Levand fail_connect:
273dc4f60c2SGeoff Levand ps3_virq_destroy(*virq);
274dc4f60c2SGeoff Levand fail_setup:
275dc4f60c2SGeoff Levand return result;
276dc4f60c2SGeoff Levand }
277dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_irq_plug_setup);
278dc4f60c2SGeoff Levand
279dc4f60c2SGeoff Levand /**
280dc4f60c2SGeoff Levand * ps3_irq_plug_destroy - Generic outlet and virq related teardown.
281dc4f60c2SGeoff Levand * @virq: The assigned Linux virq.
282dc4f60c2SGeoff Levand *
283dc4f60c2SGeoff Levand * Disconnects the irq plug and tears down virq.
284dc4f60c2SGeoff Levand * Do not call for system bus event interrupts setup with
285dc4f60c2SGeoff Levand * ps3_sb_event_receive_port_setup().
286dc4f60c2SGeoff Levand */
287dc4f60c2SGeoff Levand
ps3_irq_plug_destroy(unsigned int virq)288dc4f60c2SGeoff Levand int ps3_irq_plug_destroy(unsigned int virq)
289861be32cSGeoff Levand {
290861be32cSGeoff Levand int result;
291ec775d0eSThomas Gleixner const struct ps3_private *pd = irq_get_chip_data(virq);
292861be32cSGeoff Levand
29332b9074bSGeoff Levand DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__,
294aab83500SGeoff Levand __LINE__, pd->ppe_id, pd->thread_id, virq);
295861be32cSGeoff Levand
2968126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq));
2979263e85aSGeoff Levand
298aab83500SGeoff Levand result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq);
299861be32cSGeoff Levand
300861be32cSGeoff Levand if (result)
30132b9074bSGeoff Levand FAIL("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
302861be32cSGeoff Levand __func__, __LINE__, ps3_result(result));
303861be32cSGeoff Levand
304dc4f60c2SGeoff Levand ps3_virq_destroy(virq);
305dc4f60c2SGeoff Levand
306b1eeb38eSGeert Uytterhoeven return result;
307861be32cSGeoff Levand }
308dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy);
309861be32cSGeoff Levand
310861be32cSGeoff Levand /**
311dc4f60c2SGeoff Levand * ps3_event_receive_port_setup - Setup an event receive port.
312861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
313861be32cSGeoff Levand * serviced on.
3142832a81dSGeoff Levand * @virq: The assigned Linux virq.
3152832a81dSGeoff Levand *
3162832a81dSGeoff Levand * The virq can be used with lv1_connect_interrupt_event_receive_port() to
317dc4f60c2SGeoff Levand * arrange to receive interrupts from system-bus devices, or with
318dc4f60c2SGeoff Levand * ps3_send_event_locally() to signal events.
3192832a81dSGeoff Levand */
3202832a81dSGeoff Levand
ps3_event_receive_port_setup(enum ps3_cpu_binding cpu,unsigned int * virq)321dc4f60c2SGeoff Levand int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq)
3222832a81dSGeoff Levand {
3232832a81dSGeoff Levand int result;
324b17b3df1SStephen Rothwell u64 outlet;
3252832a81dSGeoff Levand
3262832a81dSGeoff Levand result = lv1_construct_event_receive_port(&outlet);
3272832a81dSGeoff Levand
3282832a81dSGeoff Levand if (result) {
32932b9074bSGeoff Levand FAIL("%s:%d: lv1_construct_event_receive_port failed: %s\n",
3302832a81dSGeoff Levand __func__, __LINE__, ps3_result(result));
331ef24ba70SMichael Ellerman *virq = 0;
3322832a81dSGeoff Levand return result;
3332832a81dSGeoff Levand }
3342832a81dSGeoff Levand
335dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq);
336861be32cSGeoff Levand BUG_ON(result);
3372832a81dSGeoff Levand
338861be32cSGeoff Levand return result;
3392832a81dSGeoff Levand }
340dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup);
3412832a81dSGeoff Levand
342dc4f60c2SGeoff Levand /**
343dc4f60c2SGeoff Levand * ps3_event_receive_port_destroy - Destroy an event receive port.
344dc4f60c2SGeoff Levand * @virq: The assigned Linux virq.
345dc4f60c2SGeoff Levand *
346dc4f60c2SGeoff Levand * Since ps3_event_receive_port_destroy destroys the receive port outlet,
347dc4f60c2SGeoff Levand * SB devices need to call disconnect_interrupt_event_receive_port() before
348dc4f60c2SGeoff Levand * this.
349dc4f60c2SGeoff Levand */
350dc4f60c2SGeoff Levand
ps3_event_receive_port_destroy(unsigned int virq)351dc4f60c2SGeoff Levand int ps3_event_receive_port_destroy(unsigned int virq)
3522832a81dSGeoff Levand {
3532832a81dSGeoff Levand int result;
3542832a81dSGeoff Levand
35532b9074bSGeoff Levand DBG(" -> %s:%d virq %u\n", __func__, __LINE__, virq);
3569263e85aSGeoff Levand
3578126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq));
3582832a81dSGeoff Levand
3592832a81dSGeoff Levand result = lv1_destruct_event_receive_port(virq_to_hw(virq));
3602832a81dSGeoff Levand
3612832a81dSGeoff Levand if (result)
36232b9074bSGeoff Levand FAIL("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
3632832a81dSGeoff Levand __func__, __LINE__, ps3_result(result));
3642832a81dSGeoff Levand
3659263e85aSGeoff Levand /*
3669263e85aSGeoff Levand * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu()
3679263e85aSGeoff Levand * calls from interrupt context (smp_call_function) when kexecing.
368dc4f60c2SGeoff Levand */
369dc4f60c2SGeoff Levand
37032b9074bSGeoff Levand DBG(" <- %s:%d\n", __func__, __LINE__);
3712832a81dSGeoff Levand return result;
3722832a81dSGeoff Levand }
3732832a81dSGeoff Levand
ps3_send_event_locally(unsigned int virq)3742832a81dSGeoff Levand int ps3_send_event_locally(unsigned int virq)
3752832a81dSGeoff Levand {
3762832a81dSGeoff Levand return lv1_send_event_locally(virq_to_hw(virq));
3772832a81dSGeoff Levand }
3782832a81dSGeoff Levand
3792832a81dSGeoff Levand /**
380dc4f60c2SGeoff Levand * ps3_sb_event_receive_port_setup - Setup a system bus event receive port.
381861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
382861be32cSGeoff Levand * serviced on.
3836bb5cf10SGeoff Levand * @dev: The system bus device instance.
3842832a81dSGeoff Levand * @virq: The assigned Linux virq.
3852832a81dSGeoff Levand *
3862832a81dSGeoff Levand * An event irq represents a virtual device interrupt. The interrupt_id
3872832a81dSGeoff Levand * coresponds to the software interrupt number.
3882832a81dSGeoff Levand */
3892832a81dSGeoff Levand
ps3_sb_event_receive_port_setup(struct ps3_system_bus_device * dev,enum ps3_cpu_binding cpu,unsigned int * virq)3906bb5cf10SGeoff Levand int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev,
3916bb5cf10SGeoff Levand enum ps3_cpu_binding cpu, unsigned int *virq)
3922832a81dSGeoff Levand {
393dc4f60c2SGeoff Levand /* this should go in system-bus.c */
394dc4f60c2SGeoff Levand
3952832a81dSGeoff Levand int result;
3962832a81dSGeoff Levand
397dc4f60c2SGeoff Levand result = ps3_event_receive_port_setup(cpu, virq);
3982832a81dSGeoff Levand
3992832a81dSGeoff Levand if (result)
4002832a81dSGeoff Levand return result;
4012832a81dSGeoff Levand
4026bb5cf10SGeoff Levand result = lv1_connect_interrupt_event_receive_port(dev->bus_id,
4036bb5cf10SGeoff Levand dev->dev_id, virq_to_hw(*virq), dev->interrupt_id);
4042832a81dSGeoff Levand
4052832a81dSGeoff Levand if (result) {
40632b9074bSGeoff Levand FAIL("%s:%d: lv1_connect_interrupt_event_receive_port"
4072832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__,
4082832a81dSGeoff Levand ps3_result(result));
409dc4f60c2SGeoff Levand ps3_event_receive_port_destroy(*virq);
410ef24ba70SMichael Ellerman *virq = 0;
4112832a81dSGeoff Levand return result;
4122832a81dSGeoff Levand }
4132832a81dSGeoff Levand
41432b9074bSGeoff Levand DBG("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
4156bb5cf10SGeoff Levand dev->interrupt_id, *virq);
4162832a81dSGeoff Levand
4172832a81dSGeoff Levand return 0;
4182832a81dSGeoff Levand }
419dc4f60c2SGeoff Levand EXPORT_SYMBOL(ps3_sb_event_receive_port_setup);
4202832a81dSGeoff Levand
ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device * dev,unsigned int virq)4216bb5cf10SGeoff Levand int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev,
4226bb5cf10SGeoff Levand unsigned int virq)
4232832a81dSGeoff Levand {
424dc4f60c2SGeoff Levand /* this should go in system-bus.c */
425dc4f60c2SGeoff Levand
4262832a81dSGeoff Levand int result;
4272832a81dSGeoff Levand
42832b9074bSGeoff Levand DBG(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
4296bb5cf10SGeoff Levand dev->interrupt_id, virq);
4302832a81dSGeoff Levand
4316bb5cf10SGeoff Levand result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id,
4326bb5cf10SGeoff Levand dev->dev_id, virq_to_hw(virq), dev->interrupt_id);
4332832a81dSGeoff Levand
4342832a81dSGeoff Levand if (result)
43532b9074bSGeoff Levand FAIL("%s:%d: lv1_disconnect_interrupt_event_receive_port"
4362832a81dSGeoff Levand " failed: %s\n", __func__, __LINE__,
4372832a81dSGeoff Levand ps3_result(result));
4382832a81dSGeoff Levand
439dc4f60c2SGeoff Levand result = ps3_event_receive_port_destroy(virq);
440dc4f60c2SGeoff Levand BUG_ON(result);
4412832a81dSGeoff Levand
4429263e85aSGeoff Levand /*
4439263e85aSGeoff Levand * ps3_event_receive_port_destroy() destroys the IRQ plug,
4449263e85aSGeoff Levand * so don't call ps3_irq_plug_destroy() here.
4459263e85aSGeoff Levand */
4469263e85aSGeoff Levand
4479263e85aSGeoff Levand result = ps3_virq_destroy(virq);
4489263e85aSGeoff Levand BUG_ON(result);
4499263e85aSGeoff Levand
45032b9074bSGeoff Levand DBG(" <- %s:%d\n", __func__, __LINE__);
4512832a81dSGeoff Levand return result;
4522832a81dSGeoff Levand }
453dc4f60c2SGeoff Levand EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy);
4542832a81dSGeoff Levand
4552832a81dSGeoff Levand /**
456dc4f60c2SGeoff Levand * ps3_io_irq_setup - Setup a system bus io irq.
457dc4f60c2SGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
458dc4f60c2SGeoff Levand * serviced on.
459dc4f60c2SGeoff Levand * @interrupt_id: The device interrupt id read from the system repository.
460dc4f60c2SGeoff Levand * @virq: The assigned Linux virq.
461dc4f60c2SGeoff Levand *
462dc4f60c2SGeoff Levand * An io irq represents a non-virtualized device interrupt. interrupt_id
463dc4f60c2SGeoff Levand * coresponds to the interrupt number of the interrupt controller.
464dc4f60c2SGeoff Levand */
465dc4f60c2SGeoff Levand
ps3_io_irq_setup(enum ps3_cpu_binding cpu,unsigned int interrupt_id,unsigned int * virq)466dc4f60c2SGeoff Levand int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
467dc4f60c2SGeoff Levand unsigned int *virq)
468dc4f60c2SGeoff Levand {
469dc4f60c2SGeoff Levand int result;
470b17b3df1SStephen Rothwell u64 outlet;
471dc4f60c2SGeoff Levand
472dc4f60c2SGeoff Levand result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);
473dc4f60c2SGeoff Levand
474dc4f60c2SGeoff Levand if (result) {
47532b9074bSGeoff Levand FAIL("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
476dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result));
477dc4f60c2SGeoff Levand return result;
478dc4f60c2SGeoff Levand }
479dc4f60c2SGeoff Levand
480dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq);
481dc4f60c2SGeoff Levand BUG_ON(result);
482dc4f60c2SGeoff Levand
483dc4f60c2SGeoff Levand return result;
484dc4f60c2SGeoff Levand }
485dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_io_irq_setup);
486dc4f60c2SGeoff Levand
ps3_io_irq_destroy(unsigned int virq)487dc4f60c2SGeoff Levand int ps3_io_irq_destroy(unsigned int virq)
488dc4f60c2SGeoff Levand {
489dc4f60c2SGeoff Levand int result;
4909263e85aSGeoff Levand unsigned long outlet = virq_to_hw(virq);
491dc4f60c2SGeoff Levand
4928126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq));
4939263e85aSGeoff Levand
4949263e85aSGeoff Levand /*
4959263e85aSGeoff Levand * lv1_destruct_io_irq_outlet() will destroy the IRQ plug,
4969263e85aSGeoff Levand * so call ps3_irq_plug_destroy() first.
4979263e85aSGeoff Levand */
4989263e85aSGeoff Levand
4999263e85aSGeoff Levand result = ps3_irq_plug_destroy(virq);
5009263e85aSGeoff Levand BUG_ON(result);
5019263e85aSGeoff Levand
5029263e85aSGeoff Levand result = lv1_destruct_io_irq_outlet(outlet);
503dc4f60c2SGeoff Levand
504dc4f60c2SGeoff Levand if (result)
50532b9074bSGeoff Levand FAIL("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
506dc4f60c2SGeoff Levand __func__, __LINE__, ps3_result(result));
507dc4f60c2SGeoff Levand
508dc4f60c2SGeoff Levand return result;
509dc4f60c2SGeoff Levand }
510dc4f60c2SGeoff Levand EXPORT_SYMBOL_GPL(ps3_io_irq_destroy);
511dc4f60c2SGeoff Levand
512dc4f60c2SGeoff Levand /**
513dc4f60c2SGeoff Levand * ps3_vuart_irq_setup - Setup the system virtual uart virq.
514861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
515861be32cSGeoff Levand * serviced on.
5162832a81dSGeoff Levand * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
5172832a81dSGeoff Levand * @virq: The assigned Linux virq.
5182832a81dSGeoff Levand *
5192832a81dSGeoff Levand * The system supports only a single virtual uart, so multiple calls without
5202832a81dSGeoff Levand * freeing the interrupt will return a wrong state error.
5212832a81dSGeoff Levand */
5222832a81dSGeoff Levand
ps3_vuart_irq_setup(enum ps3_cpu_binding cpu,void * virt_addr_bmp,unsigned int * virq)523dc4f60c2SGeoff Levand int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
524861be32cSGeoff Levand unsigned int *virq)
5252832a81dSGeoff Levand {
5262832a81dSGeoff Levand int result;
527b17b3df1SStephen Rothwell u64 outlet;
528861be32cSGeoff Levand u64 lpar_addr;
5292832a81dSGeoff Levand
530861be32cSGeoff Levand BUG_ON(!is_kernel_addr((u64)virt_addr_bmp));
5312832a81dSGeoff Levand
5322832a81dSGeoff Levand lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp));
5332832a81dSGeoff Levand
5342832a81dSGeoff Levand result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet);
5352832a81dSGeoff Levand
5362832a81dSGeoff Levand if (result) {
53732b9074bSGeoff Levand FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
5382832a81dSGeoff Levand __func__, __LINE__, ps3_result(result));
5392832a81dSGeoff Levand return result;
5402832a81dSGeoff Levand }
5412832a81dSGeoff Levand
542dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq);
543861be32cSGeoff Levand BUG_ON(result);
5442832a81dSGeoff Levand
545861be32cSGeoff Levand return result;
5462832a81dSGeoff Levand }
5477626e78dSGeoff Levand EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup);
5482832a81dSGeoff Levand
ps3_vuart_irq_destroy(unsigned int virq)549dc4f60c2SGeoff Levand int ps3_vuart_irq_destroy(unsigned int virq)
5502832a81dSGeoff Levand {
5512832a81dSGeoff Levand int result;
5522832a81dSGeoff Levand
5538126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq));
5542832a81dSGeoff Levand result = lv1_deconfigure_virtual_uart_irq();
5552832a81dSGeoff Levand
5562832a81dSGeoff Levand if (result) {
55732b9074bSGeoff Levand FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
5582832a81dSGeoff Levand __func__, __LINE__, ps3_result(result));
5592832a81dSGeoff Levand return result;
5602832a81dSGeoff Levand }
5612832a81dSGeoff Levand
562dc4f60c2SGeoff Levand result = ps3_irq_plug_destroy(virq);
563dc4f60c2SGeoff Levand BUG_ON(result);
5642832a81dSGeoff Levand
5652832a81dSGeoff Levand return result;
5662832a81dSGeoff Levand }
5677626e78dSGeoff Levand EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy);
5682832a81dSGeoff Levand
5692832a81dSGeoff Levand /**
570dc4f60c2SGeoff Levand * ps3_spe_irq_setup - Setup an spe virq.
571861be32cSGeoff Levand * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
572861be32cSGeoff Levand * serviced on.
5732832a81dSGeoff Levand * @spe_id: The spe_id returned from lv1_construct_logical_spe().
5742832a81dSGeoff Levand * @class: The spe interrupt class {0,1,2}.
5752832a81dSGeoff Levand * @virq: The assigned Linux virq.
5762832a81dSGeoff Levand *
5772832a81dSGeoff Levand */
5782832a81dSGeoff Levand
ps3_spe_irq_setup(enum ps3_cpu_binding cpu,unsigned long spe_id,unsigned int class,unsigned int * virq)579dc4f60c2SGeoff Levand int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id,
580861be32cSGeoff Levand unsigned int class, unsigned int *virq)
5812832a81dSGeoff Levand {
5822832a81dSGeoff Levand int result;
583b17b3df1SStephen Rothwell u64 outlet;
5842832a81dSGeoff Levand
5852832a81dSGeoff Levand BUG_ON(class > 2);
5862832a81dSGeoff Levand
5872832a81dSGeoff Levand result = lv1_get_spe_irq_outlet(spe_id, class, &outlet);
5882832a81dSGeoff Levand
5892832a81dSGeoff Levand if (result) {
59032b9074bSGeoff Levand FAIL("%s:%d: lv1_get_spe_irq_outlet failed: %s\n",
5912832a81dSGeoff Levand __func__, __LINE__, ps3_result(result));
5922832a81dSGeoff Levand return result;
5932832a81dSGeoff Levand }
5942832a81dSGeoff Levand
595dc4f60c2SGeoff Levand result = ps3_irq_plug_setup(cpu, outlet, virq);
596861be32cSGeoff Levand BUG_ON(result);
5972832a81dSGeoff Levand
598861be32cSGeoff Levand return result;
5992832a81dSGeoff Levand }
6002832a81dSGeoff Levand
ps3_spe_irq_destroy(unsigned int virq)601dc4f60c2SGeoff Levand int ps3_spe_irq_destroy(unsigned int virq)
6022832a81dSGeoff Levand {
6039263e85aSGeoff Levand int result;
6049263e85aSGeoff Levand
6058126708aSLennert Buytenhek ps3_chip_mask(irq_get_irq_data(virq));
6069263e85aSGeoff Levand
6079263e85aSGeoff Levand result = ps3_irq_plug_destroy(virq);
608dc4f60c2SGeoff Levand BUG_ON(result);
6099263e85aSGeoff Levand
6109263e85aSGeoff Levand return result;
6112832a81dSGeoff Levand }
6122832a81dSGeoff Levand
613b1eeb38eSGeert Uytterhoeven
6142832a81dSGeoff Levand #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
6152832a81dSGeoff Levand #define PS3_PLUG_MAX 63
6162832a81dSGeoff Levand
6172832a81dSGeoff Levand #if defined(DEBUG)
_dump_64_bmp(const char * header,const u64 * p,unsigned cpu,const char * func,int line)618861be32cSGeoff Levand static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu,
6192832a81dSGeoff Levand const char* func, int line)
6202832a81dSGeoff Levand {
62132b9074bSGeoff Levand pr_debug("%s:%d: %s %u {%04llx_%04llx_%04llx_%04llx}\n",
6222832a81dSGeoff Levand func, line, header, cpu,
6232832a81dSGeoff Levand *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff,
6242832a81dSGeoff Levand *p & 0xffff);
6252832a81dSGeoff Levand }
6262832a81dSGeoff Levand
_dump_256_bmp(const char * header,const u64 * p,unsigned cpu,const char * func,int line)627848cfdc5SGeoff Levand static void __maybe_unused _dump_256_bmp(const char *header,
628861be32cSGeoff Levand const u64 *p, unsigned cpu, const char* func, int line)
6292832a81dSGeoff Levand {
63032b9074bSGeoff Levand pr_debug("%s:%d: %s %u {%016llx:%016llx:%016llx:%016llx}\n",
6312832a81dSGeoff Levand func, line, header, cpu, p[0], p[1], p[2], p[3]);
6322832a81dSGeoff Levand }
6332832a81dSGeoff Levand
6342832a81dSGeoff Levand #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__)
_dump_bmp(struct ps3_private * pd,const char * func,int line)6359633ac8dSGeoff Levand static void _dump_bmp(struct ps3_private* pd, const char* func, int line)
6362832a81dSGeoff Levand {
6372832a81dSGeoff Levand unsigned long flags;
6382832a81dSGeoff Levand
63932b9074bSGeoff Levand spin_lock_irqsave(&pd->bmp_lock, flags);
640aab83500SGeoff Levand _dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line);
64132b9074bSGeoff Levand _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line);
64232b9074bSGeoff Levand spin_unlock_irqrestore(&pd->bmp_lock, flags);
6432832a81dSGeoff Levand }
6442832a81dSGeoff Levand
6452832a81dSGeoff Levand #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
_dump_mask(struct ps3_private * pd,const char * func,int line)646848cfdc5SGeoff Levand static void __maybe_unused _dump_mask(struct ps3_private *pd,
6472832a81dSGeoff Levand const char* func, int line)
6482832a81dSGeoff Levand {
6492832a81dSGeoff Levand unsigned long flags;
6502832a81dSGeoff Levand
65132b9074bSGeoff Levand spin_lock_irqsave(&pd->bmp_lock, flags);
65232b9074bSGeoff Levand _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line);
65332b9074bSGeoff Levand spin_unlock_irqrestore(&pd->bmp_lock, flags);
6542832a81dSGeoff Levand }
6552832a81dSGeoff Levand #else
dump_bmp(struct ps3_private * pd)6569633ac8dSGeoff Levand static void dump_bmp(struct ps3_private* pd) {};
6572832a81dSGeoff Levand #endif /* defined(DEBUG) */
6582832a81dSGeoff Levand
ps3_host_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hwirq)659bae1d8f1SGrant Likely static int ps3_host_map(struct irq_domain *h, unsigned int virq,
6602832a81dSGeoff Levand irq_hw_number_t hwirq)
6612832a81dSGeoff Levand {
66232b9074bSGeoff Levand DBG("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
663861be32cSGeoff Levand virq);
6642832a81dSGeoff Levand
665ec775d0eSThomas Gleixner irq_set_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq);
6662832a81dSGeoff Levand
667861be32cSGeoff Levand return 0;
6682832a81dSGeoff Levand }
6692832a81dSGeoff Levand
ps3_host_match(struct irq_domain * h,struct device_node * np,enum irq_domain_bus_token bus_token)670ad3aedfbSMarc Zyngier static int ps3_host_match(struct irq_domain *h, struct device_node *np,
671ad3aedfbSMarc Zyngier enum irq_domain_bus_token bus_token)
6728528ab84SMichael Ellerman {
6738528ab84SMichael Ellerman /* Match all */
6748528ab84SMichael Ellerman return 1;
6758528ab84SMichael Ellerman }
6768528ab84SMichael Ellerman
6779f70b8ebSGrant Likely static const struct irq_domain_ops ps3_host_ops = {
6789633ac8dSGeoff Levand .map = ps3_host_map,
6798528ab84SMichael Ellerman .match = ps3_host_match,
6802832a81dSGeoff Levand };
6812832a81dSGeoff Levand
ps3_register_ipi_debug_brk(unsigned int cpu,unsigned int virq)6822832a81dSGeoff Levand void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
6832832a81dSGeoff Levand {
6849633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu);
6852832a81dSGeoff Levand
68632b9074bSGeoff Levand set_bit(63 - virq, &pd->ipi_debug_brk_mask);
6872832a81dSGeoff Levand
68832b9074bSGeoff Levand DBG("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__,
68932b9074bSGeoff Levand cpu, virq, pd->ipi_debug_brk_mask);
6902832a81dSGeoff Levand }
6912832a81dSGeoff Levand
ps3_register_ipi_irq(unsigned int cpu,unsigned int virq)69272f3bea0SGeoff Levand void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq)
69372f3bea0SGeoff Levand {
69472f3bea0SGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu);
69572f3bea0SGeoff Levand
69672f3bea0SGeoff Levand set_bit(63 - virq, &pd->ipi_mask);
69772f3bea0SGeoff Levand
69872f3bea0SGeoff Levand DBG("%s:%d: cpu %u, virq %u, ipi_mask %lxh\n", __func__, __LINE__,
69972f3bea0SGeoff Levand cpu, virq, pd->ipi_mask);
70072f3bea0SGeoff Levand }
70172f3bea0SGeoff Levand
ps3_get_irq(void)7029263e85aSGeoff Levand static unsigned int ps3_get_irq(void)
7032832a81dSGeoff Levand {
70469111bacSChristoph Lameter struct ps3_private *pd = this_cpu_ptr(&ps3_private);
705861be32cSGeoff Levand u64 x = (pd->bmp.status & pd->bmp.mask);
7069cf9e196SBenjamin Herrenschmidt unsigned int plug;
7072832a81dSGeoff Levand
7082832a81dSGeoff Levand /* check for ipi break first to stop this cpu ASAP */
7092832a81dSGeoff Levand
71032b9074bSGeoff Levand if (x & pd->ipi_debug_brk_mask)
71132b9074bSGeoff Levand x &= pd->ipi_debug_brk_mask;
7122832a81dSGeoff Levand
7139cf9e196SBenjamin Herrenschmidt asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x));
7149cf9e196SBenjamin Herrenschmidt plug &= 0x3f;
7152832a81dSGeoff Levand
716ef24ba70SMichael Ellerman if (unlikely(!plug)) {
71732b9074bSGeoff Levand DBG("%s:%d: no plug found: thread_id %llu\n", __func__,
718aab83500SGeoff Levand __LINE__, pd->thread_id);
7199633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0));
7209633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1));
721ef24ba70SMichael Ellerman return 0;
7222832a81dSGeoff Levand }
7232832a81dSGeoff Levand
7242832a81dSGeoff Levand #if defined(DEBUG)
725*7c576f4dSMarc Zyngier if (unlikely(plug < NR_IRQS_LEGACY || plug > PS3_PLUG_MAX)) {
7269633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 0));
7279633ac8dSGeoff Levand dump_bmp(&per_cpu(ps3_private, 1));
7282832a81dSGeoff Levand BUG();
7292832a81dSGeoff Levand }
7302832a81dSGeoff Levand #endif
73172f3bea0SGeoff Levand
73272f3bea0SGeoff Levand /* IPIs are EOIed here. */
73372f3bea0SGeoff Levand
73472f3bea0SGeoff Levand if (test_bit(63 - plug, &pd->ipi_mask))
73572f3bea0SGeoff Levand lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, plug);
73672f3bea0SGeoff Levand
7372832a81dSGeoff Levand return plug;
7382832a81dSGeoff Levand }
7392832a81dSGeoff Levand
ps3_init_IRQ(void)7402832a81dSGeoff Levand void __init ps3_init_IRQ(void)
7412832a81dSGeoff Levand {
7422832a81dSGeoff Levand int result;
7432832a81dSGeoff Levand unsigned cpu;
744bae1d8f1SGrant Likely struct irq_domain *host;
7452832a81dSGeoff Levand
7466fa6c8e2SGrant Likely host = irq_domain_add_nomap(NULL, PS3_PLUG_MAX + 1, &ps3_host_ops, NULL);
7472832a81dSGeoff Levand irq_set_default_host(host);
7482832a81dSGeoff Levand
7492832a81dSGeoff Levand for_each_possible_cpu(cpu) {
7509633ac8dSGeoff Levand struct ps3_private *pd = &per_cpu(ps3_private, cpu);
7512832a81dSGeoff Levand
752aab83500SGeoff Levand lv1_get_logical_ppe_id(&pd->ppe_id);
753aab83500SGeoff Levand pd->thread_id = get_hard_smp_processor_id(cpu);
75432b9074bSGeoff Levand spin_lock_init(&pd->bmp_lock);
7552832a81dSGeoff Levand
75632b9074bSGeoff Levand DBG("%s:%d: ppe_id %llu, thread_id %llu, bmp %lxh\n",
757aab83500SGeoff Levand __func__, __LINE__, pd->ppe_id, pd->thread_id,
758407e24a0SGeoff Levand ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
759407e24a0SGeoff Levand
760aab83500SGeoff Levand result = lv1_configure_irq_state_bitmap(pd->ppe_id,
761aab83500SGeoff Levand pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
7622832a81dSGeoff Levand
7632832a81dSGeoff Levand if (result)
76432b9074bSGeoff Levand FAIL("%s:%d: lv1_configure_irq_state_bitmap failed:"
7652832a81dSGeoff Levand " %s\n", __func__, __LINE__,
7662832a81dSGeoff Levand ps3_result(result));
7672832a81dSGeoff Levand }
7682832a81dSGeoff Levand
7692832a81dSGeoff Levand ppc_md.get_irq = ps3_get_irq;
7702832a81dSGeoff Levand }
7719263e85aSGeoff Levand
ps3_shutdown_IRQ(int cpu)7729263e85aSGeoff Levand void ps3_shutdown_IRQ(int cpu)
7739263e85aSGeoff Levand {
7749263e85aSGeoff Levand int result;
7759263e85aSGeoff Levand u64 ppe_id;
7769263e85aSGeoff Levand u64 thread_id = get_hard_smp_processor_id(cpu);
7779263e85aSGeoff Levand
7789263e85aSGeoff Levand lv1_get_logical_ppe_id(&ppe_id);
7799263e85aSGeoff Levand result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0);
7809263e85aSGeoff Levand
7815c949070SStephen Rothwell DBG("%s:%d: lv1_configure_irq_state_bitmap (%llu:%llu/%d) %s\n", __func__,
7829263e85aSGeoff Levand __LINE__, ppe_id, thread_id, cpu, ps3_result(result));
7839263e85aSGeoff Levand }
784