xref: /haiku/src/system/kernel/arch/x86/pic.cpp (revision d0f2d8282f3f59a1af7fe2d340d2af0cb36a9b20)
1 /*
2  * Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 #include <arch/x86/pic.h>
10 
11 #include <arch/cpu.h>
12 #include <arch/int.h>
13 
14 #include <arch/x86/arch_int.h>
15 
16 #include <int.h>
17 
18 
19 //#define TRACE_PIC
20 #ifdef TRACE_PIC
21 #	define TRACE(x) dprintf x
22 #else
23 #	define TRACE(x) ;
24 #endif
25 
26 
27 // Definitions for the PIC 8259 controller
28 // (this is not a complete list, only what we're actually using)
29 
30 #define PIC_MASTER_CONTROL		0x20
31 #define PIC_MASTER_MASK			0x21
32 #define PIC_SLAVE_CONTROL		0xa0
33 #define PIC_SLAVE_MASK			0xa1
34 #define PIC_MASTER_INIT1		PIC_MASTER_CONTROL
35 #define PIC_MASTER_INIT2		PIC_MASTER_MASK
36 #define PIC_MASTER_INIT3		PIC_MASTER_MASK
37 #define PIC_MASTER_INIT4		PIC_MASTER_MASK
38 #define PIC_SLAVE_INIT1			PIC_SLAVE_CONTROL
39 #define PIC_SLAVE_INIT2			PIC_SLAVE_MASK
40 #define PIC_SLAVE_INIT3			PIC_SLAVE_MASK
41 #define PIC_SLAVE_INIT4			PIC_SLAVE_MASK
42 
43 // the edge/level trigger control registers
44 #define PIC_MASTER_TRIGGER_MODE	0x4d0
45 #define PIC_SLAVE_TRIGGER_MODE	0x4d1
46 
47 #define PIC_INIT1				0x10
48 #define PIC_INIT1_SEND_INIT4	0x01
49 #define PIC_INIT3_IR2_IS_SLAVE	0x04
50 #define PIC_INIT3_SLAVE_ID2		0x02
51 #define PIC_INIT4_x86_MODE		0x01
52 
53 #define PIC_CONTROL3			0x08
54 #define PIC_CONTROL3_READ_ISR	0x03
55 #define PIC_CONTROL3_READ_IRR	0x02
56 
57 #define PIC_NON_SPECIFIC_EOI	0x20
58 
59 #define PIC_SLAVE_INT_BASE		8
60 #define PIC_NUM_INTS			0x0f
61 
62 
63 static uint16 sLevelTriggeredInterrupts = 0;
64 	// binary mask: 1 level, 0 edge
65 
66 
67 /*!	Tests if the interrupt in-service register of the responsible
68 	PIC is set for interrupts 7 and 15, and if that's not the case,
69 	it must assume it's a spurious interrupt.
70 */
71 static bool
pic_is_spurious_interrupt(int32 num)72 pic_is_spurious_interrupt(int32 num)
73 {
74 	if (num != 7)
75 		return false;
76 
77 	// Note, detecting spurious interrupts on line 15 obviously doesn't
78 	// work correctly - and since those are extremely rare, anyway, we
79 	// just ignore them
80 
81 	out8(PIC_CONTROL3 | PIC_CONTROL3_READ_ISR, PIC_MASTER_CONTROL);
82 	int32 isr = in8(PIC_MASTER_CONTROL);
83 	out8(PIC_CONTROL3 | PIC_CONTROL3_READ_IRR, PIC_MASTER_CONTROL);
84 
85 	return (isr & 0x80) == 0;
86 }
87 
88 
89 static bool
pic_is_level_triggered_interrupt(int32 num)90 pic_is_level_triggered_interrupt(int32 num)
91 {
92 	if (num < 0 || num > PIC_NUM_INTS)
93 		return false;
94 
95 	return (sLevelTriggeredInterrupts & (1 << num)) != 0;
96 }
97 
98 
99 /*!	Sends a non-specified EOI (end of interrupt) notice to the PIC in
100 	question (or both of them).
101 	This clears the PIC interrupt in-service bit.
102 */
103 static bool
pic_end_of_interrupt(int32 num)104 pic_end_of_interrupt(int32 num)
105 {
106 	if (num < 0 || num > PIC_NUM_INTS)
107 		return false;
108 
109 	// PIC 8259 controlled interrupt
110 	if (num >= PIC_SLAVE_INT_BASE)
111 		out8(PIC_NON_SPECIFIC_EOI, PIC_SLAVE_CONTROL);
112 
113 	// we always need to acknowledge the master PIC
114 	out8(PIC_NON_SPECIFIC_EOI, PIC_MASTER_CONTROL);
115 	return true;
116 }
117 
118 
119 static void
pic_enable_io_interrupt(int32 num)120 pic_enable_io_interrupt(int32 num)
121 {
122 	// interrupt is specified "normalized"
123 	if (num < 0 || num > PIC_NUM_INTS)
124 		return;
125 
126 	// enable PIC 8259 controlled interrupt
127 
128 	TRACE(("pic_enable_io_interrupt: irq %ld\n", num));
129 
130 	if (num < PIC_SLAVE_INT_BASE)
131 		out8(in8(PIC_MASTER_MASK) & ~(1 << num), PIC_MASTER_MASK);
132 	else
133 		out8(in8(PIC_SLAVE_MASK) & ~(1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK);
134 }
135 
136 
137 static void
pic_disable_io_interrupt(int32 num)138 pic_disable_io_interrupt(int32 num)
139 {
140 	// interrupt is specified "normalized"
141 	// never disable slave pic line IRQ 2
142 	if (num < 0 || num > PIC_NUM_INTS || num == 2)
143 		return;
144 
145 	// disable PIC 8259 controlled interrupt
146 
147 	TRACE(("pic_disable_io_interrupt: irq %ld\n", num));
148 
149 	if (num < PIC_SLAVE_INT_BASE)
150 		out8(in8(PIC_MASTER_MASK) | (1 << num), PIC_MASTER_MASK);
151 	else
152 		out8(in8(PIC_SLAVE_MASK) | (1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK);
153 }
154 
155 
156 static void
pic_configure_io_interrupt(int32 num,uint32 config)157 pic_configure_io_interrupt(int32 num, uint32 config)
158 {
159 	uint8 value;
160 	int32 localBit;
161 	if (num < 0 || num > PIC_NUM_INTS || num == 2)
162 		return;
163 
164 	TRACE(("pic_configure_io_interrupt: irq %ld; config 0x%08lx\n", num, config));
165 
166 	if (num < PIC_SLAVE_INT_BASE) {
167 		value = in8(PIC_MASTER_TRIGGER_MODE);
168 		localBit = num;
169 	} else {
170 		value = in8(PIC_SLAVE_TRIGGER_MODE);
171 		localBit = num - PIC_SLAVE_INT_BASE;
172 	}
173 
174 	if (config & B_LEVEL_TRIGGERED)
175 		value |= 1 << localBit;
176 	else
177 		value &= ~(1 << localBit);
178 
179 	if (num < PIC_SLAVE_INT_BASE)
180 		out8(value, PIC_MASTER_TRIGGER_MODE);
181 	else
182 		out8(value, PIC_SLAVE_TRIGGER_MODE);
183 
184 	sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE)
185 		| (in8(PIC_SLAVE_TRIGGER_MODE) << 8);
186 }
187 
188 
189 void
pic_init()190 pic_init()
191 {
192 	static const interrupt_controller picController = {
193 		"8259 PIC",
194 		&pic_enable_io_interrupt,
195 		&pic_disable_io_interrupt,
196 		&pic_configure_io_interrupt,
197 		&pic_is_spurious_interrupt,
198 		&pic_is_level_triggered_interrupt,
199 		&pic_end_of_interrupt,
200 		NULL
201 	};
202 
203 	// Start initialization sequence for the master and slave PICs
204 	out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_MASTER_INIT1);
205 	out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_SLAVE_INIT1);
206 
207 	// Set start of interrupts to 0x20 for master, 0x28 for slave
208 	out8(ARCH_INTERRUPT_BASE, PIC_MASTER_INIT2);
209 	out8(ARCH_INTERRUPT_BASE + PIC_SLAVE_INT_BASE, PIC_SLAVE_INIT2);
210 
211 	// Specify cascading through interrupt 2
212 	out8(PIC_INIT3_IR2_IS_SLAVE, PIC_MASTER_INIT3);
213 	out8(PIC_INIT3_SLAVE_ID2, PIC_SLAVE_INIT3);
214 
215 	// Set both to operate in 8086 mode
216 	out8(PIC_INIT4_x86_MODE, PIC_MASTER_INIT4);
217 	out8(PIC_INIT4_x86_MODE, PIC_SLAVE_INIT4);
218 
219 	out8(0xfb, PIC_MASTER_MASK);	// Mask off all interrupts (except slave pic line IRQ 2).
220 	out8(0xff, PIC_SLAVE_MASK); 	// Mask off interrupts on the slave.
221 
222 	// determine which interrupts are level or edge triggered
223 
224 #if 0
225 	// should set everything possible to level triggered
226 	out8(0xf8, PIC_MASTER_TRIGGER_MODE);
227 	out8(0xde, PIC_SLAVE_TRIGGER_MODE);
228 #endif
229 
230 	sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE)
231 		| (in8(PIC_SLAVE_TRIGGER_MODE) << 8);
232 
233 	TRACE(("PIC level trigger mode: 0x%08lx\n", sLevelTriggeredInterrupts));
234 
235 	reserve_io_interrupt_vectors(16, 0, INTERRUPT_TYPE_EXCEPTION);
236 
237 	// make the pic controller the current one
238 	arch_int_set_interrupt_controller(picController);
239 }
240 
241 
242 void
pic_disable(uint16 & enabledInterrupts)243 pic_disable(uint16& enabledInterrupts)
244 {
245 	enabledInterrupts = ~(in8(PIC_MASTER_MASK) | in8(PIC_SLAVE_MASK) << 8);
246 	enabledInterrupts &= 0xfffb; // remove slave PIC from the mask
247 
248 	// Mask off all interrupts on master and slave
249 	out8(0xff, PIC_MASTER_MASK);
250 	out8(0xff, PIC_SLAVE_MASK);
251 
252 	free_io_interrupt_vectors(16, 0);
253 }
254