Skip to content

Commit

Permalink
drivers: interrupt_controller: Add arm v8m nvic device driver
Browse files Browse the repository at this point in the history
Adds naive nvic device driver implementation for demo
purposes

Signed-off-by: Bjarki Arge Andreasen <bjarki@arge-andreasen.me>
  • Loading branch information
bjarki-andreasen committed Jan 7, 2024
1 parent 3cc749e commit d25a73c
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ add_subdirectory_ifdef(CONFIG_I2S i2s)
add_subdirectory_ifdef(CONFIG_I3C i3c)
add_subdirectory_ifdef(CONFIG_IEEE802154 ieee802154)
add_subdirectory_ifdef(CONFIG_INPUT input)
add_subdirectory_ifdef(CONFIG_INTC intc)
add_subdirectory_ifdef(CONFIG_IPM ipm)
add_subdirectory_ifdef(CONFIG_KSCAN kscan)
add_subdirectory_ifdef(CONFIG_LED led)
Expand Down
1 change: 1 addition & 0 deletions drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ source "drivers/i2s/Kconfig"
source "drivers/i3c/Kconfig"
source "drivers/ieee802154/Kconfig"
source "drivers/input/Kconfig"
source "drivers/intc/Kconfig"
source "drivers/interrupt_controller/Kconfig"
source "drivers/interrupt_controller/Kconfig.shared_irq"
source "drivers/ipm/Kconfig"
Expand Down
6 changes: 6 additions & 0 deletions drivers/intc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright (c) 2023 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0

zephyr_library()

zephyr_library_sources_ifdef(CONFIG_INTC_ARM_V8M_NVIC intc_arm_v8m_nvic.c)
11 changes: 11 additions & 0 deletions drivers/intc/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright (c) 2023 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0

menuconfig INTC
bool "INTC (INTerrupt Controller) drivers"

if INTC

rsource "Kconfig.arm_v8m_nvic"

endif # INTC
7 changes: 7 additions & 0 deletions drivers/intc/Kconfig.arm_v8m_nvic
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2023 Bjarki Arge Andreasen
# SPDX-License-Identifier: Apache-2.0

