From 878730f2e1482ec23a04df25ec6024b8d22fc641 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Sun, 7 Jan 2024 19:20:49 +0100 Subject: [PATCH] drivers: interrupt_controller: Add arm v8m nvic device driver Adds naive nvic device driver implementation for demo purposes Signed-off-by: Bjarki Arge Andreasen --- drivers/CMakeLists.txt | 1 + drivers/Kconfig | 1 + drivers/intc/CMakeLists.txt | 6 + drivers/intc/Kconfig | 11 + drivers/intc/Kconfig.arm_v8m_nvic | 7 + drivers/intc/intc_arm_v8m_nvic.c | 223 ++++++++++++++++++ .../interrupt-controller/arm,v8m-nvic.yaml | 4 + include/zephyr/drivers/intc/arm_v8m_nvic.h | 58 +++++ .../zephyr/drivers/intc/direct_irq_handler.h | 1 + 9 files changed, 312 insertions(+) create mode 100644 drivers/intc/CMakeLists.txt create mode 100644 drivers/intc/Kconfig create mode 100644 drivers/intc/Kconfig.arm_v8m_nvic create mode 100644 drivers/intc/intc_arm_v8m_nvic.c create mode 100644 include/zephyr/drivers/intc/arm_v8m_nvic.h diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 6310146bb3d47e..12f474ff3e93fe 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -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) diff --git a/drivers/Kconfig b/drivers/Kconfig index 45b03e4829c6b4..86bdb26fd05721 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -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" diff --git a/drivers/intc/CMakeLists.txt b/drivers/intc/CMakeLists.txt new file mode 100644 index 00000000000000..6f1993c2150535 --- /dev/null +++ b/drivers/intc/CMakeLists.txt @@ -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) diff --git a/drivers/intc/Kconfig b/drivers/intc/Kconfig new file mode 100644 index 00000000000000..add612f54f22dd --- /dev/null +++ b/drivers/intc/Kconfig @@ -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 diff --git a/drivers/intc/Kconfig.arm_v8m_nvic b/drivers/intc/Kconfig.arm_v8m_nvic new file mode 100644 index 00000000000000..f11dd3e3e64e8a --- /dev/null +++ b/drivers/intc/Kconfig.arm_v8m_nvic @@ -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 diff --git a/drivers/intc/intc_arm_v8m_nvic.c b/drivers/intc/intc_arm_v8m_nvic.c new file mode 100644 index 00000000000000..55388f14f16958 --- /dev/null +++ b/drivers/intc/intc_arm_v8m_nvic.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2023 Bjarki Arge Andreasen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#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) diff --git a/dts/bindings/interrupt-controller/arm,v8m-nvic.yaml b/dts/bindings/interrupt-controller/arm,v8m-nvic.yaml index 9c69bfb20439e2..caa9246258fdca 100644 --- a/dts/bindings/interrupt-controller/arm,v8m-nvic.yaml +++ b/dts/bindings/interrupt-controller/arm,v8m-nvic.yaml @@ -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 diff --git a/include/zephyr/drivers/intc/arm_v8m_nvic.h b/include/zephyr/drivers/intc/arm_v8m_nvic.h new file mode 100644 index 00000000000000..62df450bb5c528 --- /dev/null +++ b/include/zephyr/drivers/intc/arm_v8m_nvic.h @@ -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) diff --git a/include/zephyr/drivers/intc/direct_irq_handler.h b/include/zephyr/drivers/intc/direct_irq_handler.h index 5dcfdbb3db95cc..d088d15c005add 100644 --- a/include/zephyr/drivers/intc/direct_irq_handler.h +++ b/include/zephyr/drivers/intc/direct_irq_handler.h @@ -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 #endif /* ZEPHYR_INCLUDE_DRIVERS_INTC_DIRECT_IRQ_HANDLER_H_ */