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>
This commit is contained in:
Felipe Neves 2021-08-30 10:04:34 -03:00 committed by Anas Nashif
commit b97c2da2f2
12 changed files with 381 additions and 37 deletions

View file

@ -23,5 +23,6 @@ zephyr_library_sources_ifdef(CONFIG_RV32M1_INTMUX intc_rv32m1_intmux.c
zephyr_library_sources_ifdef(CONFIG_SAM0_EIC intc_sam0_eic.c) 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_SHARED_IRQ intc_shared_irq.c)
zephyr_library_sources_ifdef(CONFIG_INTC_ESP32 intc_esp32.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_SWERV_PIC intc_swerv_pic.c)
zephyr_library_sources_ifdef(CONFIG_VEXRISCV_LITEX_IRQ intc_vexriscv_litex.c) zephyr_library_sources_ifdef(CONFIG_VEXRISCV_LITEX_IRQ intc_vexriscv_litex.c)

View file

@ -69,6 +69,8 @@ source "drivers/interrupt_controller/Kconfig.intel_vtd"
source "drivers/interrupt_controller/Kconfig.esp32" source "drivers/interrupt_controller/Kconfig.esp32"
source "drivers/interrupt_controller/Kconfig.esp32c3"
source "drivers/interrupt_controller/Kconfig.xec" source "drivers/interrupt_controller/Kconfig.xec"
endmenu endmenu

View file

@ -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.

View file

@ -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];
}
}

View file

