]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/pic-i8259.cpp
b8d60fd77b6956530e3919a66e42a84657ce90cf
[l4.git] / kernel / fiasco / src / kern / pic-i8259.cpp
1 INTERFACE[i8259]:
2
3 #include "initcalls.h"
4
5 EXTENSION class Pic
6 {
7 public:
8   enum
9   {
10     MASTER_PIC_BASE = 0x20,
11     SLAVES_PIC_BASE = 0xa0,
12     OFF_ICW         = 0x00,
13     OFF_OCW         = 0x01,
14
15     MASTER_ICW      = MASTER_PIC_BASE + OFF_ICW,
16     MASTER_OCW      = MASTER_PIC_BASE + OFF_OCW,
17     SLAVES_ICW      = SLAVES_PIC_BASE + OFF_ICW,
18     SLAVES_OCW      = SLAVES_PIC_BASE + OFF_OCW,
19
20
21     /*
22     **  ICW1                            
23     */
24
25     ICW_TEMPLATE    = 0x10,
26     
27     LEVL_TRIGGER    = 0x08,
28     EDGE_TRIGGER    = 0x00,
29     ADDR_INTRVL4    = 0x04,
30     ADDR_INTRVL8    = 0x00,
31     SINGLE__MODE    = 0x02,
32     CASCADE_MODE    = 0x00,
33     ICW4__NEEDED    = 0x01,
34     NO_ICW4_NEED    = 0x00,
35
36     /*
37     **  ICW3                            
38     */
39
40     SLAVE_ON_IR0    = 0x01,
41     SLAVE_ON_IR1    = 0x02,
42     SLAVE_ON_IR2    = 0x04,
43     SLAVE_ON_IR3    = 0x08,
44     SLAVE_ON_IR4    = 0x10,
45     SLAVE_ON_IR5    = 0x20,
46     SLAVE_ON_IR6    = 0x40,
47     SLAVE_ON_IR7    = 0x80,
48     
49     I_AM_SLAVE_0    = 0x00,
50     I_AM_SLAVE_1    = 0x01,
51     I_AM_SLAVE_2    = 0x02,
52     I_AM_SLAVE_3    = 0x03,
53     I_AM_SLAVE_4    = 0x04,
54     I_AM_SLAVE_5    = 0x05,
55     I_AM_SLAVE_6    = 0x06,
56     I_AM_SLAVE_7    = 0x07,
57
58     /*
59     **  ICW4                            
60     */
61     
62     SNF_MODE_ENA    = 0x10,
63     SNF_MODE_DIS    = 0x00,
64     BUFFERD_MODE    = 0x08,
65     NONBUFD_MODE    = 0x00,
66     AUTO_EOI_MOD    = 0x02,
67     NRML_EOI_MOD    = 0x00,
68     I8086_EMM_MOD   = 0x01,
69     SET_MCS_MODE    = 0x00,
70
71     /*
72     **  OCW1                            
73     */
74     
75     PICM_MASK       = 0xFF,
76     PICS_MASK       = 0xFF,
77     
78     /*
79     **  OCW2                            
80     */
81     
82     NON_SPEC_EOI    = 0x20,
83     SPECIFIC_EOI    = 0x60,
84     ROT_NON_SPEC    = 0xa0,
85     SET_ROT_AEOI    = 0x80,
86     RSET_ROTAEOI    = 0x00,
87     ROT_SPEC_EOI    = 0xe0,
88     SET_PRIORITY    = 0xc0,
89     NO_OPERATION    = 0x40,
90     
91     SND_EOI_IR0    = 0x00,
92     SND_EOI_IR1    = 0x01,
93     SND_EOI_IR2    = 0x02,
94     SND_EOI_IR3    = 0x03,
95     SND_EOI_IR4    = 0x04,
96     SND_EOI_IR5    = 0x05,
97     SND_EOI_IR6    = 0x06,
98     SND_EOI_IR7    = 0x07,
99  
100     /*
101     **  OCW3                            
102     */
103     
104     OCW_TEMPLATE    = 0x08,
105     SPECIAL_MASK    = 0x40,
106     MASK_MDE_SET    = 0x20,
107     MASK_MDE_RST    = 0x00,
108     POLL_COMMAND    = 0x04,
109     NO_POLL_CMND    = 0x00,
110     READ_NEXT_RD    = 0x02,
111     READ_IR_ONRD    = 0x00,
112     READ_IS_ONRD    = 0x01,
113     
114     /*
115     **  Standard PIC initialization values for PCs.
116     */
117     PICM_ICW1       = (ICW_TEMPLATE | EDGE_TRIGGER | ADDR_INTRVL8 
118                        | CASCADE_MODE | ICW4__NEEDED),
119     PICM_ICW3       = SLAVE_ON_IR2,
120     PICM_ICW4       = (SNF_MODE_DIS | NONBUFD_MODE | NRML_EOI_MOD 
121                        | I8086_EMM_MOD),
122
123     PICS_ICW1       = (ICW_TEMPLATE | EDGE_TRIGGER | ADDR_INTRVL8 
124                        | CASCADE_MODE | ICW4__NEEDED),
125     PICS_ICW3       = I_AM_SLAVE_2,
126     PICS_ICW4       = (SNF_MODE_DIS | NONBUFD_MODE | NRML_EOI_MOD
127                        | I8086_EMM_MOD),
128   };
129
130   static int special_fully_nested_mode;
131 };
132
133 IMPLEMENTATION[i8259]:
134
135 #include <cstring>
136 #include <cstdio>
137 #include <cassert>
138
139 #include "io.h"
140 #include "boot_info.h"
141 #include "cmdline.h"
142 #include "config.h"
143 #include "initcalls.h"
144 #include "mem_layout.h"
145
146 int Pic::special_fully_nested_mode = 1; // be compatible with Jochen's L4
147
148 IMPLEMENT FIASCO_INIT
149 void
150 Pic::init()
151 {
152   pic_init(0x20,0x28);
153 }
154
155 PUBLIC static inline
156 unsigned
157 Pic::nr_irqs()
158 { return 16; }
159
160
161 //
162 // this version of pic_init() overrides the OSKIT's.
163 //
164 // differences to standard AT initialization: 
165 // . we use the "special fully nested mode" to let irq sources on the
166 //   slave irq raise nested
167 // . we give irq 2 (= slave pic) the highest prio on the master pic; this
168 //   results in the following sequence of prios: 8-15,3-7,0,1
169 //   this way, the timer interrupt on irq 8 always gets thru (even if 
170 //   some user irq handler doesn't acknowledge its irq!)
171 //
172 static FIASCO_INIT
173 void
174 Pic::pic_init(unsigned char master_base, unsigned char slave_base)
175 {
176   // disallow all interrupts before we selectively enable them 
177   Pic::disable_all_save();
178   /*
179    * Set the LINTEN bit in the HyperTransport Transaction
180    * Control Register.
181    *
182    * This will cause EXTINT and NMI interrupts routed over the
183    * hypertransport bus to be fed into the LAPIC LINT0/LINT1.  If
184    * the bit isn't set, the interrupts will go to the general cpu
185    * INTR/NMI pins.  On a dual-core cpu the interrupt winds up
186    * going to BOTH cpus.  The first cpu that does the interrupt ack
187    * cycle will get the correct interrupt.  The second cpu that does
188    * it will get a spurious interrupt vector (typically IRQ 7).
189    */
190 #if 0
191   if ((cpu_id & 0xff0) == 0xf30) 
192 #endif
193     {
194 #if 0
195       Unsigned32 tcr;
196       Io::out32(0x0cf8,
197           (1 << 31) | /* enable */
198           (0 << 16) | /* bus */
199           (24 << 11) |        /* dev (cpu + 24) */
200           (0 << 8) |  /* func */
201           0x68                /* reg */
202           );
203       tcr = Io::in32(0xcfc);
204       if ((tcr & 0x00010000) == 0) {
205           Io::out32(0xcfc, tcr|0x00010000);
206           printf("AMD: Rerouting HyperTransport "
207               "EXTINT/NMI to APIC\n");
208       }
209       Io::out32(0x0cf8, 0);
210 #endif
211   }
212   // VMware isn't able to deal with the special fully nested mode
213   // correctly so we simply don't use it while running under
214   // VMware. Otherwise VMware will barf with 
215   // *** VMware Workstation internal monitor error ***
216   // BUG F(152):393 bugNr=4388
217
218   if (strstr (Cmdline::cmdline(), " -nosfn"))
219     special_fully_nested_mode = 0;
220
221   if (special_fully_nested_mode)
222     {
223       puts ("Enabling special fully nested mode for PIC");
224       /* Initialize the master. */
225
226       Io::out8_p(PICM_ICW1, MASTER_ICW);
227       Io::out8_p(master_base, MASTER_OCW);
228       Io::out8_p(PICM_ICW3, MASTER_OCW);
229       Io::out8_p(SNF_MODE_ENA | PICM_ICW4, MASTER_OCW);
230
231       /* Initialize the slave. */
232       Io::out8_p(PICS_ICW1, SLAVES_ICW);
233       Io::out8_p(slave_base, SLAVES_OCW);
234       Io::out8_p(PICS_ICW3, SLAVES_OCW);
235       Io::out8_p(SNF_MODE_ENA | PICS_ICW4, SLAVES_OCW);
236
237       // the timer interrupt should have the highest priority so that it
238       // always gets through
239       if ( ! Config::profiling
240           && Config::pic_prio_modify
241           && Config::scheduler_mode == Config::SCHED_RTC)
242         {
243           // setting specific rotation (specific priority) 
244           // -- see Intel 8259A reference manual
245           // irq 1 on master hast lowest prio
246           // => irq 2 (cascade) = irqs 8..15 have highest prio
247           Io::out8_p(SET_PRIORITY | 1, MASTER_ICW);
248           // irq 7 on slave has lowest prio
249           // => irq 0 on slave (= irq 8) has highest prio
250           Io::out8_p(SET_PRIORITY | 7, SLAVES_ICW);
251         }
252     }
253   else
254     {
255       printf("%sUsing (normal) fully nested PIC mode\n",
256           Config::found_vmware ? "Found VMware: " : "");
257
258       /* Initialize the master. */
259       Io::out8_p(PICM_ICW1, MASTER_ICW);
260       Io::out8_p(master_base, MASTER_OCW);
261       Io::out8_p(PICM_ICW3, MASTER_OCW);
262       Io::out8_p(PICM_ICW4, MASTER_OCW);
263
264       /* Initialize the slave. */
265       Io::out8_p(PICS_ICW1, SLAVES_ICW);
266       Io::out8_p(slave_base, SLAVES_OCW);
267       Io::out8_p(PICS_ICW3, SLAVES_OCW);
268       Io::out8_p(PICS_ICW4, SLAVES_OCW);
269     }
270
271   // set initial masks
272   Io::out8_p(0xfb, MASTER_OCW); // unmask irq2
273   Io::out8_p(0xff, SLAVES_OCW); // mask everything
274
275   /* Ack any bogus intrs by setting the End Of Interrupt bit. */
276   Io::out8_p(NON_SPEC_EOI, MASTER_ICW);
277   Io::out8_p(NON_SPEC_EOI, SLAVES_ICW);
278
279   // disallow all interrupts before we selectively enable them 
280   Pic::disable_all_save();
281
282 }
283
284 IMPLEMENT inline NEEDS["io.h"]
285 void
286 Pic::disable_locked(unsigned irq)
287 {
288   if (irq < 8)
289     Io::out8(Io::in8(MASTER_OCW) | (1 << irq), MASTER_OCW);
290   else
291     Io::out8(Io::in8(SLAVES_OCW) | (1 << (irq-8)), SLAVES_OCW);
292 }
293
294 IMPLEMENT inline NEEDS["io.h"]
295 void
296 Pic::enable_locked(unsigned irq, unsigned /*prio*/)
297 {
298   if (irq < 8)
299     Io::out8(Io::in8(MASTER_OCW) & ~(1 << irq), MASTER_OCW);
300   else
301     {
302       Io::out8(Io::in8(MASTER_OCW) & ~(1 << 2), MASTER_OCW);
303       Io::out8(Io::in8(SLAVES_OCW) & ~(1 << (irq-8)), SLAVES_OCW);
304     }
305 }
306
307 IMPLEMENT inline NEEDS["io.h"]
308 void
309 Pic::acknowledge_locked(unsigned irq)
310 {
311   if (irq >= 8)
312     {
313       Io::out8(NON_SPEC_EOI, SLAVES_ICW); // EOI slave
314       if (special_fully_nested_mode)
315         {
316           Io::out8(OCW_TEMPLATE | READ_NEXT_RD | READ_IS_ONRD, SLAVES_ICW);
317           if (Io::in8(SLAVES_ICW))      // slave still active?
318             return;                 // -- don't EOI master
319         }
320     }
321   Io::out8(NON_SPEC_EOI, MASTER_ICW); // EOI master
322 }
323
324 IMPLEMENT inline
325 void
326 Pic::block_locked(unsigned)
327 {}
328
329 IMPLEMENT inline NEEDS["io.h"]
330 Pic::Status
331 Pic::disable_all_save()
332 {
333   Status s;
334   s  = Io::in8(MASTER_OCW);
335   s |= Io::in8(SLAVES_OCW) << 8;
336   Io::out8( 0xff, MASTER_OCW );
337   Io::out8( 0xff, SLAVES_OCW );
338   
339   return s;
340 }
341
342 IMPLEMENT inline NEEDS["io.h"]
343 void
344 Pic::restore_all( Status s )
345 {
346   Io::out8( s & 0x0ff, MASTER_OCW );
347   Io::out8( (s >> 8) & 0x0ff, SLAVES_OCW );
348 }
349
350
351 IMPLEMENT inline NEEDS[<cassert>]
352 void
353 Pic::set_cpu(unsigned, unsigned cpu)
354 {
355   (void)cpu;
356   assert(cpu == 0);
357 }