xtensa: intel_s1000: implement interrupt mechanism
intel_s1000 has multiple levels of interrupts consisting of core, CAVS
Logic and designware interrupt controller. This patchset modifies
the regular gen_isr mechanism to support these multiple levels.
Change-Id: I0450666d4e601dfbc8cadc9c9d8100afb61a214c
Signed-off-by: Rajavardhan Gundi <rajavardhan.gundi@intel.com>
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
diff --git a/arch/xtensa/soc/intel_s1000/CMakeLists.txt b/arch/xtensa/soc/intel_s1000/CMakeLists.txt
index e69de29..bb8015e 100644
--- a/arch/xtensa/soc/intel_s1000/CMakeLists.txt
+++ b/arch/xtensa/soc/intel_s1000/CMakeLists.txt
@@ -0,0 +1,3 @@
+zephyr_library()
+zephyr_library_include_directories(${PROJECT_SOURCE_DIR}/drivers)
+zephyr_library_sources(soc.c)
diff --git a/arch/xtensa/soc/intel_s1000/soc.c b/arch/xtensa/soc/intel_s1000/soc.c
new file mode 100644
index 0000000..e4f67c4
--- /dev/null
+++ b/arch/xtensa/soc/intel_s1000/soc.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2017 Intel Corporation
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <device.h>
+#include <xtensa_api.h>
+#include <xtensa/xtruntime.h>
+#include <logging/sys_log.h>
+#include <board.h>
+#include <irq_nextlevel.h>
+
+void _soc_irq_enable(u32_t irq)
+{
+ struct device *dev_cavs, *dev_ictl;
+
+ switch (XTENSA_IRQ_NUMBER(irq)) {
+ case CAVS_ICTL_0_IRQ:
+ dev_cavs = device_get_binding(CONFIG_CAVS_ICTL_0_NAME);
+ break;
+ case CAVS_ICTL_1_IRQ:
+ dev_cavs = device_get_binding(CONFIG_CAVS_ICTL_1_NAME);
+ break;
+ case CAVS_ICTL_2_IRQ:
+ dev_cavs = device_get_binding(CONFIG_CAVS_ICTL_2_NAME);
+ break;
+ case CAVS_ICTL_3_IRQ:
+ dev_cavs = device_get_binding(CONFIG_CAVS_ICTL_3_NAME);
+ break;
+ default:
+ /* regular interrupt */
+ _xtensa_irq_enable(XTENSA_IRQ_NUMBER(irq));
+ return;
+ }
+
+ if (!dev_cavs) {
+ SYS_LOG_DBG("board: CAVS device binding failed\n");
+ return;
+ }
+
+ /* If the control comes here it means the specified interrupt
+ * is in either CAVS interrupt logic or DW interrupt controller
+ */
+ _xtensa_irq_enable(XTENSA_IRQ_NUMBER(irq));
+
+ switch (CAVS_IRQ_NUMBER(irq)) {
+ case DW_ICTL_IRQ_CAVS_OFFSET:
+ dev_ictl = device_get_binding(CONFIG_DW_ICTL_NAME);
+ break;
+ default:
+ /* The source of the interrupt is in CAVS interrupt logic */
+ irq_enable_next_level(dev_cavs, CAVS_IRQ_NUMBER(irq));
+ return;
+ }
+
+ if (!dev_ictl) {
+ SYS_LOG_DBG("board: DW intr_control device binding failed\n");
+ return;
+ }
+
+ /* If the control comes here it means the specified interrupt
+ * is in DW interrupt controller
+ */
+ irq_enable_next_level(dev_cavs, CAVS_IRQ_NUMBER(irq));
+
+ /* Manipulate the relevant bit in the interrupt controller
+ * register as needed
+ */
+ irq_enable_next_level(dev_ictl, INTR_CNTL_IRQ_NUM(irq));
+}
+
+void _soc_irq_disable(u32_t irq)
+{
+ struct device *dev_cavs, *dev_ictl;
+
+ switch (XTENSA_IRQ_NUMBER(irq)) {
+ case CAVS_ICTL_0_IRQ:
+ dev_cavs = device_get_binding(CONFIG_CAVS_ICTL_0_NAME);
+ break;
+ case CAVS_ICTL_1_IRQ:
+ dev_cavs = device_get_binding(CONFIG_CAVS_ICTL_1_NAME);
+ break;
+ case CAVS_ICTL_2_IRQ:
+ dev_cavs = device_get_binding(CONFIG_CAVS_ICTL_2_NAME);
+ break;
+ case CAVS_ICTL_3_IRQ:
+ dev_cavs = device_get_binding(CONFIG_CAVS_ICTL_3_NAME);
+ break;
+ default:
+ /* regular interrupt */
+ _xtensa_irq_disable(XTENSA_IRQ_NUMBER(irq));
+ return;
+ }
+
+ if (!dev_cavs) {
+ SYS_LOG_DBG("board: CAVS device binding failed\n");
+ return;
+ }
+
+ /* If the control comes here it means the specified interrupt
+ * is in either CAVS interrupt logic or DW interrupt controller
+ */
+
+ switch (CAVS_IRQ_NUMBER(irq)) {
+ case DW_ICTL_IRQ_CAVS_OFFSET:
+ dev_ictl = device_get_binding(CONFIG_DW_ICTL_NAME);
+ break;
+ default:
+ /* The source of the interrupt is in CAVS interrupt logic */
+ irq_disable_next_level(dev_cavs, CAVS_IRQ_NUMBER(irq));
+
+ /* Disable the parent IRQ if all children are disabled */
+ if (!irq_is_enabled_next_level(dev_cavs)) {
+ _xtensa_irq_disable(XTENSA_IRQ_NUMBER(irq));
+ }
+ return;
+ }
+
+ if (!dev_ictl) {
+ SYS_LOG_DBG("board: DW intr_control device binding failed\n");
+ return;
+ }
+
+ /* If the control comes here it means the specified interrupt
+ * is in DW interrupt controller.
+ * Manipulate the relevant bit in the interrupt controller
+ * register as needed
+ */
+ irq_disable_next_level(dev_ictl, INTR_CNTL_IRQ_NUM(irq));
+
+ /* Disable the parent IRQ if all children are disabled */
+ if (!irq_is_enabled_next_level(dev_ictl)) {
+ irq_disable_next_level(dev_cavs, CAVS_IRQ_NUMBER(irq));
+
+ if (!irq_is_enabled_next_level(dev_cavs)) {
+ _xtensa_irq_disable(XTENSA_IRQ_NUMBER(irq));
+ }
+ }
+}
diff --git a/arch/xtensa/soc/intel_s1000/soc.h b/arch/xtensa/soc/intel_s1000/soc.h
index 30bae33..59e228d 100644
--- a/arch/xtensa/soc/intel_s1000/soc.h
+++ b/arch/xtensa/soc/intel_s1000/soc.h
@@ -6,21 +6,73 @@
#ifndef __INC_SOC_H
#define __INC_SOC_H
+/* macros related to interrupt handling */
+#define XTENSA_IRQ_NUM_SHIFT 0
+#define CAVS_IRQ_NUM_SHIFT 8
+#define INTR_CNTL_IRQ_NUM_SHIFT 16
+#define XTENSA_IRQ_NUM_MASK 0xff
+#define CAVS_IRQ_NUM_MASK 0xff
+#define INTR_CNTL_IRQ_NUM_MASK 0xff
+
+/*
+ * IRQs are mapped on 3 levels. 4th level is left 0x00.
+ *
+ * 1. Peripheral Register bit offset.
+ * 2. CAVS logic bit offset.
+ * 3. Core interrupt number.
+ */
+#define XTENSA_IRQ_NUMBER(_irq) \
+ ((_irq >> XTENSA_IRQ_NUM_SHIFT) & XTENSA_IRQ_NUM_MASK)
+#define CAVS_IRQ_NUMBER(_irq) \
+ (((_irq >> CAVS_IRQ_NUM_SHIFT) & CAVS_IRQ_NUM_MASK) - 1)
+#define INTR_CNTL_IRQ_NUM(_irq) \
+ (((_irq >> INTR_CNTL_IRQ_NUM_SHIFT) & INTR_CNTL_IRQ_NUM_MASK) - 1)
+
+/* CAVS interrupt logic */
+#define CAVS_ICTL_BASE_ADDR 0x00078800
+#define CAVS_ICTL_0_IRQ 0x00000006
+#define CAVS_ICTL_0_IRQ_FLAGS 0
+
+#define CAVS_ICTL_1_IRQ 0x0000000A
+#define CAVS_ICTL_1_IRQ_FLAGS 0
+
+#define CAVS_ICTL_2_IRQ 0x0000000D
+#define CAVS_ICTL_2_IRQ_FLAGS 0
+
+#define CAVS_ICTL_3_IRQ 0x00000010
+#define CAVS_ICTL_3_IRQ_FLAGS 0
+
+/* DW interrupt controller */
+#define DW_ICTL_BASE_ADDR 0x00081800
+#define DW_ICTL_IRQ 0x00000706
+#define DW_ICTL_IRQ_CAVS_OFFSET CAVS_IRQ_NUMBER(DW_ICTL_IRQ)
+#define DW_ICTL_NUM_IRQS 9
+#define DW_ICTL_IRQ_FLAGS 0
+
/* GPIO */
#define GPIO_DW_0_BASE_ADDR 0x00080C00
#define GPIO_DW_0_BITS 32
#define GPIO_DW_PORT_0_INT_MASK 0
#define GPIO_DW_0_IRQ_FLAGS 0
-#define GPIO_DW_0_IRQ 20
+#define GPIO_DW_0_IRQ 0x00040706
+#define GPIO_DW_0_IRQ_ICTL_OFFSET INTR_CNTL_IRQ_NUM(GPIO_DW_0_IRQ)
/* UART - UART0 */
#define UART_NS16550_PORT_0_BASE_ADDR 0x00080800
#define UART_NS16550_PORT_0_CLK_FREQ 38400000
+#define UART_NS16550_PORT_0_IRQ 0x00030706
+#define UART_NS16550_P0_IRQ_ICTL_OFFSET INTR_CNTL_IRQ_NUM(\
+ UART_NS16550_PORT_0_IRQ)
+#define UART_IRQ_FLAGS 0
/* I2C - I2C0 */
#define I2C_DW_0_BASE_ADDR 0x00080400
-#define I2C_DW_0_IRQ 20
+#define I2C_DW_0_IRQ 0x00020706
+#define I2C_DW_0_IRQ_ICTL_OFFSET INTR_CNTL_IRQ_NUM(I2C_DW_0_IRQ)
#define I2C_DW_IRQ_FLAGS 0
#define I2C_DW_CLOCK_SPEED 38
+extern void _soc_irq_enable(u32_t irq);
+extern void _soc_irq_disable(u32_t irq);
+
#endif /* __INC_SOC_H */
diff --git a/boards/xtensa/intel_s1000_crb/Kconfig.defconfig b/boards/xtensa/intel_s1000_crb/Kconfig.defconfig
index 041aeed..06fa93a 100644
--- a/boards/xtensa/intel_s1000_crb/Kconfig.defconfig
+++ b/boards/xtensa/intel_s1000_crb/Kconfig.defconfig
@@ -10,6 +10,45 @@
config BOARD_XTENSA
def_bool y
+config CAVS_ICTL_0_OFFSET
+ default 0x06
+config CAVS_ICTL_1_OFFSET
+ default 0x0A
+config CAVS_ICTL_2_OFFSET
+ default 0x0D
+config CAVS_ICTL_3_OFFSET
+ default 0x10
+
+config DW_ICTL_OFFSET
+ default 0x07
+
+config 2ND_LVL_INTR_00_OFFSET
+ default CAVS_ICTL_0_OFFSET
+config 2ND_LVL_INTR_01_OFFSET
+ default CAVS_ICTL_1_OFFSET
+config 2ND_LVL_INTR_02_OFFSET
+ default CAVS_ICTL_2_OFFSET
+config 2ND_LVL_INTR_03_OFFSET
+ default CAVS_ICTL_3_OFFSET
+config 3RD_LVL_INTR_00_OFFSET
+ default DW_ICTL_OFFSET
+
+config MAX_IRQ_PER_AGGREGATOR
+ default 32
+config NUM_2ND_LEVEL_AGGREGATORS
+ default 4
+config NUM_3RD_LEVEL_AGGREGATORS
+ default 1
+config 2ND_LVL_ISR_TBL_OFFSET
+ default 21
+config 3RD_LVL_ISR_TBL_OFFSET
+ default 149
+
+config CAVS_ISR_TBL_OFFSET
+ default 2ND_LVL_ISR_TBL_OFFSET
+config DW_ISR_TBL_OFFSET
+ default 3RD_LVL_ISR_TBL_OFFSET
+
config GPIO_DW_0_NAME
default "GPIO_PORTA"
config GPIO_DW_0_IRQ_PRI
@@ -33,6 +72,8 @@
default 115200
config UART_NS16550_PORT_0_OPTIONS
default 0
+config UART_INTERRUPT_DRIVEN
+ def_bool y
endif # UART_NS16550_PORT_0
diff --git a/boards/xtensa/intel_s1000_crb/intel_s1000_crb_defconfig b/boards/xtensa/intel_s1000_crb/intel_s1000_crb_defconfig
index 86f4693..5bcdeb8 100644
--- a/boards/xtensa/intel_s1000_crb/intel_s1000_crb_defconfig
+++ b/boards/xtensa/intel_s1000_crb/intel_s1000_crb_defconfig
@@ -14,6 +14,12 @@
CONFIG_XTENSA_RESET_VECTOR=y
CONFIG_XTENSA_USE_CORE_CRT1=y
+CONFIG_MULTI_LEVEL_INTERRUPTS=y
+CONFIG_2ND_LEVEL_INTERRUPTS=y
+CONFIG_3RD_LEVEL_INTERRUPTS=y
+CONFIG_CAVS_ICTL=y
+CONFIG_DW_ICTL=y
+
CONFIG_GPIO=y
CONFIG_GPIO_DW=y
CONFIG_GPIO_DW_0=y
diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig
index 8152bbc..181a800 100644
--- a/drivers/interrupt_controller/Kconfig
+++ b/drivers/interrupt_controller/Kconfig
@@ -6,7 +6,6 @@
# SPDX-License-Identifier: Apache-2.0
#
-
menu "Interrupt Controllers"
config LOAPIC
diff --git a/include/arch/xtensa/xtensa_irq.h b/include/arch/xtensa/xtensa_irq.h
index 3727f93..80933a9 100644
--- a/include/arch/xtensa/xtensa_irq.h
+++ b/include/arch/xtensa/xtensa_irq.h
@@ -8,9 +8,28 @@
#include <xtensa_api.h>
#include <xtensa/xtruntime.h>
+#include <board.h>
+
+#define CONFIG_GEN_IRQ_START_VECTOR 0
+
+#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS
+
+#define CONFIG_NUM_IRQS (XCHAL_NUM_INTERRUPTS +\
+ (CONFIG_NUM_2ND_LEVEL_AGGREGATORS +\
+ CONFIG_NUM_3RD_LEVEL_AGGREGATORS) *\
+ CONFIG_MAX_IRQ_PER_AGGREGATOR)
+
+#define _arch_irq_enable(irq) _soc_irq_enable(irq)
+#define _arch_irq_disable(irq) _soc_irq_disable(irq)
+
+#else
#define CONFIG_NUM_IRQS XCHAL_NUM_INTERRUPTS
-#define CONFIG_GEN_IRQ_START_VECTOR 0
+
+#define _arch_irq_enable(irq) _xtensa_irq_enable(irq)
+#define _arch_irq_disable(irq) _xtensa_irq_disable(irq)
+
+#endif
/**
*
@@ -22,7 +41,7 @@
*
* @return N/A
*/
-static ALWAYS_INLINE void _arch_irq_enable(u32_t irq)
+static ALWAYS_INLINE void _xtensa_irq_enable(u32_t irq)
{
_xt_ints_on(1 << irq);
}
@@ -36,7 +55,7 @@
*
* @return N/A
*/
-static ALWAYS_INLINE void _arch_irq_disable(u32_t irq)
+static ALWAYS_INLINE void _xtensa_irq_disable(u32_t irq)
{
_xt_ints_off(1 << irq);
}