diff options
Diffstat (limited to 'arch/sparc64/kernel/irq.c')
-rw-r--r-- | arch/sparc64/kernel/irq.c | 146 |
1 files changed, 114 insertions, 32 deletions
diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index c3d068c7a412..b5ff3ee5ace1 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/seq_file.h> | 22 | #include <linux/seq_file.h> |
23 | #include <linux/bootmem.h> | 23 | #include <linux/bootmem.h> |
24 | #include <linux/irq.h> | 24 | #include <linux/irq.h> |
25 | #include <linux/msi.h> | ||
25 | 26 | ||
26 | #include <asm/ptrace.h> | 27 | #include <asm/ptrace.h> |
27 | #include <asm/processor.h> | 28 | #include <asm/processor.h> |
@@ -87,7 +88,6 @@ struct ino_bucket ivector_table[NUM_IVECS] __attribute__ ((aligned (SMP_CACHE_BY | |||
87 | #define irq_work(__cpu) &(trap_block[(__cpu)].irq_worklist) | 88 | #define irq_work(__cpu) &(trap_block[(__cpu)].irq_worklist) |
88 | 89 | ||
89 | static unsigned int virt_to_real_irq_table[NR_IRQS]; | 90 | static unsigned int virt_to_real_irq_table[NR_IRQS]; |
90 | static unsigned char virt_irq_cur = 1; | ||
91 | 91 | ||
92 | static unsigned char virt_irq_alloc(unsigned int real_irq) | 92 | static unsigned char virt_irq_alloc(unsigned int real_irq) |
93 | { | 93 | { |
@@ -95,26 +95,32 @@ static unsigned char virt_irq_alloc(unsigned int real_irq) | |||
95 | 95 | ||
96 | BUILD_BUG_ON(NR_IRQS >= 256); | 96 | BUILD_BUG_ON(NR_IRQS >= 256); |
97 | 97 | ||
98 | ent = virt_irq_cur; | 98 | for (ent = 1; ent < NR_IRQS; ent++) { |
99 | if (!virt_to_real_irq_table[ent]) | ||
100 | break; | ||
101 | } | ||
99 | if (ent >= NR_IRQS) { | 102 | if (ent >= NR_IRQS) { |
100 | printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); | 103 | printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); |
101 | return 0; | 104 | return 0; |
102 | } | 105 | } |
103 | 106 | ||
104 | virt_irq_cur = ent + 1; | ||
105 | virt_to_real_irq_table[ent] = real_irq; | 107 | virt_to_real_irq_table[ent] = real_irq; |
106 | 108 | ||
107 | return ent; | 109 | return ent; |
108 | } | 110 | } |
109 | 111 | ||
110 | #if 0 /* Currently unused. */ | 112 | static void virt_irq_free(unsigned int virt_irq) |
111 | static unsigned char real_to_virt_irq(unsigned int real_irq) | ||
112 | { | 113 | { |
113 | struct ino_bucket *bucket = __bucket(real_irq); | 114 | unsigned int real_irq; |
114 | 115 | ||
115 | return bucket->virt_irq; | 116 | if (virt_irq >= NR_IRQS) |
117 | return; | ||
118 | |||
119 | real_irq = virt_to_real_irq_table[virt_irq]; | ||
120 | virt_to_real_irq_table[virt_irq] = 0; | ||
121 | |||
122 | __bucket(real_irq)->virt_irq = 0; | ||
116 | } | 123 | } |
117 | #endif | ||
118 | 124 | ||
119 | static unsigned int virt_to_real_irq(unsigned char virt_irq) | 125 | static unsigned int virt_to_real_irq(unsigned char virt_irq) |
120 | { | 126 | { |
@@ -268,8 +274,7 @@ static int irq_choose_cpu(unsigned int virt_irq) | |||
268 | 274 | ||
269 | static void sun4u_irq_enable(unsigned int virt_irq) | 275 | static void sun4u_irq_enable(unsigned int virt_irq) |
270 | { | 276 | { |
271 | irq_desc_t *desc = irq_desc + virt_irq; | 277 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
272 | struct irq_handler_data *data = desc->handler_data; | ||
273 | 278 | ||
274 | if (likely(data)) { | 279 | if (likely(data)) { |
275 | unsigned long cpuid, imap; | 280 | unsigned long cpuid, imap; |
@@ -286,8 +291,7 @@ static void sun4u_irq_enable(unsigned int virt_irq) | |||
286 | 291 | ||
287 | static void sun4u_irq_disable(unsigned int virt_irq) | 292 | static void sun4u_irq_disable(unsigned int virt_irq) |
288 | { | 293 | { |
289 | irq_desc_t *desc = irq_desc + virt_irq; | 294 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
290 | struct irq_handler_data *data = desc->handler_data; | ||
291 | 295 | ||
292 | if (likely(data)) { | 296 | if (likely(data)) { |
293 | unsigned long imap = data->imap; | 297 | unsigned long imap = data->imap; |
@@ -300,8 +304,7 @@ static void sun4u_irq_disable(unsigned int virt_irq) | |||
300 | 304 | ||
301 | static void sun4u_irq_end(unsigned int virt_irq) | 305 | static void sun4u_irq_end(unsigned int virt_irq) |
302 | { | 306 | { |
303 | irq_desc_t *desc = irq_desc + virt_irq; | 307 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
304 | struct irq_handler_data *data = desc->handler_data; | ||
305 | 308 | ||
306 | if (likely(data)) | 309 | if (likely(data)) |
307 | upa_writel(ICLR_IDLE, data->iclr); | 310 | upa_writel(ICLR_IDLE, data->iclr); |
@@ -344,6 +347,20 @@ static void sun4v_irq_disable(unsigned int virt_irq) | |||
344 | } | 347 | } |
345 | } | 348 | } |
346 | 349 | ||
350 | #ifdef CONFIG_PCI_MSI | ||
351 | static void sun4v_msi_enable(unsigned int virt_irq) | ||
352 | { | ||
353 | sun4v_irq_enable(virt_irq); | ||
354 | unmask_msi_irq(virt_irq); | ||
355 | } | ||
356 | |||
357 | static void sun4v_msi_disable(unsigned int virt_irq) | ||
358 | { | ||
359 | mask_msi_irq(virt_irq); | ||
360 | sun4v_irq_disable(virt_irq); | ||
361 | } | ||
362 | #endif | ||
363 | |||
347 | static void sun4v_irq_end(unsigned int virt_irq) | 364 | static void sun4v_irq_end(unsigned int virt_irq) |
348 | { | 365 | { |
349 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 366 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
@@ -362,8 +379,7 @@ static void sun4v_irq_end(unsigned int virt_irq) | |||
362 | static void run_pre_handler(unsigned int virt_irq) | 379 | static void run_pre_handler(unsigned int virt_irq) |
363 | { | 380 | { |
364 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 381 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |
365 | irq_desc_t *desc = irq_desc + virt_irq; | 382 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
366 | struct irq_handler_data *data = desc->handler_data; | ||
367 | 383 | ||
368 | if (likely(data->pre_handler)) { | 384 | if (likely(data->pre_handler)) { |
369 | data->pre_handler(__irq_ino(__irq(bucket)), | 385 | data->pre_handler(__irq_ino(__irq(bucket)), |
@@ -402,30 +418,47 @@ static struct irq_chip sun4v_irq_ack = { | |||
402 | .end = sun4v_irq_end, | 418 | .end = sun4v_irq_end, |
403 | }; | 419 | }; |
404 | 420 | ||
421 | #ifdef CONFIG_PCI_MSI | ||
422 | static struct irq_chip sun4v_msi = { | ||
423 | .typename = "sun4v+msi", | ||
424 | .mask = mask_msi_irq, | ||
425 | .unmask = unmask_msi_irq, | ||
426 | .enable = sun4v_msi_enable, | ||
427 | .disable = sun4v_msi_disable, | ||
428 | .ack = run_pre_handler, | ||
429 | .end = sun4v_irq_end, | ||
430 | }; | ||
431 | #endif | ||
432 | |||
405 | void irq_install_pre_handler(int virt_irq, | 433 | void irq_install_pre_handler(int virt_irq, |
406 | void (*func)(unsigned int, void *, void *), | 434 | void (*func)(unsigned int, void *, void *), |
407 | void *arg1, void *arg2) | 435 | void *arg1, void *arg2) |
408 | { | 436 | { |
409 | irq_desc_t *desc = irq_desc + virt_irq; | 437 | struct irq_handler_data *data = get_irq_chip_data(virt_irq); |
410 | struct irq_handler_data *data = desc->handler_data; | 438 | struct irq_chip *chip; |
411 | 439 | ||
412 | data->pre_handler = func; | 440 | data->pre_handler = func; |
413 | data->pre_handler_arg1 = arg1; | 441 | data->pre_handler_arg1 = arg1; |
414 | data->pre_handler_arg2 = arg2; | 442 | data->pre_handler_arg2 = arg2; |
415 | 443 | ||
416 | if (desc->chip == &sun4u_irq_ack || | 444 | chip = get_irq_chip(virt_irq); |
417 | desc->chip == &sun4v_irq_ack) | 445 | if (chip == &sun4u_irq_ack || |
446 | chip == &sun4v_irq_ack | ||
447 | #ifdef CONFIG_PCI_MSI | ||
448 | || chip == &sun4v_msi | ||
449 | #endif | ||
450 | ) | ||
418 | return; | 451 | return; |
419 | 452 | ||
420 | desc->chip = (desc->chip == &sun4u_irq ? | 453 | chip = (chip == &sun4u_irq ? |
421 | &sun4u_irq_ack : &sun4v_irq_ack); | 454 | &sun4u_irq_ack : &sun4v_irq_ack); |
455 | set_irq_chip(virt_irq, chip); | ||
422 | } | 456 | } |
423 | 457 | ||
424 | unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) | 458 | unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) |
425 | { | 459 | { |
426 | struct ino_bucket *bucket; | 460 | struct ino_bucket *bucket; |
427 | struct irq_handler_data *data; | 461 | struct irq_handler_data *data; |
428 | irq_desc_t *desc; | ||
429 | int ino; | 462 | int ino; |
430 | 463 | ||
431 | BUG_ON(tlb_type == hypervisor); | 464 | BUG_ON(tlb_type == hypervisor); |
@@ -434,11 +467,11 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) | |||
434 | bucket = &ivector_table[ino]; | 467 | bucket = &ivector_table[ino]; |
435 | if (!bucket->virt_irq) { | 468 | if (!bucket->virt_irq) { |
436 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); | 469 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); |
437 | irq_desc[bucket->virt_irq].chip = &sun4u_irq; | 470 | set_irq_chip(bucket->virt_irq, &sun4u_irq); |
438 | } | 471 | } |
439 | 472 | ||
440 | desc = irq_desc + bucket->virt_irq; | 473 | data = get_irq_chip_data(bucket->virt_irq); |
441 | if (unlikely(desc->handler_data)) | 474 | if (unlikely(data)) |
442 | goto out; | 475 | goto out; |
443 | 476 | ||
444 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); | 477 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); |
@@ -446,7 +479,7 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) | |||
446 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); | 479 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); |
447 | prom_halt(); | 480 | prom_halt(); |
448 | } | 481 | } |
449 | desc->handler_data = data; | 482 | set_irq_chip_data(bucket->virt_irq, data); |
450 | 483 | ||
451 | data->imap = imap; | 484 | data->imap = imap; |
452 | data->iclr = iclr; | 485 | data->iclr = iclr; |
@@ -460,7 +493,6 @@ unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino) | |||
460 | struct ino_bucket *bucket; | 493 | struct ino_bucket *bucket; |
461 | struct irq_handler_data *data; | 494 | struct irq_handler_data *data; |
462 | unsigned long sysino; | 495 | unsigned long sysino; |
463 | irq_desc_t *desc; | ||
464 | 496 | ||
465 | BUG_ON(tlb_type != hypervisor); | 497 | BUG_ON(tlb_type != hypervisor); |
466 | 498 | ||
@@ -468,11 +500,11 @@ unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino) | |||
468 | bucket = &ivector_table[sysino]; | 500 | bucket = &ivector_table[sysino]; |
469 | if (!bucket->virt_irq) { | 501 | if (!bucket->virt_irq) { |
470 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); | 502 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); |
471 | irq_desc[bucket->virt_irq].chip = &sun4v_irq; | 503 | set_irq_chip(bucket->virt_irq, &sun4v_irq); |
472 | } | 504 | } |
473 | 505 | ||
474 | desc = irq_desc + bucket->virt_irq; | 506 | data = get_irq_chip_data(bucket->virt_irq); |
475 | if (unlikely(desc->handler_data)) | 507 | if (unlikely(data)) |
476 | goto out; | 508 | goto out; |
477 | 509 | ||
478 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); | 510 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); |
@@ -480,7 +512,7 @@ unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino) | |||
480 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); | 512 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); |
481 | prom_halt(); | 513 | prom_halt(); |
482 | } | 514 | } |
483 | desc->handler_data = data; | 515 | set_irq_chip_data(bucket->virt_irq, data); |
484 | 516 | ||
485 | /* Catch accidental accesses to these things. IMAP/ICLR handling | 517 | /* Catch accidental accesses to these things. IMAP/ICLR handling |
486 | * is done by hypervisor calls on sun4v platforms, not by direct | 518 | * is done by hypervisor calls on sun4v platforms, not by direct |
@@ -493,6 +525,56 @@ out: | |||
493 | return bucket->virt_irq; | 525 | return bucket->virt_irq; |
494 | } | 526 | } |
495 | 527 | ||
528 | #ifdef CONFIG_PCI_MSI | ||
529 | unsigned int sun4v_build_msi(u32 devhandle, unsigned int *virt_irq_p, | ||
530 | unsigned int msi_start, unsigned int msi_end) | ||
531 | { | ||
532 | struct ino_bucket *bucket; | ||
533 | struct irq_handler_data *data; | ||
534 | unsigned long sysino; | ||
535 | unsigned int devino; | ||
536 | |||
537 | BUG_ON(tlb_type != hypervisor); | ||
538 | |||
539 | /* Find a free devino in the given range. */ | ||
540 | for (devino = msi_start; devino < msi_end; devino++) { | ||
541 | sysino = sun4v_devino_to_sysino(devhandle, devino); | ||
542 | bucket = &ivector_table[sysino]; | ||
543 | if (!bucket->virt_irq) | ||
544 | break; | ||
545 | } | ||
546 | if (devino >= msi_end) | ||
547 | return 0; | ||
548 | |||
549 | sysino = sun4v_devino_to_sysino(devhandle, devino); | ||
550 | bucket = &ivector_table[sysino]; | ||
551 | bucket->virt_irq = virt_irq_alloc(__irq(bucket)); | ||
552 | *virt_irq_p = bucket->virt_irq; | ||
553 | set_irq_chip(bucket->virt_irq, &sun4v_msi); | ||
554 | |||
555 | data = get_irq_chip_data(bucket->virt_irq); | ||
556 | if (unlikely(data)) | ||
557 | return devino; | ||
558 | |||
559 | data = kzalloc(sizeof(struct irq_handler_data), GFP_ATOMIC); | ||
560 | if (unlikely(!data)) { | ||
561 | prom_printf("IRQ: kzalloc(irq_handler_data) failed.\n"); | ||
562 | prom_halt(); | ||
563 | } | ||
564 | set_irq_chip_data(bucket->virt_irq, data); | ||
565 | |||
566 | data->imap = ~0UL; | ||
567 | data->iclr = ~0UL; | ||
568 | |||
569 | return devino; | ||
570 | } | ||
571 | |||
572 | void sun4v_destroy_msi(unsigned int virt_irq) | ||
573 | { | ||
574 | virt_irq_free(virt_irq); | ||
575 | } | ||
576 | #endif | ||
577 | |||
496 | void ack_bad_irq(unsigned int virt_irq) | 578 | void ack_bad_irq(unsigned int virt_irq) |
497 | { | 579 | { |
498 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); | 580 | struct ino_bucket *bucket = virt_irq_to_bucket(virt_irq); |