65a658f29f43e2f69319828f564fd6faa1e4d23d
[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 conditions:
11 // 
12 //     * Redistributions of source code must retain the above copyright notice,
13 //       this list of conditions and the following disclaimers.
14 // 
15 //     * Redistributions in binary form must reproduce the above copyright notice,
16 //       this list of conditions and the following disclaimers in the
17 //       documentation and/or other materials provided with the distribution.
18 // 
19 //     * The names of the Software's authors and/or contributors
20 //       may not be used to endorse or promote products derived from
21 //       this Software without specific prior written permission.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
25 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
26 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
29 // SOFTWARE.
30
31 #include <kern/irq.h>
32 #include <kern/kernel.h>
33 #include <kern/io.h>
34 #include <kern/i8259.h>
35
36 namespace IRQ {
37         using namespace IRQ;
38
39         // This assumes a second i8259 cascaded on IRQ2 of the first i8259,
40         // with I/O port bases of 0x20 and 0xa0, as every i8259 I've
41         // encountered has been configured that way, even on non-x86
42         // platforms.  This can be made more general later if need be.
43         //
44         // FIXME: For non-x86 platforms, the I/O window base must be added in.
45         
46         inline void I8259::output_mask(int off)
47         {
48                 ll_out_8(off ? 0xa1 : 0x21, cached_masks[0]);
49         }
50         
51         inline void I8259::mask(u32 irq)
52         {
53                 assert(irq < 16);
54         
55                 int off = irq / 8;
56                 int bit = irq % 8;
57                 
58                 cached_masks[off] |= (1 << bit);
59                 output_mask(off);
60         }
61                 
62         void I8259::unmask(u32 irq)
63         {
64                 assert(irq < 16);
65
66                 int off = irq / 8;
67                 int bit = irq % 8;
68                 
69                 cached_masks[off] &= ~(1 << bit);
70                 output_mask(off);
71         }
72                 
73         void I8259::mask_and_ack(u32 irq)
74         {
75                 assert(irq < 16);
76                 int bit = irq % 8;
77
78                 mask(irq);
79                 
80                 if (irq < 8) {
81                         ll_out_8(0x20, 0x60 | bit);
82                 } else {
83                         ll_out_8(0xa0, 0x60 | bit);
84                         ll_out_8(0x20, 0x62);
85                 }
86         }
87         
88         int I8259::get_pending_irq()
89         {
90         #if defined(_LL_ARCH_x86) || defined(_LL_ARCH_x64)
91                 assert(0);
92                 return -1;
93         #else
94                 ll_out_8(0x20, 0x0b);
95                 u8 ret = ll_in_8(0x20);
96                 
97                 if (!(ret & 0x80))
98                         return -1;
99                 
100                 ret &= 7;
101                 
102                 if (ret != 2)
103                         return ret;
104                 
105                 ll_out_8(0xa0, 0x0b);
106                 ret = ll_in_8(0xa0);
107                 
108                 // Shouldn't happen, as even if the device de-asserts the
109                 // cascaded interrupt, it should wait for EOI.
110                         
111                 if (!(ret & 0x80))
112                         return -1;
113                 
114                 return (ret & 7) | 8;
115         #endif
116         }
117                 
118         void I8259::init()
119         {
120                 // Initialize each 8259 for edge triggered, cascade, 8086 mode. 
121                 // Interrupt vectors are set to 0x20-0x2f (this only matters on
122                 // x86/x64).
123                 
124                 ll_out_8(0x20, 0x11);
125                 ll_out_8(0x21, 0x20);
126                 ll_out_8(0x21, 0x04);
127                 ll_out_8(0x21, 0x01);
128                 
129                 ll_out_8(0xa0, 0x11);
130                 ll_out_8(0xa1, 0x28);
131                 ll_out_8(0xa1, 0x02);
132                 ll_out_8(0xa1, 0x01);
133                 
134                 // Mask all IRQs initially.
135                 
136                 cached_masks[0] = cached_masks[1] = 0xff;
137                 ll_out_8(0x21, 0xff);
138                 ll_out_8(0xa1, 0xff);
139                 
140                 bzero(irqslots, sizeof(irqslots));
141
142                 for (int i = 0; i < 16; i++)
143                         irqslots[i].controller = this;
144         
145                 irqs = irqslots;
146                 num_irqs = 16;
147         }
148
149         I8259 i8259;
150 }