i8259.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. #include "u.h"
  2. #include "../port/lib.h"
  3. #include "mem.h"
  4. #include "dat.h"
  5. #include "fns.h"
  6. #include "io.h"
  7. /*
  8. * 8259 interrupt controllers
  9. */
  10. enum
  11. {
  12. Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */
  13. Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */
  14. Int1ctl= 0xA0, /* control port */
  15. Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */
  16. Icw1= 0x10, /* select bit in ctl register */
  17. Ocw2= 0x00,
  18. Ocw3= 0x08,
  19. EOI= 0x20, /* non-specific end of interrupt */
  20. Elcr1= 0x4D0, /* Edge/Level Triggered Register */
  21. Elcr2= 0x4D1,
  22. };
  23. static Lock i8259lock;
  24. static int i8259mask = 0xFFFF; /* disabled interrupts */
  25. int i8259elcr; /* mask of level-triggered interrupts */
  26. void
  27. i8259init(void)
  28. {
  29. int x;
  30. ioalloc(Int0ctl, 2, 0, "i8259.0");
  31. ioalloc(Int1ctl, 2, 0, "i8259.1");
  32. ilock(&i8259lock);
  33. /*
  34. * Set up the first 8259 interrupt processor.
  35. * Make 8259 interrupts start at CPU vector VectorPIC.
  36. * Set the 8259 as master with edge triggered
  37. * input with fully nested interrupts.
  38. */
  39. outb(Int0ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered,
  40. ICW4 will be sent */
  41. outb(Int0aux, VectorPIC); /* ICW2 - interrupt vector offset */
  42. outb(Int0aux, 0x04); /* ICW3 - have slave on level 2 */
  43. outb(Int0aux, 0x01); /* ICW4 - 8086 mode, not buffered */
  44. /*
  45. * Set up the second 8259 interrupt processor.
  46. * Make 8259 interrupts start at CPU vector VectorPIC+8.
  47. * Set the 8259 as slave with edge triggered
  48. * input with fully nested interrupts.
  49. */
  50. outb(Int1ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered,
  51. ICW4 will be sent */
  52. outb(Int1aux, VectorPIC+8); /* ICW2 - interrupt vector offset */
  53. outb(Int1aux, 0x02); /* ICW3 - I am a slave on level 2 */
  54. outb(Int1aux, 0x01); /* ICW4 - 8086 mode, not buffered */
  55. outb(Int1aux, (i8259mask>>8) & 0xFF);
  56. /*
  57. * pass #2 8259 interrupts to #1
  58. */
  59. i8259mask &= ~0x04;
  60. outb(Int0aux, i8259mask & 0xFF);
  61. /*
  62. * Set Ocw3 to return the ISR when ctl read.
  63. * After initialisation status read is set to IRR.
  64. * Read IRR first to possibly deassert an outstanding
  65. * interrupt.
  66. */
  67. inb(Int0ctl);
  68. outb(Int0ctl, Ocw3|0x03);
  69. inb(Int1ctl);
  70. outb(Int1ctl, Ocw3|0x03);
  71. /*
  72. * Check for Edge/Level register.
  73. * This check may not work for all chipsets.
  74. * First try a non-intrusive test - the bits for
  75. * IRQs 13, 8, 2, 1 and 0 must be edge (0). If
  76. * that's OK try a R/W test.
  77. */
  78. x = (inb(Elcr2)<<8)|inb(Elcr1);
  79. if(!(x & 0x2107)){
  80. outb(Elcr1, 0);
  81. if(inb(Elcr1) == 0){
  82. outb(Elcr1, 0x20);
  83. if(inb(Elcr1) == 0x20)
  84. i8259elcr = x;
  85. outb(Elcr1, x & 0xFF);
  86. print("ELCR: %4.4uX\n", i8259elcr);
  87. }
  88. }
  89. iunlock(&i8259lock);
  90. }
  91. int
  92. i8259isr(int vno)
  93. {
  94. int irq, isr;
  95. if(vno < VectorPIC || vno > VectorPIC+MaxIrqPIC)
  96. return 0;
  97. irq = vno-VectorPIC;
  98. /*
  99. * tell the 8259 that we're done with the
  100. * highest level interrupt (interrupts are still
  101. * off at this point)
  102. */
  103. ilock(&i8259lock);
  104. isr = inb(Int0ctl);
  105. outb(Int0ctl, EOI);
  106. if(irq >= 8){
  107. isr |= inb(Int1ctl)<<8;
  108. outb(Int1ctl, EOI);
  109. }
  110. iunlock(&i8259lock);
  111. return isr & (1<<irq);
  112. }
  113. int
  114. i8259enable(Vctl* v)
  115. {
  116. int irq, irqbit;
  117. /*
  118. * Given an IRQ, enable the corresponding interrupt in the i8259
  119. * and return the vector to be used. The i8259 is set to use a fixed
  120. * range of vectors starting at VectorPIC.
  121. */
  122. irq = v->irq;
  123. if(irq < 0 || irq > MaxIrqPIC){
  124. print("i8259enable: irq %d out of range\n", irq);
  125. return -1;
  126. }
  127. irqbit = 1<<irq;
  128. ilock(&i8259lock);
  129. if(!(i8259mask & irqbit) && !(i8259elcr & irqbit)){
  130. print("i8259enable: irq %d shared but not level\n", irq);
  131. iunlock(&i8259lock);
  132. return -1;
  133. }
  134. i8259mask &= ~irqbit;
  135. if(irq < 8)
  136. outb(Int0aux, i8259mask & 0xFF);
  137. else
  138. outb(Int1aux, (i8259mask>>8) & 0xFF);
  139. if(i8259elcr & irqbit)
  140. v->eoi = i8259isr;
  141. else
  142. v->isr = i8259isr;
  143. iunlock(&i8259lock);
  144. return VectorPIC+irq;
  145. }
  146. int
  147. i8259vecno(int irq)
  148. {
  149. return VectorPIC+irq;
  150. }
  151. int
  152. i8259disable(int irq)
  153. {
  154. int irqbit;
  155. /*
  156. * Given an IRQ, disable the corresponding interrupt
  157. * in the 8259.
  158. */
  159. if(irq < 0 || irq > MaxIrqPIC){
  160. print("i8259disable: irq %d out of range\n", irq);
  161. return -1;
  162. }
  163. irqbit = 1<<irq;
  164. ilock(&i8259lock);
  165. if(!(i8259mask & irqbit)){
  166. i8259mask |= irqbit;
  167. if(irq < 8)
  168. outb(Int0aux, i8259mask & 0xFF);
  169. else
  170. outb(Int1aux, (i8259mask>>8) & 0xFF);
  171. }
  172. iunlock(&i8259lock);
  173. return 0;
  174. }
  175. void
  176. i8259on(void)
  177. {
  178. outb(Int0aux, i8259mask&0xFF);
  179. outb(Int1aux, (i8259mask>>8)&0xFF);
  180. }
  181. void
  182. i8259off(void)
  183. {
  184. outb(Int0aux, 0xFF);
  185. outb(Int1aux, 0xFF);
  186. }