aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/ps3/interrupt.c
diff options
context:
space:
mode:
authorGeoff Levand <geoffrey.levand@am.sony.com>2007-04-30 17:01:01 -0400
committerPaul Mackerras <paulus@samba.org>2007-05-02 06:04:31 -0400
commitdc4f60c25ae71e8278dcf909486e4aa34de7eecb (patch)
treeb6715d447588d05038cc1f655874df513d312f86 /arch/powerpc/platforms/ps3/interrupt.c
parent12828856630e616742e092c8ccbda6ebc56a9375 (diff)
[POWERPC] PS3: Interrupt routine fixups.
Fixups for the ps3 interrupt routines to support all HV device in a generic way. Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms/ps3/interrupt.c')
-rw-r--r--arch/powerpc/platforms/ps3/interrupt.c234
1 files changed, 166 insertions, 68 deletions
diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c
index 631c3009561..9da82c266ba 100644
--- a/arch/powerpc/platforms/ps3/interrupt.c
+++ b/arch/powerpc/platforms/ps3/interrupt.c
@@ -89,7 +89,18 @@ struct ps3_private {
89 89
90static DEFINE_PER_CPU(struct ps3_private, ps3_private); 90static DEFINE_PER_CPU(struct ps3_private, ps3_private);
91 91
92int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, 92/**
93 * ps3_virq_setup - virq related setup.
94 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
95 * serviced on.
96 * @outlet: The HV outlet from the various create outlet routines.
97 * @virq: The assigned Linux virq.
98 *
99 * Calls irq_create_mapping() to get a virq and sets the chip data to
100 * ps3_private data.
101 */
102
103int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
93 unsigned int *virq) 104 unsigned int *virq)
94{ 105{
95 int result; 106 int result;
@@ -111,17 +122,6 @@ int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet,
111 goto fail_create; 122 goto fail_create;
112 } 123 }
113 124
114 /* Binds outlet to cpu + virq. */
115
116 result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0);
117
118 if (result) {
119 pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
120 __func__, __LINE__, ps3_result(result));
121 result = -EPERM;
122 goto fail_connect;
123 }
124
125 pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, 125 pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__,
126 outlet, cpu, *virq); 126 outlet, cpu, *virq);
127 127
@@ -136,94 +136,118 @@ int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet,
136 return result; 136 return result;
137 137
138fail_set: 138fail_set:
139 lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq);
140fail_connect:
141 irq_dispose_mapping(*virq); 139 irq_dispose_mapping(*virq);
142fail_create: 140fail_create:
143 return result; 141 return result;
144} 142}
145EXPORT_SYMBOL_GPL(ps3_alloc_irq);
146 143
147int ps3_free_irq(unsigned int virq) 144/**
145 * ps3_virq_destroy - virq related teardown.
146 * @virq: The assigned Linux virq.
147 *
148 * Clears chip data and calls irq_dispose_mapping() for the virq.
149 */
150
151int ps3_virq_destroy(unsigned int virq)
148{ 152{
149 int result;
150 const struct ps3_private *pd = get_irq_chip_data(virq); 153 const struct ps3_private *pd = get_irq_chip_data(virq);
151 154
152 pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, 155 pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__,
153 pd->node, pd->cpu, virq); 156 pd->node, pd->cpu, virq);
154 157
155 result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq);
156
157 if (result)
158 pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
159 __func__, __LINE__, ps3_result(result));
160
161 set_irq_chip_data(virq, NULL); 158 set_irq_chip_data(virq, NULL);
162 irq_dispose_mapping(virq); 159 irq_dispose_mapping(virq);
163 return result; 160
161 pr_debug("%s:%d <-\n", __func__, __LINE__);
162 return 0;
164} 163}
165EXPORT_SYMBOL_GPL(ps3_free_irq);
166 164
167/** 165/**
168 * ps3_alloc_io_irq - Assign a virq to a system bus device. 166 * ps3_irq_plug_setup - Generic outlet and virq related setup.
169 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 167 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
170 * serviced on. 168 * serviced on.
171 * @interrupt_id: The device interrupt id read from the system repository. 169 * @outlet: The HV outlet from the various create outlet routines.
172 * @virq: The assigned Linux virq. 170 * @virq: The assigned Linux virq.
173 * 171 *
174 * An io irq represents a non-virtualized device interrupt. interrupt_id 172 * Sets up virq and connects the irq plug.
175 * coresponds to the interrupt number of the interrupt controller.
176 */ 173 */
177 174
178int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id, 175int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
179 unsigned int *virq) 176 unsigned int *virq)
180{ 177{
181 int result; 178 int result;
182 unsigned long outlet; 179 struct ps3_private *pd;
183 180
184 result = lv1_construct_io_irq_outlet(interrupt_id, &outlet); 181 result = ps3_virq_setup(cpu, outlet, virq);
185 182
186 if (result) { 183 if (result) {
187 pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n", 184 pr_debug("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__);
188 __func__, __LINE__, ps3_result(result)); 185 goto fail_setup;
189 return result;
190 } 186 }
191 187
192 result = ps3_alloc_irq(cpu, outlet, virq); 188 pd = get_irq_chip_data(*virq);
193 BUG_ON(result); 189
190 /* Binds outlet to cpu + virq. */
191
192 result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0);
194 193
194 if (result) {
195 pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
196 __func__, __LINE__, ps3_result(result));
197 result = -EPERM;
198 goto fail_connect;
199 }
200
201 return result;
202
203fail_connect:
204 ps3_virq_destroy(*virq);
205fail_setup:
195 return result; 206 return result;
196} 207}
197EXPORT_SYMBOL_GPL(ps3_alloc_io_irq); 208EXPORT_SYMBOL_GPL(ps3_irq_plug_setup);
209
210/**
211 * ps3_irq_plug_destroy - Generic outlet and virq related teardown.
212 * @virq: The assigned Linux virq.
213 *
214 * Disconnects the irq plug and tears down virq.
215 * Do not call for system bus event interrupts setup with
216 * ps3_sb_event_receive_port_setup().
217 */
198 218
199int ps3_free_io_irq(unsigned int virq) 219int ps3_irq_plug_destroy(unsigned int virq)
200{ 220{
201 int result; 221 int result;
222 const struct ps3_private *pd = get_irq_chip_data(virq);
202 223
203 result = lv1_destruct_io_irq_outlet(virq_to_hw(virq)); 224 pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__,
225 pd->node, pd->cpu, virq);
226
227 result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq);
204 228
205 if (result) 229 if (result)
206 pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", 230 pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
207 __func__, __LINE__, ps3_result(result)); 231 __func__, __LINE__, ps3_result(result));
208 232
209 ps3_free_irq(virq); 233 ps3_virq_destroy(virq);
210 234
211 return result; 235 return result;
212} 236}
213EXPORT_SYMBOL_GPL(ps3_free_io_irq); 237EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy);
214 238
215/** 239/**
216 * ps3_alloc_event_irq - Allocate a virq for use with a system event. 240 * ps3_event_receive_port_setup - Setup an event receive port.
217 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 241 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
218 * serviced on. 242 * serviced on.
219 * @virq: The assigned Linux virq. 243 * @virq: The assigned Linux virq.
220 * 244 *
221 * The virq can be used with lv1_connect_interrupt_event_receive_port() to 245 * The virq can be used with lv1_connect_interrupt_event_receive_port() to
222 * arrange to receive events, or with ps3_send_event_locally() to signal 246 * arrange to receive interrupts from system-bus devices, or with
223 * events. 247 * ps3_send_event_locally() to signal events.
224 */ 248 */
225 249
226int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq) 250int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq)
227{ 251{
228 int result; 252 int result;
229 unsigned long outlet; 253 unsigned long outlet;
@@ -237,17 +261,27 @@ int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq)
237 return result; 261 return result;
238 } 262 }
239 263
240 result = ps3_alloc_irq(cpu, outlet, virq); 264 result = ps3_irq_plug_setup(cpu, outlet, virq);
241 BUG_ON(result); 265 BUG_ON(result);
242 266
243 return result; 267 return result;
244} 268}
269EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup);
270
271/**
272 * ps3_event_receive_port_destroy - Destroy an event receive port.
273 * @virq: The assigned Linux virq.
274 *
275 * Since ps3_event_receive_port_destroy destroys the receive port outlet,
276 * SB devices need to call disconnect_interrupt_event_receive_port() before
277 * this.
278 */
245 279
246int ps3_free_event_irq(unsigned int virq) 280int ps3_event_receive_port_destroy(unsigned int virq)
247{ 281{
248 int result; 282 int result;
249 283
250 pr_debug(" -> %s:%d\n", __func__, __LINE__); 284 pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq);
251 285
252 result = lv1_destruct_event_receive_port(virq_to_hw(virq)); 286 result = lv1_destruct_event_receive_port(virq_to_hw(virq));
253 287
@@ -255,11 +289,17 @@ int ps3_free_event_irq(unsigned int virq)
255 pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", 289 pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
256 __func__, __LINE__, ps3_result(result)); 290 __func__, __LINE__, ps3_result(result));
257 291
258 ps3_free_irq(virq); 292 /* lv1_destruct_event_receive_port() destroys the IRQ plug,
293 * so don't call ps3_irq_plug_destroy() here.
294 */
295
296 result = ps3_virq_destroy(virq);
297 BUG_ON(result);
259 298
260 pr_debug(" <- %s:%d\n", __func__, __LINE__); 299 pr_debug(" <- %s:%d\n", __func__, __LINE__);
261 return result; 300 return result;
262} 301}
302EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy);
263 303
264int ps3_send_event_locally(unsigned int virq) 304int ps3_send_event_locally(unsigned int virq)
265{ 305{
@@ -267,7 +307,7 @@ int ps3_send_event_locally(unsigned int virq)
267} 307}
268 308
269/** 309/**
270 * ps3_connect_event_irq - Assign a virq to a system bus device. 310 * ps3_sb_event_receive_port_setup - Setup a system bus event receive port.
271 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 311 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
272 * serviced on. 312 * serviced on.
273 * @did: The HV device identifier read from the system repository. 313 * @did: The HV device identifier read from the system repository.
@@ -278,13 +318,15 @@ int ps3_send_event_locally(unsigned int virq)
278 * coresponds to the software interrupt number. 318 * coresponds to the software interrupt number.
279 */ 319 */
280 320
281int ps3_connect_event_irq(enum ps3_cpu_binding cpu, 321int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu,
282 const struct ps3_device_id *did, unsigned int interrupt_id, 322 const struct ps3_device_id *did, unsigned int interrupt_id,
283 unsigned int *virq) 323 unsigned int *virq)
284{ 324{
325 /* this should go in system-bus.c */
326
285 int result; 327 int result;
286 328
287 result = ps3_alloc_event_irq(cpu, virq); 329 result = ps3_event_receive_port_setup(cpu, virq);
288 330
289 if (result) 331 if (result)
290 return result; 332 return result;
@@ -296,7 +338,7 @@ int ps3_connect_event_irq(enum ps3_cpu_binding cpu,
296 pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port" 338 pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port"
297 " failed: %s\n", __func__, __LINE__, 339 " failed: %s\n", __func__, __LINE__,
298 ps3_result(result)); 340 ps3_result(result));
299 ps3_free_event_irq(*virq); 341 ps3_event_receive_port_destroy(*virq);
300 *virq = NO_IRQ; 342 *virq = NO_IRQ;
301 return result; 343 return result;
302 } 344 }
@@ -306,10 +348,13 @@ int ps3_connect_event_irq(enum ps3_cpu_binding cpu,
306 348
307 return 0; 349 return 0;
308} 350}
351EXPORT_SYMBOL(ps3_sb_event_receive_port_setup);
309 352
310int ps3_disconnect_event_irq(const struct ps3_device_id *did, 353int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did,
311 unsigned int interrupt_id, unsigned int virq) 354 unsigned int interrupt_id, unsigned int virq)
312{ 355{
356 /* this should go in system-bus.c */
357
313 int result; 358 int result;
314 359
315 pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__, 360 pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
@@ -323,14 +368,65 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did,
323 " failed: %s\n", __func__, __LINE__, 368 " failed: %s\n", __func__, __LINE__,
324 ps3_result(result)); 369 ps3_result(result));
325 370
326 ps3_free_event_irq(virq); 371 result = ps3_event_receive_port_destroy(virq);
372 BUG_ON(result);
327 373
328 pr_debug(" <- %s:%d\n", __func__, __LINE__); 374 pr_debug(" <- %s:%d\n", __func__, __LINE__);
329 return result; 375 return result;
330} 376}
377EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy);
331 378
332/** 379/**
333 * ps3_alloc_vuart_irq - Configure the system virtual uart virq. 380 * ps3_io_irq_setup - Setup a system bus io irq.
381 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
382 * serviced on.
383 * @interrupt_id: The device interrupt id read from the system repository.
384 * @virq: The assigned Linux virq.
385 *
386 * An io irq represents a non-virtualized device interrupt. interrupt_id
387 * coresponds to the interrupt number of the interrupt controller.
388 */
389
390int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
391 unsigned int *virq)
392{
393 int result;
394 unsigned long outlet;
395
396 result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);
397
398 if (result) {
399 pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
400 __func__, __LINE__, ps3_result(result));
401 return result;
402 }
403
404 result = ps3_irq_plug_setup(cpu, outlet, virq);
405 BUG_ON(result);
406
407 return result;
408}
409EXPORT_SYMBOL_GPL(ps3_io_irq_setup);
410
411int ps3_io_irq_destroy(unsigned int virq)
412{
413 int result;
414
415 result = lv1_destruct_io_irq_outlet(virq_to_hw(virq));
416
417 if (result)
418 pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
419 __func__, __LINE__, ps3_result(result));
420
421 result = ps3_irq_plug_destroy(virq);
422 BUG_ON(result);
423
424 return result;
425}
426EXPORT_SYMBOL_GPL(ps3_io_irq_destroy);
427
428/**
429 * ps3_vuart_irq_setup - Setup the system virtual uart virq.
334 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 430 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
335 * serviced on. 431 * serviced on.
336 * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. 432 * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
@@ -340,7 +436,7 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did,
340 * freeing the interrupt will return a wrong state error. 436 * freeing the interrupt will return a wrong state error.
341 */ 437 */
342 438
343int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp, 439int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
344 unsigned int *virq) 440 unsigned int *virq)
345{ 441{
346 int result; 442 int result;
@@ -359,13 +455,13 @@ int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
359 return result; 455 return result;
360 } 456 }
361 457
362 result = ps3_alloc_irq(cpu, outlet, virq); 458 result = ps3_irq_plug_setup(cpu, outlet, virq);
363 BUG_ON(result); 459 BUG_ON(result);
364 460
365 return result; 461 return result;
366} 462}
367 463
368int ps3_free_vuart_irq(unsigned int virq) 464int ps3_vuart_irq_destroy(unsigned int virq)
369{ 465{
370 int result; 466 int result;
371 467
@@ -377,13 +473,14 @@ int ps3_free_vuart_irq(unsigned int virq)
377 return result; 473 return result;
378 } 474 }
379 475
380 ps3_free_irq(virq); 476 result = ps3_irq_plug_destroy(virq);
477 BUG_ON(result);
381 478
382 return result; 479 return result;
383} 480}
384 481
385/** 482/**
386 * ps3_alloc_spe_irq - Configure an spe virq. 483 * ps3_spe_irq_setup - Setup an spe virq.
387 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be 484 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
388 * serviced on. 485 * serviced on.
389 * @spe_id: The spe_id returned from lv1_construct_logical_spe(). 486 * @spe_id: The spe_id returned from lv1_construct_logical_spe().
@@ -392,7 +489,7 @@ int ps3_free_vuart_irq(unsigned int virq)
392 * 489 *
393 */ 490 */
394 491
395int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id, 492int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id,
396 unsigned int class, unsigned int *virq) 493 unsigned int class, unsigned int *virq)
397{ 494{
398 int result; 495 int result;
@@ -408,15 +505,16 @@ int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id,
408 return result; 505 return result;
409 } 506 }
410 507
411 result = ps3_alloc_irq(cpu, outlet, virq); 508 result = ps3_irq_plug_setup(cpu, outlet, virq);
412 BUG_ON(result); 509 BUG_ON(result);
413 510
414 return result; 511 return result;
415} 512}
416 513
417int ps3_free_spe_irq(unsigned int virq) 514int ps3_spe_irq_destroy(unsigned int virq)
418{ 515{
419 ps3_free_irq(virq); 516 int result = ps3_irq_plug_destroy(virq);
517 BUG_ON(result);
420 return 0; 518 return 0;
421} 519}
422 520