summaryrefslogtreecommitdiff
path: root/ecos/packages/kernel/current/include/intr.hxx
blob: 7b0be3fa9cc36b14947695aa8f93597bd1aa0f74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
#ifndef CYGONCE_KERNEL_INTR_HXX
#define CYGONCE_KERNEL_INTR_HXX

//==========================================================================
//
//      intr.hxx
//
//      Interrupt class declaration(s)
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):   nickg
// Contributors:        nickg
// Date:        1997-09-09
// Purpose:     Define Interrupt class interfaces
// Description: The classes defined here provide the APIs for handling
//              interrupts.
// Usage:       #include "intr.hxx"
//              
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <cyg/kernel/ktypes.h>

#include <cyg/kernel/smp.hxx>

// -------------------------------------------------------------------------
// Default definitions

// Some HALs define the ISR table to be a different size to the number
// of ISR vectors. These HALs will define CYGNUM_HAL_ISR_TABLE_SIZE. All
// other HALs will have the table size equal to the number of vectors.

#ifndef CYGNUM_HAL_ISR_TABLE_SIZE
# define CYGNUM_HAL_ISR_TABLE_SIZE      CYGNUM_HAL_ISR_COUNT
#endif

// -------------------------------------------------------------------------
// Function prototype typedefs


// VSR = Vector Service Routine. This is the code attached directly to an
// interrupt vector. It is very architecture/platform specific and usually
// must be written in assembler.

typedef void  cyg_VSR();

// ISR = Interrupt Service Routine. This is called from the default
// VSR in response to an interrupt. It may access shared data but may
// not call kernel routines. The return value may be
// Cyg_Interrupt::HANDLED and/or Cyg_Interrupt::CALL_DSR.

typedef cyg_uint32 cyg_ISR(cyg_vector vector, CYG_ADDRWORD data);

// DSR = Deferred Service Routine. This is called if the ISR returns
// the Cyg_Interrupt::CALL_DSR bit. It is called at a "safe" point in
// the kernel where it may make calls on kernel routines. The count
// argument indicates how many times the ISR has asked for the DSR to
// be posted since the last time the DSR ran.

typedef void cyg_DSR(cyg_vector vector, cyg_ucount32 count, CYG_ADDRWORD data);

// -------------------------------------------------------------------------
// Include HAL definitions

class Cyg_Interrupt;

#include <cyg/hal/hal_arch.h>

#include <cyg/hal/hal_intr.h>

#ifndef HAL_INTERRUPT_STACK_CALL_PENDING_DSRS
#define HAL_INTERRUPT_STACK_CALL_PENDING_DSRS()    \
      Cyg_Interrupt::call_pending_DSRs_inner()
#endif

externC void interrupt_end(
    cyg_uint32          isr_ret,
    Cyg_Interrupt       *intr,
    HAL_SavedRegisters  *ctx
    );

externC void cyg_interrupt_post_dsr( CYG_ADDRWORD intr_obj );
externC void cyg_interrupt_call_pending_DSRs( void );

// -------------------------------------------------------------------------
// Interrupt class. This both represents each interrupt and provides a static
// interface for controlling the interrupt hardware.

class Cyg_Interrupt
{

    friend class Cyg_Scheduler;
    friend void interrupt_end( cyg_uint32,
                               Cyg_Interrupt *,
                               HAL_SavedRegisters *);
    friend void cyg_interrupt_post_dsr( CYG_ADDRWORD intr_obj );
    friend void cyg_interrupt_call_pending_DSRs( void );
    
    cyg_vector          vector;         // Interrupt vector

    cyg_priority        priority;       // Queuing priority
    
    cyg_ISR             *isr;           // Pointer to ISR

    cyg_DSR             *dsr;           // Pointer to DSR

    CYG_ADDRWORD        data;           // Data pointer


    
    // DSR handling interface called by the scheduler

                                        // Check for pending DSRs
    static cyg_bool     DSRs_pending();

                                        // Call any pending DSRs
    static void         call_pending_DSRs();
    static void         call_pending_DSRs_inner();

