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);
 }