Commit | Line | Data |
---|---|---|
2832a81d GL |
1 | /* |
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> | |
4b16f8e2 | 22 | #include <linux/export.h> |
2832a81d GL |
23 | #include <linux/irq.h> |
24 | ||
25 | #include <asm/machdep.h> | |
26 | #include <asm/udbg.h> | |
2832a81d | 27 | #include <asm/lv1call.h> |
42d284bc | 28 | #include <asm/smp.h> |
2832a81d GL |
29 | |
30 | #include "platform.h" | |
31 | ||
32 | #if defined(DEBUG) | |
83bb643d | 33 | #define DBG udbg_printf |
32b9074b | 34 | #define FAIL udbg_printf |
2832a81d | 35 | #else |
32b9074b GL |
36 | #define DBG pr_devel |
37 | #define FAIL pr_debug | |
2832a81d GL |
38 | #endif |
39 | ||
861be32c GL |
40 | /** |
41 | * struct ps3_bmp - a per cpu irq status and mask bitmap structure | |
42 | * @status: 256 bit status bitmap indexed by plug | |
32b9074b | 43 | * @unused_1: Alignment |
861be32c | 44 | * @mask: 256 bit mask bitmap indexed by plug |
32b9074b | 45 | * @unused_2: Alignment |
861be32c | 46 | * |
b595076a | 47 | * The HV maintains per SMT thread mappings of HV outlet to HV plug on |
861be32c GL |
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(). | |
57715765 GL |
51 | * 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. | |
861be32c GL |
54 | * |
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. | |
46ca0d15 SR |
63 | * |
64 | * The mask is declared as unsigned long so we can use set/clear_bit on it. | |
861be32c GL |
65 | */ |
66 | ||
57715765 GL |
67 | #define PS3_BMP_MINALIGN 64 |
68 | ||
861be32c GL |
69 | struct ps3_bmp { |
70 | struct { | |
71 | u64 status; | |
72 | u64 unused_1[3]; | |
46ca0d15 | 73 | unsigned long mask; |
861be32c GL |
74 | u64 unused_2[3]; |
75 | }; | |
861be32c GL |
76 | }; |
77 | ||
78 | /** | |
79 | * struct ps3_private - a per cpu data structure | |
80 | * @bmp: ps3_bmp structure | |
32b9074b GL |
81 | * @bmp_lock: Syncronize access to bmp. |
82 | * @ipi_debug_brk_mask: Mask for debug break IPIs | |
aab83500 GL |
83 | * @ppe_id: HV logical_ppe_id |
84 | * @thread_id: HV thread_id | |
32b9074b | 85 | * @ipi_mask: Mask of IPI virqs |
861be32c GL |
86 | */ |
87 | ||
88 | struct ps3_private { | |
57715765 | 89 | struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); |
32b9074b | 90 | spinlock_t bmp_lock; |
aab83500 GL |
91 | u64 ppe_id; |
92 | u64 thread_id; | |
32b9074b | 93 | unsigned long ipi_debug_brk_mask; |
72f3bea0 | 94 | unsigned long ipi_mask; |
861be32c GL |
95 | }; |
96 | ||
97 | static DEFINE_PER_CPU(struct ps3_private, ps3_private); | |
98 | ||
743c1bb0 GL |
99 | /** |
100 | * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp. | |
101 | * @virq: The assigned Linux virq. | |
102 | * | |
103 | * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). | |
104 | */ | |
105 | ||
8126708a | 106 | static void ps3_chip_mask(struct irq_data *d) |
743c1bb0 | 107 | { |
8126708a | 108 | struct ps3_private *pd = irq_data_get_irq_chip_data(d); |
743c1bb0 GL |
109 | unsigned long flags; |
110 | ||
32b9074b | 111 | DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__, |
8126708a | 112 | pd->thread_id, d->irq); |
743c1bb0 GL |
113 | |
114 | local_irq_save(flags); | |
8126708a | 115 | clear_bit(63 - d->irq, &pd->bmp.mask); |
aab83500 | 116 | lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); |
743c1bb0 GL |
117 | local_irq_restore(flags); |
118 | } | |
119 | ||
120 | /** | |
121 | * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp. | |
122 | * @virq: The assigned Linux virq. | |
123 | * | |
124 | * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). | |
125 | */ | |
126 | ||
8126708a | 127 | static void ps3_chip_unmask(struct irq_data *d) |
743c1bb0 | 128 | { |
8126708a | 129 | struct ps3_private *pd = irq_data_get_irq_chip_data(d); |
743c1bb0 GL |
130 | unsigned long flags; |
131 | ||
32b9074b | 132 | DBG("%s:%d: thread_id %llu, virq %d\n", __func__, __LINE__, |
8126708a | 133 | pd->thread_id, d->irq); |
743c1bb0 GL |
134 | |
135 | local_irq_save(flags); | |
8126708a | 136 | set_bit(63 - d->irq, &pd->bmp.mask); |
aab83500 | 137 | lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); |
743c1bb0 GL |
138 | local_irq_restore(flags); |
139 | } | |
140 | ||
141 | /** | |
142 | * ps3_chip_eoi - HV end-of-interrupt. | |
143 | * @virq: The assigned Linux virq. | |
144 | * | |
145 | * Calls lv1_end_of_interrupt_ext(). | |
146 | */ | |
147 | ||
8126708a | 148 | static void ps3_chip_eoi(struct irq_data *d) |
743c1bb0 | 149 | { |
8126708a | 150 | const struct ps3_private *pd = irq_data_get_irq_chip_data(d); |
72f3bea0 GL |
151 | |
152 | /* non-IPIs are EOIed here. */ | |
153 | ||
154 | if (!test_bit(63 - d->irq, &pd->ipi_mask)) | |
155 | lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, d->irq); | |
743c1bb0 GL |
156 | } |
157 | ||
158 | /** | |
159 | * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip. | |
160 | */ | |
161 | ||
162 | static struct irq_chip ps3_irq_chip = { | |
b27df672 | 163 | .name = "ps3", |
8126708a LB |
164 | .irq_mask = ps3_chip_mask, |
165 | .irq_unmask = ps3_chip_unmask, | |
166 | .irq_eoi = ps3_chip_eoi, | |
743c1bb0 GL |
167 | }; |
168 | ||
dc4f60c2 GL |
169 | /** |
170 | * ps3_virq_setup - virq related setup. | |
171 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | |
172 | * serviced on. | |
173 | * @outlet: The HV outlet from the various create outlet routines. | |
174 | * @virq: The assigned Linux virq. | |
175 | * | |
176 | * Calls irq_create_mapping() to get a virq and sets the chip data to | |
177 | * ps3_private data. | |
178 | */ | |
179 | ||
fdedb4ca GU |
180 | static int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet, |
181 | unsigned int *virq) | |
861be32c GL |
182 | { |
183 | int result; | |
184 | struct ps3_private *pd; | |
185 | ||
186 | /* This defines the default interrupt distribution policy. */ | |
187 | ||
188 | if (cpu == PS3_BINDING_CPU_ANY) | |
189 | cpu = 0; | |
190 | ||
191 | pd = &per_cpu(ps3_private, cpu); | |
192 | ||
193 | *virq = irq_create_mapping(NULL, outlet); | |
194 | ||
195 | if (*virq == NO_IRQ) { | |
32b9074b | 196 | FAIL("%s:%d: irq_create_mapping failed: outlet %lu\n", |
861be32c GL |
197 | __func__, __LINE__, outlet); |
198 | result = -ENOMEM; | |
199 | goto fail_create; | |
200 | } | |
201 | ||
32b9074b | 202 | DBG("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, |
861be32c GL |
203 | outlet, cpu, *virq); |
204 | ||
ec775d0e | 205 | result = irq_set_chip_data(*virq, pd); |
861be32c GL |
206 | |
207 | if (result) { | |
32b9074b | 208 | FAIL("%s:%d: irq_set_chip_data failed\n", |
861be32c GL |
209 | __func__, __LINE__); |
210 | goto fail_set; | |
211 | } | |
212 | ||
8126708a | 213 | ps3_chip_mask(irq_get_irq_data(*virq)); |
9263e85a | 214 | |
861be32c GL |
215 | return result; |
216 | ||
217 | fail_set: | |
861be32c GL |
218 | irq_dispose_mapping(*virq); |
219 | fail_create: | |
220 | return result; | |
221 | } | |
222 | ||
dc4f60c2 GL |
223 | /** |
224 | * ps3_virq_destroy - virq related teardown. | |
225 | * @virq: The assigned Linux virq. | |
226 | * | |
227 | * Clears chip data and calls irq_dispose_mapping() for the virq. | |
228 | */ | |
229 | ||
fdedb4ca | 230 | static int ps3_virq_destroy(unsigned int virq) |
861be32c | 231 | { |
ec775d0e | 232 | const struct ps3_private *pd = irq_get_chip_data(virq); |
861be32c | 233 | |
32b9074b | 234 | DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__, |
aab83500 | 235 | __LINE__, pd->ppe_id, pd->thread_id, virq); |
861be32c | 236 | |
ec775d0e | 237 | irq_set_chip_data(virq, NULL); |
861be32c | 238 | irq_dispose_mapping(virq); |
dc4f60c2 | 239 | |
32b9074b | 240 | DBG("%s:%d <-\n", __func__, __LINE__); |
dc4f60c2 | 241 | return 0; |
861be32c GL |
242 | } |
243 | ||
2832a81d | 244 | /** |
dc4f60c2 | 245 | * ps3_irq_plug_setup - Generic outlet and virq related setup. |
861be32c GL |
246 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be |
247 | * serviced on. | |
dc4f60c2 | 248 | * @outlet: The HV outlet from the various create outlet routines. |
2832a81d GL |
249 | * @virq: The assigned Linux virq. |
250 | * | |
dc4f60c2 | 251 | * Sets up virq and connects the irq plug. |
2832a81d GL |
252 | */ |
253 | ||
dc4f60c2 | 254 | int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, |
861be32c | 255 | unsigned int *virq) |
2832a81d GL |
256 | { |
257 | int result; | |
dc4f60c2 | 258 | struct ps3_private *pd; |
2832a81d | 259 | |
dc4f60c2 | 260 | result = ps3_virq_setup(cpu, outlet, virq); |
2832a81d GL |
261 | |
262 | if (result) { | |
32b9074b | 263 | FAIL("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__); |
dc4f60c2 | 264 | goto fail_setup; |
2832a81d GL |
265 | } |
266 | ||
ec775d0e | 267 | pd = irq_get_chip_data(*virq); |
dc4f60c2 GL |
268 | |
269 | /* Binds outlet to cpu + virq. */ | |
270 | ||
aab83500 GL |
271 | result = lv1_connect_irq_plug_ext(pd->ppe_id, pd->thread_id, *virq, |
272 | outlet, 0); | |
2832a81d | 273 | |
dc4f60c2 | 274 | if (result) { |
32b9074b | 275 | FAIL("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", |
dc4f60c2 GL |
276 | __func__, __LINE__, ps3_result(result)); |
277 | result = -EPERM; | |
278 | goto fail_connect; | |
279 | } | |
280 | ||
281 | return result; | |
282 | ||
283 | fail_connect: | |
284 | ps3_virq_destroy(*virq); | |
285 | fail_setup: | |
861be32c | 286 | return result; |
2832a81d | 287 | } |
dc4f60c2 GL |
288 | EXPORT_SYMBOL_GPL(ps3_irq_plug_setup); |
289 | ||
290 | /** | |
291 | * ps3_irq_plug_destroy - Generic outlet and virq related teardown. | |
292 | * @virq: The assigned Linux virq. | |
293 | * | |
294 | * Disconnects the irq plug and tears down virq. | |
295 | * Do not call for system bus event interrupts setup with | |
296 | * ps3_sb_event_receive_port_setup(). | |
297 | */ | |
2832a81d | 298 | |
dc4f60c2 | 299 | int ps3_irq_plug_destroy(unsigned int virq) |
2832a81d GL |
300 | { |
301 | int result; | |
ec775d0e | 302 | const struct ps3_private *pd = irq_get_chip_data(virq); |
2832a81d | 303 | |
32b9074b | 304 | DBG("%s:%d: ppe_id %llu, thread_id %llu, virq %u\n", __func__, |
aab83500 | 305 | __LINE__, pd->ppe_id, pd->thread_id, virq); |
dc4f60c2 | 306 | |
8126708a | 307 | ps3_chip_mask(irq_get_irq_data(virq)); |
9263e85a | 308 | |
aab83500 | 309 | result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq); |
2832a81d | 310 | |
ded84bcb | 311 | if (result) |
32b9074b | 312 | FAIL("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", |
dc4f60c2 | 313 | __func__, __LINE__, ps3_result(result)); |
2832a81d | 314 | |
dc4f60c2 | 315 | ps3_virq_destroy(virq); |
2832a81d GL |
316 | |
317 | return result; | |
318 | } | |
dc4f60c2 | 319 | EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy); |
2832a81d GL |
320 | |
321 | /** | |
dc4f60c2 | 322 | * ps3_event_receive_port_setup - Setup an event receive port. |
861be32c GL |
323 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be |
324 | * serviced on. | |
2832a81d GL |
325 | * @virq: The assigned Linux virq. |
326 | * | |
327 | * The virq can be used with lv1_connect_interrupt_event_receive_port() to | |
dc4f60c2 GL |
328 | * arrange to receive interrupts from system-bus devices, or with |
329 | * ps3_send_event_locally() to signal events. | |
2832a81d GL |
330 | */ |
331 | ||
dc4f60c2 | 332 | int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq) |
2832a81d GL |
333 | { |
334 | int result; | |
b17b3df1 | 335 | u64 outlet; |
2832a81d GL |
336 | |
337 | result = lv1_construct_event_receive_port(&outlet); | |
338 | ||
339 | if (result) { | |
32b9074b | 340 | FAIL("%s:%d: lv1_construct_event_receive_port failed: %s\n", |
2832a81d GL |
341 | __func__, __LINE__, ps3_result(result)); |
342 | *virq = NO_IRQ; | |
343 | return result; | |
344 | } | |
345 | ||
dc4f60c2 | 346 | result = ps3_irq_plug_setup(cpu, outlet, virq); |
861be32c | 347 | BUG_ON(result); |
2832a81d | 348 | |
861be32c | 349 | return result; |
2832a81d | 350 | } |
dc4f60c2 GL |
351 | EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup); |
352 | ||
353 | /** | |
354 | * ps3_event_receive_port_destroy - Destroy an event receive port. | |
355 | * @virq: The assigned Linux virq. | |
356 | * | |
357 | * Since ps3_event_receive_port_destroy destroys the receive port outlet, | |
358 | * SB devices need to call disconnect_interrupt_event_receive_port() before | |
359 | * this. | |
360 | */ | |
2832a81d | 361 | |
dc4f60c2 | 362 | int ps3_event_receive_port_destroy(unsigned int virq) |
2832a81d GL |
363 | { |
364 | int result; | |
365 | ||
32b9074b | 366 | DBG(" -> %s:%d virq %u\n", __func__, __LINE__, virq); |
9263e85a | 367 | |
8126708a | 368 | ps3_chip_mask(irq_get_irq_data(virq)); |
2832a81d GL |
369 | |
370 | result = lv1_destruct_event_receive_port(virq_to_hw(virq)); | |
371 | ||
372 | if (result) | |
32b9074b | 373 | FAIL("%s:%d: lv1_destruct_event_receive_port failed: %s\n", |
2832a81d GL |
374 | __func__, __LINE__, ps3_result(result)); |
375 | ||
9263e85a GL |
376 | /* |
377 | * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu() | |
378 | * calls from interrupt context (smp_call_function) when kexecing. | |
dc4f60c2 GL |
379 | */ |
380 | ||
32b9074b | 381 | DBG(" <- %s:%d\n", __func__, __LINE__); |
2832a81d GL |
382 | return result; |
383 | } | |
384 | ||
385 | int ps3_send_event_locally(unsigned int virq) | |
386 | { | |
387 | return lv1_send_event_locally(virq_to_hw(virq)); | |
388 | } | |
389 | ||
390 | /** | |
dc4f60c2 | 391 | * ps3_sb_event_receive_port_setup - Setup a system bus event receive port. |
861be32c GL |
392 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be |
393 | * serviced on. | |
6bb5cf10 | 394 | * @dev: The system bus device instance. |
2832a81d GL |
395 | * @virq: The assigned Linux virq. |
396 | * | |
397 | * An event irq represents a virtual device interrupt. The interrupt_id | |
398 | * coresponds to the software interrupt number. | |
399 | */ | |
400 | ||
6bb5cf10 GL |
401 | int ps3_sb_event_receive_port_setup(struct ps3_system_bus_device *dev, |
402 | enum ps3_cpu_binding cpu, unsigned int *virq) | |
2832a81d | 403 | { |
dc4f60c2 GL |
404 | /* this should go in system-bus.c */ |
405 | ||
2832a81d GL |
406 | int result; |
407 | ||
dc4f60c2 | 408 | result = ps3_event_receive_port_setup(cpu, virq); |
2832a81d GL |
409 | |
410 | if (result) | |
411 | return result; | |
412 | ||
6bb5cf10 GL |
413 | result = lv1_connect_interrupt_event_receive_port(dev->bus_id, |
414 | dev->dev_id, virq_to_hw(*virq), dev->interrupt_id); | |
2832a81d GL |
415 | |
416 | if (result) { | |
32b9074b | 417 | FAIL("%s:%d: lv1_connect_interrupt_event_receive_port" |
2832a81d GL |
418 | " failed: %s\n", __func__, __LINE__, |
419 | ps3_result(result)); | |
dc4f60c2 | 420 | ps3_event_receive_port_destroy(*virq); |
2832a81d GL |
421 | *virq = NO_IRQ; |
422 | return result; | |
423 | } | |
424 | ||
32b9074b | 425 | DBG("%s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, |
6bb5cf10 | 426 | dev->interrupt_id, *virq); |
2832a81d GL |
427 | |
428 | return 0; | |
429 | } | |
dc4f60c2 | 430 | EXPORT_SYMBOL(ps3_sb_event_receive_port_setup); |
2832a81d | 431 | |
6bb5cf10 GL |
432 | int ps3_sb_event_receive_port_destroy(struct ps3_system_bus_device *dev, |
433 | unsigned int virq) | |
2832a81d | 434 | { |
dc4f60c2 GL |
435 | /* this should go in system-bus.c */ |
436 | ||
2832a81d GL |
437 | int result; |
438 | ||
32b9074b | 439 | DBG(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, |
6bb5cf10 | 440 | dev->interrupt_id, virq); |
2832a81d | 441 | |
6bb5cf10 GL |
442 | result = lv1_disconnect_interrupt_event_receive_port(dev->bus_id, |
443 | dev->dev_id, virq_to_hw(virq), dev->interrupt_id); | |
2832a81d GL |
444 | |
445 | if (result) | |
32b9074b | 446 | FAIL("%s:%d: lv1_disconnect_interrupt_event_receive_port" |
2832a81d GL |
447 | " failed: %s\n", __func__, __LINE__, |
448 | ps3_result(result)); | |
449 | ||
dc4f60c2 GL |
450 | result = ps3_event_receive_port_destroy(virq); |
451 | BUG_ON(result); | |
2832a81d | 452 | |
9263e85a GL |
453 | /* |
454 | * ps3_event_receive_port_destroy() destroys the IRQ plug, | |
455 | * so don't call ps3_irq_plug_destroy() here. | |
456 | */ | |
457 | ||
458 | result = ps3_virq_destroy(virq); | |
459 | BUG_ON(result); | |
460 | ||
32b9074b | 461 | DBG(" <- %s:%d\n", __func__, __LINE__); |
2832a81d GL |
462 | return result; |
463 | } | |
dc4f60c2 | 464 | EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy); |
2832a81d GL |
465 | |
466 | /** | |
dc4f60c2 GL |
467 | * ps3_io_irq_setup - Setup a system bus io irq. |
468 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | |
469 | * serviced on. | |
470 | * @interrupt_id: The device interrupt id read from the system repository. | |
471 | * @virq: The assigned Linux virq. | |
472 | * | |
473 | * An io irq represents a non-virtualized device interrupt. interrupt_id | |
474 | * coresponds to the interrupt number of the interrupt controller. | |
475 | */ | |
476 | ||
477 | int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id, | |
478 | unsigned int *virq) | |
479 | { | |
480 | int result; | |
b17b3df1 | 481 | u64 outlet; |
dc4f60c2 GL |
482 | |
483 | result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); | |
484 | ||
485 | if (result) { | |
32b9074b | 486 | FAIL("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", |
dc4f60c2 GL |
487 | __func__, __LINE__, ps3_result(result)); |
488 | return result; | |
489 | } | |
490 | ||
491 | result = ps3_irq_plug_setup(cpu, outlet, virq); | |
492 | BUG_ON(result); | |
493 | ||
494 | return result; | |
495 | } | |
496 | EXPORT_SYMBOL_GPL(ps3_io_irq_setup); | |
497 | ||
498 | int ps3_io_irq_destroy(unsigned int virq) | |
499 | { | |
500 | int result; | |
9263e85a | 501 | unsigned long outlet = virq_to_hw(virq); |
dc4f60c2 | 502 | |
8126708a | 503 | ps3_chip_mask(irq_get_irq_data(virq)); |
dc4f60c2 | 504 | |
9263e85a GL |
505 | /* |
506 | * lv1_destruct_io_irq_outlet() will destroy the IRQ plug, | |
507 | * so call ps3_irq_plug_destroy() first. | |
508 | */ | |
dc4f60c2 GL |
509 | |
510 | result = ps3_irq_plug_destroy(virq); | |
511 | BUG_ON(result); | |
512 | ||
9263e85a GL |
513 | result = lv1_destruct_io_irq_outlet(outlet); |
514 | ||
515 | if (result) | |
32b9074b | 516 | FAIL("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", |
9263e85a GL |
517 | __func__, __LINE__, ps3_result(result)); |
518 | ||
dc4f60c2 GL |
519 | return result; |
520 | } | |
521 | EXPORT_SYMBOL_GPL(ps3_io_irq_destroy); | |
522 | ||
523 | /** | |
524 | * ps3_vuart_irq_setup - Setup the system virtual uart virq. | |
861be32c GL |
525 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be |
526 | * serviced on. | |
2832a81d GL |
527 | * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. |
528 | * @virq: The assigned Linux virq. | |
529 | * | |
530 | * The system supports only a single virtual uart, so multiple calls without | |
531 | * freeing the interrupt will return a wrong state error. | |
532 | */ | |
533 | ||
dc4f60c2 | 534 | int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp, |
861be32c | 535 | unsigned int *virq) |
2832a81d GL |
536 | { |
537 | int result; | |
b17b3df1 | 538 | u64 outlet; |
861be32c | 539 | u64 lpar_addr; |
2832a81d | 540 | |
861be32c | 541 | BUG_ON(!is_kernel_addr((u64)virt_addr_bmp)); |
2832a81d GL |
542 | |
543 | lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); | |
544 | ||
545 | result = lv1_configure_virtual_uart_irq(lpar_addr, &outlet); | |
546 | ||
547 | if (result) { | |
32b9074b | 548 | FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", |
2832a81d GL |
549 | __func__, __LINE__, ps3_result(result)); |
550 | return result; | |
551 | } | |
552 | ||
dc4f60c2 | 553 | result = ps3_irq_plug_setup(cpu, outlet, virq); |
861be32c | 554 | BUG_ON(result); |
2832a81d | 555 | |
861be32c | 556 | return result; |
2832a81d | 557 | } |
7626e78d | 558 | EXPORT_SYMBOL_GPL(ps3_vuart_irq_setup); |
2832a81d | 559 | |
dc4f60c2 | 560 | int ps3_vuart_irq_destroy(unsigned int virq) |
2832a81d GL |
561 | { |
562 | int result; | |
563 | ||
8126708a | 564 | ps3_chip_mask(irq_get_irq_data(virq)); |
2832a81d GL |
565 | result = lv1_deconfigure_virtual_uart_irq(); |
566 | ||
567 | if (result) { | |
32b9074b | 568 | FAIL("%s:%d: lv1_configure_virtual_uart_irq failed: %s\n", |
2832a81d GL |
569 | __func__, __LINE__, ps3_result(result)); |
570 | return result; | |
571 | } | |
572 | ||
dc4f60c2 GL |
573 | result = ps3_irq_plug_destroy(virq); |
574 | BUG_ON(result); | |
2832a81d GL |
575 | |
576 | return result; | |
577 | } | |
7626e78d | 578 | EXPORT_SYMBOL_GPL(ps3_vuart_irq_destroy); |
2832a81d GL |
579 | |
580 | /** | |
dc4f60c2 | 581 | * ps3_spe_irq_setup - Setup an spe virq. |
861be32c GL |
582 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be |
583 | * serviced on. | |
2832a81d GL |
584 | * @spe_id: The spe_id returned from lv1_construct_logical_spe(). |
585 | * @class: The spe interrupt class {0,1,2}. | |
586 | * @virq: The assigned Linux virq. | |
587 | * | |
588 | */ | |
589 | ||
dc4f60c2 | 590 | int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id, |
861be32c | 591 | unsigned int class, unsigned int *virq) |
2832a81d GL |
592 | { |
593 | int result; | |
b17b3df1 | 594 | u64 outlet; |
2832a81d GL |
595 | |
596 | BUG_ON(class > 2); | |
597 | ||
598 | result = lv1_get_spe_irq_outlet(spe_id, class, &outlet); | |
599 | ||
600 | if (result) { | |
32b9074b | 601 | FAIL("%s:%d: lv1_get_spe_irq_outlet failed: %s\n", |
2832a81d GL |
602 | __func__, __LINE__, ps3_result(result)); |
603 | return result; | |
604 | } | |
605 | ||
dc4f60c2 | 606 | result = ps3_irq_plug_setup(cpu, outlet, virq); |
861be32c | 607 | BUG_ON(result); |
2832a81d | 608 | |
861be32c | 609 | return result; |
2832a81d GL |
610 | } |
611 | ||
dc4f60c2 | 612 | int ps3_spe_irq_destroy(unsigned int virq) |
2832a81d | 613 | { |
9263e85a GL |
614 | int result; |
615 | ||
8126708a | 616 | ps3_chip_mask(irq_get_irq_data(virq)); |
9263e85a GL |
617 | |
618 | result = ps3_irq_plug_destroy(virq); | |
dc4f60c2 | 619 | BUG_ON(result); |
9263e85a GL |
620 | |
621 | return result; | |
2832a81d GL |
622 | } |
623 | ||
b1eeb38e | 624 | |
2832a81d GL |
625 | #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) |
626 | #define PS3_PLUG_MAX 63 | |
627 | ||
2832a81d | 628 | #if defined(DEBUG) |
861be32c | 629 | static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, |
2832a81d GL |
630 | const char* func, int line) |
631 | { | |
32b9074b | 632 | pr_debug("%s:%d: %s %u {%04llx_%04llx_%04llx_%04llx}\n", |
2832a81d GL |
633 | func, line, header, cpu, |
634 | *p >> 48, (*p >> 32) & 0xffff, (*p >> 16) & 0xffff, | |
635 | *p & 0xffff); | |
636 | } | |
637 | ||
848cfdc5 | 638 | static void __maybe_unused _dump_256_bmp(const char *header, |
861be32c | 639 | const u64 *p, unsigned cpu, const char* func, int line) |
2832a81d | 640 | { |
32b9074b | 641 | pr_debug("%s:%d: %s %u {%016llx:%016llx:%016llx:%016llx}\n", |
2832a81d GL |
642 | func, line, header, cpu, p[0], p[1], p[2], p[3]); |
643 | } | |
644 | ||
645 | #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) | |
9633ac8d | 646 | static void _dump_bmp(struct ps3_private* pd, const char* func, int line) |
2832a81d GL |
647 | { |
648 | unsigned long flags; | |
649 | ||
32b9074b | 650 | spin_lock_irqsave(&pd->bmp_lock, flags); |
aab83500 | 651 | _dump_64_bmp("stat", &pd->bmp.status, pd->thread_id, func, line); |
32b9074b GL |
652 | _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line); |
653 | spin_unlock_irqrestore(&pd->bmp_lock, flags); | |
2832a81d GL |
654 | } |
655 | ||
656 | #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) | |
848cfdc5 | 657 | static void __maybe_unused _dump_mask(struct ps3_private *pd, |
2832a81d GL |
658 | const char* func, int line) |
659 | { | |
660 | unsigned long flags; | |
661 | ||
32b9074b GL |
662 | spin_lock_irqsave(&pd->bmp_lock, flags); |
663 | _dump_64_bmp("mask", (u64*)&pd->bmp.mask, pd->thread_id, func, line); | |
664 | spin_unlock_irqrestore(&pd->bmp_lock, flags); | |
2832a81d GL |
665 | } |
666 | #else | |
9633ac8d | 667 | static void dump_bmp(struct ps3_private* pd) {}; |
2832a81d GL |
668 | #endif /* defined(DEBUG) */ |
669 | ||
bae1d8f1 | 670 | static int ps3_host_map(struct irq_domain *h, unsigned int virq, |
2832a81d GL |
671 | irq_hw_number_t hwirq) |
672 | { | |
32b9074b | 673 | DBG("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, |
861be32c | 674 | virq); |
2832a81d | 675 | |
ec775d0e | 676 | irq_set_chip_and_handler(virq, &ps3_irq_chip, handle_fasteoi_irq); |
2832a81d | 677 | |
861be32c | 678 | return 0; |
2832a81d GL |
679 | } |
680 | ||
bae1d8f1 | 681 | static int ps3_host_match(struct irq_domain *h, struct device_node *np) |
8528ab84 ME |
682 | { |
683 | /* Match all */ | |
684 | return 1; | |
685 | } | |
686 | ||
9f70b8eb | 687 | static const struct irq_domain_ops ps3_host_ops = { |
9633ac8d | 688 | .map = ps3_host_map, |
8528ab84 | 689 | .match = ps3_host_match, |
2832a81d GL |
690 | }; |
691 | ||
692 | void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) | |
693 | { | |
9633ac8d | 694 | struct ps3_private *pd = &per_cpu(ps3_private, cpu); |
2832a81d | 695 | |
32b9074b | 696 | set_bit(63 - virq, &pd->ipi_debug_brk_mask); |
2832a81d | 697 | |
32b9074b GL |
698 | DBG("%s:%d: cpu %u, virq %u, mask %lxh\n", __func__, __LINE__, |
699 | cpu, virq, pd->ipi_debug_brk_mask); | |
2832a81d GL |
700 | } |
701 | ||
72f3bea0 GL |
702 | void __init ps3_register_ipi_irq(unsigned int cpu, unsigned int virq) |
703 | { | |
704 | struct ps3_private *pd = &per_cpu(ps3_private, cpu); | |
705 | ||
706 | set_bit(63 - virq, &pd->ipi_mask); | |
707 | ||
708 | DBG("%s:%d: cpu %u, virq %u, ipi_mask %lxh\n", __func__, __LINE__, | |
709 | cpu, virq, pd->ipi_mask); | |
710 | } | |
711 | ||
9263e85a | 712 | static unsigned int ps3_get_irq(void) |
2832a81d | 713 | { |
9cf9e196 | 714 | struct ps3_private *pd = &__get_cpu_var(ps3_private); |
861be32c | 715 | u64 x = (pd->bmp.status & pd->bmp.mask); |
9cf9e196 | 716 | unsigned int plug; |
2832a81d GL |
717 | |
718 | /* check for ipi break first to stop this cpu ASAP */ | |
719 | ||
32b9074b GL |
720 | if (x & pd->ipi_debug_brk_mask) |
721 | x &= pd->ipi_debug_brk_mask; | |
2832a81d | 722 | |
9cf9e196 BH |
723 | asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x)); |
724 | plug &= 0x3f; | |
2832a81d | 725 | |
ad18c3db | 726 | if (unlikely(plug == NO_IRQ)) { |
32b9074b | 727 | DBG("%s:%d: no plug found: thread_id %llu\n", __func__, |
aab83500 | 728 | __LINE__, pd->thread_id); |
9633ac8d GL |
729 | dump_bmp(&per_cpu(ps3_private, 0)); |
730 | dump_bmp(&per_cpu(ps3_private, 1)); | |
2832a81d GL |
731 | return NO_IRQ; |
732 | } | |
733 | ||
734 | #if defined(DEBUG) | |
9cf9e196 | 735 | if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) { |
9633ac8d GL |
736 | dump_bmp(&per_cpu(ps3_private, 0)); |
737 | dump_bmp(&per_cpu(ps3_private, 1)); | |
2832a81d GL |
738 | BUG(); |
739 | } | |
740 | #endif | |
72f3bea0 GL |
741 | |
742 | /* IPIs are EOIed here. */ | |
743 | ||
744 | if (test_bit(63 - plug, &pd->ipi_mask)) | |
745 | lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, plug); | |
746 | ||
2832a81d GL |
747 | return plug; |
748 | } | |
749 | ||
750 | void __init ps3_init_IRQ(void) | |
751 | { | |
752 | int result; | |
2832a81d | 753 | unsigned cpu; |
bae1d8f1 | 754 | struct irq_domain *host; |
2832a81d | 755 | |
6fa6c8e2 | 756 | host = irq_domain_add_nomap(NULL, PS3_PLUG_MAX + 1, &ps3_host_ops, NULL); |
2832a81d | 757 | irq_set_default_host(host); |
2832a81d GL |
758 | |
759 | for_each_possible_cpu(cpu) { | |
9633ac8d | 760 | struct ps3_private *pd = &per_cpu(ps3_private, cpu); |
2832a81d | 761 | |
aab83500 GL |
762 | lv1_get_logical_ppe_id(&pd->ppe_id); |
763 | pd->thread_id = get_hard_smp_processor_id(cpu); | |
32b9074b | 764 | spin_lock_init(&pd->bmp_lock); |
2832a81d | 765 | |
32b9074b | 766 | DBG("%s:%d: ppe_id %llu, thread_id %llu, bmp %lxh\n", |
aab83500 | 767 | __func__, __LINE__, pd->ppe_id, pd->thread_id, |
407e24a0 GL |
768 | ps3_mm_phys_to_lpar(__pa(&pd->bmp))); |
769 | ||
aab83500 GL |
770 | result = lv1_configure_irq_state_bitmap(pd->ppe_id, |
771 | pd->thread_id, ps3_mm_phys_to_lpar(__pa(&pd->bmp))); | |
2832a81d GL |
772 | |
773 | if (result) | |
32b9074b | 774 | FAIL("%s:%d: lv1_configure_irq_state_bitmap failed:" |
2832a81d GL |
775 | " %s\n", __func__, __LINE__, |
776 | ps3_result(result)); | |
777 | } | |
778 | ||
779 | ppc_md.get_irq = ps3_get_irq; | |
780 | } | |
9263e85a GL |
781 | |
782 | void ps3_shutdown_IRQ(int cpu) | |
783 | { | |
784 | int result; | |
785 | u64 ppe_id; | |
786 | u64 thread_id = get_hard_smp_processor_id(cpu); | |
787 | ||
788 | lv1_get_logical_ppe_id(&ppe_id); | |
789 | result = lv1_configure_irq_state_bitmap(ppe_id, thread_id, 0); | |
790 | ||
5c949070 | 791 | DBG("%s:%d: lv1_configure_irq_state_bitmap (%llu:%llu/%d) %s\n", __func__, |
9263e85a GL |
792 | __LINE__, ppe_id, thread_id, cpu, ps3_result(result)); |
793 | } |