    // DSR handling interface called by the scheduler and HAL 
    // interrupt arbiters.

    void                post_dsr();     // Post the DSR for this interrupt


    
    // Data structures for handling DSR calls.  We implement two DSR
    // handling mechanisms, a list based one and a table based
    // one. The list based mechanism is safe with respect to temporary
    // overloads and will not run out of resource. However it requires
    // extra data per interrupt object, and interrupts must be turned
    // off briefly when delivering the DSR. The table based mechanism
    // does not need unnecessary interrupt switching, but may be prone
    // to overflow on overload. However, since a correctly programmed
    // real time application should not experience such a condition,
    // the table based mechanism is more efficient for real use. The
    // list based mechainsm is enabled by default since it is safer to
    // use during development.

#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_TABLE
    
    static Cyg_Interrupt *dsr_table[CYGNUM_KERNEL_CPU_MAX]
                                   [CYGNUM_KERNEL_INTERRUPTS_DSRS_TABLE_SIZE]
                                   CYGBLD_ANNOTATE_VARIABLE_INTR;

    static cyg_ucount32 dsr_table_head[CYGNUM_KERNEL_CPU_MAX]
                                      CYGBLD_ANNOTATE_VARIABLE_INTR;

    static volatile cyg_ucount32 dsr_table_tail[CYGNUM_KERNEL_CPU_MAX]
                                               CYGBLD_ANNOTATE_VARIABLE_INTR;

#endif
#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST

    // Number of DSR posts made
    volatile cyg_ucount32 dsr_count CYGBLD_ANNOTATE_VARIABLE_INTR; 

    // next DSR in list
    Cyg_Interrupt* volatile next_dsr CYGBLD_ANNOTATE_VARIABLE_INTR; 

    // head of static list of pending DSRs
    static Cyg_Interrupt* volatile dsr_list[CYGNUM_KERNEL_CPU_MAX]
                                           CYGBLD_ANNOTATE_VARIABLE_INTR;

#  ifdef CYGSEM_KERNEL_INTERRUPTS_DSRS_LIST_FIFO
    // tail of static list of pending DSRs
    static Cyg_Interrupt* volatile dsr_list_tail[CYGNUM_KERNEL_CPU_MAX]
                                           CYGBLD_ANNOTATE_VARIABLE_INTR;
#  endif

#endif  // defined  CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST

#ifdef CYGIMP_KERNEL_INTERRUPTS_CHAIN

    // The default mechanism for handling interrupts is to attach just
    // one Interrupt object to each vector. In some cases, and on some
    // hardware, this is not possible, and each vector must carry a chain
    // of interrupts.

    Cyg_Interrupt       *next;          // Next Interrupt in list

    // Chaining ISR inserted in HAL vector
    static cyg_uint32 chain_isr(cyg_vector vector, CYG_ADDRWORD data);    

    // Table of interrupt chains
    static Cyg_Interrupt *chain_list[CYGNUM_HAL_ISR_TABLE_SIZE];
    
#endif

    // Interrupt disable data. Interrupt disable can be nested. On
    // each CPU this is controlled by disable_counter[cpu]. When the
    // counter is first incremented from zero to one, the
    // interrupt_disable_spinlock is claimed using spin_intsave(), the
    // original interrupt enable state being saved in
    // interrupt_disable_state[cpu].  When the counter is decremented
    // back to zero the spinlock is cleared using clear_intsave().

    // The spinlock is necessary in SMP systems since a thread
    // accessing data shared with an ISR may be scheduled on a
    // different CPU to the one that handles the interrupt. So, merely
    // blocking local interrupts would be ineffective. SMP aware
    // device drivers should either use their own spinlocks to protect
    // data, or use the API supported by this class, via
    // cyg_drv_isr_lock()/_unlock(). Note that it now becomes
    // essential that ISRs do this if they are to be SMP-compatible.

    // In a single CPU system, this mechanism reduces to just
    // disabling/enabling interrupts.

