blob: 3735cd14f614f350fa15eedd247f7d97f0bff704 [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>
27#include <asm/ps3.h>
28#include <asm/lv1call.h>
29
30#include "platform.h"
31
32#if defined(DEBUG)
33#define DBG(fmt...) udbg_printf(fmt)
34#else
35#define DBG(fmt...) do{if(0)printk(fmt);}while(0)
36#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().
51 *
52 * The HV supports 256 plugs per thread, assigned as {0..255}, for a total
53 * of 512 plugs supported on a processor. To simplify the logic this
54 * implementation equates HV plug value to Linux virq value, constrains each
55 * interrupt to have a system wide unique plug number, and limits the range
56 * of the plug values to map into the first dword of the bitmaps. This
57 * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note
58 * that there is no constraint on how many in this set an individual thread
59 * can acquire.
60 */
61
62struct ps3_bmp {
63 struct {
64 u64 status;
65 u64 unused_1[3];
66 u64 mask;
67 u64 unused_2[3];
68 };
69 u64 ipi_debug_brk_mask;
70 spinlock_t lock;
71};
72
73/**
74 * struct ps3_private - a per cpu data structure
75 * @bmp: ps3_bmp structure
76 * @node: HV logical_ppe_id
77 * @cpu: HV thread_id
78 */
79
80struct ps3_private {
81 struct ps3_bmp bmp __attribute__ ((aligned (64)));
82 u64 node;
83 unsigned int cpu;
84};
85
86static DEFINE_PER_CPU(struct ps3_private, ps3_private);
87
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -080088int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet,
Geoff Levand861be322007-01-26 19:08:08 -080089 unsigned int *virq)
90{
91 int result;
92 struct ps3_private *pd;
93
94 /* This defines the default interrupt distribution policy. */
95
96 if (cpu == PS3_BINDING_CPU_ANY)
97 cpu = 0;
98
99 pd = &per_cpu(ps3_private, cpu);
100
101 *virq = irq_create_mapping(NULL, outlet);
102
103 if (*virq == NO_IRQ) {
104 pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n",
105 __func__, __LINE__, outlet);
106 result = -ENOMEM;
107 goto fail_create;
108 }
109
110 /* Binds outlet to cpu + virq. */
111
112 result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0);
113
114 if (result) {
115 pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
116 __func__, __LINE__, ps3_result(result));
117 result = -EPERM;
118 goto fail_connect;
119 }
120
121 pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__,
122 outlet, cpu, *virq);
123
124 result = set_irq_chip_data(*virq, pd);
125
126 if (result) {
127 pr_debug("%s:%d: set_irq_chip_data failed\n",
128 __func__, __LINE__);
129 goto fail_set;
130 }
131
132 return result;
133
134fail_set:
135 lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq);
136fail_connect:
137 irq_dispose_mapping(*virq);
138fail_create:
139 return result;
140}
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800141EXPORT_SYMBOL_GPL(ps3_alloc_irq);
Geoff Levand861be322007-01-26 19:08:08 -0800142
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800143int ps3_free_irq(unsigned int virq)
Geoff Levand861be322007-01-26 19:08:08 -0800144{
145 int result;
146 const struct ps3_private *pd = get_irq_chip_data(virq);
147
148 pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__,
149 pd->node, pd->cpu, virq);
150
151 result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq);
152
153 if (result)
154 pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
155 __func__, __LINE__, ps3_result(result));
156
157 set_irq_chip_data(virq, NULL);
158 irq_dispose_mapping(virq);
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800159 return result;
Geoff Levand861be322007-01-26 19:08:08 -0800160}
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800161EXPORT_SYMBOL_GPL(ps3_free_irq);
Geoff Levand861be322007-01-26 19:08:08 -0800162
163/**
Geoff Levand2832a812006-11-23 00:46:56 +0100164 * ps3_alloc_io_irq - Assign a virq to a system bus device.
Geoff Levand861be322007-01-26 19:08:08 -0800165 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
166 * serviced on.
167 * @interrupt_id: The device interrupt id read from the system repository.
Geoff Levand2832a812006-11-23 00:46:56 +0100168 * @virq: The assigned Linux virq.
169 *
170 * An io irq represents a non-virtualized device interrupt. interrupt_id
171 * coresponds to the interrupt number of the interrupt controller.
172 */
173
Geoff Levand861be322007-01-26 19:08:08 -0800174int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
175 unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100176{
177 int result;
178 unsigned long outlet;
179
180 result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);
181
182 if (result) {
183 pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
184 __func__, __LINE__, ps3_result(result));
185 return result;
186 }
187
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800188 result = ps3_alloc_irq(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800189 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100190
Geoff Levand861be322007-01-26 19:08:08 -0800191 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100192}
193
194int ps3_free_io_irq(unsigned int virq)
195{
196 int result;
197
198 result = lv1_destruct_io_irq_outlet(virq_to_hw(virq));
199
Geert Uytterhoevended84bc2006-12-21 13:57:16 +0100200 if (result)
Geoff Levand2832a812006-11-23 00:46:56 +0100201 pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
202 __func__, __LINE__, ps3_result(result));
203
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800204 ps3_free_irq(virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100205
206 return result;
207}
208
209/**
210 * ps3_alloc_event_irq - Allocate a virq for use with a system event.
Geoff Levand861be322007-01-26 19:08:08 -0800211 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
212 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100213 * @virq: The assigned Linux virq.
214 *
215 * The virq can be used with lv1_connect_interrupt_event_receive_port() to
216 * arrange to receive events, or with ps3_send_event_locally() to signal
217 * events.
218 */
219
Geoff Levand861be322007-01-26 19:08:08 -0800220int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100221{
222 int result;
223 unsigned long outlet;
224
225 result = lv1_construct_event_receive_port(&outlet);
226
227 if (result) {
228 pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n",
229 __func__, __LINE__, ps3_result(result));
230 *virq = NO_IRQ;
231 return result;
232 }
233
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800234 result = ps3_alloc_irq(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800235 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100236
Geoff Levand861be322007-01-26 19:08:08 -0800237 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100238}
239
240int ps3_free_event_irq(unsigned int virq)
241{
242 int result;
243
244 pr_debug(" -> %s:%d\n", __func__, __LINE__);
245
246 result = lv1_destruct_event_receive_port(virq_to_hw(virq));
247
248 if (result)
249 pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
250 __func__, __LINE__, ps3_result(result));
251
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800252 ps3_free_irq(virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100253
254 pr_debug(" <- %s:%d\n", __func__, __LINE__);
255 return result;
256}
257
258int ps3_send_event_locally(unsigned int virq)
259{
260 return lv1_send_event_locally(virq_to_hw(virq));
261}
262
263/**
264 * ps3_connect_event_irq - Assign a virq to a system bus device.
Geoff Levand861be322007-01-26 19:08:08 -0800265 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
266 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100267 * @did: The HV device identifier read from the system repository.
268 * @interrupt_id: The device interrupt id read from the system repository.
269 * @virq: The assigned Linux virq.
270 *
271 * An event irq represents a virtual device interrupt. The interrupt_id
272 * coresponds to the software interrupt number.
273 */
274
Geoff Levand861be322007-01-26 19:08:08 -0800275int ps3_connect_event_irq(enum ps3_cpu_binding cpu,
276 const struct ps3_device_id *did, unsigned int interrupt_id,
277 unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100278{
279 int result;
280
Geoff Levand861be322007-01-26 19:08:08 -0800281 result = ps3_alloc_event_irq(cpu, virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100282
283 if (result)
284 return result;
285
286 result = lv1_connect_interrupt_event_receive_port(did->bus_id,
287 did->dev_id, virq_to_hw(*virq), interrupt_id);
288
289 if (result) {
290 pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port"
291 " failed: %s\n", __func__, __LINE__,
292 ps3_result(result));
293 ps3_free_event_irq(*virq);
294 *virq = NO_IRQ;
295 return result;
296 }
297
298 pr_debug("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
299 interrupt_id, *virq);
300
301 return 0;
302}
303
304int ps3_disconnect_event_irq(const struct ps3_device_id *did,
305 unsigned int interrupt_id, unsigned int virq)
306{
307 int result;
308
309 pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
310 interrupt_id, virq);
311
312 result = lv1_disconnect_interrupt_event_receive_port(did->bus_id,
313 did->dev_id, virq_to_hw(virq), interrupt_id);
314
315 if (result)
316 pr_debug("%s:%d: lv1_disconnect_interrupt_event_receive_port"
317 " failed: %s\n", __func__, __LINE__,
318 ps3_result(result));
319
320 ps3_free_event_irq(virq);
321
322 pr_debug(" <- %s:%d\n", __func__, __LINE__);
323 return result;
324}
325
326/**
327 * ps3_alloc_vuart_irq - Configure the system virtual uart virq.
Geoff Levand861be322007-01-26 19:08:08 -0800328 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
329 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100330 * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
331 * @virq: The assigned Linux virq.
332 *
333 * The system supports only a single virtual uart, so multiple calls without
334 * freeing the interrupt will return a wrong state error.
335 */
336
Geoff Levand861be322007-01-26 19:08:08 -0800337int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
338 unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100339{
340 int result;
341 unsigned long outlet;
Geoff Levand861be322007-01-26 19:08:08 -0800342 u64 lpar_addr;
Geoff Levand2832a812006-11-23 00:46:56 +0100343
Geoff Levand861be322007-01-26 19:08:08 -0800344 BUG_ON(!is_kernel_addr((u64)virt_addr_bmp));
Geoff Levand2832a812006-11-23 00:46:56 +0100345
346 lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp));
347
348 result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet);
349
350 if (result) {
351 pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
352 __func__, __LINE__, ps3_result(result));
353 return result;
354 }
355
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800356 result = ps3_alloc_irq(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800357 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100358
Geoff Levand861be322007-01-26 19:08:08 -0800359 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100360}
361
362int ps3_free_vuart_irq(unsigned int virq)
363{
364 int result;
365
366 result = lv1_deconfigure_virtual_uart_irq();
367
368 if (result) {
369 pr_debug("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n",
370 __func__, __LINE__, ps3_result(result));
371 return result;
372 }
373
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800374 ps3_free_irq(virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100375
376 return result;
377}
378
379/**
380 * ps3_alloc_spe_irq - Configure an spe virq.
Geoff Levand861be322007-01-26 19:08:08 -0800381 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
382 * serviced on.
Geoff Levand2832a812006-11-23 00:46:56 +0100383 * @spe_id: The spe_id returned from lv1_construct_logical_spe().
384 * @class: The spe interrupt class {0,1,2}.
385 * @virq: The assigned Linux virq.
386 *
387 */
388
Geoff Levand861be322007-01-26 19:08:08 -0800389int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id,
390 unsigned int class, unsigned int *virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100391{
392 int result;
393 unsigned long outlet;
394
395 BUG_ON(class > 2);
396
397 result = lv1_get_spe_irq_outlet(spe_id, class, &outlet);
398
399 if (result) {
400 pr_debug("%s:%d: lv1_get_spe_irq_outlet failed: %s\n",
401 __func__, __LINE__, ps3_result(result));
402 return result;
403 }
404
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800405 result = ps3_alloc_irq(cpu, outlet, virq);
Geoff Levand861be322007-01-26 19:08:08 -0800406 BUG_ON(result);
Geoff Levand2832a812006-11-23 00:46:56 +0100407
Geoff Levand861be322007-01-26 19:08:08 -0800408 return result;
Geoff Levand2832a812006-11-23 00:46:56 +0100409}
410
411int ps3_free_spe_irq(unsigned int virq)
412{
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800413 ps3_free_irq(virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100414 return 0;
415}
416
Geert Uytterhoevenb1eeb382007-01-26 19:08:12 -0800417
Geoff Levand2832a812006-11-23 00:46:56 +0100418#define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
419#define PS3_PLUG_MAX 63
420
Geoff Levand2832a812006-11-23 00:46:56 +0100421#if defined(DEBUG)
Geoff Levand861be322007-01-26 19:08:08 -0800422static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu,
Geoff Levand2832a812006-11-23 00:46:56 +0100423 const char* func, int line)
424{
425 pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n",
426 func, line, header, cpu,
427 *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff,
428 *p & 0xffff);
429}
430
431static void __attribute__ ((unused)) _dump_256_bmp(const char *header,
Geoff Levand861be322007-01-26 19:08:08 -0800432 const u64 *p, unsigned cpu, const char* func, int line)
Geoff Levand2832a812006-11-23 00:46:56 +0100433{
434 pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n",
435 func, line, header, cpu, p[0], p[1], p[2], p[3]);
436}
437
438#define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__)
Geoff Levand9633ac82007-01-26 19:07:59 -0800439static void _dump_bmp(struct ps3_private* pd, const char* func, int line)
Geoff Levand2832a812006-11-23 00:46:56 +0100440{
441 unsigned long flags;
442
443 spin_lock_irqsave(&pd->bmp.lock, flags);
444 _dump_64_bmp("stat", &pd->bmp.status, pd->cpu, func, line);
445 _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line);
446 spin_unlock_irqrestore(&pd->bmp.lock, flags);
447}
448
449#define dump_mask(_x) _dump_mask(_x, __func__, __LINE__)
Geoff Levand9633ac82007-01-26 19:07:59 -0800450static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd,
Geoff Levand2832a812006-11-23 00:46:56 +0100451 const char* func, int line)
452{
453 unsigned long flags;
454
455 spin_lock_irqsave(&pd->bmp.lock, flags);
456 _dump_64_bmp("mask", &pd->bmp.mask, pd->cpu, func, line);
457 spin_unlock_irqrestore(&pd->bmp.lock, flags);
458}
459#else
Geoff Levand9633ac82007-01-26 19:07:59 -0800460static void dump_bmp(struct ps3_private* pd) {};
Geoff Levand2832a812006-11-23 00:46:56 +0100461#endif /* defined(DEBUG) */
462
Geoff Levand9633ac82007-01-26 19:07:59 -0800463static void ps3_chip_mask(unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100464{
Geoff Levand9633ac82007-01-26 19:07:59 -0800465 struct ps3_private *pd = get_irq_chip_data(virq);
Geoff Levand861be322007-01-26 19:08:08 -0800466 u64 bit = 0x8000000000000000UL >> virq;
467 u64 *p = &pd->bmp.mask;
468 u64 old;
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800469 unsigned long flags;
Geoff Levand2832a812006-11-23 00:46:56 +0100470
471 pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
472
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800473 local_irq_save(flags);
474 asm volatile(
475 "1: ldarx %0,0,%3\n"
476 "andc %0,%0,%2\n"
477 "stdcx. %0,0,%3\n"
478 "bne- 1b"
479 : "=&r" (old), "+m" (*p)
480 : "r" (bit), "r" (p)
481 : "cc" );
Geoff Levand2832a812006-11-23 00:46:56 +0100482
Geoff Levand2832a812006-11-23 00:46:56 +0100483 lv1_did_update_interrupt_mask(pd->node, pd->cpu);
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800484 local_irq_restore(flags);
Geoff Levand2832a812006-11-23 00:46:56 +0100485}
486
Geoff Levand9633ac82007-01-26 19:07:59 -0800487static void ps3_chip_unmask(unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100488{
Geoff Levand9633ac82007-01-26 19:07:59 -0800489 struct ps3_private *pd = get_irq_chip_data(virq);
Geoff Levand861be322007-01-26 19:08:08 -0800490 u64 bit = 0x8000000000000000UL >> virq;
491 u64 *p = &pd->bmp.mask;
492 u64 old;
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800493 unsigned long flags;
Geoff Levand2832a812006-11-23 00:46:56 +0100494
495 pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
496
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800497 local_irq_save(flags);
498 asm volatile(
499 "1: ldarx %0,0,%3\n"
500 "or %0,%0,%2\n"
501 "stdcx. %0,0,%3\n"
502 "bne- 1b"
503 : "=&r" (old), "+m" (*p)
504 : "r" (bit), "r" (p)
505 : "cc" );
Geoff Levand2832a812006-11-23 00:46:56 +0100506
Geoff Levand2832a812006-11-23 00:46:56 +0100507 lv1_did_update_interrupt_mask(pd->node, pd->cpu);
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800508 local_irq_restore(flags);
Geoff Levand2832a812006-11-23 00:46:56 +0100509}
510
Geoff Levand9633ac82007-01-26 19:07:59 -0800511static void ps3_chip_eoi(unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100512{
Geoff Levand407e24a2007-01-26 19:08:02 -0800513 const struct ps3_private *pd = get_irq_chip_data(virq);
514 lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100515}
516
517static struct irq_chip irq_chip = {
518 .typename = "ps3",
Geoff Levand9633ac82007-01-26 19:07:59 -0800519 .mask = ps3_chip_mask,
520 .unmask = ps3_chip_unmask,
521 .eoi = ps3_chip_eoi,
Geoff Levand2832a812006-11-23 00:46:56 +0100522};
523
Geoff Levand9633ac82007-01-26 19:07:59 -0800524static void ps3_host_unmap(struct irq_host *h, unsigned int virq)
Geoff Levand2832a812006-11-23 00:46:56 +0100525{
Geoff Levand861be322007-01-26 19:08:08 -0800526 set_irq_chip_data(virq, NULL);
Geoff Levand2832a812006-11-23 00:46:56 +0100527}
528
Geoff Levand9633ac82007-01-26 19:07:59 -0800529static int ps3_host_map(struct irq_host *h, unsigned int virq,
Geoff Levand2832a812006-11-23 00:46:56 +0100530 irq_hw_number_t hwirq)
531{
Geoff Levand861be322007-01-26 19:08:08 -0800532 pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
533 virq);
Geoff Levand2832a812006-11-23 00:46:56 +0100534
535 set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq);
536
Geoff Levand861be322007-01-26 19:08:08 -0800537 return 0;
Geoff Levand2832a812006-11-23 00:46:56 +0100538}
539
Geoff Levand9633ac82007-01-26 19:07:59 -0800540static struct irq_host_ops ps3_host_ops = {
541 .map = ps3_host_map,
542 .unmap = ps3_host_unmap,
Geoff Levand2832a812006-11-23 00:46:56 +0100543};
544
545void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
546{
Geoff Levand9633ac82007-01-26 19:07:59 -0800547 struct ps3_private *pd = &per_cpu(ps3_private, cpu);
Geoff Levand2832a812006-11-23 00:46:56 +0100548
549 pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq;
550
551 pr_debug("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__,
552 cpu, virq, pd->bmp.ipi_debug_brk_mask);
553}
554
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800555unsigned int ps3_get_irq(void)
Geoff Levand2832a812006-11-23 00:46:56 +0100556{
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800557 struct ps3_private *pd = &__get_cpu_var(ps3_private);
Geoff Levand861be322007-01-26 19:08:08 -0800558 u64 x = (pd->bmp.status & pd->bmp.mask);
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800559 unsigned int plug;
Geoff Levand2832a812006-11-23 00:46:56 +0100560
561 /* check for ipi break first to stop this cpu ASAP */
562
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800563 if (x & pd->bmp.ipi_debug_brk_mask)
564 x &= pd->bmp.ipi_debug_brk_mask;
Geoff Levand2832a812006-11-23 00:46:56 +0100565
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800566 asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x));
567 plug &= 0x3f;
Geoff Levand2832a812006-11-23 00:46:56 +0100568
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800569 if (unlikely(plug) == NO_IRQ) {
Geoff Levand2832a812006-11-23 00:46:56 +0100570 pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__,
571 pd->cpu);
Geoff Levand9633ac82007-01-26 19:07:59 -0800572 dump_bmp(&per_cpu(ps3_private, 0));
573 dump_bmp(&per_cpu(ps3_private, 1));
Geoff Levand2832a812006-11-23 00:46:56 +0100574 return NO_IRQ;
575 }
576
577#if defined(DEBUG)
Benjamin Herrenschmidt9cf9e192007-01-26 19:08:05 -0800578 if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) {
Geoff Levand9633ac82007-01-26 19:07:59 -0800579 dump_bmp(&per_cpu(ps3_private, 0));
580 dump_bmp(&per_cpu(ps3_private, 1));
Geoff Levand2832a812006-11-23 00:46:56 +0100581 BUG();
582 }
583#endif
584 return plug;
585}
586
587void __init ps3_init_IRQ(void)
588{
589 int result;
Geoff Levand2832a812006-11-23 00:46:56 +0100590 unsigned cpu;
591 struct irq_host *host;
592
Geoff Levand9633ac82007-01-26 19:07:59 -0800593 host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops,
Geoff Levand2832a812006-11-23 00:46:56 +0100594 PS3_INVALID_OUTLET);
595 irq_set_default_host(host);
596 irq_set_virq_count(PS3_PLUG_MAX + 1);
597
598 for_each_possible_cpu(cpu) {
Geoff Levand9633ac82007-01-26 19:07:59 -0800599 struct ps3_private *pd = &per_cpu(ps3_private, cpu);
Geoff Levand2832a812006-11-23 00:46:56 +0100600
Geoff Levand407e24a2007-01-26 19:08:02 -0800601 lv1_get_logical_ppe_id(&pd->node);
602 pd->cpu = get_hard_smp_processor_id(cpu);
Geoff Levand2832a812006-11-23 00:46:56 +0100603 spin_lock_init(&pd->bmp.lock);
604
Geoff Levand407e24a2007-01-26 19:08:02 -0800605 pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__,
606 __LINE__, pd->node, pd->cpu,
607 ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
608
609 result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu,
610 ps3_mm_phys_to_lpar(__pa(&pd->bmp)));
Geoff Levand2832a812006-11-23 00:46:56 +0100611
612 if (result)
613 pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:"
614 " %s\n", __func__, __LINE__,
615 ps3_result(result));
616 }
617
618 ppc_md.get_irq = ps3_get_irq;
619}