s390_ext.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /*
  2. * Copyright IBM Corp. 1999,2010
  3. * Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com>,
  4. * Martin Schwidefsky <schwidefsky@de.ibm.com>,
  5. */
  6. #include <linux/kernel_stat.h>
  7. #include <linux/interrupt.h>
  8. #include <linux/module.h>
  9. #include <linux/kernel.h>
  10. #include <linux/ftrace.h>
  11. #include <linux/errno.h>
  12. #include <linux/slab.h>
  13. #include <asm/s390_ext.h>
  14. #include <asm/irq_regs.h>
  15. #include <asm/cputime.h>
  16. #include <asm/lowcore.h>
  17. #include <asm/irq.h>
  18. #include "entry.h"
  19. struct ext_int_info {
  20. struct ext_int_info *next;
  21. ext_int_handler_t handler;
  22. __u16 code;
  23. };
  24. /*
  25. * ext_int_hash[index] is the start of the list for all external interrupts
  26. * that hash to this index. With the current set of external interrupts
  27. * (0x1202 external call, 0x1004 cpu timer, 0x2401 hwc console, 0x4000
  28. * iucv and 0x2603 pfault) this is always the first element.
  29. */
  30. static struct ext_int_info *ext_int_hash[256];
  31. static inline int ext_hash(__u16 code)
  32. {
  33. return (code + (code >> 9)) & 0xff;
  34. }
  35. int register_external_interrupt(__u16 code, ext_int_handler_t handler)
  36. {
  37. struct ext_int_info *p;
  38. int index;
  39. p = kmalloc(sizeof(*p), GFP_ATOMIC);
  40. if (!p)
  41. return -ENOMEM;
  42. p->code = code;
  43. p->handler = handler;
  44. index = ext_hash(code);
  45. p->next = ext_int_hash[index];
  46. ext_int_hash[index] = p;
  47. return 0;
  48. }
  49. EXPORT_SYMBOL(register_external_interrupt);
  50. int unregister_external_interrupt(__u16 code, ext_int_handler_t handler)
  51. {
  52. struct ext_int_info *p, *q;
  53. int index;
  54. index = ext_hash(code);
  55. q = NULL;
  56. p = ext_int_hash[index];
  57. while (p) {
  58. if (p->code == code && p->handler == handler)
  59. break;
  60. q = p;
  61. p = p->next;
  62. }
  63. if (!p)
  64. return -ENOENT;
  65. if (q)
  66. q->next = p->next;
  67. else
  68. ext_int_hash[index] = p->next;
  69. kfree(p);
  70. return 0;
  71. }
  72. EXPORT_SYMBOL(unregister_external_interrupt);
  73. void __irq_entry do_extint(struct pt_regs *regs, unsigned int ext_int_code,
  74. unsigned int param32, unsigned long param64)
  75. {
  76. struct pt_regs *old_regs;
  77. unsigned short code;
  78. struct ext_int_info *p;
  79. int index;
  80. code = (unsigned short) ext_int_code;
  81. old_regs = set_irq_regs(regs);
  82. s390_idle_check(regs, S390_lowcore.int_clock,
  83. S390_lowcore.async_enter_timer);
  84. irq_enter();
  85. if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator)
  86. /* Serve timer interrupts first. */
  87. clock_comparator_work();
  88. kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++;
  89. if (code != 0x1004)
  90. __get_cpu_var(s390_idle).nohz_delay = 1;
  91. index = ext_hash(code);
  92. for (p = ext_int_hash[index]; p; p = p->next) {
  93. if (likely(p->code == code))
  94. p->handler(ext_int_code, param32, param64);
  95. }
  96. irq_exit();
  97. set_irq_regs(old_regs);
  98. }