config INTC_ARM_V8M_NVIC
bool "Arm V8M Nested Vector Interrupt Controller"
default y
depends on DT_HAS_ARM_V8M_NVIC_ENABLED
223 changes: 223 additions & 0 deletions drivers/intc/intc_arm_v8m_nvic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/*
* Copyright (c) 2023 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <zephyr/drivers/intc.h>
#include <zephyr/drivers/intc/direct_irq_handler.h>
#include <cmsis_core.h>
#include <zephyr/sys/barrier.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/linker/sections.h>

#define DT_DRV_COMPAT arm_v8m_nvic

struct intc_arm_v8m_nvic_data {
struct k_spinlock lock;
};

struct intc_arm_v8m_nvic_config {
uint8_t max_irq_priority;
uint16_t num_irqs;
struct intc_irq **irqs;
};

typedef void (*intc_arm_v8m_nvic_vth_t)(void);

static int arm_v8m_nvic_request_irq(struct intc_irq *irq)
{
const struct intc_irq_spec *spec = irq->spec;
const struct device *intc = spec->intc;
struct intc_arm_v8m_nvic_data *data = intc->data;
const struct intc_arm_v8m_nvic_config *config = intc->config;

if (spec->irq >= config->num_irqs) {
return -ENODEV;
}

if (spec->priority > config->max_irq_priority) {
return -EINVAL;
}

K_SPINLOCK(&data->lock) {
NVIC_DisableIRQ(spec->irq);
NVIC_ClearPendingIRQ(spec->irq);
NVIC_SetPriority(spec->irq, spec->priority);
config->irqs[spec->irq] = irq;
}

return 0;
}

static int arm_v8m_nvic_enable_irq(const struct intc_irq_spec *spec)
{
const struct device *intc = spec->intc;
struct intc_arm_v8m_nvic_data *data = intc->data;
const struct intc_arm_v8m_nvic_config *config = intc->config;

if (spec->irq >= config->num_irqs) {
return -ENODEV;
}

K_SPINLOCK(&data->lock) {
NVIC_EnableIRQ(spec->irq);
}

return 0;
}

static int arm_v8m_nvic_disable_irq(const struct intc_irq_spec *spec)
{
const struct device *intc = spec->intc;
struct intc_arm_v8m_nvic_data *data = intc->data;
const struct intc_arm_v8m_nvic_config *config = intc->config;

if (spec->irq >= config->num_irqs) {
return -ENODEV;
}

K_SPINLOCK(&data->lock) {
NVIC_DisableIRQ(spec->irq);
}

return 0;
}

static int arm_v8m_nvic_irq_is_enabled(const struct intc_irq_spec *spec)
{
const struct device *intc = spec->intc;
struct intc_arm_v8m_nvic_data *data = intc->data;
const struct intc_arm_v8m_nvic_config *config = intc->config;
int ret;

if (spec->irq >= config->num_irqs) {
return -ENODEV;
}

K_SPINLOCK(&data->lock) {
ret = NVIC_GetEnableIRQ(spec->irq);
}

return ret;
}

static int arm_v8m_nvic_trigger_irq(const struct intc_irq_spec *spec)
{
const struct device *intc = spec->intc;
struct intc_arm_v8m_nvic_data *data = intc->data;
const struct intc_arm_v8m_nvic_config *config = intc->config;

if (spec->irq >= config->num_irqs) {
return -ENODEV;
}

K_SPINLOCK(&data->lock) {
NVIC_SetPendingIRQ(spec->irq);
}

return 0;
}

static int arm_v8m_nvic_release_irq(struct intc_irq *irq)
{
const struct intc_irq_spec *spec = irq->spec;
const struct device *intc = spec->intc;
struct intc_arm_v8m_nvic_data *data = intc->data;
const struct intc_arm_v8m_nvic_config *config = intc->config;

if (spec->irq >= config->num_irqs) {
return -ENODEV;
}

K_SPINLOCK(&data->lock) {
NVIC_DisableIRQ(spec->irq);
config->irqs[spec->irq] = NULL;
}

return 0;
}

static const struct intc_driver_api arm_v8m_nvic_api = {
.request_irq = arm_v8m_nvic_request_irq,
.enable_irq = arm_v8m_nvic_enable_irq,
.disable_irq = arm_v8m_nvic_disable_irq,
.irq_enabled = arm_v8m_nvic_irq_is_enabled,
.trigger_irq = arm_v8m_nvic_trigger_irq,
.release_irq = arm_v8m_nvic_release_irq,
};

static inline uint32_t arm_v8m_nvic_get_active_irq_num(void)
{
return __get_IPSR() - 16;
}

#define ARM_V8M_NVIC_DEFAULT_IRQ_HANDLER_DEFINE(inst) \
INTC_DT_ARM_V8M_NVIC_DIRECT_IRQ_HANDLER_DEFINE(arm_v8m_nvic_default_handler##inst) \
{ \
uint32_t irq_num = arm_v8m_nvic_get_active_irq_num(); \
struct intc_irq **irqs = arm_v8m_nvic_irqs##inst; \
struct intc_irq *irq = irqs[irq_num]; \
\
if (irq != NULL) { \
irq->handler(irq); \
} else { \
while (1) { \
\
} \
} \
\
return (INTC_DIRECT_IRQ_HANDLER_FLAG_PM | INTC_DIRECT_IRQ_HANDLER_FLAG_SWAP); \
}

#define ARM_V8M_NVIC_IRQ_HANDLER_DEFINE(i, inst) \
__weak void INTC_DT_INST_IRQ_HANDLER_NAME(inst, i)(void) \
{ \
UTIL_CAT(arm_v8m_nvic_default_handler, inst)(); \
}

#define ARM_V8M_NVIC_IRQ_HANDLERS_DEFINE(inst, n) \
LISTIFY(n, \
ARM_V8M_NVIC_IRQ_HANDLER_DEFINE, \
(), \
inst \
)

#define ARM_V8M_NVIC_VECTOR_DEFINE(i, inst) \
INTC_DT_INST_IRQ_HANDLER_NAME(inst, i)

#define ARM_V8M_NVIC_VECTORS_DEFINE(inst, n) \
LISTIFY(n, \
ARM_V8M_NVIC_VECTOR_DEFINE, \
(,), \
inst \
)

#define ARM_V8M_NVIC_VECTOR_TABLE_DEFINE(inst, n) \
intc_arm_v8m_nvic_vth_t __irq_vector_table _irq_vector_table[] = { \
ARM_V8M_NVIC_VECTORS_DEFINE(inst, n) \
};

#define ARM_V8M_NVIC_DEVICE(inst) \
static struct intc_irq *arm_v8m_nvic_irqs##inst[DT_INST_PROP(inst, arm_num_irq)]; \
\
static struct intc_arm_v8m_nvic_data arm_v8m_nvic_data##inst; \
\
static struct intc_arm_v8m_nvic_config arm_v8m_nvic_config##inst = { \
.max_irq_priority = BIT_MASK(DT_INST_PROP(inst, arm_num_irq_priority_bits)), \
.num_irqs = DT_INST_PROP(inst, arm_num_irq), \
.irqs = arm_v8m_nvic_irqs##inst, \
}; \
\
ARM_V8M_NVIC_DEFAULT_IRQ_HANDLER_DEFINE(inst) \
\
ARM_V8M_NVIC_IRQ_HANDLERS_DEFINE(inst, DT_INST_PROP(inst, arm_num_irq)) \
\
ARM_V8M_NVIC_VECTOR_TABLE_DEFINE(inst, DT_INST_PROP(inst, arm_num_irq)) \
\
DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &arm_v8m_nvic_data##inst, \
&arm_v8m_nvic_config##inst, PRE_KERNEL_1, 0, \
&arm_v8m_nvic_api);

DT_INST_FOREACH_STATUS_OKAY(ARM_V8M_NVIC_DEVICE)
4 changes: 4 additions & 0 deletions dts/bindings/interrupt-controller/arm,v8m-nvic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ properties:
type: int
description: number of bits of IRQ priorities

arm,num-irq:
type: int
description: number of connected interrupt lines

"#interrupt-cells":
const: 2

Expand Down
58 changes: 58 additions & 0 deletions include/zephyr/drivers/intc/arm_v8m_nvic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2023 Bjarki Arge Andreasen
*
* SPDX-License-Identifier: Apache-2.0
*/

