blob: e2de899d74632a03996a2bf968fd371b793466b7 [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>
22#include <linux/module.h>
23#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 *
47 * The HV mantains per SMT thread mappings of HV outlet to HV plug on
48 * 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.
63 */
64
Geoff Levand57715762007-01-26 19:08:16 -080065#define PS3_BMP_MINALIGN 64
66
Geoff Levand861be322007-01-26 19:08:08 -080067struct ps3_bmp {
68 struct {
69 u64 status;
70 u64 unused_1[3];
71 u64 mask;
72 u64 unused_2[3];
73 };
74 u64 ipi_debug_brk_mask;
75 spinlock_t lock;
76};
77
78/**
79 * struct ps3_private - a per cpu data structure
80 * @bmp: ps3_bmp structure
Geoff Levandaab83502007-06-16 08:06:04 +100081 * @ppe_id: HV logical_ppe_id
82 * @thread_id: HV thread_id
Geoff Levand861be322007-01-26 19:08:08 -080083 */
84
85struct ps3_private {
Geoff Levand57715762007-01-26 19:08:16 -080086 struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN)));
Geoff Levandaab83502007-06-16 08:06:04 +100087 u64 ppe_id;
88 u64 thread_id;
Geoff Levand861be322007-01-26 19:08:08 -080089};
90
91static DEFINE_PER_CPU(struct ps3_private, ps3_private);
92
Geoff Levanddc4f60c2007-05-01 07:01:01 +100093/**
Geoff Levand743c1bb2007-06-16 07:19:18 +100094 * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp.
95 * @virq: The assigned Linux virq.
96 *
97 * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
98 */
99
100static void ps3_chip_mask(unsigned int virq)
101{
102 struct ps3_private *pd = get_irq_chip_data(virq);
103 u64 bit = 0x8000000000000000UL >> virq;
104 u64 *p = &pd->bmp.mask;
105 u64 old;
106 unsigned long flags;
107
Geoff Levandaab83502007-06-16 08:06:04 +1000108 pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__,
109 pd->thread_id, virq);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000110
111 local_irq_save(flags);
112 asm volatile(
113 "1: ldarx %0,0,%3\n"
114 "andc %0,%0,%2\n"
115 "stdcx. %0,0,%3\n"
116 "bne- 1b"
117 : "=&r" (old), "+m" (*p)
118 : "r" (bit), "r" (p)
119 : "cc" );
120
Geoff Levandaab83502007-06-16 08:06:04 +1000121 lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000122 local_irq_restore(flags);
123}
124
125/**
126 * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp.
127 * @virq: The assigned Linux virq.
128 *
129 * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask().
130 */
131
132static void ps3_chip_unmask(unsigned int virq)
133{
134 struct ps3_private *pd = get_irq_chip_data(virq);
135 u64 bit = 0x8000000000000000UL >> virq;
136 u64 *p = &pd->bmp.mask;
137 u64 old;
138 unsigned long flags;
139
Geoff Levandaab83502007-06-16 08:06:04 +1000140 pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__,
141 pd->thread_id, virq);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000142
143 local_irq_save(flags);
144 asm volatile(
145 "1: ldarx %0,0,%3\n"
146 "or %0,%0,%2\n"
147 "stdcx. %0,0,%3\n"
148 "bne- 1b"
149 : "=&r" (old), "+m" (*p)
150 : "r" (bit), "r" (p)
151 : "cc" );
152
Geoff Levandaab83502007-06-16 08:06:04 +1000153 lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000154 local_irq_restore(flags);
155}
156
157/**
158 * ps3_chip_eoi - HV end-of-interrupt.
159 * @virq: The assigned Linux virq.
160 *
161 * Calls lv1_end_of_interrupt_ext().
162 */
163
164static void ps3_chip_eoi(unsigned int virq)
165{
166 const struct ps3_private *pd = get_irq_chip_data(virq);
Geoff Levandaab83502007-06-16 08:06:04 +1000167 lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, virq);
Geoff Levand743c1bb2007-06-16 07:19:18 +1000168}
169
170/**
171 * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip.
172 */
173
174static struct irq_chip ps3_irq_chip = {
175 .typename = "ps3",
176 .mask = ps3_chip_mask,
177 .unmask = ps3_chip_unmask,
178 .eoi = ps3_chip_eoi,
179};
180
181/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000182 * ps3_virq_setup - virq related setup.
183 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
184 * serviced on.
185 * @outlet: The HV outlet from the various create outlet routines.
186 * @virq: The assigned Linux virq.
187 *
188 * Calls irq_create_mapping() to get a virq and sets the chip data to
189 * ps3_private data.
190 */
191
192int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
Geoff Levand861be322007-01-26 19:08:08 -0800193 unsigned int *virq)
194{
195 int result;
196 struct ps3_private *pd;
197
198 /* This defines the default interrupt distribution policy. */
199
200 if (cpu == PS3_BINDING_CPU_ANY)
201 cpu = 0;
202
203 pd = &per_cpu(ps3_private, cpu);
204
205 *virq = irq_create_mapping(NULL, outlet);
206
207 if (*virq == NO_IRQ) {
208 pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n",
209 __func__, __LINE__, outlet);
210 result = -ENOMEM;
211 goto fail_create;
212 }
213
Geoff Levand861be322007-01-26 19:08:08 -0800214 pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__,
215 outlet, cpu, *virq);
216
217 result = set_irq_chip_data(*virq, pd);
218
219 if (result) {
220 pr_debug("%s:%d: set_irq_chip_data failed\n",
221 __func__, __LINE__);
222 goto fail_set;
223 }
224
Geoff Levand9263e852007-06-16 07:19:32 +1000225 ps3_chip_mask(*virq);
226
Geoff Levand861be322007-01-26 19:08:08 -0800227 return result;
228
229fail_set:
Geoff Levand861be322007-01-26 19:08:08 -0800230 irq_dispose_mapping(*virq);
231fail_create:
232 return result;
233}
234
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000235/**
236 * ps3_virq_destroy - virq related teardown.
237 * @virq: The assigned Linux virq.
238 *
239 * Clears chip data and calls irq_dispose_mapping() for the virq.
240 */
241
242int ps3_virq_destroy(unsigned int virq)
243{
244 const struct ps3_private *pd = get_irq_chip_data(virq);
245
Geoff Levandaab83502007-06-16 08:06:04 +1000246 pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__,
247 __LINE__, pd->ppe_id, pd->thread_id, virq);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000248
249 set_irq_chip_data(virq, NULL);
250 irq_dispose_mapping(virq);
251
252 pr_debug("%s:%d <-\n", __func__, __LINE__);
253 return 0;
254}
255
256/**
257 * ps3_irq_plug_setup - Generic outlet and virq related setup.
258 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
259 * serviced on.
260 * @outlet: The HV outlet from the various create outlet routines.
261 * @virq: The assigned Linux virq.
262 *
263 * Sets up virq and connects the irq plug.
264 */
265
266int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
267 unsigned int *virq)
268{
269 int result;
270 struct ps3_private *pd;
271
272 result = ps3_virq_setup(cpu, outlet, virq);
273
274 if (result) {
275 pr_debug("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__);
276 goto fail_setup;
277 }
278
279 pd = get_irq_chip_data(*virq);
280
281 /* Binds outlet to cpu + virq. */
282
Geoff Levandaab83502007-06-16 08:06:04 +1000283 result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq,
284 outlet, 0);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000285
286 if (result) {
287 pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
288 __func__, __LINE__, ps3_result(result));
289 result = -EPERM;
290 goto fail_connect;
291 }
292
293 return result;
294
295fail_connect:
296 ps3_virq_destroy(*virq);
297fail_setup:
298 return result;
299}
300EXPORT_SYMBOL_GPL(ps3_irq_plug_setup);
301
302/**
303 * ps3_irq_plug_destroy - Generic outlet and virq related teardown.
304 * @virq: The assigned Linux virq.
305 *
306 * Disconnects the irq plug and tears down virq.
307 * Do not call for system bus event interrupts setup with
308 * ps3_sb_event_receive_port_setup().
309 */
310
311int ps3_irq_plug_destroy(unsigned int virq)
Geoff Levand861be322007-01-26 19:08:08 -0800312{
313 int result;
314 const struct ps3_private *pd = get_irq_chip_data(virq);
315
Geoff Levandaab83502007-06-16 08:06:04 +1000316 pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__,
317 __LINE__, pd->ppe_id, pd->thread_id, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800318
Geoff Levand9263e852007-06-16 07:19:32 +1000319 ps3_chip_mask(virq);
320
Geoff Levandaab83502007-06-16 08:06:04 +1000321 result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800322
323 if (result)
324 pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
325 __func__, __LINE__, ps3_result(result));
326
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000327 ps3_virq_destroy(virq);
328
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800329 return result;
Geoff Levand861be322007-01-26 19:08:08 -0800330}
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000331EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy);
Geoff Levand861be322007-01-26 19:08:08 -0800332
333/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000334 * ps3_event_receive_port_setup - Setup an event receive port.
Geoff Levand861be322007-01-26 19:08:08 -0800335 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
336 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100337 * @virq: The assigned Linux virq.
338 *
339 * The virq can be used with lv1_connect_interrupt_event_receive_port() to
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000340 * arrange to receive interrupts from system-bus devices, or with
341 * ps3_send_event_locally() to signal events.
Geoff Levand2832a812006-11-23 00:46:56 +0100342 */
343
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000344int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100345{
346 int result;
347 unsigned long outlet;
348
349 result = lv1_construct_event_receive_port(&outlet);
350
351 if (result) {
352 pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n",
353 __func__, __LINE__, ps3_result(result));
354 *virq = NO_IRQ;
355 return result;
356 }
357
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000358 result = ps3_irq_plug_setup(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800359 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100360
Geoff Levand861be322007-01-26 19:08:08 -0800361 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100362}
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000363EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup);
Geoff Levand2832a812006-11-23 00:46:56 +0100364
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000365/**
366 * ps3_event_receive_port_destroy - Destroy an event receive port.
367 * @virq: The assigned Linux virq.
368 *
369 * Since ps3_event_receive_port_destroy destroys the receive port outlet,
370 * SB devices need to call disconnect_interrupt_event_receive_port() before
371 * this.
372 */
373
374int ps3_event_receive_port_destroy(unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100375{
376 int result;
377
Geoff Levand9263e852007-06-16 07:19:32 +1000378 pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq);
379
380 ps3_chip_mask(virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100381
382 result = lv1_destruct_event_receive_port(virq_to_hw(virq));
383
384 if (result)
385 pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
386 __func__, __LINE__, ps3_result(result));
387
Geoff Levand9263e852007-06-16 07:19:32 +1000388 /*
389 * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu()
390 * calls from interrupt context (smp_call_function) when kexecing.
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000391 */
392
Geoff Levand2832a812006-11-23 00:46:56 +0100393 pr_debug(" <- %s:%d\n", __func__, __LINE__);
394 return result;
395}
396
397int ps3_send_event_locally(unsigned int virq)
398{
399 return lv1_send_event_locally(virq_to_hw(virq));
400}
401
402/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000403 * ps3_sb_event_receive_port_setup - Setup a system bus event receive port.
Geoff Levand861be322007-01-26 19:08:08 -0800404 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
405 * serviced on.
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000406 * @dev: The system bus device instance.
Geoff Levand2832a812006-11-23 00:46:56 +0100407 * @virq: The assigned Linux virq.
408 *
409 * An event irq represents a virtual device interrupt. The interrupt_id
410 * coresponds to the software interrupt number.
411 */
412
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000413int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev,
414 enum ps3_cpu_binding cpu, unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100415{
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000416 /* this should go in system-bus.c */
417
Geoff Levand2832a812006-11-23 00:46:56 +0100418 int result;
419
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000420 result = ps3_event_receive_port_setup(cpu, virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100421
422 if (result)
423 return result;
424
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000425 result = lv1_connect_interrupt_event_receive_port(dev->bus_id,
426 dev->dev_id, virq_to_hw(*virq), dev->interrupt_id);
Geoff Levand2832a812006-11-23 00:46:56 +0100427
428 if (result) {
429 pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port"
430 " failed: %s\n", __func__, __LINE__,
431 ps3_result(result));
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000432 ps3_event_receive_port_destroy(*virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100433 *virq = NO_IRQ;
434 return result;
435 }
436
437 pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000438 dev->interrupt_id, *virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100439
440 return 0;
441}
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000442EXPORT_SYMBOL(ps3_sb_event_receive_port_setup);
Geoff Levand2832a812006-11-23 00:46:56 +0100443
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000444int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev,
445 unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100446{
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000447 /* this should go in system-bus.c */
448
Geoff Levand2832a812006-11-23 00:46:56 +0100449 int result;
450
451 pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000452 dev->interrupt_id, virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100453
Geoff Levand6bb5cf12007-06-16 07:52:02 +1000454 result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id,
455 dev->dev_id, virq_to_hw(virq), dev->interrupt_id);
Geoff Levand2832a812006-11-23 00:46:56 +0100456
457 if (result)
458 pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port"
459 " failed: %s\n", __func__, __LINE__,
460 ps3_result(result));
461
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000462 result = ps3_event_receive_port_destroy(virq);
463 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100464
Geoff Levand9263e852007-06-16 07:19:32 +1000465 /*
466 * ps3_event_receive_port_destroy() destroys the IRQ plug,
467 * so don't call ps3_irq_plug_destroy() here.
468 */
469
470 result = ps3_virq_destroy(virq);
471 BUG_ON(result);
472
Geoff Levand2832a812006-11-23 00:46:56 +0100473 pr_debug(" <- %s:%d\n", __func__, __LINE__);
474 return result;
475}
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000476EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy);
Geoff Levand2832a812006-11-23 00:46:56 +0100477
478/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000479 * ps3_io_irq_setup - Setup a system bus io irq.
480 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
481 * serviced on.
482 * @interrupt_id: The device interrupt id read from the system repository.
483 * @virq: The assigned Linux virq.
484 *
485 * An io irq represents a non-virtualized device interrupt. interrupt_id
486 * coresponds to the interrupt number of the interrupt controller.
487 */
488
489int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
490 unsigned int *virq)
491{
492 int result;
493 unsigned long outlet;
494
495 result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);
496
497 if (result) {
498 pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
499 __func__, __LINE__, ps3_result(result));
500 return result;
501 }
502
503 result = ps3_irq_plug_setup(cpu, outlet, virq);
504 BUG_ON(result);
505
506 return result;
507}
508EXPORT_SYMBOL_GPL(ps3_io_irq_setup);
509
510int ps3_io_irq_destroy(unsigned int virq)
511{
512 int result;
Geoff Levand9263e852007-06-16 07:19:32 +1000513 unsigned long outlet = virq_to_hw(virq);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000514
Geoff Levand9263e852007-06-16 07:19:32 +1000515 ps3_chip_mask(virq);
516
517 /*
518 * lv1_destruct_io_irq_outlet() will destroy the IRQ plug,
519 * so call ps3_irq_plug_destroy() first.
520 */
521
522 result = ps3_irq_plug_destroy(virq);
523 BUG_ON(result);
524
525 result = lv1_destruct_io_irq_outlet(outlet);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000526
527 if (result)
528 pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
529 __func__, __LINE__, ps3_result(result));
530
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000531 return result;
532}
533EXPORT_SYMBOL_GPL(ps3_io_irq_destroy);
534
535/**
536 * ps3_vuart_irq_setup - Setup the system virtual uart virq.
Geoff Levand861be322007-01-26 19:08:08 -0800537 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
538 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100539 * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
540 * @virq: The assigned Linux virq.
541 *
542 * The system supports only a single virtual uart, so multiple calls without
543 * freeing the interrupt will return a wrong state error.
544 */
545
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000546int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
Geoff Levand861be322007-01-26 19:08:08 -0800547 unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100548{
549 int result;
550 unsigned long outlet;
Geoff Levand861be322007-01-26 19:08:08 -0800551 u64 lpar_addr;
Geoff Levand2832a812006-11-23 00:46:56 +0100552
Geoff Levand861be322007-01-26 19:08:08 -0800553 BUG_ON(!is_kernel_addr((u64)virt_addr_bmp));
Geoff Levand2832a812006-11-23 00:46:56 +0100554
555 lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp));
556
557 result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet);
558
559 if (result) {
560 pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
561 __func__, __LINE__, ps3_result(result));
562 return result;
563 }
564
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000565 result = ps3_irq_plug_setup(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800566 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100567
Geoff Levand861be322007-01-26 19:08:08 -0800568 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100569}
Geoff Levand7626e782007-06-16 08:01:06 +1000570EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup);
Geoff Levand2832a812006-11-23 00:46:56 +0100571
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000572int ps3_vuart_irq_destroy(unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100573{
574 int result;
575
Geoff Levand9263e852007-06-16 07:19:32 +1000576 ps3_chip_mask(virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100577 result = lv1_deconfigure_virtual_uart_irq();
578
579 if (result) {
580 pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
581 __func__, __LINE__, ps3_result(result));
582 return result;
583 }
584
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000585 result = ps3_irq_plug_destroy(virq);
586 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100587
588 return result;
589}
Geoff Levand7626e782007-06-16 08:01:06 +1000590EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy);
Geoff Levand2832a812006-11-23 00:46:56 +0100591
592/**
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000593 * ps3_spe_irq_setup - Setup an spe virq.
Geoff Levand861be322007-01-26 19:08:08 -0800594 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
595 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100596 * @spe_id: The spe_id returned from lv1_construct_logical_spe().
597 * @class: The spe interrupt class {0,1,2}.
598 * @virq: The assigned Linux virq.
599 *
600 */
601
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000602int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id,
Geoff Levand861be322007-01-26 19:08:08 -0800603 unsigned int class, unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100604{
605 int result;
606 unsigned long outlet;
607
608 BUG_ON(class > 2);
609
610 result = lv1_get_spe_irq_outlet(spe_id, class, &outlet);
611
612 if (result) {
613 pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n",
614 __func__, __LINE__, ps3_result(result));
615 return result;
616 }
617
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000618 result = ps3_irq_plug_setup(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800619 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100620
Geoff Levand861be322007-01-26 19:08:08 -0800621 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100622}
623
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000624int ps3_spe_irq_destroy(unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100625{
Geoff Levand9263e852007-06-16 07:19:32 +1000626 int result;
627
628 ps3_chip_mask(virq);
629
630 result = ps3_irq_plug_destroy(virq);
Geoff Levanddc4f60c2007-05-01 07:01:01 +1000631 BUG_ON(result);
Geoff Levand9263e852007-06-16 07:19:32 +1000632
633 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100634}
635
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800636
Geoff Levand2832a812006-11-23 00:46:56 +0100637#define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
638#define PS3_PLUG_MAX 63
639
Geoff Levand2832a812006-11-23 00:46:56 +0100640#if defined(DEBUG)
Geoff Levand861be322007-01-26 19:08:08 -0800641static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu,
Geoff Levand2832a812006-11-23 00:46:56 +0100642 const char* func, int line)
643{
644 pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n",
645 func, line, header, cpu,
646 *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff,
647 *p & 0xffff);
648}
649
Geoff Levand848cfdc2007-06-16 07:18:14 +1000650static void __maybe_unused _dump_256_bmp(const char *header,
Geoff Levand861be322007-01-26 19:08:08 -0800651 const u64 *p, unsigned cpu, const char* func, int line)
Geoff Levand2832a812006-11-23 00:46:56 +0100652{
653 pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n",
654 func, line, header, cpu, p[0], p[1], p[2], p[3]);
655}
656
657#define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__)
Geoff Levand9633ac82007-01-26 19:07:59 -0800658static void _dump_bmp(struct ps3_private* pd, const char* func, int line)
Geoff Levand2832a812006-11-23 00:46:56 +0100659{
660 unsigned long flags;
661
662 spin_lock_irqsave(&pd->bmp.lock, flags);
Geoff Levandaab83502007-06-16 08:06:04 +1000663 _dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line);
664 _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line);
Geoff Levand2832a812006-11-23 00:46:56 +0100665 spin_unlock_irqrestore(&pd->bmp.lock, flags);
666}
667
668#define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
Geoff Levand848cfdc2007-06-16 07:18:14 +1000669static void __maybe_unused _dump_mask(struct ps3_private *pd,
Geoff Levand2832a812006-11-23 00:46:56 +0100670 const char* func, int line)
671{
672 unsigned long flags;
673
674 spin_lock_irqsave(&pd->bmp.lock, flags);
Geoff Levandaab83502007-06-16 08:06:04 +1000675 _dump_64_bmp("mask", &pd->bmp.mask, pd->thread_id, func, line);
Geoff Levand2832a812006-11-23 00:46:56 +0100676 spin_unlock_irqrestore(&pd->bmp.lock, flags);
677}
678#else
Geoff Levand9633ac82007-01-26 19:07:59 -0800679static void dump_bmp(struct ps3_private* pd) {};
Geoff Levand2832a812006-11-23 00:46:56 +0100680#endif /* defined(DEBUG) */
681
Geoff Levand9633ac82007-01-26 19:07:59 -0800682static void ps3_host_unmap(struct irq_host *h, unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100683{
Geoff Levand861be322007-01-26 19:08:08 -0800684 set_irq_chip_data(virq, NULL);
Geoff Levand2832a812006-11-23 00:46:56 +0100685}
686
Geoff Levand9633ac82007-01-26 19:07:59 -0800687static int ps3_host_map(struct irq_host *h, unsigned int virq,
Geoff Levand2832a812006-11-23 00:46:56 +0100688 irq_hw_number_t hwirq)
689{
Geoff Levand861be322007-01-26 19:08:08 -0800690 pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
691 virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100692
Geoff Levand9263e852007-06-16 07:19:32 +1000693 set_irq_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq);
Geoff Levand2832a812006-11-23 00:46:56 +0100694
Geoff Levand861be322007-01-26 19:08:08 -0800695 return 0;
Geoff Levand2832a812006-11-23 00:46:56 +0100696}
697
Geoff Levand9633ac82007-01-26 19:07:59 -0800698static struct irq_host_ops ps3_host_ops = {
699 .map = ps3_host_map,
700 .unmap = ps3_host_unmap,
Geoff Levand2832a812006-11-23 00:46:56 +0100701};
702
703void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
704{
Geoff Levand9633ac82007-01-26 19:07:59 -0800705 struct ps3_private *pd = &per_cpu(ps3_private, cpu);
Geoff Levand2832a812006-11-23 00:46:56 +0100706
707 pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq;
708
709 pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__,
710 cpu, virq, pd->bmp.ipi_debug_brk_mask);
711}
712
Geoff Levand9263e852007-06-16 07:19:32 +1000713static unsigned int ps3_get_irq(void)
Geoff Levand2832a812006-11-23 00:46:56 +0100714{
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800715 struct ps3_private *pd = &__get_cpu_var(ps3_private);
Geoff Levand861be322007-01-26 19:08:08 -0800716 u64 x = (pd->bmp.status & pd->bmp.mask);
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800717 unsigned int plug;
Geoff Levand2832a812006-11-23 00:46:56 +0100718
719 /* check for ipi break first to stop this cpu ASAP */
720
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800721 if (x & pd->bmp.ipi_debug_brk_mask)
722 x &= pd->bmp.ipi_debug_brk_mask;
Geoff Levand2832a812006-11-23 00:46:56 +0100723
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800724 asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x));
725 plug &= 0x3f;
Geoff Levand2832a812006-11-23 00:46:56 +0100726
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800727 if (unlikely(plug) == NO_IRQ) {
Geoff Levandaab83502007-06-16 08:06:04 +1000728 pr_debug("%s:%d: no plug found: thread_id %lu\n", __func__,
729 __LINE__, pd->thread_id);
Geoff Levand9633ac82007-01-26 19:07:59 -0800730 dump_bmp(&per_cpu(ps3_private, 0));
731 dump_bmp(&per_cpu(ps3_private, 1));
Geoff Levand2832a812006-11-23 00:46:56 +0100732 return NO_IRQ;
733 }
734
735#if defined(DEBUG)
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800736 if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) {
Geoff Levand9633ac82007-01-26 19:07:59 -0800737 dump_bmp(&per_cpu(ps3_private, 0));
738 dump_bmp(&per_cpu(ps3_private, 1));
Geoff Levand2832a812006-11-23 00:46:56 +0100739 BUG();
740 }
741#endif
742 return plug;
743}
744
745void __init ps3_init_IRQ(void)
746{
747 int result;
Geoff Levand2832a812006-11-23 00:46:56 +0100748 unsigned cpu;
749 struct irq_host *host;
750
Geoff Levand9633ac82007-01-26 19:07:59 -0800751 host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops,
Geoff Levand2832a812006-11-23 00:46:56 +0100752 PS3_INVALID_OUTLET);
753 irq_set_default_host(host);
754 irq_set_virq_count(PS3_PLUG_MAX + 1);
755
756 for_each_possible_cpu(cpu) {
Geoff Levand9633ac82007-01-26 19:07:59 -0800757 struct ps3_private *pd = &per_cpu(ps3_private, cpu);
Geoff Levand2832a812006-11-23 00:46:56 +0100758
Geoff Levandaab83502007-06-16 08:06:04 +1000759 lv1_get_logical_ppe_id(&pd->ppe_id);
760 pd->thread_id = get_hard_smp_processor_id(cpu);
Geoff Levand2832a812006-11-23 00:46:56 +0100761 spin_lock_init(&pd->bmp.lock);
762
Geoff Levandaab83502007-06-16 08:06:04 +1000763 pr_debug("%s:%d: ppe_id %lu, thread_id %lu, bmp %lxh\n",
764 __func__, __LINE__, pd->ppe_id, pd->thread_id,
Geoff Levand407e24a2007-01-26 19:08:02 -0800765 ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
766
Geoff Levandaab83502007-06-16 08:06:04 +1000767 result = lv1_configure_irq_state_bitmap(pd->ppe_id,
768 pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
Geoff Levand2832a812006-11-23 00:46:56 +0100769
770 if (result)
771 pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:"
772 " %s\n", __func__, __LINE__,
773 ps3_result(result));
774 }
775
776 ppc_md.get_irq = ps3_get_irq;
777}
Geoff Levand9263e852007-06-16 07:19:32 +1000778
779void ps3_shutdown_IRQ(int cpu)
780{
781 int result;
782 u64 ppe_id;
783 u64 thread_id = get_hard_smp_processor_id(cpu);
784
785 lv1_get_logical_ppe_id(&ppe_id);
786 result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0);
787
788 DBG("%s:%d: lv1_configure_irq_state_bitmap (%lu:%lu/%d) %s\n", __func__,
789 __LINE__, ppe_id, thread_id, cpu, ps3_result(result));
790}