]> git.buserror.net Git - polintos/scott/priv.git/blob - kernel/io/irq/i8259.cc
Switch to a simple X11-style license.
[polintos/scott/priv.git] / kernel / io / irq / i8259.cc
1 // io/irq/i8259.cc -- Intel 8259-compatible IRQ support
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal with
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 // of the Software, and to permit persons to whom the Software is furnished to do
10 // so, subject to the following condition:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
21 // SOFTWARE.
22
23 #include <kern/irq.h>
24 #include <kern/kernel.h>
25 #include <kern/io.h>
26 #include <kern/i8259.h>
27
28 namespace IRQ {
29         using namespace IRQ;
30
31         // This assumes a second i8259 cascaded on IRQ2 of the first i8259,
32         // with I/O port bases of 0x20 and 0xa0, as every i8259 I've
33         // encountered has been configured that way, even on non-x86
34         // platforms.  This can be made more general later if need be.
35         //
36         // FIXME: For non-x86 platforms, the I/O window base must be added in.
37         
38         inline void I8259::output_mask(int off)
39         {
40                 ll_out_8(off ? 0xa1 : 0x21, cached_masks[0]);
41         }
42         
43         inline void I8259::mask(u32 irq)
44         {
45                 assert(irq < 16);
46         
47                 int off = irq / 8;
48                 int bit = irq % 8;
49                 
50                 cached_masks[off] |= (1 << bit);
51                 output_mask(off);
52         }
53                 
54         void I8259::unmask(u32 irq)
55         {
56                 assert(irq < 16);
57
58                 int off = irq / 8;
59                 int bit = irq % 8;
60                 
61                 cached_masks[off] &= ~(1 << bit);
62                 output_mask(off);
63         }
64                 
65         void I8259::mask_and_ack(u32 irq)
66         {
67                 assert(irq < 16);
68                 int bit = irq % 8;
69
70                 mask(irq);
71                 
72                 if (irq < 8) {
73                         ll_out_8(0x20, 0x60 | bit);
74                 } else {
75                         ll_out_8(0xa0, 0x60 | bit);
76                         ll_out_8(0x20, 0x62);
77                 }
78         }
79         
80         int I8259::get_pending_irq()
81         {
82         #if defined(_LL_ARCH_x86) || defined(_LL_ARCH_x64)
83                 assert(0);
84                 return -1;
85         #else
86                 ll_out_8(0x20, 0x0b);
87                 u8 ret = ll_in_8(0x20);
88                 
89                 if (!(ret & 0x80))
90                         return -1;
91                 
92                 ret &= 7;
93                 
94                 if (ret != 2)
95                         return ret;
96                 
97                 ll_out_8(0xa0, 0x0b);
98                 ret = ll_in_8(0xa0);
99                 
100                 // Shouldn't happen, as even if the device de-asserts the
101                 // cascaded interrupt, it should wait for EOI.
102                         
103                 if (!(ret & 0x80))
104                         return -1;
105                 
106                 return (ret & 7) | 8;
107         #endif
108         }
109                 
110         void I8259::init()
111         {
112                 // Initialize each 8259 for edge triggered, cascade, 8086 mode. 
113                 // Interrupt vectors are set to 0x20-0x2f (this only matters on
114                 // x86/x64).
115                 
116                 ll_out_8(0x20, 0x11);
117                 ll_out_8(0x21, 0x20);
118                 ll_out_8(0x21, 0x04);
119                 ll_out_8(0x21, 0x01);
120                 
121                 ll_out_8(0xa0, 0x11);
122                 ll_out_8(0xa1, 0x28);
123                 ll_out_8(0xa1, 0x02);
124                 ll_out_8(0xa1, 0x01);
125                 
126                 // Mask all IRQs initially.
127                 
128                 cached_masks[0] = cached_masks[1] = 0xff;
129                 ll_out_8(0x21, 0xff);
130                 ll_out_8(0xa1, 0xff);
131                 
132                 bzero(irqslots, sizeof(irqslots));
133
134                 for (int i = 0; i < 16; i++)
135                         irqslots[i].controller = this;
136         
137                 irqs = irqslots;
138                 num_irqs = 16;
139         }
140
141         I8259 i8259;
142 }