source: rtems/c/src/lib/libbsp/i386/shared/irq/irq.c @ facebbb

Last change on this file since facebbb was facebbb, checked in by Pavel Pisa <pisa@…>, on Oct 2, 2016 at 10:19:12 AM

bsps/i386: Separate variable for i8259 IRQs disable due to in progress state.

The global state of enabled and disabled interrupts has to hold
interrupts really disabled by drivers and system. If the state is
combined with interrupts temporarily disabled because they are
processed at given time then it is impossible to maintain state
by interrupt handlers in drivers.

  • Property mode set to 100644
File size: 10.0 KB
Line 
1/*
2 *  This file contains the implementation of the function described in irq.h
3 */
4
5/*
6 *  Copyright (c) 2009 embedded brains GmbH
7 *  Copyright (C) 1998 valette@crf.canon.fr
8 *
9 *  The license and distribution terms for this file may be
10 *  found in the file LICENSE in this distribution or at
11 *  http://www.rtems.org/license/LICENSE.
12 */
13
14#include <bsp.h>
15#include <bsp/irq.h>
16#include <bsp/irq-generic.h>
17#include <rtems/score/cpu.h>
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <inttypes.h>
22
23
24#include "elcr.h"
25
26/*
27 * pointer to the mask representing the additionnal irq vectors
28 * that must be disabled when a particular entry is activated.
29 * They will be dynamically computed from teh prioruty table given
30 * in BSP_rtems_irq_mngt_set();
31 * CAUTION : this table is accessed directly by interrupt routine
32 *           prologue.
33 */
34static rtems_i8259_masks irq_mask_or_tbl[BSP_IRQ_LINES_NUMBER];
35
36/*
37 * Stats of interrupts dispatched.
38 */
39static uint32_t irq_count[BSP_IRQ_VECTOR_NUMBER] = {0};
40static uint32_t spurious_count;
41
42/*
43 * Edge or level trigger interrupts.
44 */
45static enum intr_trigger irq_trigger[BSP_IRQ_LINES_NUMBER];
46
47/*-------------------------------------------------------------------------+
48| Cache for 1st and 2nd PIC IRQ line's mssk (enabled or disabled) register.
49+--------------------------------------------------------------------------*/
50/*
51 * lower byte is interrupt mask on the master PIC.
52 * while upper bits are interrupt on the slave PIC.
53 * This cache is initialized in ldseg.s
54 */
55static rtems_i8259_masks i8259a_imr_cache = 0xFFFB;
56static rtems_i8259_masks i8259a_in_progress = 0;
57
58static inline
59void BSP_i8259a_irq_update_master_imr( void )
60{
61  rtems_i8259_masks mask = i8259a_in_progress | i8259a_imr_cache;
62  outport_byte( PIC_MASTER_IMR_IO_PORT, mask & 0xff );
63}
64
65static inline
66void BSP_i8259a_irq_update_slave_imr( void )
67{
68  rtems_i8259_masks mask = i8259a_in_progress | i8259a_imr_cache;
69  outport_byte( PIC_SLAVE_IMR_IO_PORT, ( mask >> 8 ) & 0xff );
70}
71
72/*
73 * Print the stats.
74 */
75uint32_t BSP_irq_count_dump(FILE *f)
76{
77  uint32_t tot = 0;
78  int      i;
79 if ( !f )
80   f = stdout;
81 fprintf(f,"SPURIOUS: %9"PRIu32"\n", spurious_count);
82 for ( i = 0; i < BSP_IRQ_VECTOR_NUMBER; i++ ) {
83   char type = '-';
84   if (i < BSP_IRQ_LINES_NUMBER)
85     type = irq_trigger[i] == INTR_TRIGGER_EDGE ? 'E' : 'L';
86   tot += irq_count[i];
87   fprintf(f,"IRQ %2u: %c %9"PRIu32"\n", i, type, irq_count[i]);
88 }
89 return tot;
90}
91
92/*
93 * Is the IRQ valid?
94 */
95static inline bool BSP_i8259a_irq_valid(const rtems_irq_number irqLine)
96{
97  return ((int)irqLine >= BSP_IRQ_VECTOR_LOWEST_OFFSET) &&
98    ((int)irqLine <= BSP_IRQ_MAX_ON_i8259A);
99}
100
101/*
102 * Read the IRR register. The default.
103 */
104static inline uint8_t BSP_i8259a_irq_int_request_reg(uint32_t ioport)
105{
106  uint8_t isr;
107  inport_byte(ioport, isr);
108  return isr;
109}
110
111/*
112 * Read the ISR register. Keep the default of the IRR.
113 */
114static inline uint8_t BSP_i8259a_irq_in_service_reg(uint32_t ioport)
115{
116  uint8_t isr;
117  outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR | PIC_OCW3_RIS);
118  inport_byte(ioport, isr);
119  outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR);
120  return isr;
121}
122
123/*-------------------------------------------------------------------------+
124|         Function:  BSP_irq_disable_at_i8259a
125|      Description: Mask IRQ line in appropriate PIC chip.
126| Global Variables: i8259a_imr_cache, i8259a_in_progress
127|        Arguments: vector_offset - number of IRQ line to mask.
128|          Returns: 0 is OK.
129+--------------------------------------------------------------------------*/
130static int BSP_irq_disable_at_i8259a(const rtems_irq_number irqLine)
131{
132  unsigned short        mask;
133  rtems_interrupt_level level;
134
135  rtems_interrupt_disable(level);
136
137  mask = 1 << irqLine;
138  i8259a_imr_cache |= mask;
139
140  if (irqLine < 8)
141  {
142    BSP_i8259a_irq_update_master_imr();
143  }
144  else
145  {
146    BSP_i8259a_irq_update_slave_imr();
147  }
148
149  rtems_interrupt_enable(level);
150
151  return 0;
152}
153
154/*-------------------------------------------------------------------------+
155|         Function:  BSP_irq_enable_at_i8259a
156|      Description: Unmask IRQ line in appropriate PIC chip.
157| Global Variables: i8259a_imr_cache, i8259a_in_progress
158|        Arguments: irqLine - number of IRQ line to mask.
159|          Returns: Nothing.
160+--------------------------------------------------------------------------*/
161static int BSP_irq_enable_at_i8259a(const rtems_irq_number irqLine)
162{
163  unsigned short        mask;
164  rtems_interrupt_level level;
165  uint8_t               isr;
166  uint8_t               irr;
167
168  rtems_interrupt_disable(level);
169
170  mask = 1 << irqLine;
171  i8259a_imr_cache &= ~mask;
172
173  if (irqLine < 8)
174  {
175    isr = BSP_i8259a_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT);
176    irr = BSP_i8259a_irq_int_request_reg(PIC_MASTER_COMMAND_IO_PORT);
177    BSP_i8259a_irq_update_master_imr();
178  }
179  else
180  {
181    isr = BSP_i8259a_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT);
182    irr = BSP_i8259a_irq_int_request_reg(PIC_SLAVE_COMMAND_IO_PORT);
183    BSP_i8259a_irq_update_slave_imr();
184  }
185
186  if (((isr ^ irr) & mask) != 0)
187    printk("i386: isr=%x irr=%x\n", isr, irr);
188
189  rtems_interrupt_enable(level);
190
191  return 0;
192} /* mask_irq */
193
194/*-------------------------------------------------------------------------+
195|         Function: BSP_irq_ack_at_i8259a
196|      Description: Signal generic End Of Interrupt (EOI) to appropriate PIC.
197| Global Variables: None.
198|        Arguments: irqLine - number of IRQ line to acknowledge.
199|          Returns: Nothing.
200+--------------------------------------------------------------------------*/
201static int BSP_irq_ack_at_i8259a(const rtems_irq_number irqLine)
202{
203  uint8_t slave_isr = 0;
204
205  if (irqLine >= 8) {
206   outport_byte(PIC_SLAVE_COMMAND_IO_PORT, PIC_EOI);
207   slave_isr = BSP_i8259a_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT);
208  }
209
210  /*
211   * Only issue the EOI to the master if there are no more interrupts in
212   * service for the slave. i8259a data sheet page 18, The Special Fully Nested
213   * Mode, b.
214   */
215  if (slave_isr == 0)
216    outport_byte(PIC_MASTER_COMMAND_IO_PORT, PIC_EOI);
217
218  return 0;
219
220} /* ackIRQ */
221
222/*
223 * ------------------------ RTEMS Irq helper functions ----------------
224 */
225
226static rtems_irq_prio irqPrioTable[BSP_IRQ_LINES_NUMBER]={
227  /*
228   * actual priorities for each interrupt source:
229   *    0   means that only current interrupt is masked
230   *    255 means all other interrupts are masked
231   * The second entry has a priority of 255 because
232   * it is the slave pic entry and is should always remain
233   * unmasked.
234   */
235  0,0,
236  255,
237  0, 0, 0, 0,  0,  0,  0,  0,  0,  0,  0,  0
238};
239
240static void compute_i8259_masks_from_prio (void)
241{
242  rtems_interrupt_level level;
243  unsigned int i;
244  unsigned int j;
245
246  rtems_interrupt_disable(level);
247
248  /*
249   * Always mask at least current interrupt to prevent re-entrance
250   */
251  for (i=0; i < BSP_IRQ_LINES_NUMBER; i++) {
252    * ((unsigned short*) &irq_mask_or_tbl[i]) = (1 << i);
253    for (j = 0; j < BSP_IRQ_LINES_NUMBER; j++) {
254      /*
255       * Mask interrupts at i8259 level that have a lower priority
256       */
257      if (irqPrioTable [i] > irqPrioTable [j]) {
258        * ((unsigned short*) &irq_mask_or_tbl[i]) |= (1 << j);
259      }
260    }
261  }
262
263  rtems_interrupt_enable(level);
264}
265
266static inline bool bsp_interrupt_vector_is_valid(rtems_vector_number vector)
267{
268  return BSP_i8259a_irq_valid((const rtems_irq_number) vector);
269}
270
271rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector)
272{
273  if (bsp_interrupt_vector_is_valid(vector))
274    BSP_irq_enable_at_i8259a(vector);
275  return RTEMS_SUCCESSFUL;
276}
277
278rtems_status_code bsp_interrupt_vector_disable(rtems_vector_number vector)
279{
280  if (bsp_interrupt_vector_is_valid(vector))
281    BSP_irq_disable_at_i8259a(vector);
282  return RTEMS_SUCCESSFUL;
283}
284
285rtems_status_code bsp_interrupt_facility_initialize(void)
286{
287  int i;
288
289  /*
290   * set up internal tables used by rtems interrupt prologue
291   */
292  compute_i8259_masks_from_prio();
293
294  /*
295   * must enable slave pic anyway
296   */
297  BSP_irq_enable_at_i8259a(2);
298
299  /*
300   * Probe the ELCR.
301   */
302  elcr_probe();
303
304  for (i = 0; i < BSP_IRQ_LINES_NUMBER; i++)
305    irq_trigger[i] = elcr_read_trigger(i);
306
307  return RTEMS_SUCCESSFUL;
308}
309
310/*
311 * Global so the asm handler can call it.
312 */
313void BSP_dispatch_isr(int vector);
314
315void BSP_dispatch_isr(int vector)
316{
317  rtems_i8259_masks in_progress_save = 0;
318
319  if (vector < BSP_IRQ_VECTOR_NUMBER) {
320    /*
321     * Hardware?
322     */
323    if (vector <= BSP_IRQ_MAX_ON_i8259A) {
324      /*
325       * See if this is a spurious interrupt.
326       */
327      if ((vector == 7 || vector == 15)) {
328        /*
329         * Only check it there no handler for 7 or 15.
330         */
331        if (bsp_interrupt_handler_is_empty(vector)) {
332          /*
333           * Read the ISR register to see if IRQ 7/15 is really pending.
334           */
335          uint8_t isr = BSP_i8259a_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT);
336          if ((isr & (1 << 7)) == 0) {
337            ++spurious_count;
338            return;
339          }
340        }
341      }
342
343      /*
344       * Save the current cached value for the IMR. It will have the bit for this
345       * vector clear.
346       */
347      if (vector <= BSP_IRQ_MAX_ON_i8259A) {
348        in_progress_save = i8259a_in_progress;
349        i8259a_in_progress |= irq_mask_or_tbl[vector];
350        BSP_i8259a_irq_update_master_imr();
351        BSP_i8259a_irq_update_slave_imr();
352      }
353
354      /*
355       * Do not use auto-EOI as some slave PIC do not work correctly.
356       */
357      BSP_irq_ack_at_i8259a(vector);
358    }
359
360    /*
361     * Count the interrupt.
362     */
363    irq_count[vector]++;
364
365    RTEMS_COMPILER_MEMORY_BARRIER();
366    /*
367     * Allow nesting.
368     */
369    __asm__ __volatile__("sti");
370
371    bsp_interrupt_handler_dispatch(vector);
372
373    /*
374     * Disallow nesting.
375     */
376    __asm__ __volatile__("cli");
377
378    RTEMS_COMPILER_MEMORY_BARRIER();
379
380    if (vector <= BSP_IRQ_MAX_ON_i8259A) {
381      /*
382       * Put the mask back but keep this vector masked if the trigger type is
383       * level. The driver or a thread level interrupt server needs to enable it
384       * again.
385       */
386      if (vector <= BSP_IRQ_MAX_ON_i8259A) {
387        i8259a_in_progress = in_progress_save;
388        BSP_i8259a_irq_update_master_imr();
389        BSP_i8259a_irq_update_slave_imr();
390      }
391    }
392  }
393}
Note: See TracBrowser for help on using the repository browser.