[Sound-open-firmware] [PATCH] core: irq: Add support for nested interrupts.

Liam Girdwood liam.r.girdwood at linux.intel.com
Tue Jan 23 17:39:55 CET 2018


From: Keyon Jie <yang.jie at linux.intel.com>

Allow interrupts to have a parent and child relationship in order to
support nested interrupts between different HW interrupt controllers.

This patch allows child handler to be registered for secondary interrupt
controllers and allow multiple child interrupt sources to share a single
interrupt pin.

Signed-off-by: Keyon Jie <yang.jie at linux.intel.com>
---
 src/include/reef/interrupt.h                       |  57 +++---
 src/lib/Makefile.am                                |   3 +-
 src/lib/interrupt.c                                | 204 +++++++++++++++++++++
 src/platform/baytrail/include/platform/interrupt.h |  17 ++
 4 files changed, 256 insertions(+), 25 deletions(-)
 create mode 100644 src/lib/interrupt.c

diff --git a/src/include/reef/interrupt.h b/src/include/reef/interrupt.h
index ea7d057..33733ff 100644
--- a/src/include/reef/interrupt.h
+++ b/src/include/reef/interrupt.h
@@ -33,40 +33,47 @@
 
 #include <stdint.h>
 #include <arch/interrupt.h>
+#include <platform/interrupt.h>
 #include <reef/trace.h>
 #include <reef/debug.h>
+#include <reef/lock.h>
 
-#define trace_irq(__e)	trace_event(TRACE_CLASS_IRQ | __e)
+#define trace_irq(__e)	trace_event(TRACE_CLASS_IRQ, __e)
+#define trace_irq_error(__e)	trace_error(TRACE_CLASS_IRQ,  __e)
 
-static inline int interrupt_register(int irq,
-	void(*handler)(void *arg), void *arg)
-{
-	return arch_interrupt_register(irq, handler, arg);
-}
+/* child interrupt source */
+struct irq_child {
+	uint32_t enabled;
 
-static inline void interrupt_unregister(int irq)
-{
-	arch_interrupt_unregister(irq);
-}
+	void (*handler)(void *arg);
+	void *handler_arg;
+};
 
-static inline uint32_t interrupt_enable(uint32_t irq)
-{
-	return arch_interrupt_enable_mask(1 << irq);
-}
+/* parent source */
+struct irq_parent {
+	int num;
+	void (*handler)(void *arg);
+	uint32_t enabled_count;
+	spinlock_t lock;
 
-static inline uint32_t interrupt_disable(uint32_t irq)
-{
-	return arch_interrupt_disable_mask(1 <<irq);
-}
+	uint32_t num_children;
+	struct irq_child *child[PLATFORM_IRQ_CHILDREN];
+};
+
+int interrupt_register(uint32_t irq,
+	void(*handler)(void *arg), void *arg);
+void interrupt_unregister(uint32_t irq);
+uint32_t interrupt_enable(uint32_t irq);
+uint32_t interrupt_disable(uint32_t irq);
 
 static inline void interrupt_set(int irq)
 {
-	arch_interrupt_set(irq);
+	arch_interrupt_set(REEF_IRQ_NUMBER(irq));
 }
 
 static inline void interrupt_clear(int irq)
 {
-	arch_interrupt_clear(irq);
+	arch_interrupt_clear(REEF_IRQ_NUMBER(irq));
 }
 
 static inline uint32_t interrupt_global_disable(void)
@@ -79,9 +86,11 @@ static inline void interrupt_global_enable(uint32_t flags)
 	arch_interrupt_global_enable(flags);
 }
 
