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