~kris/9p

9hist

ref: 7919c04083daafc2e151dad543efcfa8672771f5 9hist/pc/i8259.c -rw-r--r-- 3.5 KiB
7919c040 — David du Colombier Plan 9 from Bell Labs 1998-09-17 28 years ago
                                                                                
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
#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 int int0mask;			/* interrupts enabled for first 8259 */
static int int1mask;			/* interrupts enabled for second 8259 */
static int elcr;			/* mask of level-triggered interrupts */
static Lock i8259lock;

void
i8259init(void)
{
	int elcr1;

	ilock(&i8259lock);
	int0mask = 0xFF;
	int1mask = 0xFF;

	/*
	 *  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 */

	/*
	 *  pass #2 8259 interrupts to #1
	 */
	int0mask &= ~0x04;
	outb(Int0aux, int0mask);

	/*
	 * 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.
	 */
	elcr1 = inb(Elcr1);
	outb(Elcr1, 0);
	if(inb(Elcr1) == 0){
		outb(Elcr1, 0x20);
		if(inb(Elcr1) == 0x20)
			elcr = (inb(Elcr2)<<8)|elcr1;
	}
	outb(Elcr1, elcr1);
	iunlock(&i8259lock);
//	if(elcr)
//		print("ELCR: %4.4uX\n", elcr);
}

int
i8259isr(int vno)
{
	int isr;

	if(vno < VectorPIC || vno > VectorPIC+MaxIrqPIC)
		return 0;

	/*
	 *  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(vno >= VectorPIC+8){
		isr |= inb(Int1ctl)<<8;
		outb(Int1ctl, EOI);
	}
	iunlock(&i8259lock);

	return isr & (1<<(vno-VectorPIC));
}

int
i8259enable(Vctl* v)
{
	int irq;

	/*
	 * 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;
	}

	ilock(&i8259lock);
	if(irq < 8){
		int0mask &= ~(1<<irq);
		outb(Int0aux, int0mask);
	}
	else{
		int1mask &= ~(1<<(irq-8));
		outb(Int1aux, int1mask);
	}

	if(elcr & (1<<irq))
		v->eoi = i8259isr;
	else
		v->isr = i8259isr;
	iunlock(&i8259lock);

	return VectorPIC+irq;
}