Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions arch/riscv/core/irq_manage.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#ifdef CONFIG_RISCV_HAS_PLIC
#include <zephyr/drivers/interrupt_controller/riscv_plic.h>
#endif
#if defined(CONFIG_RISCV_APLIC_DIRECT)
#include <zephyr/drivers/interrupt_controller/riscv_aplic_direct.h>
#endif

LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);

Expand Down Expand Up @@ -46,6 +49,13 @@ FUNC_NORETURN void z_irq_spurious(const void *unused)

LOG_ERR("PLIC interrupt line causing the IRQ: %d (%p)", save_irq, save_dev);
}
#elif defined(RISCV_APLIC_DIRECT)
if (cause == RISCV_IRQ_MEXT) {
unsigned int save_irq = riscv_aplic_get_saved_irq();
const struct device *save_dev = riscv_aplic_get_saved_dev();

LOG_ERR("APLIC interrupt line causing the IRQ: %d (%p)", save_irq, save_dev);
}
#endif
z_riscv_fatal_error(K_ERR_SPURIOUS_IRQ, NULL);
CODE_UNREACHABLE;
Expand Down
1 change: 1 addition & 0 deletions drivers/interrupt_controller/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ zephyr_library_sources_ifdef(CONFIG_RENESAS_RZ_ICU_V2 intc_renesas_rz_icu.c)
zephyr_library_sources_ifdef(CONFIG_RENESAS_RZ_TINT intc_renesas_rz_tint.c)
zephyr_library_sources_ifdef(CONFIG_RISCV_AIA intc_riscv_aia.c)
zephyr_library_sources_ifdef(CONFIG_RISCV_APLIC intc_riscv_aplic.c)
zephyr_library_sources_ifdef(CONFIG_RISCV_APLIC_DIRECT intc_riscv_aplic_direct.c)
zephyr_library_sources_ifdef(CONFIG_RISCV_APLIC_MSI intc_riscv_aplic_msi.c)
zephyr_library_sources_ifdef(CONFIG_RISCV_IMSIC intc_riscv_imsic.c)
zephyr_library_sources_ifdef(CONFIG_RV32M1_INTMUX intc_rv32m1_intmux.c)
Expand Down
11 changes: 10 additions & 1 deletion drivers/interrupt_controller/Kconfig.riscv_aia
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,19 @@ config RISCV_APLIC_MSI
Enable MSI delivery mode for the APLIC.
Routes wired interrupts to IMSIC as Message-Signaled Interrupts.

config RISCV_APLIC_DIRECT
bool "RISC-V APLIC direct delivery mode"
default y
depends on RISCV_APLIC
depends on !$(dt_compat_all_has_prop,$(DT_COMPAT_RISCV_APLIC),msi-parent)
help
Enable direct delivery mode for the APLIC.
Routes wired interrupts directly to the Hart's MEXT interrupt line.

config RISCV_AIA
bool "RISC-V AIA unified coordinator"
default y
depends on RISCV_APLIC_MSI
depends on RISCV_APLIC_MSI || RISCV_APLIC_DIRECT
select MULTI_LEVEL_INTERRUPTS
select 2ND_LEVEL_INTERRUPTS
help
Expand Down
25 changes: 21 additions & 4 deletions drivers/interrupt_controller/intc_riscv_aia.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@
#include <zephyr/devicetree/interrupt_controller.h>
#include <zephyr/drivers/interrupt_controller/riscv_aia.h>
#include <zephyr/drivers/interrupt_controller/riscv_aplic.h>
#if defined(CONFIG_RISCV_APLIC_DIRECT)
#include <zephyr/drivers/interrupt_controller/riscv_aplic_direct.h>
#endif
#if defined(CONFIG_RISCV_APLIC_MSI)
#include <zephyr/drivers/interrupt_controller/riscv_imsic.h>
#endif
#include <zephyr/irq_multilevel.h>
#include <zephyr/logging/log.h>
#include <zephyr/sw_isr_table.h>

LOG_MODULE_REGISTER(intc_riscv_aia, CONFIG_LOG_DEFAULT_LEVEL);

#define APLIC_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(riscv_aplic)
#define APLIC_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(riscv_aplic)
#define APLIC_ISR_TABLE_OFFSET INTC_BASE_ISR_TBL_OFFSET(APLIC_NODE)

