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
63static 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*/
71static bool
72pic_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
89static bool
90pic_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*/
103static bool
104pic_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
119static void
120pic_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
137static void
138pic_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
156static void
157pic_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
189void
190pic_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
242void
243pic_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