xref: /plan9-contrib/sys/src/9k/k10/i8259.c (revision 099a302f446d4403b6040f6424f6808f43846106)
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