summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Hellstrom <daniel@gaisler.com>2017-05-05 15:42:45 +0200
committerDaniel Hellstrom <daniel@gaisler.com>2017-05-14 12:31:59 +0200
commit8670c46470bca00738c348ae98ccd616ce2cf1c2 (patch)
treea7e3e0b8f2146d159005e3cdcae1884905a3d049
parentleon: allow SMP boot from any CPU (diff)
downloadrtems-8670c46470bca00738c348ae98ccd616ce2cf1c2.tar.bz2
leon, genirq: SMP support for PCI peripherals
The common interrupt layer for GRLIB PCI perihperals is prepared for SMP support by this patch. The existing locking (interrupt disabling) is replaced by a new requirement on the user to implement locking before calling the genirq API. This approach avoids taking more locks than necessary. The split up of the locks also introduces that the user must allocate memory to describe ISR handlers, to avoid calling malloc()/free() while possessing a spin-lock and interrupts are globally disabled.
-rw-r--r--c/src/lib/libbsp/sparc/shared/include/genirq.h47
-rw-r--r--c/src/lib/libbsp/sparc/shared/irq/genirq.c58
2 files changed, 63 insertions, 42 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/include/genirq.h b/c/src/lib/libbsp/sparc/shared/include/genirq.h
index d03b812437..673be173b1 100644
--- a/c/src/lib/libbsp/sparc/shared/include/genirq.h
+++ b/c/src/lib/libbsp/sparc/shared/include/genirq.h
@@ -35,7 +35,7 @@ struct genirq_stats {
extern genirq_t genirq_init(int number_of_irqs);
/* Free the dynamically allocated memory that the genirq interface has
- * allocated.
+ * allocated. Also the handlers will be freed.
*
* Returns zero on success, otherwise failure.
*/
@@ -47,33 +47,54 @@ extern void genirq_destroy(genirq_t d);
*/
extern int genirq_check(genirq_t d, int irq);
-/* Register shared interrupt handler.
+/* Allocate one ISR handler and initialize it. Input to genirq_register().
*
- * \param irq The interrupt number to register ISR on
* \param isr The interrupt service routine called upon IRQ
* \param arg The argument given to isr() when called.
*
+ * Returns a pointer on success, on failure NULL is returned.
+ */
+extern void *genirq_alloc_handler(genirq_handler isr, void *arg);
+
+/* Free handler memory */
+#define genirq_free_handler(handler) free(handler)
+
+/* Register shared interrupt handler previously initialized with
+ * genirq_alloc_handler().
+ *
+ * NOTE: internal list structures are accessed and needs to be protected by
+ * spin-locks/IRQ disable by the user to guarantee a correct behaviour.
+ *
+ * \param irq The interrupt number to register ISR on
+ * \param handler Install the pre- allocated and initialized handler.
+ *
* Return Values
* -1 = Failed
* 0 = Handler registered Successfully, first handler on this IRQ
* 1 = Handler registered Successfully, _not_ first handler on this IRQ
*/
-extern int genirq_register(genirq_t d, int irq, genirq_handler isr, void *arg);
+extern int genirq_register(genirq_t d, int irq, void *handler);
-/* Unregister an previous registered interrupt handler
+/* Unregister an previous registered interrupt handler. It is the user's
+ * responsibility to free the handler returned by genirq_unregister().
+ *
+ * NOTE: internal list structures are accessed and needs to be protected by
+ * spin-locks/IRQ disable by the user to guarantee a correct behaviour.
*
* Return Values
- * -1 = ISR not registered before
- * 0 = ISR unregistered
- * 1 = Unable to unregister enabled ISR
+ * NULL = ISR not registered before or unable to unregister enabled ISR
+ * Pointer = ISR sucessfully unregistered. Returned is the handler pointer
+ * previously allocated with genirq_alloc_handler().
*/
-extern int genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg);
+extern void *genirq_unregister(genirq_t d, int irq,
+ genirq_handler isr, void *arg);
/* Enables IRQ only for this isr[arg] combination. Records if this
* is the first interrupt enable, only then must interrupts be enabled
* on the interrupt controller.
*
- * IRQs must be disabled before entering this function.
+ * NOTE: internal list structures are accessed and needs to be protected by
+ * spin-locks/IRQ disable by the user to guarantee a correct behaviour.
*
* Return values
* -1 = Failure, for example isr[arg] not registered on this irq
@@ -86,7 +107,8 @@ extern int genirq_enable(genirq_t d, int irq, genirq_handler isr, void *arg);
* is the only interrupt handler that is enabled on this IRQ, only then
* must interrupts be disabled on the interrupt controller.
*
- * IRQs must be disabled before entering this function.
+ * NOTE: internal list structures are accessed and needs to be protected by
+ * spin-locks/IRQ disable by the user to guarantee a correct behaviour.
*
* Return values
* -1 = Failure, for example isr[arg] not registered on this irq
@@ -97,6 +119,9 @@ extern int genirq_disable(genirq_t d, int irq, genirq_handler isr, void *arg);
/* Must be called by user when an IRQ has fired, the argument 'irq'
* is the IRQ number of the IRQ which was fired.
+ *
+ * NOTE: internal list structures are accessed and needs to be protected by
+ * spin-locks/IRQ disable by the user to guarantee a correct behaviour.
*/
extern void genirq_doirq(genirq_t d, int irq);
diff --git a/c/src/lib/libbsp/sparc/shared/irq/genirq.c b/c/src/lib/libbsp/sparc/shared/irq/genirq.c
index ef6272ce41..bba642e701 100644
--- a/c/src/lib/libbsp/sparc/shared/irq/genirq.c
+++ b/c/src/lib/libbsp/sparc/shared/irq/genirq.c
@@ -1,6 +1,6 @@
/*
* Generic interrupt helpers mainly for GRLIB PCI peripherals
- *
+ *
* COPYRIGHT (c) 2008.
* Cobham Gaisler AB.
*
@@ -47,6 +47,7 @@ genirq_t genirq_init(int number_of_irqs)
return NULL;
memset(priv, 0, size);
priv->genirq_max = number_of_irqs - 1;
+
return priv;
}
@@ -64,7 +65,7 @@ void genirq_destroy(genirq_t d)
while ( isrentry ) {
tmp = isrentry;
isrentry = isrentry->next;
- free(tmp);
+ genirq_free_handler(tmp);
}
}
@@ -81,56 +82,53 @@ int genirq_check(genirq_t d, int irq)
return 0;
}
-int genirq_register(genirq_t d, int irq, genirq_handler isr, void *arg)
+void *genirq_alloc_handler(genirq_handler isr, void *arg)
+{
+ struct genirq_handler_entry *newentry;
+
+ newentry = malloc(sizeof(struct genirq_handler_entry));
+ 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;
- rtems_interrupt_level level;
-
- if ( genirq_check(d, irq) )
- return -1;
+ struct genirq_handler_entry *isrentry, *newentry = handler;
- newentry = malloc(sizeof(*newentry));
- if ( !newentry )
+ if ( genirq_check(d, irq) )
return -1;
- /* Initialize ISR entry */
- newentry->isr = isr;
- newentry->arg = arg;
- newentry->enabled = 0;
-
- rtems_interrupt_disable(level);
-
/* Insert new ISR entry first into table */
irqentry = &priv->genirq_table[irq];
isrentry = irqentry->head;
irqentry->head = newentry;
newentry->next = isrentry;
- rtems_interrupt_enable(level);
-
if ( isrentry )
return 1; /* This is the first handler on this IRQ */
return 0;
}
-int genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg)
+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;
- rtems_interrupt_level level;
- int ret;
+ void *ret;
if ( genirq_check(d, irq) )
- return -1;
+ return NULL;
/* Remove isr[arg] from ISR list */
irqentry = &priv->genirq_table[irq];
- ret = -1;
-
- rtems_interrupt_disable(level);
+ ret = NULL;
prev = &irqentry->head;
isrentry = irqentry->head;
@@ -139,19 +137,17 @@ int genirq_unregister(genirq_t d, int irq, genirq_handler isr, void *arg)
/* Found ISR, remove it from list */
if ( isrentry->enabled ) {
/* Can not remove enabled ISRs, disable first */
- ret = 1;
+ ret = NULL;
break;
}
*prev = isrentry->next;
- ret = 0;
+ ret = isrentry;
break;
}
prev = &isrentry->next;
isrentry = isrentry->next;
}
- rtems_interrupt_enable(level);
-
return ret;
}
@@ -226,7 +222,7 @@ void genirq_doirq(genirq_t d, int irq)
irqentry = &priv->genirq_table[irq];
irqentry->stats.irq_cnt++;
-
+
enabled = 0;
isrentry = irqentry->head;