i8259.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*
  2. * c 2001 PPC64 Team, IBM Corp
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version
  7. * 2 of the License, or (at your option) any later version.
  8. */
  9. #include <linux/stddef.h>
  10. #include <linux/init.h>
  11. #include <linux/sched.h>
  12. #include <linux/signal.h>
  13. #include <linux/cache.h>
  14. #include <linux/irq.h>
  15. #include <linux/interrupt.h>
  16. #include <asm/io.h>
  17. #include <asm/ppcdebug.h>
  18. #include "i8259.h"
  19. unsigned char cached_8259[2] = { 0xff, 0xff };
  20. #define cached_A1 (cached_8259[0])
  21. #define cached_21 (cached_8259[1])
  22. static __cacheline_aligned_in_smp DEFINE_SPINLOCK(i8259_lock);
  23. static int i8259_pic_irq_offset;
  24. static int i8259_present;
  25. int i8259_irq(int cpu)
  26. {
  27. int irq;
  28. spin_lock/*_irqsave*/(&i8259_lock/*, flags*/);
  29. /*
  30. * Perform an interrupt acknowledge cycle on controller 1
  31. */
  32. outb(0x0C, 0x20);
  33. irq = inb(0x20) & 7;
  34. if (irq == 2)
  35. {
  36. /*
  37. * Interrupt is cascaded so perform interrupt
  38. * acknowledge on controller 2
  39. */
  40. outb(0x0C, 0xA0);
  41. irq = (inb(0xA0) & 7) + 8;
  42. }
  43. else if (irq==7)
  44. {
  45. /*
  46. * This may be a spurious interrupt
  47. *
  48. * Read the interrupt status register. If the most
  49. * significant bit is not set then there is no valid
  50. * interrupt
  51. */
  52. outb(0x0b, 0x20);
  53. if(~inb(0x20)&0x80) {
  54. spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/);
  55. return -1;
  56. }
  57. }
  58. spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/);
  59. return irq;
  60. }
  61. static void i8259_mask_and_ack_irq(unsigned int irq_nr)
  62. {
  63. unsigned long flags;
  64. spin_lock_irqsave(&i8259_lock, flags);
  65. if ( irq_nr >= i8259_pic_irq_offset )
  66. irq_nr -= i8259_pic_irq_offset;
  67. if (irq_nr > 7) {
  68. cached_A1 |= 1 << (irq_nr-8);
  69. inb(0xA1); /* DUMMY */
  70. outb(cached_A1,0xA1);
  71. outb(0x20,0xA0); /* Non-specific EOI */
  72. outb(0x20,0x20); /* Non-specific EOI to cascade */
  73. } else {
  74. cached_21 |= 1 << irq_nr;
  75. inb(0x21); /* DUMMY */
  76. outb(cached_21,0x21);
  77. outb(0x20,0x20); /* Non-specific EOI */
  78. }
  79. spin_unlock_irqrestore(&i8259_lock, flags);
  80. }
  81. static void i8259_set_irq_mask(int irq_nr)
  82. {
  83. outb(cached_A1,0xA1);
  84. outb(cached_21,0x21);
  85. }
  86. static void i8259_mask_irq(unsigned int irq_nr)
  87. {
  88. unsigned long flags;
  89. spin_lock_irqsave(&i8259_lock, flags);
  90. if ( irq_nr >= i8259_pic_irq_offset )
  91. irq_nr -= i8259_pic_irq_offset;
  92. if ( irq_nr < 8 )
  93. cached_21 |= 1 << irq_nr;
  94. else
  95. cached_A1 |= 1 << (irq_nr-8);
  96. i8259_set_irq_mask(irq_nr);
  97. spin_unlock_irqrestore(&i8259_lock, flags);
  98. }
  99. static void i8259_unmask_irq(unsigned int irq_nr)
  100. {
  101. unsigned long flags;
  102. spin_lock_irqsave(&i8259_lock, flags);
  103. if ( irq_nr >= i8259_pic_irq_offset )
  104. irq_nr -= i8259_pic_irq_offset;
  105. if ( irq_nr < 8 )
  106. cached_21 &= ~(1 << irq_nr);
  107. else
  108. cached_A1 &= ~(1 << (irq_nr-8));
  109. i8259_set_irq_mask(irq_nr);
  110. spin_unlock_irqrestore(&i8259_lock, flags);
  111. }
  112. static void i8259_end_irq(unsigned int irq)
  113. {
  114. if (!(get_irq_desc(irq)->status & (IRQ_DISABLED|IRQ_INPROGRESS)) &&
  115. get_irq_desc(irq)->action)
  116. i8259_unmask_irq(irq);
  117. }
  118. struct hw_interrupt_type i8259_pic = {
  119. .typename = " i8259 ",
  120. .enable = i8259_unmask_irq,
  121. .disable = i8259_mask_irq,
  122. .ack = i8259_mask_and_ack_irq,
  123. .end = i8259_end_irq,
  124. };
  125. void __init i8259_init(int offset)
  126. {
  127. unsigned long flags;
  128. spin_lock_irqsave(&i8259_lock, flags);
  129. i8259_pic_irq_offset = offset;
  130. i8259_present = 1;
  131. /* init master interrupt controller */
  132. outb(0x11, 0x20); /* Start init sequence */
  133. outb(0x00, 0x21); /* Vector base */
  134. outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
  135. outb(0x01, 0x21); /* Select 8086 mode */
  136. outb(0xFF, 0x21); /* Mask all */
  137. /* init slave interrupt controller */
  138. outb(0x11, 0xA0); /* Start init sequence */
  139. outb(0x08, 0xA1); /* Vector base */
  140. outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
  141. outb(0x01, 0xA1); /* Select 8086 mode */
  142. outb(0xFF, 0xA1); /* Mask all */
  143. outb(cached_A1, 0xA1);
  144. outb(cached_21, 0x21);
  145. spin_unlock_irqrestore(&i8259_lock, flags);
  146. }
  147. static int i8259_request_cascade(void)
  148. {
  149. if (!i8259_present)
  150. return -ENODEV;
  151. request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT,
  152. "82c59 secondary cascade", NULL );
  153. return 0;
  154. }
  155. arch_initcall(i8259_request_cascade);