    // Disable level counter. This counts the number of times
    // interrupts have been disabled.
    static volatile cyg_int32 disable_counter[CYGNUM_KERNEL_CPU_MAX]
                                              CYGBLD_ANNOTATE_VARIABLE_INTR;

    // Interrupt disable spinlock. This is claimed by any CPU that has
    // disabled interrupts via the Cyg_Interrupt API.
    static Cyg_SpinLock interrupt_disable_spinlock CYGBLD_ANNOTATE_VARIABLE_INTR;

    // Saved interrupt state. When each CPU first disables interrupts
    // the original state of the interrupts are saved here to be
    // restored later.
    static CYG_INTERRUPT_STATE interrupt_disable_state[CYGNUM_KERNEL_CPU_MAX]
                                                       CYGBLD_ANNOTATE_VARIABLE_INTR;

    
public:

    Cyg_Interrupt                       // Initialize interrupt
    (
        cyg_vector      vector,         // Vector to attach to
        cyg_priority    priority,       // Queue priority
        CYG_ADDRWORD    data,           // Data pointer
        cyg_ISR         *isr,           // Interrupt Service Routine
        cyg_DSR         *dsr            // Deferred Service Routine
        );

    ~Cyg_Interrupt();
        
    // ISR return values
    enum {
        HANDLED  = 1,                   // Interrupt was handled
        CALL_DSR = 2                    // Schedule DSR
    };

    // Interrupt management
        
    void        attach();               // Attach to vector


    void        detach();               // Detach from vector
        
    
    // Static Interrupt management functions

    // Get the current service routine
    static void get_vsr(cyg_vector vector, cyg_VSR **vsr);

    // Install a vector service routine
    static void set_vsr(
        cyg_vector vector,              // hardware vector to replace
        cyg_VSR *vsr,                   // my new service routine
        cyg_VSR **old = NULL            // pointer to old vsr, if required
        );


    // Static interrupt masking functions

    // Disable interrupts at the CPU
    static void disable_interrupts();

    // Re-enable CPU interrupts
    static void enable_interrupts();

    // Are interrupts enabled at the CPU?
    static inline cyg_bool interrupts_enabled()
    {
        return (0 == disable_counter[CYG_KERNEL_CPU_THIS()]);
    }
    
    // Get the vector for the following calls
    inline cyg_vector get_vector() 
    {
        return vector;
    }
    
    // Static PIC control functions
    
    // Mask a specific interrupt in a PIC
    static void mask_interrupt(cyg_vector vector);
    // The same but not interrupt safe
    static void mask_interrupt_intunsafe(cyg_vector vector);

    // Clear PIC mask
    static void unmask_interrupt(cyg_vector vector);
    // The same but not interrupt safe
    static void unmask_interrupt_intunsafe(cyg_vector vector);

    // Acknowledge interrupt at PIC
    static void acknowledge_interrupt(cyg_vector vector);

    // Change interrupt detection at PIC
    static void configure_interrupt(
        cyg_vector vector,              // vector to control
        cyg_bool level,                 // level or edge triggered
        cyg_bool up                     // hi/lo level, rising/falling edge
        );

#ifdef CYGPKG_KERNEL_SMP_SUPPORT

    // SMP support for associating an interrupt with a specific CPU.
    
    static void set_cpu( cyg_vector, HAL_SMP_CPU_TYPE cpu );
    static HAL_SMP_CPU_TYPE get_cpu( cyg_vector );
    
#endif    
};

#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS
// -------------------------------------------------------------------------
// Check for pending DSRs

inline cyg_bool Cyg_Interrupt::DSRs_pending()
{
    HAL_SMP_CPU_TYPE cpu = CYG_KERNEL_CPU_THIS();
#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_TABLE
    
    return dsr_table_head[cpu] != dsr_table_tail[cpu];

#endif
#ifdef CYGIMP_KERNEL_INTERRUPTS_DSRS_LIST
    
    return dsr_list[cpu] != NULL;

#endif
};
#endif // CYGIMP_KERNEL_INTERRUPTS_DSRS

// -------------------------------------------------------------------------
#endif // ifndef CYGONCE_KERNEL_INTR_HXX
// EOF intr.hxx