@ -13,12 +13,11 @@
#include <rom/ets_sys.h> #include <rom/ets_sys.h>
#include <esp_attr.h> #include <esp_attr.h>
#include <drivers/interrupt_controller/intc_esp32c3.h>
#include <drivers/timer/system_timer.h> #include <drivers/timer/system_timer.h>
#include <sys_clock.h> #include <sys_clock.h>
#include <soc.h> #include <soc.h>
#define SYS_TIMER_CPU_IRQ 16
#define CYC_PER_TICK ((uint32_t)((uint64_t)sys_clock_hw_cycles_per_sec() \ #define CYC_PER_TICK ((uint32_t)((uint64_t)sys_clock_hw_cycles_per_sec() \
/ (uint64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC)) / (uint64_t)CONFIG_SYS_CLOCK_TICKS_PER_SEC))
#define MAX_CYC 0xffffffffu #define MAX_CYC 0xffffffffu
@ -45,7 +44,6 @@ static uint64_t systimer_alarm(void)
static void sys_timer_isr(const void *arg) static void sys_timer_isr(const void *arg)
{ {
ARG_UNUSED(arg); ARG_UNUSED(arg);
systimer_ll_clear_alarm_int(SYSTIMER_ALARM_0); systimer_ll_clear_alarm_int(SYSTIMER_ALARM_0);
k_spinlock_key_t key = k_spin_lock(&lock); k_spinlock_key_t key = k_spin_lock(&lock);
@ -72,17 +70,18 @@ int sys_clock_driver_init(const struct device *dev)
{ {
ARG_UNUSED(dev); ARG_UNUSED(dev);
esp_rom_intr_matrix_set(0, esp_intr_alloc(ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE,
ETS_SYSTIMER_TARGET0_EDGE_INTR_SOURCE, 0,
SYS_TIMER_CPU_IRQ); sys_timer_isr,
IRQ_CONNECT(SYS_TIMER_CPU_IRQ, 0, sys_timer_isr, NULL, 0); NULL,
NULL);
systimer_hal_init(); systimer_hal_init();
systimer_hal_connect_alarm_counter(SYSTIMER_ALARM_0, SYSTIMER_COUNTER_1); systimer_hal_connect_alarm_counter(SYSTIMER_ALARM_0, SYSTIMER_COUNTER_1);
systimer_hal_enable_counter(SYSTIMER_COUNTER_1); systimer_hal_enable_counter(SYSTIMER_COUNTER_1);
systimer_hal_counter_can_stall_by_cpu(SYSTIMER_COUNTER_1, 0, true); systimer_hal_counter_can_stall_by_cpu(SYSTIMER_COUNTER_1, 0, true);
last_count = systimer_alarm(); last_count = systimer_alarm();
set_systimer_alarm(last_count + CYC_PER_TICK); set_systimer_alarm(last_count + CYC_PER_TICK);
irq_enable(SYS_TIMER_CPU_IRQ);
return 0; return 0;
} }

View file

@ -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

View file

@ -4,5 +4,6 @@ zephyr_sources(
idle.c idle.c
vectors.S vectors.S
soc_irq.S soc_irq.S
soc_irq.c
soc.c soc.c
) )

View file

@ -9,7 +9,7 @@ config SOC
default "esp32c3" default "esp32c3"
config NUM_IRQS config NUM_IRQS
default 32 default 62
config GEN_ISR_TABLES config GEN_ISR_TABLES
default y default y

View file

@ -13,18 +13,15 @@
#include <soc/cache_memory.h> #include <soc/cache_memory.h>
#include "hal/soc_ll.h" #include "hal/soc_ll.h"
#include "esp_spi_flash.h" #include "esp_spi_flash.h"
#include <riscv/interrupt.h>
#include <soc/interrupt_reg.h> #include <soc/interrupt_reg.h>
#include <drivers/interrupt_controller/intc_esp32c3.h>
#include <kernel_structs.h> #include <kernel_structs.h>
#include <string.h> #include <string.h>
#include <toolchain/gcc.h> #include <toolchain/gcc.h>
#include <soc.h> #include <soc.h>
#define ESP32C3_INTC_DEFAULT_PRIO 15
extern void _PrepC(void); 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, * This is written in C rather than assembly since, during the port bring up,
@ -94,15 +91,15 @@ void __attribute__((section(".iram1"))) __start(void)
esp_rom_cache_set_idrom_mmu_size(cache_mmu_irom_size, esp_rom_cache_set_idrom_mmu_size(cache_mmu_irom_size,
CACHE_DROM_MMU_MAX_END - 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, /* Enable wireless phy subsystem clock,
* This needs to be done before the kernel starts * This needs to be done before the kernel starts
*/ */
REG_CLR_BIT(SYSTEM_WIFI_CLK_EN_REG, SYSTEM_WIFI_CLK_SDIOSLAVE_EN); 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); SET_PERI_REG_MASK(SYSTEM_WIFI_CLK_EN_REG, SYSTEM_WIFI_CLK_EN);
/*Initialize the esp32c3 interrupt controller */
esp_intr_initialize();
/* Start Zephyr */ /* Start Zephyr */
_PrepC(); _PrepC();
@ -167,24 +164,3 @@ void sys_arch_reboot(int type)
{ {
esp_restart_noos(); 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));
}

View file

@ -41,6 +41,8 @@ extern void esp_rom_uart_tx_wait_idle(uint8_t uart_no);
extern STATUS esp_rom_uart_tx_one_char(uint8_t chr); extern STATUS esp_rom_uart_tx_one_char(uint8_t chr);
extern STATUS esp_rom_uart_rx_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 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 */ #endif /* _ASMLANGUAGE */

View file

@ -9,6 +9,7 @@
/* Exports */ /* Exports */
GTEXT(__soc_is_irq) GTEXT(__soc_is_irq)
GTEXT(__soc_handle_irq) GTEXT(__soc_handle_irq)
GTEXT(soc_intr_get_next_source)
SECTION_FUNC(exception.other, __soc_is_irq) SECTION_FUNC(exception.other, __soc_is_irq)
csrr a0, mcause csrr a0, mcause
@ -16,4 +17,10 @@ SECTION_FUNC(exception.other, __soc_is_irq)
ret ret
SECTION_FUNC(exception.other, __soc_handle_irq) 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 ret

View file

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