IRQ_PARENT_ENTRY_DEFINE(riscv_aia, DEVICE_DT_GET(APLIC_NODE), DT_IRQN(APLIC_NODE),
Expand All @@ -49,8 +54,9 @@ void riscv_aia_irq_enable(uint32_t irq)
return;
}

#ifdef CONFIG_RISCV_IMSIC
riscv_imsic_enable_eiid(src);

#endif
#ifdef CONFIG_RISCV_APLIC_MSI
riscv_aplic_msi_route(aplic, src, 0, src);
#endif
Expand All @@ -68,7 +74,10 @@ void riscv_aia_irq_disable(uint32_t irq)
}

riscv_aplic_enable_src(aplic, src, false);

#ifdef CONFIG_RISCV_IMSIC
riscv_imsic_disable_eiid(src);
#endif
}

int riscv_aia_irq_is_enabled(uint32_t irq)
Expand All @@ -80,21 +89,29 @@ int riscv_aia_irq_is_enabled(uint32_t irq)
return 0;
}

#ifdef CONFIG_RISCV_IMSIC
return riscv_imsic_is_enabled(src);
#else
return riscv_aplic_is_enabled(src);
#endif
}

void riscv_aia_set_priority(uint32_t irq, uint32_t prio)
{
uint32_t src = riscv_aia_irq_to_src(irq);

#if defined(CONFIG_RISCV_APLIC_MSI)
/*
* APLIC-MSI mode has no per-source priority registers. Priority in AIA
* is handled via IMSIC EITHRESHOLD or implicit EIID ordering.
*/
uint32_t src = riscv_aia_irq_to_src(irq);

if (prio != 0) {
LOG_WRN("AIA-MSI: per-source priority not supported (src %u, prio %u ignored)", src,
prio);
}
#else
riscv_aplic_set_priority(irq, prio);
#endif
}

void riscv_aia_config_source(uint32_t irq, uint32_t mode)
Expand Down
58 changes: 46 additions & 12 deletions drivers/interrupt_controller/intc_riscv_aplic.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/drivers/interrupt_controller/riscv_aplic.h>
#if defined(CONFIG_RISCV_APLIC_DIRECT)
#include <zephyr/drivers/interrupt_controller/riscv_aplic_direct.h>
#endif
#include <zephyr/devicetree/interrupt_controller.h>
#include <zephyr/logging/log.h>
#include <zephyr/sw_isr_table.h>
#include <zephyr/sys/util.h>

#include "intc_riscv_aplic_priv.h"
Expand Down Expand Up @@ -78,28 +83,57 @@ static int aplic_init(const struct device *dev)
#ifdef CONFIG_RISCV_APLIC_MSI
return aplic_msi_init(dev);
#else
/* Direct delivery mode - just enable the domain */
riscv_aplic_domain_enable(dev, true);
return 0;
return aplic_direct_init(dev);
#endif
}

uint32_t riscv_aplic_get_num_sources(const struct device *dev)
{
const struct aplic_cfg *cfg = dev->config;

return cfg->num_sources;
}

#if defined(CONFIG_RISCV_APLIC_DIRECT)
/* With direct delivery mode enabled the APLIC must register an IRQ handler
* on initialization.
*/
#define APLIC_INTC_IRQ_FUNC_DECLARE(inst) static void aplic_irq_config_func_##inst(void)
#define APLIC_INTC_IRQ_FUNC_DEFINE(inst) \
static void aplic_irq_config_func_##inst(void) \
{ \
IRQ_CONNECT(DT_INST_IRQN(inst), 0, aplic_irq_handler, DEVICE_DT_INST_GET(inst), \
0); \
irq_enable(DT_INST_IRQN(inst)); \
}