-uint32_t platform_interrupt_get_enabled(void);
-void platform_interrupt_clear(uint32_t irq, uint32_t mask);
-void platform_interrupt_mask(uint32_t irq, uint32_t mask);
-void platform_interrupt_unmask(uint32_t irq, uint32_t mask);
+/* called by platform interrupt ops */
+int irq_register_child(struct irq_parent *parent, int irq,
+	void (*handler)(void *arg), void *arg);
+void irq_unregister_child(struct irq_parent *parent, int irq);
+uint32_t irq_enable_child(struct irq_parent *parent, int irq);
+uint32_t irq_disable_child(struct irq_parent *parent, int irq);
 
 #endif
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 0a566d0..c4da98c 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -7,7 +7,8 @@ libcore_a_SOURCES = \
 	notifier.c \
 	trace.c \
 	schedule.c \
-	agent.c
+	agent.c \
+	interrupt.c
 
 if BUILD_DMA_TRACE
 libcore_a_SOURCES += \
diff --git a/src/lib/interrupt.c b/src/lib/interrupt.c
new file mode 100644
index 0000000..4516cf3
--- /dev/null
+++ b/src/lib/interrupt.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2017, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of the Intel Corporation nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author: Keyon Jie <yang.jie at linux.intel.com>
+ *         Liam Girdwood <liam.r.girdwood at linux.intel.com>
+ *
+ */
+
+#include <reef/interrupt.h>
+#include <reef/interrupt-map.h>
+#include <reef/alloc.h>
+#include <arch/interrupt.h>
+#include <platform/interrupt.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+int irq_register_child(struct irq_parent *parent, int irq,
+	void (*handler)(void *arg), void *arg)
+{
+	int ret = 0;
+
+	if (parent == NULL)
+		return -EINVAL;
+
+	spin_lock(&parent->lock);
+
+	/* does child already exist ? */
+	if (parent->child[REEF_IRQ_BIT(irq)]) {
+		/* already registered, return */
+		goto finish;
+	}
+
+	/* init child */
+	parent->child[REEF_IRQ_BIT(irq)] =
+		rzalloc(RZONE_SYS, RFLAGS_NONE, sizeof(struct irq_child));
+	parent->child[REEF_IRQ_BIT(irq)]->enabled = 0;
+	parent->child[REEF_IRQ_BIT(irq)]->handler = handler;
+	parent->child[REEF_IRQ_BIT(irq)]->handler_arg = arg;
+
+	/* do we need to register parent ? */
+	if (parent->num_children == 0) {
+		ret = arch_interrupt_register(parent->num,
+			parent->handler, parent);
+	}
+
+	/* increment number of children */
+	parent->num_children += 1;
+
+finish:
+	spin_unlock(&parent->lock);
+	return ret;
+
+}
+
+void irq_unregister_child(struct irq_parent *parent, int irq)
+{
+	spin_lock(&parent->lock);
+
+	/* does child already exist ? */
+	if (parent->child[REEF_IRQ_BIT(irq)] == NULL)
+		goto finish;
+
+	/* free child */
+	parent->num_children -= 1;
+	rfree(parent->child[REEF_IRQ_BIT(irq)]);
+	parent->child[REEF_IRQ_BIT(irq)] = NULL;
+
+	/*
+	 * unregister the root interrupt if the this l2 is
+	 * the last registered one.
+	 */
+	if (parent->num_children == 0)
+		arch_interrupt_unregister(parent->num);
+
+finish:
+	spin_unlock(&parent->lock);
+}
+
+uint32_t irq_enable_child(struct irq_parent *parent, int irq)
+{
+	struct irq_child *child;
+
+	spin_lock(&parent->lock);
+
+	child =parent->child[REEF_IRQ_BIT(irq)];
+
+	/* already enabled ? */
+	if (child->enabled)
+		goto finish;
+
+	/* enable the parent interrupt */
+	if (parent->enabled_count == 0)
+		arch_interrupt_enable_mask(1 << REEF_IRQ_NUMBER(irq));
+	child->enabled = 1;
+	parent->enabled_count++;
+
+	/* enable the child interrupt */
+	platform_interrupt_unmask(irq, 0);
+
+finish:
+	spin_unlock(&parent->lock);
+	return 0;
+
+}
+
+uint32_t irq_disable_child(struct irq_parent *parent, int irq)
+{
+	struct irq_child *child;
+
+	spin_lock(&parent->lock);
+
+	child =parent->child[REEF_IRQ_BIT(irq)];
+
+	/* already disabled ? */
+	if (!child->enabled)
+		goto finish;
+
+	/* disable the child interrupt */
+	platform_interrupt_mask(irq, 0);
+	child->enabled = 0;
+
+	/* disable the parent interrupt */
+	parent->enabled_count--;
+	if (parent->enabled_count == 0)
+		arch_interrupt_disable_mask(1 << REEF_IRQ_NUMBER(irq));
+
+finish:
+	spin_unlock(&parent->lock);
+	return 0;
+
+}
+
+int interrupt_register(uint32_t irq,
+	void (*handler)(void *arg), void *arg)
+{
+	struct irq_parent *parent;
+
+	/* no parent means we are registering DSP internal IRQ */
+	parent = platform_irq_get_parent(irq);
+	if (parent == NULL)
+		return arch_interrupt_register(irq, handler, arg);
+	else
+		return irq_register_child(parent, irq, handler, arg);
+}
+
+void interrupt_unregister(uint32_t irq)
+{
+	struct irq_parent *parent;
+
+	/* no parent means we are unregistering DSP internal IRQ */
+	parent = platform_irq_get_parent(irq);
+	if (parent == NULL)
+		arch_interrupt_unregister(irq);
+	else
+		irq_unregister_child(parent, irq);
+}
+
+uint32_t interrupt_enable(uint32_t irq)
+{
+	struct irq_parent *parent;
+
+	/* no parent means we are enabling DSP internal IRQ */
+	parent = platform_irq_get_parent(irq);
+	if (parent == NULL)
+		return arch_interrupt_enable_mask(1 << irq);
+	else
+		return irq_enable_child(parent, irq);
+}
+
+uint32_t interrupt_disable(uint32_t irq)
+{
+	struct irq_parent *parent;
+
+	/* no parent means we are disabling DSP internal IRQ */
+	parent = platform_irq_get_parent(irq);
+	if (parent == NULL)
+		return arch_interrupt_disable_mask(1 << irq);
+	else
+		return irq_disable_child(parent, irq);
+}
diff --git a/src/platform/baytrail/include/platform/interrupt.h b/src/platform/baytrail/include/platform/interrupt.h
index d197d67..650a797 100644
--- a/src/platform/baytrail/include/platform/interrupt.h
+++ b/src/platform/baytrail/include/platform/interrupt.h
@@ -32,6 +32,7 @@
 #define __INCLUDE_PLATFORM_INTERRUPT__
 
 #include <stdint.h>
+#include <string.h>
 #include <reef/interrupt-map.h>
 
 /* IRQ numbers */
@@ -84,4 +85,20 @@
 #define IRQ_MASK_EXT_SSP2	(1 << IRQ_NUM_EXT_SSP2)
 #define IRQ_MASK_EXT_DMAC2	(1 << IRQ_NUM_EXT_DMAC2)
 
+/* no nested interrupts */
+#define PLATFORM_IRQ_CHILDREN	0
+
+static inline void platform_interrupt_init(void) {}
+
+static inline struct irq_parent *platform_irq_get_parent(uint32_t irq)
+{
+	return NULL;
+}
+
+void platform_interrupt_set(int irq);
+void platform_interrupt_clear(uint32_t irq, uint32_t mask);
+uint32_t platform_interrupt_get_enabled(void);
+void platform_interrupt_mask(uint32_t irq, uint32_t mask);
+void platform_interrupt_unmask(uint32_t irq, uint32_t mask);
+
 #endif
-- 
2.14.1



More information about the Sound-open-firmware mailing list