summaryrefslogtreecommitdiffstats
path: root/bsps/shared/grlib/irq
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-12-22 18:31:04 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2019-01-22 12:46:33 +0100
commit7eb606d393306da25fd6e6aa7f8595ffb2e924fc (patch)
tree085befd6fe5e29d229fec9683735516d48e9d41e /bsps/shared/grlib/irq
parentgrlib: Move header files (diff)
downloadrtems-7eb606d393306da25fd6e6aa7f8595ffb2e924fc.tar.bz2
grlib: Move source files
Update #3678.
Diffstat (limited to 'bsps/shared/grlib/irq')
-rw-r--r--bsps/shared/grlib/irq/genirq.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/bsps/shared/grlib/irq/genirq.c b/bsps/shared/grlib/irq/genirq.c
new file mode 100644
index 0000000000..285416b0d3
--- /dev/null
+++ b/bsps/shared/grlib/irq/genirq.c
@@ -0,0 +1,244 @@
+/*
+ * Generic interrupt helpers mainly for GRLIB PCI peripherals
+ *
+ * COPYRIGHT (c) 2008.
+ * Cobham Gaisler AB.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <rtems/bspIo.h>
+#include <stdlib.h>
+#include <string.h>
+#include <grlib/genirq.h>
+
+#include <grlib/grlib_impl.h>
+
+struct genirq_handler_entry {
+ struct genirq_handler_entry *next; /* Next ISR entry for this IRQ number */
+ genirq_handler isr; /* ISR function called upon IRQ */
+ void *arg; /* custom argument to ISR */
+ int enabled; /* Inidicates if IRQ is enabled */
+};
+
+struct genirq_irq_entry {
+ struct genirq_handler_entry *head;
+ struct genirq_stats stats;
+};
+
+struct genirq_priv {
+ /* Maximum number of interrupt */
+ int genirq_max;
+ /* IRQ Table index N reflect IRQ number N */
+ struct genirq_irq_entry genirq_table[0]; /* Length depends on */
+};
+
+genirq_t genirq_init(int number_of_irqs)
+{
+ size_t size;
+ struct genirq_priv *priv;
+
+ size = sizeof(*priv) +
+ number_of_irqs * sizeof(priv->genirq_table[0]);
+
+ priv = grlib_calloc(1, size);
+ if ( !priv )
+ return NULL;
+ priv->genirq_max = number_of_irqs - 1;
+
+ return priv;
+}
+
+void genirq_destroy(genirq_t d)
+{
+ struct genirq_priv *priv = d;
+ struct genirq_irq_entry *irqentry;
+ struct genirq_handler_entry *isrentry, *tmp;
+ int i;
+
+ /* Free all registered interrupts */
+ for ( i=0; i<priv->genirq_max; i++) {
+ irqentry = &priv->genirq_table[i];
+ isrentry = irqentry->head;
+ while ( isrentry ) {
+ tmp = isrentry;
+ isrentry = isrentry->next;
+ genirq_free_handler(tmp);
+ }
+ }
+
+ free(priv);
+}
+
+int genirq_check(genirq_t d, int irq)
+{
+ struct genirq_priv *priv = d;
+
+ if ( (irq <= 0) || (irq > priv->genirq_max) )
+ return -1;
+ else
+ return 0;
+}
+
+void *genirq_alloc_handler(genirq_handler isr, void *arg)
+{
+ struct genirq_handler_entry *newentry;
+
+ newentry = grlib_malloc(sizeof(*newentry));
+ if ( newentry ) {
+ /* Initialize ISR entry */
+ newentry->isr = isr;
+ newentry->arg = arg;
+ newentry->enabled = 0;
+ }
+ return newentry;
+}
+
+int genirq_register(genirq_t d, int irq, void *handler)
+{
+ struct genirq_priv *priv = d;
+ struct genirq_irq_entry *irqentry;
+ struct genirq_handler_entry *isrentry, *newentry = handler;
+
+ if ( genirq_check(d, irq) )
+ return -1;
+
+ /* Insert new ISR entry first into table */
+ irqentry = &priv->genirq_table[irq];
+ isrentry = irqentry->head;
+ irqentry->head = newentry;
+ newentry->next = isrentry;
+
+ if ( isrentry )
+ return 1; /* This is the first handler on this IRQ */
+ return 0;
+}
+
+void *genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg)
+{
+ struct genirq_priv *priv = d;
+ struct genirq_irq_entry *irqentry;
+ struct genirq_handler_entry *isrentry, **prev;
+ void *ret;
+
+ if ( genirq_check(d, irq) )
+ return NULL;
+
+ /* Remove isr[arg] from ISR list */
+ irqentry = &priv->genirq_table[irq];
+ ret = NULL;
+
+ prev = &irqentry->head;
+ isrentry = irqentry->head;
+ while ( isrentry ) {
+ if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) {
+ /* Found ISR, remove it from list */
+ if ( isrentry->enabled ) {
+ /* Can not remove enabled ISRs, disable first */
+ ret = NULL;
+ break;
+ }
+ *prev = isrentry->next;
+ ret = isrentry;
+ break;
+ }
+ prev = &isrentry->next;
+ isrentry = isrentry->next;
+ }
+
+ return ret;
+}
+
+/* Enables or Disables ISR handler. Internal function to reduce footprint
+ * of enable/disable functions.
+ *
+ * \param action 1=enable, 0=disable ISR
+ */
+static int genirq_set_active(
+ struct genirq_priv *priv,
+ int irq,
+ genirq_handler isr,
+ void *arg,
+ int action)
+{
+ struct genirq_irq_entry *irqentry;
+ struct genirq_handler_entry *isrentry, *e = NULL;
+ int enabled;
+
+ if ( genirq_check(priv, irq) )
+ return -1;
+
+ /* Find isr[arg] in ISR list */
+ irqentry = &priv->genirq_table[irq];
+ enabled = 0;
+
+ isrentry = irqentry->head;
+ while ( isrentry ) {
+ if ( (isrentry->arg == arg) && (isrentry->isr == isr) ) {
+ /* Found ISR */
+ if ( isrentry->enabled == action ) {
+ /* The ISR is already enabled or disabled
+ * depending on request, neccessary actions
+ * were taken last time the same action was
+ * requested.
+ */
+ return 1;
+ }
+ e = isrentry;
+ } else {
+ enabled += isrentry->enabled;
+ }
+ isrentry = isrentry->next;
+ }
+
+ if ( !e )
+ return -1;
+
+ e->enabled = action;
+
+ return enabled;
+}
+
+int genirq_enable(genirq_t d, int irq, genirq_handler isr, void *arg)
+{
+ struct genirq_priv *priv = d;
+ return genirq_set_active(priv, irq, isr, arg, 1);
+}
+
+int genirq_disable(genirq_t d, int irq, genirq_handler isr, void *arg)
+{
+ struct genirq_priv *priv = d;
+ return genirq_set_active(priv, irq, isr, arg, 0);
+}
+
+void genirq_doirq(genirq_t d, int irq)
+{
+ struct genirq_priv *priv = d;
+ struct genirq_irq_entry *irqentry;
+ struct genirq_handler_entry *isrentry;
+ int enabled;
+
+ irqentry = &priv->genirq_table[irq];
+ irqentry->stats.irq_cnt++;
+
+ enabled = 0;
+
+ isrentry = irqentry->head;
+ while ( isrentry ) {
+ if ( isrentry->enabled ) {
+ enabled = 1;
+ /* Call the ISR */
+ isrentry->isr(isrentry->arg);
+ }
+ isrentry = isrentry->next;
+ }
+
+ /* Was the IRQ an IRQ without source? */
+ if ( enabled == 0 ) {
+ /* This should not happen */
+ printk("Spurious IRQ happened on IRQ %d\n", irq);
+ }
+}