#define APLIC_INIT(inst) \
IRQ_PARENT_ENTRY_DEFINE(aplic##inst, DEVICE_DT_INST_GET(inst), DT_INST_IRQN(inst), \
INTC_INST_ISR_TBL_OFFSET(inst), \
DT_INST_INTC_GET_AGGREGATOR_LEVEL(inst)); \
APLIC_INTC_IRQ_FUNC_DECLARE(inst); \
static struct aplic_data aplic_data_##inst; \
static const struct aplic_cfg aplic_cfg_##inst = { \
.base = DT_INST_REG_ADDR(inst), \
.num_sources = DT_INST_PROP(inst, riscv_num_sources), \
IF_ENABLED(CONFIG_RISCV_APLIC_MSI, \
(.imsic_addr = DT_REG_ADDR(DT_INST_PHANDLE(inst, msi_parent)),)) \
.max_prio = DT_INST_PROP(inst, riscv_max_priority), \
.irq_config_func = aplic_irq_config_func_##inst, \
.isr_table = &_sw_isr_table[INTC_INST_ISR_TBL_OFFSET(inst)], \
}; \
APLIC_INTC_IRQ_FUNC_DEFINE(inst) \
DEVICE_DT_INST_DEFINE(inst, aplic_init, NULL, &aplic_data_##inst, &aplic_cfg_##inst, \
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
#else
/* APLIC_MSI interrupts are serviced by separate IMSIC driver */
#define APLIC_INIT(inst) \
static struct aplic_data aplic_data_##inst; \
static const struct aplic_cfg aplic_cfg_##inst = { \
.base = DT_INST_REG_ADDR(inst), \
.num_sources = DT_INST_PROP(inst, riscv_num_sources), \
IF_ENABLED(CONFIG_RISCV_APLIC_MSI, \
(.imsic_addr = DT_REG_ADDR(DT_INST_PHANDLE(inst, msi_parent)),)) }; \
DEVICE_DT_INST_DEFINE(inst, aplic_init, NULL, &aplic_data_##inst, &aplic_cfg_##inst, \
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
#endif

DT_INST_FOREACH_STATUS_OKAY(APLIC_INIT)

uint32_t riscv_aplic_get_num_sources(const struct device *dev)
{
const struct aplic_cfg *cfg = dev->config;

return cfg->num_sources;
}
166 changes: 166 additions & 0 deletions drivers/interrupt_controller/intc_riscv_aplic_direct.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/drivers/interrupt_controller/riscv_aia.h>
#include <zephyr/drivers/interrupt_controller/riscv_aplic_direct.h>
#include <zephyr/arch/riscv/icsr.h>
#include <zephyr/arch/riscv/irq.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>
#include <zephyr/sw_isr_table.h>
#include <zephyr/irq.h>

#include "sw_isr_common.h"
#include "intc_riscv_aplic_priv.h"

LOG_MODULE_REGISTER(intc_riscv_aplic_direct, CONFIG_LOG_DEFAULT_LEVEL);

static uint32_t save_irq[CONFIG_MP_MAX_NUM_CPUS];
static const struct device *save_dev[CONFIG_MP_MAX_NUM_CPUS];

int riscv_aplic_direct_mode_enable(const struct device *dev, bool enable)
{
const struct aplic_cfg *cfg = dev->config;
struct aplic_data *data = dev->data;
k_spinlock_key_t key = k_spin_lock(&data->lock);
uint32_t v = rd32(cfg->base, APLIC_DOMAINCFG);

if (enable) {
/* Do not modify other bits - respect hardware/props configuration.
*/
v |= APLIC_DOMAINCFG_DM;
} else {
v &= ~APLIC_DOMAINCFG_DM;
}
wr32(cfg->base, APLIC_DOMAINCFG, v);

k_spin_unlock(&data->lock, key);

return 0;
}

int riscv_aplic_is_enabled(uint32_t irq)
{
const struct device *dev = riscv_aplic_get_dev();
const struct aplic_cfg *cfg = dev->config;
const uint32_t local_irq = irq_from_level_2(irq);

if (local_irq == 0 || local_irq > cfg->num_sources) {
return 0;
}

uint32_t enabled = rd32(cfg->base, APLIC_SETIENUM);

return !!enabled;
}

int riscv_aplic_set_priority(uint32_t irq, uint32_t prio)
{
const struct device *dev = riscv_aplic_get_dev();
const struct aplic_cfg *cfg = dev->config;
struct aplic_data *data = dev->data;
const uint32_t local_irq = irq_from_level_2(irq);

if (local_irq == 0U || local_irq > cfg->num_sources) {
return -EINVAL;
}

uint32_t target_offset = aplic_target_off(local_irq);
/* Maintain configured Hart ID in target register until IRQ affinity support is added */
uint32_t hart_id = rd32(cfg->base, target_offset) >> APLIC_TARGET_HART_SHIFT;

if (prio > cfg->max_prio) {
LOG_WRN("AIA-APLIC-Direct: Invalid priority specified (irq %u, prio %u, max_prio "
"%u)",
irq, prio, cfg->max_prio);
return -EINVAL;
}

k_spinlock_key_t key = k_spin_lock(&data->lock);

wr32(cfg->base, target_offset, (hart_id << APLIC_TARGET_HART_SHIFT) | prio);
k_spin_unlock(&data->lock, key);

return 0;
}

unsigned int riscv_aplic_get_saved_irq(void)
{
return save_irq[arch_curr_cpu()->id];
}

const struct device *riscv_aplic_get_saved_dev(void)
{
return save_dev[arch_curr_cpu()->id];
}

void aplic_irq_handler(const struct device *dev)
{
const struct aplic_cfg *cfg = dev->config;
const struct _isr_table_entry *ite;

/* Claim the interrupt - Clears pending bit if possible */
const uint32_t claimi_offset = aplic_claimi_off(arch_proc_id());
const uint32_t local_irq = rd32(cfg->base, claimi_offset) >> APLIC_INTERRUPT_IDENTITY_SHIFT;

/*
* Save IRQ in save_irq. To be used, if need be, by
* subsequent handlers registered in the _sw_isr_table table,
* as IRQ number held by the claim_complete register is
* cleared upon read.
*/
save_irq[arch_curr_cpu()->id] = local_irq;
save_dev[arch_curr_cpu()->id] = dev;

/*
* If the IRQ is out of range, call z_irq_spurious.
* A call to z_irq_spurious will not return.
*/
if (local_irq == 0U || local_irq > cfg->num_sources) {
z_irq_spurious(NULL);
return;
}

/* Call the corresponding IRQ handler in _sw_isr_table */
ite = &cfg->isr_table[local_irq];
ite->isr(ite->arg);
}

int aplic_direct_init(const struct device *dev)
{
const struct aplic_cfg *cfg = dev->config;

riscv_aplic_domain_enable(dev, true);

/* Explicitly enable direct delivery mode */
riscv_aplic_direct_mode_enable(dev, true);

/* Sources and Targets, Set all interrupt source modes as Inactive
* Source[0] is invalid, first register corresponds to Source[1]
*/
for (uint32_t src_num = 1; src_num <= cfg->num_sources; src_num++) {
/* Sources -- Delegated = 0, Source Mode = 0 (Inactive)
* Targets -- When source[i] is set as inactive, target[i] is RAZ/WI
* En/Pend -- When source[i] is inactive, interrupt pending and enable bits are also
* RAZ/WI
*/
wr32(cfg->base, aplic_sourcecfg_off(src_num), APLIC_SM_INACTIVE);
}

for (uint32_t cpu = 0; cpu < arch_num_cpus(); cpu++) {
/* idelivery = 0b1 -- Interrupt delivery is enabled */
wr32(cfg->base, aplic_idelivery_off(cpu), APLIC_IDC_IDELIVERY_ENABLE);
/* ithreshold = 0b0 -- All enabled sources can signal */
wr32(cfg->base, aplic_ithreshold_off(cpu), APLIC_IDC_ITHRESHOLD);
}

/* Configure IRQ for APLIC driver */
cfg->irq_config_func();

return 0;
}
7 changes: 7 additions & 0 deletions drivers/interrupt_controller/intc_riscv_aplic_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,19 @@
#include <zephyr/sys/util.h>
#include <zephyr/types.h>

typedef void (*riscv_aplic_irq_config_func_t)(void);

struct aplic_cfg {
uintptr_t base;
uint32_t num_sources;
#ifdef CONFIG_RISCV_APLIC_MSI
uint32_t imsic_addr;
#endif
#ifdef CONFIG_RISCV_APLIC_DIRECT
uint32_t max_prio;
riscv_aplic_irq_config_func_t irq_config_func;
const struct _isr_table_entry *isr_table;
#endif
};

struct aplic_data {
Expand Down
Loading
Loading