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