blob: 1d6f4f478fe293ebd06313aa1077c1019507473d [file] [log] [blame]
Geoff Levand2832a812006-11-23 00:46:56 +01001/*
2 * PS3 interrupt routines.
3 *
4 * Copyright (C) 2006 Sony Computer Entertainment Inc.
5 * Copyright 2006 Sony Corp.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <linux/kernel.h>
Paul Gortmaker4b16f8e2011-07-22 18:24:23 -040022#include <linux/export.h>
Geoff Levand2832a812006-11-23 00:46:56 +010023#include <linux/irq.h>
24
25#include <asm/machdep.h>
26#include <asm/udbg.h>
Geoff Levand2832a812006-11-23 00:46:56 +010027#include <asm/lv1call.h>
Stephen Rothwell42d284bc2007-05-28 10:19:08 +100028#include <asm/smp.h>
Geoff Levand2832a812006-11-23 00:46:56 +010029
30#include "platform.h"
31
32#if defined(DEBUG)
Geert Uytterhoeven83bb6432007-06-16 07:19:23 +100033#define DBG udbg_printf
Geoff Levand2832a812006-11-23 00:46:56 +010034#else
Geert Uytterhoeven83bb6432007-06-16 07:19:23 +100035#define DBG pr_debug
Geoff Levand2832a812006-11-23 00:46:56 +010036#endif
37
38/**
Geoff Levand861be322007-01-26 19:08:08 -080039 * struct ps3_bmp - a per cpu irq status and mask bitmap structure
40 * @status: 256 bit status bitmap indexed by plug
41 * @unused_1:
42 * @mask: 256 bit mask bitmap indexed by plug
43 * @unused_2:
44 * @lock:
45 * @ipi_debug_brk_mask:
46 *
Uwe Kleine-Königb5950762010-11-01 15:38:34 -040047 * The HV maintains per SMT thread mappings of HV outlet to HV plug on
Geoff Levand861be322007-01-26 19:08:08 -080048 * behalf of the guest. These mappings are implemented as 256 bit guest
49 * supplied bitmaps indexed by plug number. The addresses of the bitmaps
50 * are registered with the HV through lv1_configure_irq_state_bitmap().
Geoff Levand57715762007-01-26 19:08:16 -080051 * The HV requires that the 512 bits of status + mask not cross a page
52 * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte
53 * alignment.
Geoff Levand861be322007-01-26 19:08:08 -080054 *
55 * The HV supports 256 plugs per thread, assigned as {0..255}, for a total
56 * of 512 plugs supported on a processor. To simplify the logic this
57 * implementation equates HV plug value to Linux virq value, constrains each
58 * interrupt to have a system wide unique plug number, and limits the range
59 * of the plug values to map into the first dword of the bitmaps. This
60 * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note
61 * that there is no constraint on how many in this set an individual thread
62 * can acquire.
Stephen Rothwell46ca0d12009-01-13 20:00:29 +000063 *
64 * The mask is declared as unsigned long so we can use set/clear_bit on it.
Geoff Levand861be322007-01-26 19:08:08 -080065 */
66
Geoff Levand57715762007-01-26 19:08:16 -080067#define PS3_BMP_MINALIGN 64
68
Geoff Levand861be322007-01-26 19:08:08 -080069struct ps3_bmp {
70 struct {
71 u64 status;
72 u64 unused_1[3];
Stephen Rothwell46ca0d12009-01-13 20:00:29 +000073 unsigned long mask;
Geoff Levand861be322007-01-26 19:08:08 -080074 u64 unused_2[3];
75 };
76 u64 ipi_debug_brk_mask;
77 spinlock_t lock;
78};
79
80/**
81 * struct ps3_private - a per cpu data structure
82 * @bmp: ps3_bmp structure
Geoff Levandaab83502007-06-16 08:06:04 +100083 * @ppe_id: HV logical_ppe_id
84 * @thread_id: HV thread_id
Geoff Levand861be322007-01-26 19:08:08 -080085 */
86
87struct ps3_private {
Geoff Levand57715762007-01-26 19:08:16 -080088 struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN)));
Geoff Levandaab83502007-06-16 08:06:04 +100089 u64 ppe_id;
90 u64 thread_id;
Geoff Levand72f3bea2011-11-08 12:37:26 +000091 unsigned long ipi_mask;
Geoff Levand861be322007-01-26 19:08:08 -080092};
93
94static DEFINE_PER_CPU(struct ps3_private, ps3_private);
95
Geoff Levanddc4f60c2007-05-01 07:01:01 +100096/**
Geoff Levand743c1bb2007-06-16 07:19:18 +100097 * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp.
98 * @virq: The assigned Linux virq.
99 *
100 * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
101 */
102
Lennert Buytenhek81267082011-03-08 22:26:56 +0000103static void ps3_chip_mask(struct irq_data *d)
Geoff Levand743c1bb2007-06-16 07:19:18 +1000104{
Lennert Buytenhek81267082011-03-08 22:26:56 +0000105 struct ps3_private *pd = irq_data_get_irq_chip_data(d);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000106 unsigned long flags;
107
Stephen Rothwell5c949072009-01-13 20:02:39 +0000108 pr_debug("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__,
Lennert Buytenhek81267082011-03-08 22:26:56 +0000109 pd->thread_id, d->irq);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000110
111 local_irq_save(flags);
Lennert Buytenhek81267082011-03-08 22:26:56 +0000112 clear_bit(63 - d->irq, &pd->bmp.mask);
Geoff Levandaab83502007-06-16 08:06:04 +1000113 lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000114 local_irq_restore(flags);
115}
116
117/**
118 * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp.
119 * @virq: The assigned Linux virq.
120 *
121 * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
122 */
123
Lennert Buytenhek81267082011-03-08 22:26:56 +0000124static void ps3_chip_unmask(struct irq_data *d)
Geoff Levand743c1bb2007-06-16 07:19:18 +1000125{
Lennert Buytenhek81267082011-03-08 22:26:56 +0000126 struct ps3_private *pd = irq_data_get_irq_chip_data(d);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000127 unsigned long flags;
128
Stephen Rothwell5c949072009-01-13 20:02:39 +0000129 pr_debug("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__,
Lennert Buytenhek81267082011-03-08 22:26:56 +0000130 pd->thread_id, d->irq);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000131
132 local_irq_save(flags);
Lennert Buytenhek81267082011-03-08 22:26:56 +0000133 set_bit(63 - d->irq, &pd->bmp.mask);
Geoff Levandaab83502007-06-16 08:06:04 +1000134 lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000135 local_irq_restore(flags);
136}
137
138/**
139 * ps3_chip_eoi - HV end-of-interrupt.
140 * @virq: The assigned Linux virq.
141 *
142 * Calls lv1_end_of_interrupt_ext().
143 */
144
Lennert Buytenhek81267082011-03-08 22:26:56 +0000145static void ps3_chip_eoi(struct irq_data *d)
Geoff Levand743c1bb2007-06-16 07:19:18 +1000146{
Lennert Buytenhek81267082011-03-08 22:26:56 +0000147 const struct ps3_private *pd = irq_data_get_irq_chip_data(d);
Geoff Levand72f3bea2011-11-08 12:37:26 +0000148
149 /* non-IPIs are EOIed here. */
150
151 if (!test_bit(63 - d->irq, &pd->ipi_mask))
152 lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, d->irq);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000153}
154
155/**
156 * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip.
157 */
158
159static struct irq_chip ps3_irq_chip = {
Thomas Gleixnerb27df672009-11-18 23:44:21 +0000160 .name = "ps3",
Lennert Buytenhek81267082011-03-08 22:26:56 +0000161 .irq_mask = ps3_chip_mask,
162 .irq_unmask = ps3_chip_unmask,
163 .irq_eoi = ps3_chip_eoi,
Geoff Levand743c1bb2007-06-16 07:19:18 +1000164};
165
166/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000167 * ps3_virq_setup - virq related setup.
168 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
169 * serviced on.
170 * @outlet: The HV outlet from the various create outlet routines.
171 * @virq: The assigned Linux virq.
172 *
173 * Calls irq_create_mapping() to get a virq and sets the chip data to
174 * ps3_private data.
175 */
176
Geert Uytterhoevenfdedb4c2008-05-01 08:25:18 +1000177static int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
178 unsigned int *virq)
Geoff Levand861be322007-01-26 19:08:08 -0800179{
180 int result;
181 struct ps3_private *pd;
182
183 /* This defines the default interrupt distribution policy. */
184
185 if (cpu == PS3_BINDING_CPU_ANY)
186 cpu = 0;
187
188 pd = &per_cpu(ps3_private, cpu);
189
190 *virq = irq_create_mapping(NULL, outlet);
191
192 if (*virq == NO_IRQ) {
193 pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n",
194 __func__, __LINE__, outlet);
195 result = -ENOMEM;
196 goto fail_create;
197 }
198
Geoff Levand861be322007-01-26 19:08:08 -0800199 pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__,
200 outlet, cpu, *virq);
201
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100202 result = irq_set_chip_data(*virq, pd);
Geoff Levand861be322007-01-26 19:08:08 -0800203
204 if (result) {
Geert Uytterhoevenb618d2f2011-04-09 22:59:07 +0000205 pr_debug("%s:%d: irq_set_chip_data failed\n",
Geoff Levand861be322007-01-26 19:08:08 -0800206 __func__, __LINE__);
207 goto fail_set;
208 }
209
Lennert Buytenhek81267082011-03-08 22:26:56 +0000210 ps3_chip_mask(irq_get_irq_data(*virq));
Geoff Levand9263e852007-06-16 07:19:32 +1000211
Geoff Levand861be322007-01-26 19:08:08 -0800212 return result;
213
214fail_set:
Geoff Levand861be322007-01-26 19:08:08 -0800215 irq_dispose_mapping(*virq);
216fail_create:
217 return result;
218}
219
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000220/**
221 * ps3_virq_destroy - virq related teardown.
222 * @virq: The assigned Linux virq.
223 *
224 * Clears chip data and calls irq_dispose_mapping() for the virq.
225 */
226
Geert Uytterhoevenfdedb4c2008-05-01 08:25:18 +1000227static int ps3_virq_destroy(unsigned int virq)
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000228{
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100229 const struct ps3_private *pd = irq_get_chip_data(virq);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000230
Stephen Rothwell5c949072009-01-13 20:02:39 +0000231 pr_debug("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__,
Geoff Levandaab83502007-06-16 08:06:04 +1000232 __LINE__, pd->ppe_id, pd->thread_id, virq);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000233
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100234 irq_set_chip_data(virq, NULL);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000235 irq_dispose_mapping(virq);
236
237 pr_debug("%s:%d <-\n", __func__, __LINE__);
238 return 0;
239}
240
241/**
242 * ps3_irq_plug_setup - Generic outlet and virq related setup.
243 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
244 * serviced on.
245 * @outlet: The HV outlet from the various create outlet routines.
246 * @virq: The assigned Linux virq.
247 *
248 * Sets up virq and connects the irq plug.
249 */
250
251int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
252 unsigned int *virq)
253{
254 int result;
255 struct ps3_private *pd;
256
257 result = ps3_virq_setup(cpu, outlet, virq);
258
259 if (result) {
260 pr_debug("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__);
261 goto fail_setup;
262 }
263
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100264 pd = irq_get_chip_data(*virq);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000265
266 /* Binds outlet to cpu + virq. */
267
Geoff Levandaab83502007-06-16 08:06:04 +1000268 result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq,
269 outlet, 0);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000270
271 if (result) {
272 pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
273 __func__, __LINE__, ps3_result(result));
274 result = -EPERM;
275 goto fail_connect;
276 }
277
278 return result;
279
280fail_connect:
281 ps3_virq_destroy(*virq);
282fail_setup:
283 return result;
284}
285EXPORT_SYMBOL_GPL(ps3_irq_plug_setup);
286
287/**
288 * ps3_irq_plug_destroy - Generic outlet and virq related teardown.
289 * @virq: The assigned Linux virq.
290 *
291 * Disconnects the irq plug and tears down virq.
292 * Do not call for system bus event interrupts setup with
293 * ps3_sb_event_receive_port_setup().
294 */
295
296int ps3_irq_plug_destroy(unsigned int virq)
Geoff Levand861be322007-01-26 19:08:08 -0800297{
298 int result;
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100299 const struct ps3_private *pd = irq_get_chip_data(virq);
Geoff Levand861be322007-01-26 19:08:08 -0800300
Stephen Rothwell5c949072009-01-13 20:02:39 +0000301 pr_debug("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__,
Geoff Levandaab83502007-06-16 08:06:04 +1000302 __LINE__, pd->ppe_id, pd->thread_id, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800303
Lennert Buytenhek81267082011-03-08 22:26:56 +0000304 ps3_chip_mask(irq_get_irq_data(virq));
Geoff Levand9263e852007-06-16 07:19:32 +1000305
Geoff Levandaab83502007-06-16 08:06:04 +1000306 result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800307
308 if (result)
309 pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
310 __func__, __LINE__, ps3_result(result));
311
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000312 ps3_virq_destroy(virq);
313
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800314 return result;
Geoff Levand861be322007-01-26 19:08:08 -0800315}
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000316EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy);
Geoff Levand861be322007-01-26 19:08:08 -0800317
318/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000319 * ps3_event_receive_port_setup - Setup an event receive port.
Geoff Levand861be322007-01-26 19:08:08 -0800320 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
321 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100322 * @virq: The assigned Linux virq.
323 *
324 * The virq can be used with lv1_connect_interrupt_event_receive_port() to
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000325 * arrange to receive interrupts from system-bus devices, or with
326 * ps3_send_event_locally() to signal events.
Geoff Levand2832a812006-11-23 00:46:56 +0100327 */
328
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000329int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100330{
331 int result;
Stephen Rothwellb17b3df2009-01-13 19:59:41 +0000332 u64 outlet;
Geoff Levand2832a812006-11-23 00:46:56 +0100333
334 result = lv1_construct_event_receive_port(&outlet);
335
336 if (result) {
337 pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n",
338 __func__, __LINE__, ps3_result(result));
339 *virq = NO_IRQ;
340 return result;
341 }
342
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000343 result = ps3_irq_plug_setup(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800344 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100345
Geoff Levand861be322007-01-26 19:08:08 -0800346 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100347}
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000348EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup);
Geoff Levand2832a812006-11-23 00:46:56 +0100349
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000350/**
351 * ps3_event_receive_port_destroy - Destroy an event receive port.
352 * @virq: The assigned Linux virq.
353 *
354 * Since ps3_event_receive_port_destroy destroys the receive port outlet,
355 * SB devices need to call disconnect_interrupt_event_receive_port() before
356 * this.
357 */
358
359int ps3_event_receive_port_destroy(unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100360{
361 int result;
362
Geoff Levand9263e852007-06-16 07:19:32 +1000363 pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq);
364
Lennert Buytenhek81267082011-03-08 22:26:56 +0000365 ps3_chip_mask(irq_get_irq_data(virq));
Geoff Levand2832a812006-11-23 00:46:56 +0100366
367 result = lv1_destruct_event_receive_port(virq_to_hw(virq));
368
369 if (result)
370 pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
371 __func__, __LINE__, ps3_result(result));
372
Geoff Levand9263e852007-06-16 07:19:32 +1000373 /*
374 * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu()
375 * calls from interrupt context (smp_call_function) when kexecing.
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000376 */
377
Geoff Levand2832a812006-11-23 00:46:56 +0100378 pr_debug(" <- %s:%d\n", __func__, __LINE__);
379 return result;
380}
381
382int ps3_send_event_locally(unsigned int virq)
383{
384 return lv1_send_event_locally(virq_to_hw(virq));
385}
386
387/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000388 * ps3_sb_event_receive_port_setup - Setup a system bus event receive port.
Geoff Levand861be322007-01-26 19:08:08 -0800389 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
390 * serviced on.
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000391 * @dev: The system bus device instance.
Geoff Levand2832a812006-11-23 00:46:56 +0100392 * @virq: The assigned Linux virq.
393 *
394 * An event irq represents a virtual device interrupt. The interrupt_id
395 * coresponds to the software interrupt number.
396 */
397
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000398int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev,
399 enum ps3_cpu_binding cpu, unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100400{
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000401 /* this should go in system-bus.c */
402
Geoff Levand2832a812006-11-23 00:46:56 +0100403 int result;
404
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000405 result = ps3_event_receive_port_setup(cpu, virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100406
407 if (result)
408 return result;
409
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000410 result = lv1_connect_interrupt_event_receive_port(dev->bus_id,
411 dev->dev_id, virq_to_hw(*virq), dev->interrupt_id);
Geoff Levand2832a812006-11-23 00:46:56 +0100412
413 if (result) {
414 pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port"
415 " failed: %s\n", __func__, __LINE__,
416 ps3_result(result));
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000417 ps3_event_receive_port_destroy(*virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100418 *virq = NO_IRQ;
419 return result;
420 }
421
422 pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000423 dev->interrupt_id, *virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100424
425 return 0;
426}
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000427EXPORT_SYMBOL(ps3_sb_event_receive_port_setup);
Geoff Levand2832a812006-11-23 00:46:56 +0100428
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000429int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev,
430 unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100431{
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000432 /* this should go in system-bus.c */
433
Geoff Levand2832a812006-11-23 00:46:56 +0100434 int result;
435
436 pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000437 dev->interrupt_id, virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100438
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000439 result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id,
440 dev->dev_id, virq_to_hw(virq), dev->interrupt_id);
Geoff Levand2832a812006-11-23 00:46:56 +0100441
442 if (result)
443 pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port"
444 " failed: %s\n", __func__, __LINE__,
445 ps3_result(result));
446
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000447 result = ps3_event_receive_port_destroy(virq);
448 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100449
Geoff Levand9263e852007-06-16 07:19:32 +1000450 /*
451 * ps3_event_receive_port_destroy() destroys the IRQ plug,
452 * so don't call ps3_irq_plug_destroy() here.
453 */
454
455 result = ps3_virq_destroy(virq);
456 BUG_ON(result);
457
Geoff Levand2832a812006-11-23 00:46:56 +0100458 pr_debug(" <- %s:%d\n", __func__, __LINE__);
459 return result;
460}
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000461EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy);
Geoff Levand2832a812006-11-23 00:46:56 +0100462
463/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000464 * ps3_io_irq_setup - Setup a system bus io irq.
465 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
466 * serviced on.
467 * @interrupt_id: The device interrupt id read from the system repository.
468 * @virq: The assigned Linux virq.
469 *
470 * An io irq represents a non-virtualized device interrupt. interrupt_id
471 * coresponds to the interrupt number of the interrupt controller.
472 */
473
474int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
475 unsigned int *virq)
476{
477 int result;
Stephen Rothwellb17b3df2009-01-13 19:59:41 +0000478 u64 outlet;
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000479
480 result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);
481
482 if (result) {
483 pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
484 __func__, __LINE__, ps3_result(result));
485 return result;
486 }
487
488 result = ps3_irq_plug_setup(cpu, outlet, virq);
489 BUG_ON(result);
490
491 return result;
492}
493EXPORT_SYMBOL_GPL(ps3_io_irq_setup);
494
495int ps3_io_irq_destroy(unsigned int virq)
496{
497 int result;
Geoff Levand9263e852007-06-16 07:19:32 +1000498 unsigned long outlet = virq_to_hw(virq);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000499
Lennert Buytenhek81267082011-03-08 22:26:56 +0000500 ps3_chip_mask(irq_get_irq_data(virq));
Geoff Levand9263e852007-06-16 07:19:32 +1000501
502 /*
503 * lv1_destruct_io_irq_outlet() will destroy the IRQ plug,
504 * so call ps3_irq_plug_destroy() first.
505 */
506
507 result = ps3_irq_plug_destroy(virq);
508 BUG_ON(result);
509
510 result = lv1_destruct_io_irq_outlet(outlet);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000511
512 if (result)
513 pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
514 __func__, __LINE__, ps3_result(result));
515
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000516 return result;
517}
518EXPORT_SYMBOL_GPL(ps3_io_irq_destroy);
519
520/**
521 * ps3_vuart_irq_setup - Setup the system virtual uart virq.
Geoff Levand861be322007-01-26 19:08:08 -0800522 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
523 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100524 * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
525 * @virq: The assigned Linux virq.
526 *
527 * The system supports only a single virtual uart, so multiple calls without
528 * freeing the interrupt will return a wrong state error.
529 */
530
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000531int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
Geoff Levand861be322007-01-26 19:08:08 -0800532 unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100533{
534 int result;
Stephen Rothwellb17b3df2009-01-13 19:59:41 +0000535 u64 outlet;
Geoff Levand861be322007-01-26 19:08:08 -0800536 u64 lpar_addr;
Geoff Levand2832a812006-11-23 00:46:56 +0100537
Geoff Levand861be322007-01-26 19:08:08 -0800538 BUG_ON(!is_kernel_addr((u64)virt_addr_bmp));
Geoff Levand2832a812006-11-23 00:46:56 +0100539
540 lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp));
541
542 result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet);
543
544 if (result) {
545 pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
546 __func__, __LINE__, ps3_result(result));
547 return result;
548 }
549
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000550 result = ps3_irq_plug_setup(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800551 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100552
Geoff Levand861be322007-01-26 19:08:08 -0800553 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100554}
Geoff Levand7626e782007-06-16 08:01:06 +1000555EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup);
Geoff Levand2832a812006-11-23 00:46:56 +0100556
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000557int ps3_vuart_irq_destroy(unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100558{
559 int result;
560
Lennert Buytenhek81267082011-03-08 22:26:56 +0000561 ps3_chip_mask(irq_get_irq_data(virq));
Geoff Levand2832a812006-11-23 00:46:56 +0100562 result = lv1_deconfigure_virtual_uart_irq();
563
564 if (result) {
565 pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
566 __func__, __LINE__, ps3_result(result));
567 return result;
568 }
569
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000570 result = ps3_irq_plug_destroy(virq);
571 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100572
573 return result;
574}
Geoff Levand7626e782007-06-16 08:01:06 +1000575EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy);
Geoff Levand2832a812006-11-23 00:46:56 +0100576
577/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000578 * ps3_spe_irq_setup - Setup an spe virq.
Geoff Levand861be322007-01-26 19:08:08 -0800579 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
580 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100581 * @spe_id: The spe_id returned from lv1_construct_logical_spe().
582 * @class: The spe interrupt class {0,1,2}.
583 * @virq: The assigned Linux virq.
584 *
585 */
586
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000587int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id,
Geoff Levand861be322007-01-26 19:08:08 -0800588 unsigned int class, unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100589{
590 int result;
Stephen Rothwellb17b3df2009-01-13 19:59:41 +0000591 u64 outlet;
Geoff Levand2832a812006-11-23 00:46:56 +0100592
593 BUG_ON(class > 2);
594
595 result = lv1_get_spe_irq_outlet(spe_id, class, &outlet);
596
597 if (result) {
598 pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n",
599 __func__, __LINE__, ps3_result(result));
600 return result;
601 }
602
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000603 result = ps3_irq_plug_setup(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800604 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100605
Geoff Levand861be322007-01-26 19:08:08 -0800606 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100607}
608
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000609int ps3_spe_irq_destroy(unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100610{
Geoff Levand9263e852007-06-16 07:19:32 +1000611 int result;
612
Lennert Buytenhek81267082011-03-08 22:26:56 +0000613 ps3_chip_mask(irq_get_irq_data(virq));
Geoff Levand9263e852007-06-16 07:19:32 +1000614
615 result = ps3_irq_plug_destroy(virq);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000616 BUG_ON(result);
Geoff Levand9263e852007-06-16 07:19:32 +1000617
618 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100619}
620
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800621
Geoff Levand2832a812006-11-23 00:46:56 +0100622#define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
623#define PS3_PLUG_MAX 63
624
Geoff Levand2832a812006-11-23 00:46:56 +0100625#if defined(DEBUG)
Geoff Levand861be322007-01-26 19:08:08 -0800626static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu,
Geoff Levand2832a812006-11-23 00:46:56 +0100627 const char* func, int line)
628{
629 pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n",
630 func, line, header, cpu,
631 *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff,
632 *p & 0xffff);
633}
634
Geoff Levand848cfdc2007-06-16 07:18:14 +1000635static void __maybe_unused _dump_256_bmp(const char *header,
Geoff Levand861be322007-01-26 19:08:08 -0800636 const u64 *p, unsigned cpu, const char* func, int line)
Geoff Levand2832a812006-11-23 00:46:56 +0100637{
638 pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n",
639 func, line, header, cpu, p[0], p[1], p[2], p[3]);
640}
641
642#define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__)
Geoff Levand9633ac82007-01-26 19:07:59 -0800643static void _dump_bmp(struct ps3_private* pd, const char* func, int line)
Geoff Levand2832a812006-11-23 00:46:56 +0100644{
645 unsigned long flags;
646
647 spin_lock_irqsave(&pd->bmp.lock, flags);
Geoff Levandaab83502007-06-16 08:06:04 +1000648 _dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line);
649 _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line);
Geoff Levand2832a812006-11-23 00:46:56 +0100650 spin_unlock_irqrestore(&pd->bmp.lock, flags);
651}
652
653#define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
Geoff Levand848cfdc2007-06-16 07:18:14 +1000654static void __maybe_unused _dump_mask(struct ps3_private *pd,
Geoff Levand2832a812006-11-23 00:46:56 +0100655 const char* func, int line)
656{
657 unsigned long flags;
658
659 spin_lock_irqsave(&pd->bmp.lock, flags);
Geoff Levandaab83502007-06-16 08:06:04 +1000660 _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line);
Geoff Levand2832a812006-11-23 00:46:56 +0100661 spin_unlock_irqrestore(&pd->bmp.lock, flags);
662}
663#else
Geoff Levand9633ac82007-01-26 19:07:59 -0800664static void dump_bmp(struct ps3_private* pd) {};
Geoff Levand2832a812006-11-23 00:46:56 +0100665#endif /* defined(DEBUG) */
666
Geoff Levand9633ac82007-01-26 19:07:59 -0800667static int ps3_host_map(struct irq_host *h, unsigned int virq,
Geoff Levand2832a812006-11-23 00:46:56 +0100668 irq_hw_number_t hwirq)
669{
Geoff Levand861be322007-01-26 19:08:08 -0800670 pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
671 virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100672
Thomas Gleixnerec775d02011-03-25 16:45:20 +0100673 irq_set_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq);
Geoff Levand2832a812006-11-23 00:46:56 +0100674
Geoff Levand861be322007-01-26 19:08:08 -0800675 return 0;
Geoff Levand2832a812006-11-23 00:46:56 +0100676}
677
Michael Ellerman8528ab82007-08-28 18:47:55 +1000678static int ps3_host_match(struct irq_host *h, struct device_node *np)
679{
680 /* Match all */
681 return 1;
682}
683
Geoff Levand9633ac82007-01-26 19:07:59 -0800684static struct irq_host_ops ps3_host_ops = {
685 .map = ps3_host_map,
Michael Ellerman8528ab82007-08-28 18:47:55 +1000686 .match = ps3_host_match,
Geoff Levand2832a812006-11-23 00:46:56 +0100687};
688
689void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
690{
Geoff Levand9633ac82007-01-26 19:07:59 -0800691 struct ps3_private *pd = &per_cpu(ps3_private, cpu);
Geoff Levand2832a812006-11-23 00:46:56 +0100692
693 pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq;
694
Stephen Rothwell5c949072009-01-13 20:02:39 +0000695 pr_debug("%s:%d: cpu %u, virq %u, mask %llxh\n", __func__, __LINE__,
Geoff Levand2832a812006-11-23 00:46:56 +0100696 cpu, virq, pd->bmp.ipi_debug_brk_mask);
697}
698
Geoff Levand72f3bea2011-11-08 12:37:26 +0000699void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq)
700{
701 struct ps3_private *pd = &per_cpu(ps3_private, cpu);
702
703 set_bit(63 - virq, &pd->ipi_mask);
704
705 DBG("%s:%d: cpu %u, virq %u, ipi_mask %lxh\n", __func__, __LINE__,
706 cpu, virq, pd->ipi_mask);
707}
708
Geoff Levand9263e852007-06-16 07:19:32 +1000709static unsigned int ps3_get_irq(void)
Geoff Levand2832a812006-11-23 00:46:56 +0100710{
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800711 struct ps3_private *pd = &__get_cpu_var(ps3_private);
Geoff Levand861be322007-01-26 19:08:08 -0800712 u64 x = (pd->bmp.status & pd->bmp.mask);
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800713 unsigned int plug;
Geoff Levand2832a812006-11-23 00:46:56 +0100714
715 /* check for ipi break first to stop this cpu ASAP */
716
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800717 if (x & pd->bmp.ipi_debug_brk_mask)
718 x &= pd->bmp.ipi_debug_brk_mask;
Geoff Levand2832a812006-11-23 00:46:56 +0100719
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800720 asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x));
721 plug &= 0x3f;
Geoff Levand2832a812006-11-23 00:46:56 +0100722
Roel Kluinad18c3d2008-03-27 11:37:18 +1100723 if (unlikely(plug == NO_IRQ)) {
Stephen Rothwell5c949072009-01-13 20:02:39 +0000724 pr_debug("%s:%d: no plug found: thread_id %llu\n", __func__,
Geoff Levandaab83502007-06-16 08:06:04 +1000725 __LINE__, pd->thread_id);
Geoff Levand9633ac82007-01-26 19:07:59 -0800726 dump_bmp(&per_cpu(ps3_private, 0));
727 dump_bmp(&per_cpu(ps3_private, 1));
Geoff Levand2832a812006-11-23 00:46:56 +0100728 return NO_IRQ;
729 }
730
731#if defined(DEBUG)
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800732 if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) {
Geoff Levand9633ac82007-01-26 19:07:59 -0800733 dump_bmp(&per_cpu(ps3_private, 0));
734 dump_bmp(&per_cpu(ps3_private, 1));
Geoff Levand2832a812006-11-23 00:46:56 +0100735 BUG();
736 }
737#endif
Geoff Levand72f3bea2011-11-08 12:37:26 +0000738
739 /* IPIs are EOIed here. */
740
741 if (test_bit(63 - plug, &pd->ipi_mask))
742 lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, plug);
743
Geoff Levand2832a812006-11-23 00:46:56 +0100744 return plug;
745}
746
747void __init ps3_init_IRQ(void)
748{
749 int result;
Geoff Levand2832a812006-11-23 00:46:56 +0100750 unsigned cpu;
751 struct irq_host *host;
752
Michael Ellerman52964f82007-08-28 18:47:54 +1000753 host = irq_alloc_host(NULL, IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops,
Geoff Levand2832a812006-11-23 00:46:56 +0100754 PS3_INVALID_OUTLET);
755 irq_set_default_host(host);
756 irq_set_virq_count(PS3_PLUG_MAX + 1);
757
758 for_each_possible_cpu(cpu) {
Geoff Levand9633ac82007-01-26 19:07:59 -0800759 struct ps3_private *pd = &per_cpu(ps3_private, cpu);
Geoff Levand2832a812006-11-23 00:46:56 +0100760
Geoff Levandaab83502007-06-16 08:06:04 +1000761 lv1_get_logical_ppe_id(&pd->ppe_id);
762 pd->thread_id = get_hard_smp_processor_id(cpu);
Geoff Levand2832a812006-11-23 00:46:56 +0100763 spin_lock_init(&pd->bmp.lock);
764
Stephen Rothwell5c949072009-01-13 20:02:39 +0000765 pr_debug("%s:%d: ppe_id %llu, thread_id %llu, bmp %lxh\n",
Geoff Levandaab83502007-06-16 08:06:04 +1000766 __func__, __LINE__, pd->ppe_id, pd->thread_id,
Geoff Levand407e24a2007-01-26 19:08:02 -0800767 ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
768
Geoff Levandaab83502007-06-16 08:06:04 +1000769 result = lv1_configure_irq_state_bitmap(pd->ppe_id,
770 pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
Geoff Levand2832a812006-11-23 00:46:56 +0100771
772 if (result)
773 pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:"
774 " %s\n", __func__, __LINE__,
775 ps3_result(result));
776 }
777
778 ppc_md.get_irq = ps3_get_irq;
779}
Geoff Levand9263e852007-06-16 07:19:32 +1000780
781void ps3_shutdown_IRQ(int cpu)
782{
783 int result;
784 u64 ppe_id;
785 u64 thread_id = get_hard_smp_processor_id(cpu);
786
787 lv1_get_logical_ppe_id(&ppe_id);
788 result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0);
789
Stephen Rothwell5c949072009-01-13 20:02:39 +0000790 DBG("%s:%d: lv1_configure_irq_state_bitmap (%llu:%llu/%d) %s\n", __func__,
Geoff Levand9263e852007-06-16 07:19:32 +1000791 __LINE__, ppe_id, thread_id, cpu, ps3_result(result));
792}