source: rtems/bsps/shared/irq/irq-generic.c @ 9edb72e

5
Last change on this file since 9edb72e was 9b7c456, checked in by Sebastian Huber <sebastian.huber@…>, on 04/05/18 at 04:40:02

bsps: Move generic IRQ support to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 15.9 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup bsp_interrupt
5 *
6 * @brief Generic BSP interrupt support implementation.
7 */
8
9/*
10 * Based on concepts of Pavel Pisa, Till Straumann and Eric Valette.
11 *
12 * Copyright (c) 2008, 2017 embedded brains GmbH.
13 *
14 *  embedded brains GmbH
15 *  Dornierstr. 4
16 *  82178 Puchheim
17 *  Germany
18 *  <rtems@embedded-brains.de>
19 *
20 * The license and distribution terms for this file may be
21 * found in the file LICENSE in this distribution or at
22 * http://www.rtems.org/license/LICENSE.
23 */
24
25#include <bsp/irq-generic.h>
26#include <bsp/fatal.h>
27
28#include <stdlib.h>
29
30#include <rtems/score/apimutex.h>
31#include <rtems/score/processormask.h>
32#include <rtems/score/sysstate.h>
33
34#ifdef BSP_INTERRUPT_USE_INDEX_TABLE
35  bsp_interrupt_handler_index_type bsp_interrupt_handler_index_table
36    [BSP_INTERRUPT_VECTOR_NUMBER];
37#endif
38
39bsp_interrupt_handler_entry bsp_interrupt_handler_table
40  [BSP_INTERRUPT_HANDLER_TABLE_SIZE];
41
42/* The last entry indicates if everything is initialized */
43static uint8_t bsp_interrupt_handler_unique_table
44  [(BSP_INTERRUPT_HANDLER_TABLE_SIZE + 7 + 1) / 8];
45
46static void bsp_interrupt_handler_empty(void *arg)
47{
48  rtems_vector_number vector = (rtems_vector_number) (uintptr_t) arg;
49
50  bsp_interrupt_handler_default(vector);
51}
52
53#ifdef RTEMS_SMP
54  static void bsp_interrupt_handler_do_nothing(void *arg)
55  {
56    (void) arg;
57  }
58#endif
59
60static inline bool bsp_interrupt_is_handler_unique(rtems_vector_number index)
61{
62  rtems_vector_number i = index / 8;
63  rtems_vector_number s = index % 8;
64  return (bsp_interrupt_handler_unique_table [i] >> s) & 0x1;
65}
66
67static inline void bsp_interrupt_set_handler_unique(
68  rtems_vector_number index,
69  bool unique
70)
71{
72  rtems_vector_number i = index / 8;
73  rtems_vector_number s = index % 8;
74  if (unique) {
75    bsp_interrupt_handler_unique_table [i] |= (uint8_t) (0x1U << s);
76  } else {
77    bsp_interrupt_handler_unique_table [i] &= (uint8_t) ~(0x1U << s);
78  }
79}
80
81static inline bool bsp_interrupt_is_initialized(void)
82{
83  return bsp_interrupt_is_handler_unique(BSP_INTERRUPT_HANDLER_TABLE_SIZE);
84}
85
86static inline void bsp_interrupt_set_initialized(void)
87{
88  bsp_interrupt_set_handler_unique(BSP_INTERRUPT_HANDLER_TABLE_SIZE, true);
89}
90
91static inline bool bsp_interrupt_is_empty_handler_entry(
92  const bsp_interrupt_handler_entry *e
93)
94{
95  return e->handler == bsp_interrupt_handler_empty;
96}
97
98static inline void bsp_interrupt_clear_handler_entry(
99  bsp_interrupt_handler_entry *e,
100  rtems_vector_number vector
101)
102{
103  e->handler = bsp_interrupt_handler_empty;
104  bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
105  e->arg = (void *) (uintptr_t) vector;
106  e->info = NULL;
107  e->next = NULL;
108}
109
110static inline bool bsp_interrupt_allocate_handler_index(
111  rtems_vector_number vector,
112  rtems_vector_number *index
113)
114{
115  #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
116    rtems_vector_number i = 0;
117
118    /* The first entry will remain empty */
119    for (i = 1; i < BSP_INTERRUPT_HANDLER_TABLE_SIZE; ++i) {
120      const bsp_interrupt_handler_entry *e = &bsp_interrupt_handler_table [i];
121      if (bsp_interrupt_is_empty_handler_entry(e)) {
122        *index = i;
123        return true;
124      }
125    }
126
127    return false;
128  #else
129    *index = bsp_interrupt_handler_index(vector);
130    return true;
131  #endif
132}
133
134static bsp_interrupt_handler_entry *bsp_interrupt_allocate_handler_entry(void)
135{
136  #ifdef BSP_INTERRUPT_NO_HEAP_USAGE
137    rtems_vector_number index = 0;
138    if (bsp_interrupt_allocate_handler_index(0, &index)) {
139      return &bsp_interrupt_handler_table [index];
140    } else {
141      return NULL;
142    }
143  #else
144    return malloc(sizeof(bsp_interrupt_handler_entry));
145  #endif
146}
147
148static void bsp_interrupt_free_handler_entry(bsp_interrupt_handler_entry *e)
149{
150  #ifdef BSP_INTERRUPT_NO_HEAP_USAGE
151    bsp_interrupt_clear_handler_entry(e, 0);
152  #else
153    free(e);
154  #endif
155}
156
157void bsp_interrupt_lock(void)
158{
159  if (_System_state_Is_up(_System_state_Get())) {
160    _RTEMS_Lock_allocator();
161  }
162}
163
164void bsp_interrupt_unlock(void)
165{
166  if (_System_state_Is_up(_System_state_Get())) {
167    _RTEMS_Unlock_allocator();
168  }
169}
170
171void bsp_interrupt_initialize(void)
172{
173  rtems_status_code sc = RTEMS_SUCCESSFUL;
174  size_t i = 0;
175
176  /* Initialize handler table */
177  for (i = 0; i < BSP_INTERRUPT_HANDLER_TABLE_SIZE; ++i) {
178    bsp_interrupt_handler_table [i].handler = bsp_interrupt_handler_empty;
179    bsp_interrupt_handler_table [i].arg = (void *) i;
180  }
181
182  sc = bsp_interrupt_facility_initialize();
183  if (sc != RTEMS_SUCCESSFUL) {
184    bsp_fatal(BSP_FATAL_INTERRUPT_INITIALIZATION);
185  }
186
187  bsp_interrupt_set_initialized();
188}
189
190/**
191 * @brief Installs an interrupt handler.
192 *
193 * @ingroup bsp_interrupt
194 *
195 * @return In addition to the standard status codes this function returns:
196 * - If the BSP interrupt support is not initialized RTEMS_INTERNAL_ERROR will
197 * be returned.
198 * - If not enough memory for a new handler is available RTEMS_NO_MEMORY will
199 * be returned
200 *
201 * @see rtems_interrupt_handler_install()
202 */
203static rtems_status_code bsp_interrupt_handler_install(
204  rtems_vector_number vector,
205  const char *info,
206  rtems_option options,
207  rtems_interrupt_handler handler,
208  void *arg
209)
210{
211  rtems_interrupt_level level;
212  rtems_vector_number index = 0;
213  bsp_interrupt_handler_entry *head = NULL;
214  bool enable_vector = false;
215  bool replace = RTEMS_INTERRUPT_IS_REPLACE(options);
216
217  /* Check parameters and system state */
218  if (!bsp_interrupt_is_initialized()) {
219    return RTEMS_INTERNAL_ERROR;
220  } else if (!bsp_interrupt_is_valid_vector(vector)) {
221    return RTEMS_INVALID_ID;
222  } else if (handler == NULL) {
223    return RTEMS_INVALID_ADDRESS;
224  } else if (rtems_interrupt_is_in_progress()) {
225    return RTEMS_CALLED_FROM_ISR;
226  }
227
228  /* Lock */
229  bsp_interrupt_lock();
230
231  /* Get handler table index */
232  index = bsp_interrupt_handler_index(vector);
233
234  /* Get head entry of the handler list for current vector */
235  head = &bsp_interrupt_handler_table [index];
236
237  if (bsp_interrupt_is_empty_handler_entry(head)) {
238    if (replace) {
239      /* No handler to replace exists */
240      bsp_interrupt_unlock();
241      return RTEMS_UNSATISFIED;
242    }
243
244    /*
245     * No real handler installed yet.  So allocate a new index in
246     * the handler table and fill the entry with life.
247     */
248    if (bsp_interrupt_allocate_handler_index(vector, &index)) {
249      bsp_interrupt_disable(level);
250      bsp_interrupt_handler_table [index].arg = arg;
251      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
252      bsp_interrupt_handler_table [index].handler = handler;
253      #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
254        bsp_interrupt_handler_index_table [vector] = index;
255      #endif
256      bsp_interrupt_enable(level);
257      bsp_interrupt_handler_table [index].info = info;
258    } else {
259      /* Handler table is full */
260      bsp_interrupt_unlock();
261      return RTEMS_NO_MEMORY;
262    }
263
264    /* This is the first handler so enable the vector later */
265    enable_vector = true;
266  } else {
267    bsp_interrupt_handler_entry *current = head;
268    bsp_interrupt_handler_entry *tail = NULL;
269    bsp_interrupt_handler_entry *match = NULL;
270
271    /* Ensure that a unique handler remains unique */
272    if (
273      !replace
274        && (RTEMS_INTERRUPT_IS_UNIQUE(options)
275          || bsp_interrupt_is_handler_unique(index))
276    ) {
277      /*
278       * Tried to install a unique handler on a not empty
279       * list or there is already a unique handler installed.
280       */
281      bsp_interrupt_unlock();
282      return RTEMS_RESOURCE_IN_USE;
283    }
284
285    /*
286     * Search for the list tail and check if the handler is already
287     * installed.
288     */
289    do {
290      if (
291        match == NULL
292          && (current->handler == handler || replace)
293          && current->arg == arg
294      ) {
295        match = current;
296      }
297      tail = current;
298      current = current->next;
299    } while (current != NULL);
300
301    if (replace) {
302      /* Ensure that a handler to replace exists */
303      if (match == NULL) {
304        bsp_interrupt_unlock();
305        return RTEMS_UNSATISFIED;
306      }
307
308      /* Use existing entry */
309      current = match;
310    } else {
311      /* Ensure the handler is not already installed */
312      if (match != NULL) {
313        /* The handler is already installed */
314        bsp_interrupt_unlock();
315        return RTEMS_TOO_MANY;
316      }
317
318      /* Allocate a new entry */
319      current = bsp_interrupt_allocate_handler_entry();
320      if (current == NULL) {
321        /* Not enough memory */
322        bsp_interrupt_unlock();
323        return RTEMS_NO_MEMORY;
324      }
325    }
326
327    /* Update existing entry or set new entry */
328    current->handler = handler;
329    current->info = info;
330
331    if (!replace) {
332      /* Set new entry */
333      current->arg = arg;
334      current->next = NULL;
335
336      /* Link to list tail */
337      bsp_interrupt_disable(level);
338      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
339      tail->next = current;
340      bsp_interrupt_enable(level);
341    }
342  }
343
344  /* Make the handler unique if necessary */
345  bsp_interrupt_set_handler_unique(index, RTEMS_INTERRUPT_IS_UNIQUE(options));
346
347  /* Enable the vector if necessary */
348  if (enable_vector) {
349    bsp_interrupt_vector_enable(vector);
350  }
351
352  /* Unlock */
353  bsp_interrupt_unlock();
354
355  return RTEMS_SUCCESSFUL;
356}
357
358/**
359 * @brief Removes an interrupt handler.
360 *
361 * @ingroup bsp_interrupt
362 *
363 * @return In addition to the standard status codes this function returns
364 * RTEMS_INTERNAL_ERROR if the BSP interrupt support is not initialized.
365 *
366 * @see rtems_interrupt_handler_remove().
367 */
368static rtems_status_code bsp_interrupt_handler_remove(
369  rtems_vector_number vector,
370  rtems_interrupt_handler handler,
371  void *arg
372)
373{
374  rtems_interrupt_level level;
375  rtems_vector_number index = 0;
376  bsp_interrupt_handler_entry *head = NULL;
377  bsp_interrupt_handler_entry *current = NULL;
378  bsp_interrupt_handler_entry *previous = NULL;
379  bsp_interrupt_handler_entry *match = NULL;
380
381  /* Check parameters and system state */
382  if (!bsp_interrupt_is_initialized()) {
383    return RTEMS_INTERNAL_ERROR;
384  } else if (!bsp_interrupt_is_valid_vector(vector)) {
385    return RTEMS_INVALID_ID;
386  } else if (handler == NULL) {
387    return RTEMS_INVALID_ADDRESS;
388  } else if (rtems_interrupt_is_in_progress()) {
389    return RTEMS_CALLED_FROM_ISR;
390  }
391
392  /* Lock */
393  bsp_interrupt_lock();
394
395  /* Get handler table index */
396  index = bsp_interrupt_handler_index(vector);
397
398  /* Get head entry of the handler list for current vector */
399  head = &bsp_interrupt_handler_table [index];
400
401  /* Search for a matching entry */
402  current = head;
403  do {
404    if (current->handler == handler && current->arg == arg) {
405      match = current;
406      break;
407    }
408    previous = current;
409    current = current->next;
410  } while (current != NULL);
411
412  /* Remove the matching entry */
413  if (match != NULL) {
414    if (match->next != NULL) {
415      /*
416       * The match has a successor.  A successor is always
417       * allocated.  So replace the match with its successor
418       * and free the successor entry.
419       */
420      current = match->next;
421
422      bsp_interrupt_disable(level);
423      #ifdef RTEMS_SMP
424        match->handler = bsp_interrupt_handler_do_nothing;
425        bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
426      #endif
427      match->arg = current->arg;
428      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
429      match->handler = current->handler;
430      match->info = current->info;
431      match->next = current->next;
432      bsp_interrupt_enable(level);
433
434      bsp_interrupt_free_handler_entry(current);
435    } else if (match == head) {
436      /*
437       * The match is the list head and has no successor.
438       * The list head is stored in a static table so clear
439       * this entry.  Since now the list is empty disable the
440       * vector.
441       */
442
443      /* Disable the vector */
444      bsp_interrupt_vector_disable(vector);
445
446      /* Clear entry */
447      bsp_interrupt_disable(level);
448      bsp_interrupt_clear_handler_entry(head, vector);
449      #ifdef BSP_INTERRUPT_USE_INDEX_TABLE
450        bsp_interrupt_handler_index_table [vector] = 0;
451      #endif
452      bsp_interrupt_enable(level);
453
454      /* Allow shared handlers */
455      bsp_interrupt_set_handler_unique(index, false);
456    } else {
457      /*
458       * The match is the list tail and has a predecessor.
459       * So terminate the predecessor and free the match.
460       */
461      bsp_interrupt_disable(level);
462      previous->next = NULL;
463      bsp_interrupt_fence(ATOMIC_ORDER_RELEASE);
464      bsp_interrupt_enable(level);
465
466      bsp_interrupt_free_handler_entry(match);
467    }
468  } else {
469    /* No matching entry found */
470    bsp_interrupt_unlock();
471    return RTEMS_UNSATISFIED;
472  }
473
474  /* Unlock */
475  bsp_interrupt_unlock();
476
477  return RTEMS_SUCCESSFUL;
478}
479
480/**
481 * @brief Iterates over all installed interrupt handler of a vector.
482 *
483 * @ingroup bsp_interrupt
484 *
485 * @return In addition to the standard status codes this function returns
486 * RTEMS_INTERNAL_ERROR if the BSP interrupt support is not initialized.
487 *
488 * @see rtems_interrupt_handler_iterate().
489 */
490static rtems_status_code bsp_interrupt_handler_iterate(
491  rtems_vector_number vector,
492  rtems_interrupt_per_handler_routine routine,
493  void *arg
494)
495{
496  bsp_interrupt_handler_entry *current = NULL;
497  rtems_option options = 0;
498  rtems_vector_number index = 0;
499
500  /* Check parameters and system state */
501  if (!bsp_interrupt_is_initialized()) {
502    return RTEMS_INTERNAL_ERROR;
503  } else if (!bsp_interrupt_is_valid_vector(vector)) {
504    return RTEMS_INVALID_ID;
505  } else if (rtems_interrupt_is_in_progress()) {
506    return RTEMS_CALLED_FROM_ISR;
507  }
508
509  /* Lock */
510  bsp_interrupt_lock();
511
512  /* Interate */
513  index = bsp_interrupt_handler_index(vector);
514  current = &bsp_interrupt_handler_table [index];
515  if (!bsp_interrupt_is_empty_handler_entry(current)) {
516    do {
517      options = bsp_interrupt_is_handler_unique(index) ?
518        RTEMS_INTERRUPT_UNIQUE : RTEMS_INTERRUPT_SHARED;
519      routine(arg, current->info, options, current->handler, current->arg);
520      current = current->next;
521    } while (current != NULL);
522  }
523
524  /* Unlock */
525  bsp_interrupt_unlock();
526
527  return RTEMS_SUCCESSFUL;
528}
529
530rtems_status_code rtems_interrupt_handler_install(
531  rtems_vector_number vector,
532  const char *info,
533  rtems_option options,
534  rtems_interrupt_handler handler,
535  void *arg
536)
537{
538  return bsp_interrupt_handler_install(vector, info, options, handler, arg);
539}
540
541rtems_status_code rtems_interrupt_handler_remove(
542  rtems_vector_number vector,
543  rtems_interrupt_handler handler,
544  void *arg
545)
546{
547  return bsp_interrupt_handler_remove(vector, handler, arg);
548}
549
550rtems_status_code rtems_interrupt_handler_iterate(
551  rtems_vector_number vector,
552  rtems_interrupt_per_handler_routine routine,
553  void *arg
554)
555{
556  return bsp_interrupt_handler_iterate(vector, routine, arg);
557}
558
559bool bsp_interrupt_handler_is_empty(rtems_vector_number vector)
560{
561  rtems_vector_number index = 0;
562  bsp_interrupt_handler_entry *head = NULL;
563  bool empty;
564
565  /* For use in interrupts so no lock. */
566
567  /* Get handler table index */
568  index = bsp_interrupt_handler_index(vector);
569
570  /* Get head entry of the handler list for the vector */
571  head = &bsp_interrupt_handler_table [index];
572
573  empty = bsp_interrupt_is_empty_handler_entry(head);
574
575  return empty;
576}
577
578rtems_status_code rtems_interrupt_set_affinity(
579  rtems_vector_number  vector,
580  size_t               affinity_size,
581  const cpu_set_t     *affinity
582)
583{
584  Processor_mask             set;
585  Processor_mask_Copy_status status;
586
587  if (!bsp_interrupt_is_valid_vector(vector)) {
588    return RTEMS_INVALID_ID;
589  }
590
591  status = _Processor_mask_From_cpu_set_t(&set, affinity_size, affinity);
592  if (status != PROCESSOR_MASK_COPY_LOSSLESS) {
593    return RTEMS_INVALID_SIZE;
594  }
595
596#if defined(RTEMS_SMP)
597  bsp_interrupt_set_affinity(vector, &set);
598#endif
599  return RTEMS_SUCCESSFUL;
600}
601
602rtems_status_code rtems_interrupt_get_affinity(
603  rtems_vector_number  vector,
604  size_t               affinity_size,
605  cpu_set_t           *affinity
606)
607{
608  Processor_mask             set;
609  Processor_mask_Copy_status status;
610
611  if (!bsp_interrupt_is_valid_vector(vector)) {
612    return RTEMS_INVALID_ID;
613  }
614
615#if defined(RTEMS_SMP)
616  bsp_interrupt_get_affinity(vector, &set);
617#else
618  _Processor_mask_From_index(&set, 0);
619#endif
620
621  status = _Processor_mask_To_cpu_set_t(&set, affinity_size, affinity);
622  if (status != PROCESSOR_MASK_COPY_LOSSLESS) {
623    return RTEMS_INVALID_SIZE;
624  }
625
626  return RTEMS_SUCCESSFUL;
627}
Note: See TracBrowser for help on using the repository browser.