i8259.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include "u.h"
  10. #include "../port/lib.h"
  11. #include "mem.h"
  12. #include "dat.h"
  13. #include "fns.h"
  14. #include "io.h"
  15. /*
  16. * 8259 Interrupt Controller and compatibles.
  17. */
  18. enum { /* I/O ports */
  19. Cntrl1 = 0x20,
  20. Cntrl2 = 0xa0,
  21. Icw1 = 0, /* Initialisation Command Word 1 */
  22. Icw2 = 1,
  23. Icw3 = 1,
  24. Icw4 = 1,
  25. Ocw1 = 1, /* Operational Control Word 1 */
  26. Ocw2 = 0,
  27. Ocw3 = 0,
  28. Imr = Ocw1, /* Interrupt Mask Register */
  29. Isr = Ocw3, /* In-Service Register */
  30. Irr = Ocw3, /* Interrupt Request Register */
  31. Elcr1 = 0x4d0, /* Edge/Level Control Register */
  32. Elcr2 = 0x4d1,
  33. };
  34. enum { /* Icw1 */
  35. Ic4 = 0x01, /* there will be an Icw4 */
  36. Icw1sel = 0x10, /* Icw/Ocw select */
  37. };
  38. enum { /* Icw3 */
  39. Cascaded = 0x04, /* Cntrl1 - Cascaded Mode Enable */
  40. SlaveIRQ2 = 0x02, /* Cntrl2 - Slave Identification Code */
  41. };
  42. enum { /* Icw4 */
  43. Microprocessor = 0x01, /* 80x86-based system */
  44. };
  45. enum { /* Ocw2 */
  46. Ocw2sel = 0x00, /* Ocw2 select */
  47. Eoi = 0x20, /* Non-spcific EOI command */
  48. };
  49. enum { /* Ocw3 */
  50. Irrread = 0x02, /* Read IRQ register */
  51. Isrread = 0x03, /* Read IS register */
  52. Ocw3sel = 0x08, /* Ocw3 select */
  53. };
  54. static Lock i8259lock;
  55. static int i8259mask = ~0; /* mask of disabled interrupts */
  56. static int i8259elcr; /* mask of level interrupts */
  57. int
  58. i8259init(int vectorbase)
  59. {
  60. int elcr;
  61. vectorbase &= ~0x07;
  62. ilock(&i8259lock);
  63. /*
  64. * Boilerplate to initialise the pair of 8259 controllers,
  65. * see one of the Intel bridge datasheets for details,
  66. * e.g. 82371AB (PIIX4). The default settings are 80x86 mode,
  67. * edge-sensitive detection, normal EOI, non-buffered and
  68. * cascade mode. Cntrl1 is connected as the master and Cntrl2
  69. * as the slave; IRQ2 is used to cascade the two controllers.
  70. */
  71. outb(Cntrl1+Icw1, Icw1sel|Ic4);
  72. outb(Cntrl1+Icw2, vectorbase);
  73. outb(Cntrl1+Icw3, Cascaded);
  74. outb(Cntrl1+Icw4, Microprocessor);
  75. outb(Cntrl2+Icw1, Icw1sel|Ic4);
  76. outb(Cntrl2+Icw2, vectorbase+8);
  77. outb(Cntrl2+Icw3, SlaveIRQ2);
  78. outb(Cntrl2+Icw4, Microprocessor);
  79. /*
  80. * Set the interrupt masks, allowing interrupts
  81. * to pass from Cntrl2 to Cntrl1 on IRQ2.
  82. */
  83. i8259mask &= ~(1<<2);
  84. outb(Cntrl2+Imr, (i8259mask>>8) & 0xff);
  85. outb(Cntrl1+Imr, i8259mask & 0xff);
  86. outb(Cntrl1+Ocw2, Ocw2sel|Eoi);
  87. outb(Cntrl2+Ocw2, Ocw2sel|Eoi);
  88. /*
  89. * Set Ocw3 to return the ISR when read for i8259isr()
  90. * (after initialisation status read is set to return the IRR).
  91. * Read IRR first to possibly deassert an outstanding
  92. * interrupt.
  93. */
  94. inb(Cntrl1+Irr);
  95. outb(Cntrl1+Ocw3, Ocw3sel|Isrread);
  96. inb(Cntrl2+Irr);
  97. outb(Cntrl2+Ocw3, Ocw3sel|Isrread);
  98. /*
  99. * Check for Edge/Level Control register.
  100. * This check may not work for all chipsets.
  101. * First try a non-intrusive test - the bits for
  102. * IRQs 13, 8, 2, 1 and 0 must be edge (0). If
  103. * that's OK try a R/W test.
  104. */
  105. elcr = (inb(Elcr2)<<8)|inb(Elcr1);
  106. if(!(elcr & 0x2107)){
  107. outb(Elcr1, 0);
  108. if(inb(Elcr1) == 0){
  109. outb(Elcr1, 0x20);
  110. if(inb(Elcr1) == 0x20)
  111. i8259elcr = elcr;
  112. outb(Elcr1, elcr & 0xff);
  113. }
  114. }
  115. iunlock(&i8259lock);
  116. return vectorbase;
  117. }
  118. int
  119. i8259isr(int vno)
  120. {
  121. int irq, isr;
  122. if(vno < IdtPIC || vno > IdtPIC+15)
  123. return 0;
  124. irq = vno-IdtPIC;
  125. /*
  126. * Collect the interrupt status,
  127. * acknowledge the interrupt and return whether
  128. * the acknowledged interrupt was the correct
  129. * one (this could be better but it's not really
  130. * used).
  131. */
  132. ilock(&i8259lock);
  133. isr = inb(Cntrl1+Isr);
  134. outb(Cntrl1+Ocw2, Ocw2sel|Eoi);
  135. if(irq >= 8){
  136. isr |= inb(Cntrl2+Isr)<<8;
  137. outb(Cntrl2+Ocw2, Ocw2sel|Eoi);
  138. }
  139. iunlock(&i8259lock);
  140. return isr & (1<<irq);
  141. }
  142. #ifdef notdef
  143. int
  144. i8259irqenable(Vctl* v)
  145. {
  146. int irq, irqbit;
  147. /*
  148. * Given an IRQ, enable the corresponding interrupt in the i8259
  149. * and return the vector to be used. The i8259 is set to use a fixed
  150. * range of vectors starting at VectorPIC.
  151. */
  152. irq = v->irq;
  153. if(irq < 0 || irq > 15){
  154. print("i8259enable: irq %d out of range\n", irq);
  155. return -1;
  156. }
  157. irqbit = 1<<irq;
  158. ilock(&i8259lock);
  159. if(!(i8259mask & irqbit) && !(i8259elcr & irqbit)){
  160. print("i8259enable: irq %d shared but not level\n", irq);
  161. iunlock(&i8259lock);
  162. return -1;
  163. }
  164. i8259mask &= ~irqbit;
  165. if(irq < 8)
  166. outb(Cntrl1+Imr, i8259mask & 0xff);
  167. else
  168. outb(Cntrl2+Imr, (i8259mask>>8) & 0xff);
  169. if(i8259elcr & irqbit)
  170. v->eoi = i8259isr;
  171. else
  172. v->isr = i8259isr;
  173. iunlock(&i8259lock);
  174. v->type = "8259";
  175. return IdtPIC+irq;
  176. }
  177. int
  178. i8259irqdisable(int irq)
  179. {
  180. int irqbit;
  181. /*
  182. * Given an IRQ, disable the corresponding interrupt
  183. * in the 8259.
  184. */
  185. if(irq < 0 || irq > 15){
  186. print("i8259disable: irq %d out of range\n", irq);
  187. return -1;
  188. }
  189. irqbit = 1<<irq;
  190. ilock(&i8259lock);
  191. if(!(i8259mask & irqbit)){
  192. i8259mask |= irqbit;
  193. if(irq < 8)
  194. outb(Cntrl1+Imr, i8259mask & 0xff);
  195. else
  196. outb(Cntrl2+Imr, (i8259mask>>8) & 0xff);
  197. }
  198. iunlock(&i8259lock);
  199. return 0;
  200. }
  201. #endif /* notdef */