diff --git a/cpu/fe310/Kconfig b/cpu/fe310/Kconfig index 1335636419..bdadfff1b5 100644 --- a/cpu/fe310/Kconfig +++ b/cpu/fe310/Kconfig @@ -21,6 +21,7 @@ config CPU_FAM_FE310 select HAS_PERIPH_CPUID select HAS_PERIPH_GPIO select HAS_PERIPH_GPIO_IRQ + select HAS_PERIPH_PLIC select HAS_PERIPH_PM select HAS_PERIPH_WDT select HAS_CPP diff --git a/cpu/fe310/Makefile.features b/cpu/fe310/Makefile.features index 14b6ff70a2..9f46d6ce83 100644 --- a/cpu/fe310/Makefile.features +++ b/cpu/fe310/Makefile.features @@ -4,6 +4,7 @@ FEATURES_PROVIDED += cpp FEATURES_PROVIDED += libstdcpp FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio periph_gpio_irq +FEATURES_PROVIDED += periph_plic FEATURES_PROVIDED += periph_pm FEATURES_PROVIDED += periph_wdt FEATURES_PROVIDED += ssp diff --git a/cpu/fe310/include/plic.h b/cpu/fe310/include/plic.h new file mode 100644 index 0000000000..4e49176603 --- /dev/null +++ b/cpu/fe310/include/plic.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_fe310 + * @{ + * + * @file + * @brief Platform-Level interrupt controller driver + * + * @author Koen Zandberg + * @} + */ + +#ifndef PLIC_H +#define PLIC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief PLIC callback declaration + * + * @param irq Interrupt number + */ +typedef void (*plic_isr_cb_t)(int irq); + +/** + * @brief Initialize the Platform-level interrupt controller + */ +void plic_init(void); + +/** + * @brief Disable an interrupt on the PLIC + * + * @param irq Interrupt number + */ +void plic_disable_interrupt(unsigned irq); + +/** + * @brief Enable an interrupt on the PLIC + * + * @param irq Interrupt number + */ +void plic_enable_interrupt(unsigned irq); + +/** + * @brief Set an interrupt priority + * + * @param irq Interrupt number + * @param priority Priority + */ +void plic_set_priority(unsigned irq, unsigned priority); + +/** + * @brief Set the interrupt callback + * + * @param irq Interrupt number + * @param cb Callback to call on interrupt + */ +void plic_set_isr_cb(unsigned irq, plic_isr_cb_t cb); + +/** + * @brief External ISR callback + */ + +/** + * @brief Interrupt handler for the PLIC + * + * Must be called from the trap handler when an interrupt from the PLIC is + * pending + */ +void plic_isr_handler(void); + +#ifdef __cplusplus +} +#endif + +#endif /* PLIC_H */ +/** @} */ diff --git a/cpu/fe310/periph/plic.c b/cpu/fe310/periph/plic.c new file mode 100644 index 0000000000..a0c94e5344 --- /dev/null +++ b/cpu/fe310/periph/plic.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_fe310 + * @{ + * + * @file + * @brief Platform-Level interrupt controller driver + * + * @author Koen Zandberg + * @} + */ + +#include "vendor/encoding.h" +#include "vendor/platform.h" +#include "cpu.h" +#include "plic.h" + +/* PLIC external ISR function list */ +static plic_isr_cb_t _ext_isrs[PLIC_NUM_INTERRUPTS]; + +static inline volatile uint32_t *_get_claim_complete_addr(void) +{ + uint32_t hart_id = read_csr(mhartid); + + /* Construct the claim address */ + return &PLIC_REG(PLIC_CLAIM_OFFSET + + (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET)); +} + +static inline volatile uint32_t *_get_threshold_addr(void) +{ + uint32_t hart_id = read_csr(mhartid); + + /* Construct the claim address */ + return &PLIC_REG(PLIC_THRESHOLD_OFFSET + + (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET)); +} + +static inline volatile uint32_t *_get_irq_reg(unsigned irq) +{ + uint32_t hart_id = read_csr(mhartid); + + return &PLIC_REG(PLIC_ENABLE_OFFSET + + (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET)) + + (irq >> 5); /* Intentionally outside the PLIC_REG macro */ +} + +void plic_enable_interrupt(unsigned irq) +{ + volatile uint32_t *irq_reg = _get_irq_reg(irq); + + __atomic_fetch_or(irq_reg, 1 << (irq & 0x1f), __ATOMIC_RELAXED); +} + +void plic_disable_interrupt(unsigned irq) +{ + volatile uint32_t *irq_reg = _get_irq_reg(irq); + + __atomic_fetch_and(irq_reg, ~(1 << (irq & 0x1f)), __ATOMIC_RELAXED); +} + +void plic_set_threshold(unsigned threshold) +{ + volatile uint32_t *plic_threshold = _get_threshold_addr(); + + *plic_threshold = threshold; + +} + +void plic_set_priority(unsigned irq, unsigned priority) +{ + assert(irq <= PLIC_NUM_INTERRUPTS); + assert(irq != 0); + *(&PLIC_REG(PLIC_PRIORITY_OFFSET) + irq) = priority; +} + +static void plic_complete_interrupt(unsigned irq) +{ + volatile uint32_t *complete_addr = _get_claim_complete_addr(); + + *complete_addr = irq; +} + +static unsigned plic_claim_interrupt(void) +{ + return *_get_claim_complete_addr(); +} + +void plic_set_isr_cb(unsigned irq, plic_isr_cb_t cb) +{ + assert(irq <= PLIC_NUM_INTERRUPTS); + assert(irq != 0); + _ext_isrs[irq] = cb; +} + +void plic_init(void) +{ + for (unsigned i = 1; i <= PLIC_NUM_INTERRUPTS; i++) { + plic_disable_interrupt(i); + plic_set_priority(i, 0); + } + + plic_set_threshold(0); +} + +void plic_isr_handler(void) +{ + unsigned irq = plic_claim_interrupt(); + + /* Don't check here, just crash hard if no handler is available */ + _ext_isrs[irq](irq); + + plic_complete_interrupt(irq); +} diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index c621830b66..caba82a5ca 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -180,6 +180,11 @@ config HAS_PERIPH_MCG help Indicates that an MCG peripheral is present. +config HAS_PERIPH_PLIC + bool + help + Indicates that a RISC-V Platform-local Interrupt Controller (PLIC) peripheral is present. + config HAS_PERIPH_PM bool help