]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/i8259.cpp
Update
[l4.git] / kernel / fiasco / src / kern / i8259.cpp
1 INTERFACE [i8259]:
2
3 #include "atomic.h"
4 #include "lock_guard.h"
5 #include "irq_chip.h"
6 #include "spin_lock.h"
7 #include "types.h"
8 #include "pm.h"
9
10 template<typename IO>
11 class Irq_chip_i8259 : public Irq_chip_icu, private Pm_object
12 {
13   friend class Jdb_kern_info_pic_state;
14 public:
15   typedef typename IO::Port_addr Io_address;
16   unsigned nr_irqs() const override { return 16; }
17   int set_mode(Mword, Mode) override { return 0; }
18   bool is_edge_triggered(Mword) const override { return false; }
19   void set_cpu(Mword, Cpu_number) override {}
20   using Pm_object::register_pm;
21
22 private:
23   enum
24   {
25     OFF_ICW = 0x00,
26     OFF_OCW = 0x01,
27   };
28
29   enum
30   {
31     // ICW1
32     ICW_TEMPLATE    = 0x10,
33
34     LEVL_TRIGGER    = 0x08,
35     EDGE_TRIGGER    = 0x00,
36     ADDR_INTRVL4    = 0x04,
37     ADDR_INTRVL8    = 0x00,
38     SINGLE__MODE    = 0x02,
39     CASCADE_MODE    = 0x00,
40     ICW4__NEEDED    = 0x01,
41     NO_ICW4_NEED    = 0x00,
42
43     // ICW3
44     SLAVE_ON_IR0    = 0x01,
45     SLAVE_ON_IR1    = 0x02,
46     SLAVE_ON_IR2    = 0x04,
47     SLAVE_ON_IR3    = 0x08,
48     SLAVE_ON_IR4    = 0x10,
49     SLAVE_ON_IR5    = 0x20,
50     SLAVE_ON_IR6    = 0x40,
51     SLAVE_ON_IR7    = 0x80,
52
53     I_AM_SLAVE_0    = 0x00,
54     I_AM_SLAVE_1    = 0x01,
55     I_AM_SLAVE_2    = 0x02,
56     I_AM_SLAVE_3    = 0x03,
57     I_AM_SLAVE_4    = 0x04,
58     I_AM_SLAVE_5    = 0x05,
59     I_AM_SLAVE_6    = 0x06,
60     I_AM_SLAVE_7    = 0x07,
61
62     // ICW4
63     SNF_MODE_ENA    = 0x10,
64     SNF_MODE_DIS    = 0x00,
65     BUFFERD_MODE    = 0x08,
66     NONBUFD_MODE    = 0x00,
67     AUTO_EOI_MOD    = 0x02,
68     NRML_EOI_MOD    = 0x00,
69     I8086_EMM_MOD   = 0x01,
70     SET_MCS_MODE    = 0x00,
71
72     // OCW1
73     PICM_MASK       = 0xFF,
74     PICS_MASK       = 0xFF,
75
76     // OCW2
77     NON_SPEC_EOI    = 0x20,
78     SPECIFIC_EOI    = 0x60,
79     ROT_NON_SPEC    = 0xa0,
80     SET_ROT_AEOI    = 0x80,
81     RSET_ROTAEOI    = 0x00,
82     ROT_SPEC_EOI    = 0xe0,
83     SET_PRIORITY    = 0xc0,
84     NO_OPERATION    = 0x40,
85
86     SND_EOI_IR0    = 0x00,
87     SND_EOI_IR1    = 0x01,
88     SND_EOI_IR2    = 0x02,
89     SND_EOI_IR3    = 0x03,
90     SND_EOI_IR4    = 0x04,
91     SND_EOI_IR5    = 0x05,
92     SND_EOI_IR6    = 0x06,
93     SND_EOI_IR7    = 0x07,
94
95     // OCW3
96     OCW_TEMPLATE    = 0x08,
97     SPECIAL_MASK    = 0x40,
98     MASK_MDE_SET    = 0x20,
99     MASK_MDE_RST    = 0x00,
100     POLL_COMMAND    = 0x04,
101     NO_POLL_CMND    = 0x00,
102     READ_NEXT_RD    = 0x02,
103     READ_IR_ONRD    = 0x00,
104     READ_IS_ONRD    = 0x01,
105
106     // Standard PIC initialization values for PCs.
107     PICM_ICW1       = ICW_TEMPLATE | EDGE_TRIGGER | ADDR_INTRVL8
108                       | CASCADE_MODE | ICW4__NEEDED,
109     PICM_ICW3       = SLAVE_ON_IR2,
110     PICM_ICW4       = SNF_MODE_DIS | NONBUFD_MODE | NRML_EOI_MOD
111                       | I8086_EMM_MOD,
112
113     PICS_ICW1       = ICW_TEMPLATE | EDGE_TRIGGER | ADDR_INTRVL8
114                       | CASCADE_MODE | ICW4__NEEDED,
115     PICS_ICW3       = I_AM_SLAVE_2,
116     PICS_ICW4       = SNF_MODE_DIS | NONBUFD_MODE | NRML_EOI_MOD
117                       | I8086_EMM_MOD,
118   };
119
120
121   Unsigned8 read_ocw(Io_address base)
122   { return IO::in8(base + OFF_OCW); }
123
124   void write_ocw(Unsigned8 val, Io_address base)
125   { IO::out8(val, base + OFF_OCW); }
126
127   Unsigned8 read_icw(Io_address base)
128   { return IO::in8(base + OFF_ICW); }
129
130   void write_icw(Unsigned8 val, Io_address base)
131   { IO::out8(val, base + OFF_ICW); }
132
133   void iodelay() const { IO::iodelay(); }
134
135   // power-management hooks
136   void pm_on_suspend(Cpu_number) override
137   { _pm_saved_state = disable_all_save(); }
138
139   void pm_on_resume(Cpu_number) override
140   { restore_all(_pm_saved_state); }
141
142   Io_address _master, _slave;
143   Spin_lock<> _lock;
144   bool _sfn = false;
145
146   // power-management state
147   Unsigned16 _pm_saved_state;
148 };
149
150 template<typename IO>
151 class Irq_chip_i8259_gen : public Irq_chip_i8259<IO>
152 {
153 public:
154   typedef typename Irq_chip_i8259<IO>::Io_address Io_address;
155   Irq_chip_i8259_gen(Io_address master, Io_address slave)
156   : Irq_chip_i8259<IO>(master, slave)
157   {
158     for (auto &i: _irqs)
159       i = 0;
160   }
161
162   Irq_base *irq(Mword pin) const override
163   {
164     if (pin >= 16)
165       return 0;
166
167     return _irqs[pin];
168   }
169
170   bool alloc(Irq_base *irq, Mword pin) override
171   {
172     if (pin >= 16)
173       return false;
174
175     if (_irqs[pin])
176       return false;
177
178     if (!mp_cas(&_irqs[pin], (Irq_base *)0, irq))
179       return false;
180
181     this->bind(irq, pin);
182     return true;
183   }
184
185   bool reserve(Mword pin) override
186   {
187     if (pin >= 16)
188       return false;
189
190     if (_irqs[pin])
191       return false;
192
193     _irqs[pin] = (Irq_base*)1;
194
195     return true;
196   }
197
198   void unbind(Irq_base *irq) override
199   {
200     _irqs[irq->pin()] = 0;
201     Irq_chip_icu::unbind(irq);
202   }
203
204 private:
205   Irq_base *_irqs[16];
206 };
207
208
209 // --------------------------------------------------------
210 IMPLEMENTATION [i8259]:
211
212 /**
213  * Create a i8259 chip, does not do any hardware access.
214  * \note Hardware initalization is done in init().
215  */
216 PUBLIC template<typename IO>
217 Irq_chip_i8259<IO>::Irq_chip_i8259(Irq_chip_i8259::Io_address master,
218                                    Irq_chip_i8259::Io_address slave)
219 : _master(master), _slave(slave)
220 {}
221
222 PUBLIC template<typename IO> inline
223 Unsigned16
224 Irq_chip_i8259<IO>::disable_all_save()
225 {
226   Unsigned16 s =   (Unsigned16)read_ocw(_master)
227                  | (Unsigned16)read_ocw(_slave) << 8;
228   write_ocw(0xff, _master);
229   write_ocw(0xff, _slave);
230   return s;
231 }
232
233 PUBLIC template<typename IO> inline
234 void
235 Irq_chip_i8259<IO>::restore_all(Unsigned16 s)
236 {
237   write_ocw(s & 0x0ff, _master);
238   write_ocw((s >> 8) & 0x0ff, _slave);
239 }
240
241 /**
242  * Initialize the i8259 hardware.
243  * \pre The IO access must be enabled in the constructor if needed,
244  *      for example when using memory-mapped registers.
245  */
246 PUBLIC template<typename IO>
247 void
248 Irq_chip_i8259<IO>::init(Unsigned8 vect_base,
249                          bool use_sfn = false,
250                          bool high_prio_ir8 = false)
251 {
252   auto g = lock_guard(_lock);
253   _sfn = use_sfn;
254
255   // disable all IRQs
256   write_ocw(0xff, _master);
257   write_ocw(0xff, _slave);
258
259   write_icw(PICM_ICW1, _master); iodelay();
260   write_ocw(vect_base, _master); iodelay();
261   write_ocw((1U << 2), _master); iodelay(); // cascade at IR2
262   Unsigned8 icw4 = PICM_ICW4;
263   if (use_sfn)
264     icw4 |= SNF_MODE_ENA;
265   write_ocw(icw4, _master); iodelay();
266
267   write_icw(PICS_ICW1, _slave); iodelay();
268   write_ocw(vect_base + 8, _slave); iodelay();
269   write_ocw(PICS_ICW3, _slave); iodelay();
270   write_ocw(icw4, _slave); iodelay();
271
272   if (use_sfn && high_prio_ir8)
273     {
274       // setting specific rotation (specific priority) 
275       // -- see Intel 8259A reference manual
276       // irq 1 on master hast lowest prio
277       // => irq 2 (cascade) = irqs 8..15 have highest prio
278       write_icw(SET_PRIORITY | 1, _master); iodelay();
279       // irq 7 on slave has lowest prio
280       // => irq 0 on slave (= irq 8) has highest prio
281       write_icw(SET_PRIORITY | 7, _slave); iodelay();
282     }
283
284   // set initial masks
285   write_ocw(0xfb, _master); iodelay(); // unmask ir2
286   write_ocw(0xff, _slave); iodelay();  // mask everything
287
288   /* Ack any bogus intrs by setting the End Of Interrupt bit. */
289   write_icw(NON_SPEC_EOI, _master); iodelay();
290   write_icw(NON_SPEC_EOI, _slave); iodelay();
291 }
292
293 PRIVATE template<typename IO> inline
294 void
295 Irq_chip_i8259<IO>::_mask(Mword pin)
296 {
297   if (pin < 8)
298     write_ocw(read_ocw(_master) | (1U << pin), _master);
299   else
300     write_ocw(read_ocw(_slave) | (1U << (pin - 8)), _slave);
301 }
302
303 PUBLIC template<typename IO>
304 void
305 Irq_chip_i8259<IO>::mask(Mword pin) override
306 {
307   auto g = lock_guard(_lock);
308   _mask(pin);
309 }
310
311 PUBLIC template<typename IO>
312 void
313 Irq_chip_i8259<IO>::unmask(Mword pin) override
314 {
315   auto g = lock_guard(_lock);
316   if (pin < 8)
317     write_ocw(read_ocw(_master) & ~(1U << pin), _master);
318   else
319     write_ocw(read_ocw(_slave) & ~(1U << (pin - 8)), _slave);
320 }
321
322 PRIVATE template<typename IO> inline
323 void
324 Irq_chip_i8259<IO>::_ack(Mword pin)
325 {
326   if (pin >= 8)
327     {
328       write_icw(NON_SPEC_EOI, _slave);
329       if (_sfn)
330         {
331           write_icw(OCW_TEMPLATE | READ_NEXT_RD | READ_IS_ONRD, _slave);
332           if (read_icw(_slave))
333             return; // still active IRQs at the slave, don't EOI master
334         }
335     }
336   write_icw(NON_SPEC_EOI, _master);
337 }
338
339 PUBLIC template<typename IO>
340 void
341 Irq_chip_i8259<IO>::ack(Mword pin) override
342 {
343   auto g = lock_guard(_lock);
344   _ack(pin);
345 }
346
347 PUBLIC template<typename IO>
348 void
349 Irq_chip_i8259<IO>::mask_and_ack(Mword pin) override
350 {
351   auto g = lock_guard(_lock);
352   _mask(pin);
353   _ack(pin);
354 }
355
356 // -----------------------------------------------------------------
357 IMPLEMENTATION [i8259 && debug]:
358
359 PUBLIC template<typename IO>
360 char const *
361 Irq_chip_i8259<IO>::chip_type() const override
362 { return "i8259"; }
363