-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drivers: interrupt_controller: Add arm v8m nvic device driver
Adds naive nvic device driver implementation for demo purposes Signed-off-by: Bjarki Arge Andreasen <bjarki@arge-andreasen.me>
- Loading branch information
1 parent
3cc749e
commit d25a73c
Showing
9 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters