interrupt_controller: intc_esp32c3: added intc driver
For esp32c3 and replaces the hardcoded interrupt
attaching procedures with this new driver.
Signed-off-by: Felipe Neves <felipe.neves@espressif.com>
diff --git a/drivers/interrupt_controller/CMakeLists.txt b/drivers/interrupt_controller/CMakeLists.txt
index 41f3578..c696633 100644
--- a/drivers/interrupt_controller/CMakeLists.txt
+++ b/drivers/interrupt_controller/CMakeLists.txt
@@ -23,5 +23,6 @@
zephyr_library_sources_ifdef(CONFIG_SAM0_EIC intc_sam0_eic.c)
zephyr_library_sources_ifdef(CONFIG_SHARED_IRQ intc_shared_irq.c)
zephyr_library_sources_ifdef(CONFIG_INTC_ESP32 intc_esp32.c)
+zephyr_library_sources_ifdef(CONFIG_INTC_ESP32C3 intc_esp32c3.c)
zephyr_library_sources_ifdef(CONFIG_SWERV_PIC intc_swerv_pic.c)
zephyr_library_sources_ifdef(CONFIG_VEXRISCV_LITEX_IRQ intc_vexriscv_litex.c)
diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig
index 82915b8..9719686 100644
--- a/drivers/interrupt_controller/Kconfig
+++ b/drivers/interrupt_controller/Kconfig
@@ -69,6 +69,8 @@
source "drivers/interrupt_controller/Kconfig.esp32"
+source "drivers/interrupt_controller/Kconfig.esp32c3"
+
source "drivers/interrupt_controller/Kconfig.xec"
endmenu
diff --git a/drivers/interrupt_controller/Kconfig.esp32c3 b/drivers/interrupt_controller/Kconfig.esp32c3
new file mode 100644
index 0000000..1022dec
--- /dev/null
+++ b/drivers/interrupt_controller/Kconfig.esp32c3
@@ -0,0 +1,10 @@
+# Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
+# SPDX-License-Identifier: Apache-2.0
+
+config INTC_ESP32C3
+ bool "Enables ESP32C3 interrupt controller driver"
+ depends on SOC_ESP32C3
+ default y
+ help
+ Enables the esp32c3 interrupt controller driver to handle ISR
+ management at SoC level.
diff --git a/drivers/interrupt_controller/intc_esp32c3.c b/drivers/interrupt_controller/intc_esp32c3.c
new file mode 100644
index 0000000..4883841
--- /dev/null
+++ b/drivers/interrupt_controller/intc_esp32c3.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <soc/periph_defs.h>
+#include <limits.h>
+#include <assert.h>
+#include "soc/soc.h"
+#include <soc.h>
+#include <zephyr.h>
+#include <drivers/interrupt_controller/intc_esp32c3.h>
+#include <sw_isr_table.h>
+
+#include <logging/log.h>
+LOG_MODULE_REGISTER(intc_esp32c3, CONFIG_LOG_DEFAULT_LEVEL);
+
+#define ESP32C3_INTC_DEFAULT_PRIORITY 15
+#define ESP32C3_INTC_DEFAULT_THRESHOLD 1
+#define ESP32C3_INTC_DISABLED_SLOT 31
+#define ESP32C3_INTC_SRCS_PER_IRQ 2
+#define ESP32C3_INTC_AVAILABLE_IRQS 30
+
+static uint32_t esp_intr_enabled_mask[2] = {0, 0};
+
+static void esp_intr_default_isr(const void *arg)
+{
+ ARG_UNUSED(arg);
+ ulong_t mcause;
+
+ __asm__ volatile("csrr %0, mcause" : "=r" (mcause));
+ mcause &= SOC_MCAUSE_EXP_MASK;
+
+ LOG_DBG("Spurious interrupt, mcause: %ld, source %d", mcause, soc_intr_get_next_source());
+}
+
+static uint32_t esp_intr_find_irq_for_source(uint32_t source)
+{
+ /* in general case, each 2 sources goes routed to
+ * 1 IRQ line.
+ */
+ uint32_t irq = (source / ESP32C3_INTC_SRCS_PER_IRQ);
+
+ if (irq > ESP32C3_INTC_AVAILABLE_IRQS) {
+ LOG_DBG("Clamping the source: %d no more IRQs available", source);
+ irq = ESP32C3_INTC_AVAILABLE_IRQS;
+ } else if (irq == 0) {
+ irq = 1;
+ }
+
+ LOG_DBG("Found IRQ: %d for source: %d", irq, source);
+
+ return irq;
+}
+
+void esp_intr_initialize(void)
+{
+ /* IRQ 31 is reserved for disabled interrupts,
+ * so route all sources to it
+ */
+ for (int i = 0 ; i < ESP32C3_INTC_AVAILABLE_IRQS + 2; i++) {
+ irq_disable(i);
+ }
+
+ for (int i = 0; i < ETS_MAX_INTR_SOURCE; i++) {
+ esp_rom_intr_matrix_set(0,
+ i,
+ ESP32C3_INTC_DISABLED_SLOT);
+
+ irq_connect_dynamic(i,
+ ESP32C3_INTC_DEFAULT_PRIORITY,
+ esp_intr_default_isr,
+ NULL,
+ 0);
+ }
+
+ /* set global esp32c3's INTC masking level */
+ esprv_intc_int_set_threshold(ESP32C3_INTC_DEFAULT_THRESHOLD);
+}
+
+int esp_intr_alloc(int source,
+ int flags,
+ isr_handler_t handler,
+ void *arg,
+ void **ret_handle)
+{
+ ARG_UNUSED(flags);
+ ARG_UNUSED(ret_handle);
+
+ if (handler == NULL) {
+ return -EINVAL;
+ }
+
+ if (source < 0 || source >= ETS_MAX_INTR_SOURCE) {
+ return -EINVAL;
+ }
+
+ uint32_t key = irq_lock();
+ uint32_t irq = esp_intr_find_irq_for_source(source);
+
+ esp_rom_intr_matrix_set(0, source, irq);
+
+ irq_connect_dynamic(source,
+ ESP32C3_INTC_DEFAULT_PRIORITY,
+ handler,
+ arg,
+ 0);
+
+ if (source < 32) {
+ esp_intr_enabled_mask[0] |= (1 << source);
+ } else {
+ esp_intr_enabled_mask[1] |= (1 << (source - 32));
+ }
+
+ LOG_DBG("Enabled isrs -- 0: 0x%X -- 1: 0x%X",
+ esp_intr_enabled_mask[0], esp_intr_enabled_mask[1]);
+
+ irq_unlock(key);
+ irq_enable(irq);
+
+ return 0;
+}
+
+int esp_intr_disable(int source)
+{
+ if (source < 0 || source >= ETS_MAX_INTR_SOURCE) {
+ return -EINVAL;
+ }
+
+ uint32_t key = irq_lock();
+
+ esp_rom_intr_matrix_set(source,
+ source,
+ ESP32C3_INTC_DISABLED_SLOT);
+
+ if (source < 32) {
+ esp_intr_enabled_mask[0] &= ~(1 << source);
+ } else {
+ esp_intr_enabled_mask[1] &= ~(1 << (source - 32));
+ }
+
+ LOG_DBG("Enabled isrs -- 0: 0x%X -- 1: 0x%X",
+ esp_intr_enabled_mask[0], esp_intr_enabled_mask[1]);
+
+ irq_unlock(key);
+
+ return 0;
+}
+
+int esp_intr_enable(int source)
+{
+ if (source < 0 || source >= ETS_MAX_INTR_SOURCE) {
+ return -EINVAL;
+ }
+
+ uint32_t key = irq_lock();
+ uint32_t irq = esp_intr_find_irq_for_source(source);
+
+ irq_disable(irq);
+ esp_rom_intr_matrix_set(0, source, irq);
+
+ if (source < 32) {
+ esp_intr_enabled_mask[0] |= (1 << source);
+ } else {
+ esp_intr_enabled_mask[1] |= (1 << (source - 32));
+ }
+
+ LOG_DBG("Enabled isrs -- 0: 0x%X -- 1: 0x%X",
+ esp_intr_enabled_mask[0], esp_intr_enabled_mask[1]);
+
+ irq_enable(irq);
+ irq_unlock(key);
+
+ return 0;
+}
+
+uint32_t esp_intr_get_enabled_intmask(int status_mask_number)
+{
+ LOG_DBG("Enabled isrs -- 0: 0x%X -- 1: 0x%X",
+ esp_intr_enabled_mask[0], esp_intr_enabled_mask[1]);
+
+ if (status_mask_number == 0) {
+ return esp_intr_enabled_mask[0];
+ } else {
+ return esp_intr_enabled_mask[1];
+ }
+}
diff --git a/drivers/timer/esp32c3_sys_timer.c b/drivers/timer/esp32c3_sys_timer.c
index 65755af..1d087af 100644
--- a/drivers/timer/esp32c3_sys_timer.c
+++ b/drivers/timer/esp32c3_sys_timer.c
@@ -13,12 +13,11 @@
#include <rom/ets_sys.h>
#include <esp_attr.h>
+#include <drivers/interrupt_controller/intc_esp32c3.h>
#include <drivers/timer/system_timer.h>
#include <sys_clock.h>
#include <soc.h>
-#define SYS_TIMER_CPU_IRQ 16
-
#define CYC_PER_TICK ((uint32_t)((uint64_t)sys_clock_hw_cycles_per_sec() \
/ (uint64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC))
#define MAX_CYC 0xffffffffu
@@ -45,7 +44,6 @@
static void sys_timer_isr(const void *arg)
{
ARG_UNUSED(arg);
-
systimer_ll_clear_alarm_int(SYSTIMER_ALARM_0);
k_spinlock_key_t key = k_spin_lock(&lock);
@@ -72,17 +70,18 @@
{
ARG_UNUSED(dev);
- esp_rom_intr_matrix_set(0,
- ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE,
- SYS_TIMER_CPU_IRQ);
- IRQ_CONNECT(SYS_TIMER_CPU_IRQ, 0, sys_timer_isr, NULL, 0);
+ esp_intr_alloc(ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE,
+ 0,
+ sys_timer_isr,
+ NULL,
+ NULL);
+
systimer_hal_init();
systimer_hal_connect_alarm_counter(SYSTIMER_ALARM_0, SYSTIMER_COUNTER_1);
systimer_hal_enable_counter(SYSTIMER_COUNTER_1);
systimer_hal_counter_can_stall_by_cpu(SYSTIMER_COUNTER_1, 0, true);
last_count = systimer_alarm();
set_systimer_alarm(last_count + CYC_PER_TICK);
- irq_enable(SYS_TIMER_CPU_IRQ);
return 0;
}
diff --git a/include/drivers/interrupt_controller/intc_esp32c3.h b/include/drivers/interrupt_controller/intc_esp32c3.h
new file mode 100644
index 0000000..761f1df
--- /dev/null
+++ b/include/drivers/interrupt_controller/intc_esp32c3.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef ZEPHYR_INCLUDE_DRIVERS_ESP_INTR_ALLOC_H__
+#define ZEPHYR_INCLUDE_DRIVERS_ESP_INTR_ALLOC_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <soc.h>
+/*
+ * Interrupt allocation flags - These flags can be used to specify
+ * which interrupt qualities the code calling esp_intr_alloc* needs.
+ *
+ */
+
+/* Keep the LEVELx values as they are here; they match up with (1<<level) */
+#define ESP_INTR_FLAG_LEVEL1 (1<<1) /* Accept a Level 1 int vector, lowest priority */
+#define ESP_INTR_FLAG_EDGE (1<<9) /* Edge-triggered interrupt */
+
+/* Function prototype for interrupt handler function */
+typedef void (*isr_handler_t)(const void *arg);
+
+/**
+ * @brief Initializes interrupt table to its defaults
+ */
+void esp_intr_initialize(void);
+
+/**
+ * @brief Allocate an interrupt with the given parameters.
+ *
+ * This finds an interrupt that matches the restrictions as given in the flags
+ * parameter, maps the given interrupt source to it and hooks up the given
+ * interrupt handler (with optional argument) as well. If needed, it can return
+ * a handle for the interrupt as well.
+ *
+ * @param source The interrupt source.
+ * @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
+ * choice of interrupts that this routine can choose from. If this value
+ * is 0, it will default to allocating a non-shared interrupt of level
+ * 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared
+ * interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return
+ * from this function with the interrupt disabled.
+ * @param handler The interrupt handler.
+ * @param arg Optional argument for passed to the interrupt handler
+ * @param ret_handle Pointer to a struct intr_handle_data_t pointer to store a handle that can
+ * later be used to request details or free the interrupt. Can be NULL if no handle
+ * is required.
+ *
+ * @return -EINVAL if the combination of arguments is invalid.
+ * -ENODEV No free interrupt found with the specified flags
+ * 0 otherwise
+ */
+int esp_intr_alloc(int source,
+ int flags,
+ isr_handler_t handler,
+ void *arg,
+ void **ret_handle);
+
+/**
+ * @brief Disable the interrupt associated with the source
+ *
+ * @param source The interrupt source
+ *
+ * @return -EINVAL if the combination of arguments is invalid.
+ * 0 otherwise
+ */
+int esp_intr_disable(int source);
+
+/**
+ * @brief Enable the interrupt associated with the source
+ *
+ * @param source The interrupt source
+ * @return -EINVAL if the combination of arguments is invalid.
+ * 0 otherwise
+ */
+int esp_intr_enable(int source);
+
+/**
+ * @brief Gets the current enabled interrupts
+ *
+ * @param status_mask_number the status mask can be 0 or 1
+ * @return bitmask of enabled interrupt sources
+ */
+uint32_t esp_intr_get_enabled_intmask(int status_mask_number);
+
+#endif
diff --git a/soc/riscv/esp32c3/CMakeLists.txt b/soc/riscv/esp32c3/CMakeLists.txt
index b2fd9e6..f66f57c 100644
--- a/soc/riscv/esp32c3/CMakeLists.txt
+++ b/soc/riscv/esp32c3/CMakeLists.txt
@@ -4,5 +4,6 @@
idle.c
vectors.S
soc_irq.S
+ soc_irq.c
soc.c
)
diff --git a/soc/riscv/esp32c3/Kconfig.defconfig b/soc/riscv/esp32c3/Kconfig.defconfig
index f400b93..ca2c54e 100644
--- a/soc/riscv/esp32c3/Kconfig.defconfig
+++ b/soc/riscv/esp32c3/Kconfig.defconfig
@@ -9,7 +9,7 @@
default "esp32c3"
config NUM_IRQS
- default 32
+ default 62
config GEN_ISR_TABLES
default y
diff --git a/soc/riscv/esp32c3/soc.c b/soc/riscv/esp32c3/soc.c
index c0da3a2..197c089 100644
--- a/soc/riscv/esp32c3/soc.c
+++ b/soc/riscv/esp32c3/soc.c
@@ -13,18 +13,15 @@
#include <soc/cache_memory.h>
#include "hal/soc_ll.h"
#include "esp_spi_flash.h"
-#include <riscv/interrupt.h>
#include <soc/interrupt_reg.h>
+#include <drivers/interrupt_controller/intc_esp32c3.h>
#include <kernel_structs.h>
#include <string.h>
#include <toolchain/gcc.h>
#include <soc.h>
-#define ESP32C3_INTC_DEFAULT_PRIO 15
-
extern void _PrepC(void);
-extern void esprv_intc_int_set_threshold(int priority_threshold);
/*
* This is written in C rather than assembly since, during the port bring up,
@@ -94,15 +91,15 @@
esp_rom_cache_set_idrom_mmu_size(cache_mmu_irom_size,
CACHE_DROM_MMU_MAX_END - cache_mmu_irom_size);
- /* set global esp32c3's INTC masking level */
- esprv_intc_int_set_threshold(1);
-
/* Enable wireless phy subsystem clock,
* This needs to be done before the kernel starts
*/
REG_CLR_BIT(SYSTEM_WIFI_CLK_EN_REG, SYSTEM_WIFI_CLK_SDIOSLAVE_EN);
SET_PERI_REG_MASK(SYSTEM_WIFI_CLK_EN_REG, SYSTEM_WIFI_CLK_EN);
+ /*Initialize the esp32c3 interrupt controller */
+ esp_intr_initialize();
+
/* Start Zephyr */
_PrepC();
@@ -167,24 +164,3 @@
{
esp_restart_noos();
}
-
-void arch_irq_enable(unsigned int irq)
-{
- uint32_t key = irq_lock();
-
- esprv_intc_int_set_priority(irq, ESP32C3_INTC_DEFAULT_PRIO);
- esprv_intc_int_set_type(irq, 0);
- esprv_intc_int_enable(1 << irq);
-
- irq_unlock(key);
-}
-
-void arch_irq_disable(unsigned int irq)
-{
- esprv_intc_int_disable(1 << irq);
-}
-
-int arch_irq_is_enabled(unsigned int irq)
-{
- return (REG_READ(INTERRUPT_CORE0_CPU_INT_ENABLE_REG) & (1 << irq));
-}
diff --git a/soc/riscv/esp32c3/soc.h b/soc/riscv/esp32c3/soc.h
index 6e2cf0a..76a3cb8 100644
--- a/soc/riscv/esp32c3/soc.h
+++ b/soc/riscv/esp32c3/soc.h
@@ -41,6 +41,8 @@
extern STATUS esp_rom_uart_tx_one_char(uint8_t chr);
extern STATUS esp_rom_uart_rx_one_char(uint8_t *chr);
extern void esp_rom_ets_set_user_start(uint32_t start);
+extern void esprv_intc_int_set_threshold(int priority_threshold);
+uint32_t soc_intr_get_next_source(void);
#endif /* _ASMLANGUAGE */
diff --git a/soc/riscv/esp32c3/soc_irq.S b/soc/riscv/esp32c3/soc_irq.S
index 7edbe13..9054b17 100644
--- a/soc/riscv/esp32c3/soc_irq.S
+++ b/soc/riscv/esp32c3/soc_irq.S
@@ -9,6 +9,7 @@
/* Exports */
GTEXT(__soc_is_irq)
GTEXT(__soc_handle_irq)
+GTEXT(soc_intr_get_next_source)
SECTION_FUNC(exception.other, __soc_is_irq)
csrr a0, mcause
@@ -16,4 +17,10 @@
ret
SECTION_FUNC(exception.other, __soc_handle_irq)
+ addi sp, sp,-4
+ sw ra, 0x00(sp)
+ la t1, soc_intr_get_next_source
+ jalr ra, t1
+ lw ra, 0x00(sp)
+ addi sp, sp, 4
ret
diff --git a/soc/riscv/esp32c3/soc_irq.c b/soc/riscv/esp32c3/soc_irq.c
new file mode 100644
index 0000000..461c8c5
--- /dev/null
+++ b/soc/riscv/esp32c3/soc_irq.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2021 Espressif Systems (Shanghai) Co., Ltd.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <soc/rtc_cntl_reg.h>
+#include <soc/timer_group_reg.h>
+#include <soc/gpio_reg.h>
+#include <soc/syscon_reg.h>
+#include <soc/system_reg.h>
+#include <soc/cache_memory.h>
+#include "hal/soc_ll.h"
+#include <riscv/interrupt.h>
+#include <soc/interrupt_reg.h>
+#include <soc/periph_defs.h>
+#include <drivers/interrupt_controller/intc_esp32c3.h>
+
+#include <kernel_structs.h>
+#include <string.h>
+#include <toolchain/gcc.h>
+#include <soc.h>
+
+#define ESP32C3_INTC_DEFAULT_PRIO 15
+#define ESP32C3_INTSTATUS_SLOT1_THRESHOLD 32
+
+void arch_irq_enable(unsigned int irq)
+{
+ uint32_t key = irq_lock();
+
+ esprv_intc_int_set_priority(irq, ESP32C3_INTC_DEFAULT_PRIO);
+ esprv_intc_int_set_type(irq, INTR_TYPE_LEVEL);
+ esprv_intc_int_enable(1 << irq);
+ irq_unlock(key);
+}
+
+void arch_irq_disable(unsigned int irq)
+{
+ esprv_intc_int_disable(1 << irq);
+}
+
+int arch_irq_is_enabled(unsigned int irq)
+{
+ return (REG_READ(INTERRUPT_CORE0_CPU_INT_ENABLE_REG) & (1 << irq));
+}
+
+uint32_t soc_intr_get_next_source(void)
+{
+ uint32_t status;
+ uint32_t source;
+
+ status = REG_READ(INTERRUPT_CORE0_INTR_STATUS_0_REG) &
+ esp_intr_get_enabled_intmask(0);
+
+ if (status) {
+ source = __builtin_ffs(status) - 1;
+ } else {
+ status = REG_READ(INTERRUPT_CORE0_INTR_STATUS_1_REG) &
+ esp_intr_get_enabled_intmask(1);
+ source = (__builtin_ffs(status) - 1 + ESP32C3_INTSTATUS_SLOT1_THRESHOLD);
+ }
+
+ return source;
+}