source: rtems/c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/irq.c @ 010bf86

5
Last change on this file since 010bf86 was 010bf86, checked in by Chris Johns <chrisj@…>, on Jan 4, 2018 at 7:54:29 AM

bsps/powerpc: Use public include path

Update #3254.

  • Property mode set to 100644
File size: 9.8 KB
Line 
1/*
2 *
3 *  This file contains the PIC-independent implementation of the functions described in irq.h
4 *
5 *  Copyright (C) 1998, 1999 valette@crf.canon.fr
6 *
7 *  The license and distribution terms for this file may be
8 *  found in the file LICENSE in this distribution or at
9 *  http://www.rtems.org/license/LICENSE.
10 */
11
12#include <stdlib.h>
13
14#include <rtems.h>
15#include <stdlib.h>
16#include <rtems/bspIo.h> /* for printk */
17#include <libcpu/spr.h>
18#include <bsp/irq_supp.h>
19#include <bsp/vectors.h>
20
21/*
22 * default handler connected on each irq after bsp initialization
23 */
24static rtems_irq_connect_data   default_rtems_entry;
25
26/*
27 * location used to store initial tables used for interrupt
28 * management.
29 */
30static rtems_irq_global_settings*       internal_config;
31static rtems_irq_connect_data*          rtems_hdl_tbl;
32
33
34SPR_RW(BOOKE_TSR)
35SPR_RW(PPC405_TSR)
36
37/* legacy mode for bookE DEC exception;
38 * to avoid the double layer of function calls
39 * (dec_handler_bookE -> C_dispatch_irq_handler -> user handler)
40 * it is preferrable for the user to hook the DEC
41 * exception directly.
42 * However, the legacy mode works with less modifications
43 * of user code.
44 */
45static int C_dispatch_dec_handler_bookE (BSP_Exception_frame *frame, unsigned int excNum)
46{
47        /* clear interrupt; we must do this
48         * before C_dispatch_irq_handler()
49         * re-enables MSR_EE.
50         * Note that PPC405 uses a different SPR# for TSR
51         */
52        if ( ppc_cpu_is_bookE()==PPC_BOOKE_405)
53                _write_PPC405_TSR( BOOKE_TSR_DIS );
54        else
55                _write_BOOKE_TSR( BOOKE_TSR_DIS );
56        return C_dispatch_irq_handler(frame, ASM_DEC_VECTOR);
57}
58
59/*
60 * ------------------------ RTEMS Irq helper functions ----------------
61 */
62
63/*
64 * This function check that the value given for the irq line
65 * is valid.
66 */
67
68static int isValidInterrupt(int irq)
69{
70  if ( (irq < internal_config->irqBase) || (irq >= internal_config->irqBase + internal_config->irqNb))
71    return 0;
72  return 1;
73}
74
75/*
76 * ------------------------ RTEMS Shared Irq Handler Mngt Routines ----------------
77 */
78int BSP_install_rtems_shared_irq_handler  (const rtems_irq_connect_data* irq)
79{
80    rtems_interrupt_level   level;
81    rtems_irq_connect_data* vchain;
82
83    if (!isValidInterrupt(irq->name)) {
84      printk("Invalid interrupt vector %d\n",irq->name);
85      return 0;
86    }
87
88        /* pre-allocate memory outside of critical section */
89    vchain = (rtems_irq_connect_data*)malloc(sizeof(rtems_irq_connect_data));
90
91    rtems_interrupt_disable(level);
92
93    if ( (intptr_t)rtems_hdl_tbl[irq->name].next_handler  == -1 ) {
94      rtems_interrupt_enable(level);
95      printk("IRQ vector %d already connected to an unshared handler\n",irq->name);
96          free(vchain);
97      return 0;
98    }
99
100    /* save off topmost handler */
101    vchain[0]= rtems_hdl_tbl[irq->name];
102
103    /*
104     * store the data provided by user
105     */
106    rtems_hdl_tbl[irq->name] = *irq;
107
108    /* link chain to new topmost handler */
109    rtems_hdl_tbl[irq->name].next_handler = (void *)vchain;
110
111        /*
112         * enable_irq_at_pic is supposed to ignore
113         * requests to disable interrupts outside
114         * of the range handled by the PIC
115         */
116        BSP_enable_irq_at_pic(irq->name);
117
118    /*
119     * Enable interrupt on device
120     */
121        if (irq->on)
122        irq->on(irq);
123
124    rtems_interrupt_enable(level);
125
126    return 1;
127}
128
129/*
130 * ------------------------ RTEMS Single Irq Handler Mngt Routines ----------------
131 */
132
133int BSP_install_rtems_irq_handler  (const rtems_irq_connect_data* irq)
134{
135    rtems_interrupt_level  level;
136
137    if (!isValidInterrupt(irq->name)) {
138      printk("Invalid interrupt vector %d\n",irq->name);
139      return 0;
140    }
141    /*
142     * Check if default handler is actually connected. If not issue an error.
143     * You must first get the current handler via i386_get_current_idt_entry
144     * and then disconnect it using i386_delete_idt_entry.
145     * RATIONALE : to always have the same transition by forcing the user
146     * to get the previous handler before accepting to disconnect.
147     */
148    rtems_interrupt_disable(level);
149    if (rtems_hdl_tbl[irq->name].hdl != default_rtems_entry.hdl) {
150      rtems_interrupt_enable(level);
151      printk("IRQ vector %d already connected\n",irq->name);
152      return 0;
153    }
154
155    /*
156     * store the data provided by user
157     */
158    rtems_hdl_tbl[irq->name] = *irq;
159    rtems_hdl_tbl[irq->name].next_handler = (void *)-1;
160
161        /*
162         * enable_irq_at_pic is supposed to ignore
163         * requests to disable interrupts outside
164         * of the range handled by the PIC
165         */
166        BSP_enable_irq_at_pic(irq->name);
167
168    /*
169     * Enable interrupt on device
170     */
171        if (irq->on)
172        irq->on(irq);
173
174    rtems_interrupt_enable(level);
175
176    return 1;
177}
178
179int BSP_get_current_rtems_irq_handler   (rtems_irq_connect_data* irq)
180{
181    rtems_interrupt_level       level;
182
183    if (!isValidInterrupt(irq->name)) {
184      return 0;
185    }
186    rtems_interrupt_disable(level);
187      *irq = rtems_hdl_tbl[irq->name];
188    rtems_interrupt_enable(level);
189    return 1;
190}
191
192int BSP_remove_rtems_irq_handler  (const rtems_irq_connect_data* irq)
193{
194    rtems_irq_connect_data *pchain= NULL, *vchain = NULL;
195    rtems_interrupt_level   level;
196
197    if (!isValidInterrupt(irq->name)) {
198      return 0;
199    }
200    /*
201     * Check if default handler is actually connected. If not issue an error.
202     * You must first get the current handler via i386_get_current_idt_entry
203     * and then disconnect it using i386_delete_idt_entry.
204     * RATIONALE : to always have the same transition by forcing the user
205     * to get the previous handler before accepting to disconnect.
206     */
207    rtems_interrupt_disable(level);
208    if (rtems_hdl_tbl[irq->name].hdl != irq->hdl) {
209      rtems_interrupt_enable(level);
210      return 0;
211    }
212
213    if( (intptr_t)rtems_hdl_tbl[irq->name].next_handler != -1 )
214    {
215       int found = 0;
216
217       for( (pchain= NULL, vchain = &rtems_hdl_tbl[irq->name]);
218            (vchain->hdl != default_rtems_entry.hdl);
219            (pchain= vchain, vchain = (rtems_irq_connect_data*)vchain->next_handler) )
220       {
221          if( vchain->hdl == irq->hdl )
222          {
223             found= -1; break;
224          }
225       }
226
227       if( !found )
228       {
229          rtems_interrupt_enable(level);
230          return 0;
231       }
232    }
233    else
234        {
235                if (rtems_hdl_tbl[irq->name].hdl != irq->hdl)
236                {
237                        rtems_interrupt_enable(level);
238                        return 0;
239                }
240        }
241
242    /*
243     * Disable interrupt on device
244     */
245        if (irq->off)
246        irq->off(irq);
247
248    /*
249     * restore the default irq value
250     */
251    if( !vchain )
252    {
253       /* single handler vector... */
254       rtems_hdl_tbl[irq->name] = default_rtems_entry;
255    }
256    else
257    {
258       if( pchain )
259       {
260          /* non-first handler being removed */
261          pchain->next_handler = vchain->next_handler;
262       }
263       else
264       {
265          /* first handler isn't malloc'ed, so just overwrite it.  Since
266          the contents of vchain are being struct copied, vchain itself
267          goes away */
268          vchain = vchain->next_handler;
269          rtems_hdl_tbl[irq->name]= *vchain;
270       }
271    }
272
273        /* Only disable at PIC if we removed the last handler */
274        if ( rtems_hdl_tbl[irq->name].hdl == default_rtems_entry.hdl ) {
275                /*
276                 * disable_irq_at_pic is supposed to ignore
277                 * requests to disable interrupts outside
278                 * of the range handled by the PIC;
279                 */
280                BSP_disable_irq_at_pic(irq->name);
281        }
282
283    rtems_interrupt_enable(level);
284
285    free(vchain);
286
287    return 1;
288}
289
290/*
291 * Less cumbersome, alternate entry points;
292 * RETURNS: more traditional, 0 on success, nonzero on error
293 */
294
295static int doit(
296        int (*p)(const rtems_irq_connect_data*),
297        rtems_irq_number n,
298        rtems_irq_hdl hdl,
299        rtems_irq_hdl_param prm)
300{
301rtems_irq_connect_data  xx;
302        xx.name   = n;
303        xx.hdl    = hdl;
304        xx.handle = prm;
305        xx.on     = 0;
306        xx.off    = 0;
307        xx.isOn   = 0;
308        return ! p(&xx);
309}
310
311int BSP_rtems_int_connect(rtems_irq_number n, rtems_irq_hdl hdl, rtems_irq_hdl_param p)
312{
313        return doit(BSP_install_rtems_shared_irq_handler, n, hdl, p);
314}
315
316int BSP_rtems_int_disconnect(rtems_irq_number n, rtems_irq_hdl hdl, rtems_irq_hdl_param p)
317{
318        return doit(BSP_remove_rtems_irq_handler, n, hdl, p);
319}
320
321
322/*
323 * RTEMS Global Interrupt Handler Management Routines
324 */
325
326int BSP_rtems_irq_mngt_set(rtems_irq_global_settings* config)
327{
328    int                           i;
329    rtems_interrupt_level         level;
330    rtems_irq_connect_data*       vchain;
331
332    /*
333     * Store various code accelerators
334     */
335    internal_config             = config;
336    default_rtems_entry         = config->defaultEntry;
337    rtems_hdl_tbl               = config->irqHdlTbl;
338
339    rtems_interrupt_disable(level);
340
341        if ( !BSP_setup_the_pic(config) ) {
342                printk("PIC setup failed; leaving IRQs OFF\n");
343                return 0;
344        }
345
346        for ( i = config->irqBase; i < config->irqBase + config->irqNb; i++ ) {
347                for( vchain = &rtems_hdl_tbl[i];
348                     ((intptr_t)vchain != -1 && vchain->hdl != default_rtems_entry.hdl);
349                     vchain = (rtems_irq_connect_data*)vchain->next_handler )
350                {
351                        if (vchain->on)
352              vchain->on(vchain);
353                }
354                if ( vchain != &rtems_hdl_tbl[i] ) {
355                        /* at least one handler registered */
356                        BSP_enable_irq_at_pic(i);
357                } else {
358/* Do NOT disable; there might be boards with cascaded
359 * interrupt controllers where the BSP (incorrectly) does
360 * not ignore the cascaded interrupts in BSP_disable_irq_at_pic()!
361 * Instead, we rely on BSP_setup_the_pic() for a good
362 * initial configuration.
363 *
364                        BSP_disable_irq_at_pic(i);
365 */
366                }
367        }
368
369    rtems_interrupt_enable(level);
370
371        {
372                        ppc_exc_set_handler(ASM_EXT_VECTOR, C_dispatch_irq_handler);
373
374                        if ( ppc_cpu_is_bookE() ) {
375                                /* bookE decrementer interrupt needs to be cleared BEFORE
376                                 * dispatching the user ISR (because the user ISR is called
377                                 * with EE enabled)
378                                 * We do this so that existing DEC handlers can be used
379                                 * with minor modifications.
380                                 */
381                                ppc_exc_set_handler(ASM_BOOKE_DEC_VECTOR, C_dispatch_dec_handler_bookE);
382                        } else {
383                                ppc_exc_set_handler(ASM_DEC_VECTOR, C_dispatch_irq_handler);
384                        }
385        }
386    return 1;
387}
388
389int BSP_rtems_irq_mngt_get(rtems_irq_global_settings** config)
390{
391    *config = internal_config;
392    return 0;
393}
Note: See TracBrowser for help on using the repository browser.