extern void z_arm_int_exit(void);

#ifdef CONFIG_TRACING_ISR
extern void sys_trace_isr_enter(void);
extern void sys_trace_isr_exit(void);
#endif

#ifdef CONFIG_PM
extern void _arch_isr_direct_pm(void);
#endif

static inline void arm_v8m_nvic_direct_header(void)
{
#ifdef CONFIG_TRACING_ISR
sys_trace_isr_enter();
#endif
}

static inline void arm_v8m_nvic_direct_pm(uint32_t flags)
{
#ifdef CONFIG_PM
if (flags & INTC_DIRECT_IRQ_HANDLER_FLAG_PM) {
_arch_isr_direct_pm();
}
#endif
}

static inline void arm_v8m_nvic_direct_footer(uint32_t flags)
{
#ifdef CONFIG_TRACING_ISR
sys_trace_isr_exit();
#endif
if (flags & INTC_DIRECT_IRQ_HANDLER_FLAG_SWAP) {
z_arm_int_exit();
}
}

#define INTC_DT_ARM_V8M_NVIC_DIRECT_IRQ_HANDLER_DEFINE(name) \
static inline uint32_t UTIL_CAT(name, _body)(void); \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wattributes\"") \
__attribute__((interrupt("IRQ"))) \
void name(void) \
{ \
uint32_t flags; \
arm_v8m_nvic_direct_header(); \
flags = UTIL_CAT(name, _body)(); \
arm_v8m_nvic_direct_pm(flags); \
arm_v8m_nvic_direct_footer(flags); \
} \
_Pragma("GCC diagnostic pop") \
uint32_t UTIL_CAT(name, _body)(void)
1 change: 1 addition & 0 deletions include/zephyr/drivers/intc/direct_irq_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,6 @@
INTC_DT_DIRECT_IRQ_HANDLER_DEFINE_BY_NAME(DT_DRV_INST(inst), name)

/** Architecture specific implementations of direct IRQ handlers are included here */
#include <zephyr/drivers/intc/arm_v8m_nvic.h>

#endif /* ZEPHYR_INCLUDE_DRIVERS_INTC_DIRECT_IRQ_HANDLER_H_ */

0 comments on commit d25a73c

Please sign in to comment.