From 7103ab319a54fd836dd90000586a8c95f36a9f13 Mon Sep 17 00:00:00 2001 From: Bunnaroath Sou Date: Fri, 9 Aug 2019 10:41:15 -0700 Subject: [PATCH] New APIs to better configure and manage CLIC interrupts --- metal/drivers/riscv_cpu.h | 9 +- metal/drivers/sifive_clic0.h | 3 +- metal/interrupt.h | 153 ++++++++- src/drivers/riscv_cpu.c | 56 +++- src/drivers/sifive_clic0.c | 291 ++++++++++++++---- .../sifive_local-external-interrupts0.c | 31 ++ src/interrupt.c | 29 +- 7 files changed, 476 insertions(+), 96 deletions(-) diff --git a/metal/drivers/riscv_cpu.h b/metal/drivers/riscv_cpu.h index 997614d2..ca91e0a9 100644 --- a/metal/drivers/riscv_cpu.h +++ b/metal/drivers/riscv_cpu.h @@ -100,6 +100,7 @@ typedef enum { METAL_INTERRUPT_ID_SW = (METAL_INTERRUPT_ID_BASE + 3), METAL_INTERRUPT_ID_TMR = (METAL_INTERRUPT_ID_BASE + 7), METAL_INTERRUPT_ID_EXT = (METAL_INTERRUPT_ID_BASE + 11), + METAL_INTERRUPT_ID_CSW = (METAL_INTERRUPT_ID_BASE + 12), METAL_INTERRUPT_ID_LC0 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(0)), METAL_INTERRUPT_ID_LC1 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(1)), METAL_INTERRUPT_ID_LC2 = (METAL_INTERRUPT_ID_BASE + METAL_LOCAL_INTR(2)), @@ -164,14 +165,8 @@ struct __metal_driver_vtable_riscv_cpu_intc { void __metal_interrupt_global_enable(void); void __metal_interrupt_global_disable(void); +metal_vector_mode __metal_controller_interrupt_vector_mode(void); void __metal_controller_interrupt_vector(metal_vector_mode mode, void *vec_table); -__inline__ int __metal_controller_interrupt_is_selective_vectored (void) -{ - uintptr_t val; - - __asm__ volatile ("csrr %0, mtvec" : "=r"(val)); - return ((val & METAL_MTVEC_CLIC_VECTORED) == METAL_MTVEC_CLIC); -} __METAL_DECLARE_VTABLE(__metal_driver_vtable_riscv_cpu_intc) diff --git a/metal/drivers/sifive_clic0.h b/metal/drivers/sifive_clic0.h index db967462..7fef38d1 100644 --- a/metal/drivers/sifive_clic0.h +++ b/metal/drivers/sifive_clic0.h @@ -34,7 +34,8 @@ __METAL_DECLARE_VTABLE(__metal_driver_vtable_sifive_clic0) struct __metal_driver_sifive_clic0 { struct metal_interrupt controller; int init_done; - metal_interrupt_handler_t metal_mtvt_table[__METAL_CLIC_SUBINTERRUPTS]; + int pad[14]; + metal_interrupt_vector_handler_t metal_mtvt_table[__METAL_CLIC_SUBINTERRUPTS]; __metal_interrupt_data metal_exint_table[__METAL_CLIC_SUBINTERRUPTS]; }; #undef __METAL_MACHINE_MACROS diff --git a/metal/interrupt.h b/metal/interrupt.h index 37abcd3d..a276a6af 100644 --- a/metal/interrupt.h +++ b/metal/interrupt.h @@ -26,25 +26,43 @@ typedef enum metal_interrupt_controller_ { typedef enum metal_vector_mode_ { METAL_DIRECT_MODE = 0, METAL_VECTOR_MODE = 1, - METAL_SELECTIVE_VECTOR_MODE = 2, - METAL_HARDWARE_VECTOR_MODE = 3 + METAL_SELECTIVE_NONVECTOR_MODE = 2, + METAL_SELECTIVE_VECTOR_MODE = 3, + METAL_HARDWARE_VECTOR_MODE = 4 } metal_vector_mode; +/*! + * @brief Possible mode of privilege interrupts to operate + */ +typedef enum metal_intr_priv_mode_ { + METAL_INTR_PRIV_M_MODE = 0, + METAL_INTR_PRIV_MU_MODE = 1, + METAL_INTR_PRIV_MSU_MODE = 2 +} metal_intr_priv_mode; + /*! * @brief Function signature for interrupt callback handlers */ typedef void (*metal_interrupt_handler_t) (int, void *); +typedef void (*metal_interrupt_vector_handler_t) (void); struct metal_interrupt; struct metal_interrupt_vtable { void (*interrupt_init)(struct metal_interrupt *controller); + int (*interrupt_set_vector_mode)(struct metal_interrupt *controller, metal_vector_mode mode); + metal_vector_mode (*interrupt_get_vector_mode)(struct metal_interrupt *controller); + int (*interrupt_set_privilege)(struct metal_interrupt *controller, metal_intr_priv_mode priv); + metal_intr_priv_mode (*interrupt_get_privilege)(struct metal_interrupt *controller); + int (*interrupt_clear)(struct metal_interrupt *controller, int id); + int (*interrupt_set)(struct metal_interrupt *controller, int id); int (*interrupt_register)(struct metal_interrupt *controller, int id, metal_interrupt_handler_t isr, void *priv_data); + int (*interrupt_vector_register)(struct metal_interrupt *controller, int id, + metal_interrupt_vector_handler_t isr, void *priv_data); int (*interrupt_enable)(struct metal_interrupt *controller, int id); int (*interrupt_disable)(struct metal_interrupt *controller, int id); - int (*interrupt_vector_enable)(struct metal_interrupt *controller, - int id, metal_vector_mode mode); + int (*interrupt_vector_enable)(struct metal_interrupt *controller, int id); int (*interrupt_vector_disable)(struct metal_interrupt *controller, int id); unsigned int (*interrupt_get_threshold)(struct metal_interrupt *controller); int (*interrupt_set_threshold)(struct metal_interrupt *controller, unsigned int threshold); @@ -85,6 +103,93 @@ __inline__ void metal_interrupt_init(struct metal_interrupt *controller) struct metal_interrupt* metal_interrupt_get_controller(metal_intr_cntrl_type cntrl, int id); +/*! + * @brief Configure vector mode for an interrupt controller + * + * Configure vector mode for an interrupt controller. + * This function must be called after initialization and before + * configuring individual interrupts, registering ISR. + * + * @param controller The handle for the interrupt controller + * @param mode The vector mode of the interrupt controller. + * @return 0 upon success + */ +__inline__ int metal_interrupt_set_vector_mode(struct metal_interrupt *controller, + metal_vector_mode mode) +{ + return controller->vtable->interrupt_set_vector_mode(controller, mode); +} + +/*! + * @brief Get vector mode of a given an interrupt controller + * + * Configure vector mode for an interrupt controller. + * This function must be called after initialization and before + * configuring individual interrupts, registering ISR. + * + * @param controller The handle for the interrupt controller + * @param mode The vector mode of the interrupt controller. + * @return The interrupt vector mode + */ +__inline__ metal_vector_mode metal_interrupt_get_vector_mode(struct metal_interrupt *controller) +{ + return controller->vtable->interrupt_get_vector_mode(controller); +} + +/*! + * @brief Configure privilege mode a of given interrupt controller + * + * Configure privilege mode for a given interrupt controller. + * This function must be called after initialization and before + * configuring individual interrupts, registering ISR. + * + * @param controller The handle for the interrupt controller + * @param privilege The privilege mode of the interrupt controller. + * @return 0 upon success + */ +__inline__ int metal_interrupt_set_privilege(struct metal_interrupt *controller, + metal_intr_priv_mode privilege) +{ + return controller->vtable->interrupt_set_privilege(controller, privilege); +} + +/*! + * @brief Get privilege mode a of given interrupt controller + * + * Get privilege mode for a given interrupt controller. + * This function must be called after initialization and before + * configuring individual interrupts, registering ISR. + * + * @param controller The handle for the interrupt controller + * @return The interrupt privilege mode + */ +__inline__ metal_intr_priv_mode metal_interrupt_get_privilege(struct metal_interrupt *controller) +{ + return controller->vtable->interrupt_get_privilege(controller); +} + +/*! + * @brief clear an interrupt + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to trigger + * @return 0 upon success + */ +__inline__ int metal_interrupt_clear(struct metal_interrupt *controller, int id) +{ + return controller->vtable->interrupt_clear(controller, id); +} + +/*! + * @brief Set an interrupt + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to trigger + * @return 0 upon success + */ +__inline__ int metal_interrupt_set(struct metal_interrupt *controller, int id) +{ + return controller->vtable->interrupt_set(controller, id); +} + /*! * @brief Register an interrupt handler * @param controller The handle for the interrupt controller @@ -101,6 +206,22 @@ __inline__ int metal_interrupt_register_handler(struct metal_interrupt *controll return controller->vtable->interrupt_register(controller, id, handler, priv_data); } +/*! + * @brief Register an interrupt vector handler + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to register + * @param handler The interrupt vector handler callback + * @param priv_data Private data for the interrupt handler + * @return 0 upon success + */ +__inline__ int metal_interrupt_register_vector_handler(struct metal_interrupt *controller, + int id, + metal_interrupt_vector_handler_t handler, + void *priv_data) +{ + return controller->vtable->interrupt_vector_register(controller, id, handler, priv_data); +} + /*! * @brief Enable an interrupt * @param controller The handle for the interrupt controller @@ -123,19 +244,6 @@ __inline__ int metal_interrupt_disable(struct metal_interrupt *controller, int i return controller->vtable->interrupt_disable(controller, id); } -/*! - * @brief Enable an interrupt vector - * @param controller The handle for the interrupt controller - * @param id The interrupt ID to enable - * @param mode The interrupt mode type to enable - * @return 0 upon success - */ -__inline__ int metal_interrupt_vector_enable(struct metal_interrupt *controller, - int id, metal_vector_mode mode) -{ - return controller->vtable->interrupt_vector_enable(controller, id, mode); -} - /*! * @brief Set interrupt threshold level * @param controller The handle for the interrupt controller @@ -181,6 +289,17 @@ inline unsigned int metal_interrupt_get_priority(struct metal_interrupt *control return controller->vtable->interrupt_get_priority(controller, id); } +/*! + * @brief Enable an interrupt vector + * @param controller The handle for the interrupt controller + * @param id The interrupt ID to enable + * @return 0 upon success + */ +__inline__ int metal_interrupt_vector_enable(struct metal_interrupt *controller, int id) +{ + return controller->vtable->interrupt_vector_enable(controller, id); +} + /*! * @brief Disable an interrupt vector * @param controller The handle for the interrupt controller diff --git a/src/drivers/riscv_cpu.c b/src/drivers/riscv_cpu.c index c8921278..33b9b45e 100644 --- a/src/drivers/riscv_cpu.c +++ b/src/drivers/riscv_cpu.c @@ -131,7 +131,7 @@ void __metal_exception_handler (void) { __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); id = mcause & METAL_MCAUSE_CAUSE; if (mcause & METAL_MCAUSE_INTR) { - if ((id < METAL_INTERRUPT_ID_LC0) || + if ((id < METAL_INTERRUPT_ID_CSW) || ((mtvec & METAL_MTVEC_MASK) == METAL_MTVEC_DIRECT)) { priv = intc->metal_int_table[id].exint_data; intc->metal_int_table[id].handler(id, priv); @@ -143,7 +143,7 @@ void __metal_exception_handler (void) { __asm__ volatile ("csrr %0, 0x307" : "=r"(mtvt)); priv = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int; - mtvt_handler = (metal_interrupt_handler_t)mtvt; + mtvt_handler = (metal_interrupt_handler_t)*(uintptr_t *)mtvt; mtvt_handler(id, priv); return; } @@ -153,6 +153,24 @@ void __metal_exception_handler (void) { } } +metal_vector_mode __metal_controller_interrupt_vector_mode (void) +{ + uintptr_t val; + + asm volatile ("csrr %0, mtvec" : "=r"(val)); + val &= METAL_MTVEC_MASK; + + switch (val) { + case METAL_MTVEC_CLIC: + return METAL_SELECTIVE_VECTOR_MODE; + case METAL_MTVEC_CLIC_VECTORED: + return METAL_HARDWARE_VECTOR_MODE; + case METAL_MTVEC_VECTORED: + return METAL_VECTOR_MODE; + } + return METAL_DIRECT_MODE; +} + void __metal_controller_interrupt_vector (metal_vector_mode mode, void *vec_table) { uintptr_t trap_entry, val; @@ -162,12 +180,13 @@ void __metal_controller_interrupt_vector (metal_vector_mode mode, void *vec_tabl trap_entry = (uintptr_t)vec_table; switch (mode) { + case METAL_SELECTIVE_NONVECTOR_MODE: case METAL_SELECTIVE_VECTOR_MODE: - __asm__ volatile ("csrw 0x307, %0" :: "r"(trap_entry | METAL_MTVEC_CLIC)); + __asm__ volatile ("csrw 0x307, %0" :: "r"(trap_entry)); __asm__ volatile ("csrw mtvec, %0" :: "r"(val | METAL_MTVEC_CLIC)); break; case METAL_HARDWARE_VECTOR_MODE: - __asm__ volatile ("csrw 0x307, %0" :: "r"(trap_entry | METAL_MTVEC_CLIC_VECTORED)); + __asm__ volatile ("csrw 0x307, %0" :: "r"(trap_entry)); __asm__ volatile ("csrw mtvec, %0" :: "r"(val | METAL_MTVEC_CLIC_VECTORED)); break; case METAL_VECTOR_MODE: @@ -427,6 +446,27 @@ int __metal_driver_riscv_cpu_controller_interrupt_disable_vector(struct metal_in return -1; } +metal_vector_mode __metal_driver_riscv_cpu_controller_get_vector_mode (struct metal_interrupt *controller) +{ + return __metal_controller_interrupt_vector_mode(); +} + +int __metal_driver_riscv_cpu_controller_set_vector_mode (struct metal_interrupt *controller, + metal_vector_mode mode) +{ + struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller); + + if (mode == METAL_DIRECT_MODE) { + __metal_controller_interrupt_vector(mode, (void *)(uintptr_t)&__metal_exception_handler); + return 0; + } + if (mode == METAL_VECTOR_MODE) { + __metal_controller_interrupt_vector(mode, (void *)&intc->metal_mtvec_table); + return 0; + } + return -1; +} + int __metal_driver_riscv_cpu_controller_command_request (struct metal_interrupt *controller, int cmd, void *data) { @@ -434,8 +474,6 @@ int __metal_driver_riscv_cpu_controller_command_request (struct metal_interrupt return 0; } -extern __inline__ int __metal_controller_interrupt_is_selective_vectored(void); - /* CPU driver !!! */ unsigned long long __metal_driver_cpu_mcycle_get(struct metal_cpu *cpu) @@ -658,12 +696,12 @@ int __metal_driver_cpu_set_exception_pc(struct metal_cpu *cpu, uintptr_t mepc) } __METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_cpu_intc) = { - .controller_vtable.interrupt_init = __metal_driver_riscv_cpu_controller_interrupt_init, + .controller_vtable.interrupt_init = __metal_driver_riscv_cpu_controller_interrupt_init, .controller_vtable.interrupt_register = __metal_driver_riscv_cpu_controller_interrupt_register, .controller_vtable.interrupt_enable = __metal_driver_riscv_cpu_controller_interrupt_enable, .controller_vtable.interrupt_disable = __metal_driver_riscv_cpu_controller_interrupt_disable, - .controller_vtable.interrupt_vector_enable = __metal_driver_riscv_cpu_controller_interrupt_enable_vector, - .controller_vtable.interrupt_vector_disable = __metal_driver_riscv_cpu_controller_interrupt_disable_vector, + .controller_vtable.interrupt_get_vector_mode = __metal_driver_riscv_cpu_controller_get_vector_mode, + .controller_vtable.interrupt_set_vector_mode = __metal_driver_riscv_cpu_controller_set_vector_mode, .controller_vtable.command_request = __metal_driver_riscv_cpu_controller_command_request, }; diff --git a/src/drivers/sifive_clic0.c b/src/drivers/sifive_clic0.c index 961bd80f..12c3dac0 100644 --- a/src/drivers/sifive_clic0.c +++ b/src/drivers/sifive_clic0.c @@ -11,12 +11,6 @@ #include #include -typedef enum metal_priv_mode_ { - METAL_PRIV_M_MODE = 0, - METAL_PRIV_MU_MODE = 1, - METAL_PRIV_MSU_MODE = 2 -} metal_priv_mode; - typedef enum metal_clic_vector_{ METAL_CLIC_NONVECTOR = 0, METAL_CLIC_VECTORED = 1 @@ -30,11 +24,15 @@ struct __metal_clic_cfg { }; const struct __metal_clic_cfg __metal_clic_defaultcfg = { - .nmbits = METAL_PRIV_M_MODE, + .nmbits = METAL_INTR_PRIV_M_MODE, .nlbits = 0, .nvbit = METAL_CLIC_NONVECTOR }; +void __metal_clic0_handler(int id, void *priv) __attribute__((aligned(64))); + +void __metal_clic0_default_vector_handler (void) __attribute__((interrupt, aligned(64))); + struct __metal_clic_cfg __metal_clic0_configuration (struct __metal_driver_sifive_clic0 *clic, struct __metal_clic_cfg *cfg) { @@ -79,7 +77,7 @@ int __metal_clic0_interrupt_set_mode (struct __metal_driver_sifive_clic0 *clic, return 0; } -int __metal_clic0_interrupt_set_level (struct __metal_driver_sifive_clic0 *clic, int id, int level) +int __metal_clic0_interrupt_set_level (struct __metal_driver_sifive_clic0 *clic, int id, unsigned int level) { uint8_t mask, nmmask, nlmask, val; struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); @@ -101,7 +99,7 @@ int __metal_clic0_interrupt_set_level (struct __metal_driver_sifive_clic0 *clic, return 0; } -int __metal_clic0_interrupt_get_level (struct __metal_driver_sifive_clic0 *clic, int id) +unsigned int __metal_clic0_interrupt_get_level (struct __metal_driver_sifive_clic0 *clic, int id) { int level; uint8_t mask, val, freebits, nlbits; @@ -184,7 +182,7 @@ int __metal_clic0_interrupt_get_priority (struct __metal_driver_sifive_clic0 *cl return priority; } -int __metal_clic0_interrupt_set_vector (struct __metal_driver_sifive_clic0 *clic, int id, int enable) +int __metal_clic0_interrupt_set_vector_mode (struct __metal_driver_sifive_clic0 *clic, int id, int enable) { uint8_t mask, val; unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic); @@ -271,36 +269,102 @@ int __metal_clic0_interrupt_is_pending (struct __metal_driver_sifive_clic0 *clic int __metal_clic0_interrupt_set (struct __metal_driver_sifive_clic0 *clic, int id) { + unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic); int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic); - if ((id >= METAL_INTERRUPT_ID_LC0) && (id < num_subinterrupts)) { + if (id < num_subinterrupts) { + __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base + + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIP_BASE + id)) = METAL_ENABLE; + return 0; } - return 0; + return -1; } int __metal_clic0_interrupt_clear (struct __metal_driver_sifive_clic0 *clic, int id) { + unsigned long control_base = __metal_driver_sifive_clic0_control_base((struct metal_interrupt *)clic); int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic); - if ((id >= METAL_INTERRUPT_ID_LC0) && (id < num_subinterrupts)) { + if (id < num_subinterrupts) { + __METAL_ACCESS_ONCE((__metal_io_u8 *)(control_base + + METAL_SIFIVE_CLIC0_MMODE_APERTURE + + METAL_SIFIVE_CLIC0_CLICINTIP_BASE + id)) = METAL_DISABLE; + return 0; + } + return -1; +} + +int __metal_clic0_configure_set_vector_mode (struct __metal_driver_sifive_clic0 *clic, metal_vector_mode mode) +{ + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + + switch (mode) { + case METAL_SELECTIVE_NONVECTOR_MODE: + cfg.nvbit = METAL_CLIC_NONVECTOR; + __metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table); + break; + case METAL_SELECTIVE_VECTOR_MODE: + cfg.nvbit = METAL_CLIC_VECTORED; + __metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table); + break; + case METAL_HARDWARE_VECTOR_MODE: + cfg.nvbit = METAL_CLIC_VECTORED; + __metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table); + break; + default: + return -1; } + __metal_clic0_configuration(clic, &cfg); return 0; } -void __metal_clic0_configure_privilege (struct __metal_driver_sifive_clic0 *clic, metal_priv_mode priv) +metal_vector_mode __metal_clic0_configure_get_vector_mode (struct __metal_driver_sifive_clic0 *clic) +{ + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + metal_vector_mode mode = __metal_controller_interrupt_vector_mode(); + + if (mode == METAL_SELECTIVE_VECTOR_MODE) { + if (cfg.nvbit) { + return METAL_SELECTIVE_VECTOR_MODE; + } else { + return METAL_SELECTIVE_NONVECTOR_MODE; + } + } else { + return mode; + } +} + +int __metal_clic0_configure_set_privilege (struct __metal_driver_sifive_clic0 *clic, metal_intr_priv_mode priv) { struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); cfg.nmbits = priv; __metal_clic0_configuration(clic, &cfg); + return 0; } -void __metal_clic0_configure_level (struct __metal_driver_sifive_clic0 *clic, int level) +metal_intr_priv_mode __metal_clic0_configure_get_privilege (struct __metal_driver_sifive_clic0 *clic) { struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); - cfg.nlbits = level; + return cfg.nmbits; +} + +int __metal_clic0_configure_set_level (struct __metal_driver_sifive_clic0 *clic, int level) +{ + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + + cfg.nlbits = level & 0xF; __metal_clic0_configuration(clic, &cfg); + return 0; +} + +int __metal_clic0_configure_get_level (struct __metal_driver_sifive_clic0 *clic) +{ + struct __metal_clic_cfg cfg = __metal_clic0_configuration(clic, NULL); + + return cfg.nlbits; } unsigned long long __metal_clic0_mtime_get (struct __metal_driver_sifive_clic0 *clic) @@ -337,16 +401,13 @@ int __metal_driver_sifive_clic0_mtimecmp_set(struct metal_interrupt *controller, return 0; } -void __metal_clic0_handler(int id, void *priv) __attribute__((aligned(64))); void __metal_clic0_handler (int id, void *priv) { - int idx; struct __metal_driver_sifive_clic0 *clic = priv; int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts((struct metal_interrupt *)clic); - idx = id - METAL_INTERRUPT_ID_LC0; - if ( (idx < num_subinterrupts) && (clic->metal_mtvt_table[idx]) ) { - clic->metal_mtvt_table[idx](id, clic->metal_exint_table[idx].exint_data); + if ( (id < num_subinterrupts) && (clic->metal_exint_table[id].handler) ) { + clic->metal_exint_table[id].handler(id, clic->metal_exint_table[id].exint_data); } } @@ -354,6 +415,10 @@ void __metal_clic0_default_handler (int id, void *priv) { metal_shutdown(300); } +void __metal_clic0_default_vector_handler (void) { + metal_shutdown(400); +} + void __metal_driver_sifive_clic0_init (struct metal_interrupt *controller) { struct __metal_driver_sifive_clic0 *clic = @@ -367,8 +432,8 @@ void __metal_driver_sifive_clic0_init (struct metal_interrupt *controller) /* Initialize ist parent controller, aka cpu_intc. */ intc->vtable->interrupt_init(intc); - __metal_controller_interrupt_vector(METAL_SELECTIVE_VECTOR_MODE, - (void *)(uintptr_t)&__metal_clic0_handler); + __metal_controller_interrupt_vector(METAL_SELECTIVE_NONVECTOR_MODE, + &clic->metal_mtvt_table); /* * Register its interrupts with with parent controller, @@ -388,8 +453,10 @@ void __metal_driver_sifive_clic0_init (struct metal_interrupt *controller) level = (1 << cfg.nlbits) - 1; num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller); - for (int i = 0; i < num_subinterrupts; i++) { + clic->metal_mtvt_table[0] = &__metal_clic0_handler; + for (int i = 1; i < num_subinterrupts; i++) { clic->metal_mtvt_table[i] = NULL; + clic->metal_exint_table[i].handler = NULL; clic->metal_exint_table[i].sub_int = NULL; clic->metal_exint_table[i].exint_data = NULL; __metal_clic0_interrupt_disable(clic, i); @@ -402,31 +469,71 @@ void __metal_driver_sifive_clic0_init (struct metal_interrupt *controller) int __metal_driver_sifive_clic0_register (struct metal_interrupt *controller, int id, metal_interrupt_handler_t isr, void *priv) -{ +{ int rc = -1; int num_subinterrupts; struct __metal_driver_sifive_clic0 *clic = (struct __metal_driver_sifive_clic0 *)(controller); struct metal_interrupt *intc = __metal_driver_sifive_clic0_interrupt_parent(controller); + metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic); + + if ( ( (mode == METAL_SELECTIVE_VECTOR_MODE) && + (__metal_clic0_interrupt_is_vectored(clic, id)) ) || + (mode == METAL_HARDWARE_VECTOR_MODE) || + (mode == METAL_VECTOR_MODE) || + (mode == METAL_DIRECT_MODE) ) { + return rc; + } /* Register its interrupts with parent controller */ - if ( id < METAL_INTERRUPT_ID_LC0) { + if (id < METAL_INTERRUPT_ID_CSW) { return intc->vtable->interrupt_register(intc, id, isr, priv); } - - /* + + /* * CLIC (sub-interrupts) devices interrupts start at 16 but offset from 0 - * Reset the IDs to reflects this. + * Reset the IDs to reflects this. */ - id -= METAL_INTERRUPT_ID_LC0; num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller); + if (id < num_subinterrupts) { + if ( isr) { + clic->metal_exint_table[id].handler = isr; + clic->metal_exint_table[id].exint_data = priv; + } else { + clic->metal_exint_table[id].handler = __metal_clic0_default_handler; + clic->metal_exint_table[id].sub_int = priv; + } + rc = 0; + } + return rc; +} + +int __metal_driver_sifive_clic0_vector_register (struct metal_interrupt *controller, + int id, metal_interrupt_vector_handler_t isr, + void *priv) +{ + int rc = -1; + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + struct metal_interrupt *intc = + __metal_driver_sifive_clic0_interrupt_parent(controller); + int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller); + metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic); + + if ((mode != METAL_SELECTIVE_VECTOR_MODE) && (mode != METAL_HARDWARE_VECTOR_MODE)) { + return rc; + } + if ((mode == METAL_SELECTIVE_VECTOR_MODE) && + (__metal_clic0_interrupt_is_vectored(clic, id) == 0) ) { + return rc; + } if (id < num_subinterrupts) { if ( isr) { clic->metal_mtvt_table[id] = isr; clic->metal_exint_table[id].exint_data = priv; } else { - clic->metal_mtvt_table[id] = __metal_clic0_default_handler; + clic->metal_mtvt_table[id] = __metal_clic0_default_vector_handler; clic->metal_exint_table[id].sub_int = priv; } rc = 0; @@ -448,31 +555,20 @@ int __metal_driver_sifive_clic0_disable (struct metal_interrupt *controller, int return __metal_clic0_interrupt_disable(clic, id); } -int __metal_driver_sifive_clic0_enable_interrupt_vector(struct metal_interrupt *controller, - int id, metal_vector_mode mode) +int __metal_driver_sifive_clic0_enable_interrupt_vector(struct metal_interrupt *controller, int id) { - int num_subinterrupts; + int rc = -1; + int num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller); struct __metal_driver_sifive_clic0 *clic = (struct __metal_driver_sifive_clic0 *)(controller); + metal_vector_mode mode = __metal_clic0_configure_get_vector_mode(clic); - if (id == METAL_INTERRUPT_ID_BASE) { - if (mode == METAL_SELECTIVE_VECTOR_MODE) { - __metal_controller_interrupt_vector(mode, (void *)(uintptr_t)&__metal_clic0_handler); - return 0; - } - if (mode == METAL_HARDWARE_VECTOR_MODE) { - __metal_controller_interrupt_vector(mode, &clic->metal_mtvt_table); - return 0; - } + if ((mode != METAL_SELECTIVE_VECTOR_MODE) && (mode != METAL_HARDWARE_VECTOR_MODE)) { + return rc; } - num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller); - if ((id >= METAL_INTERRUPT_ID_LC0) && (id < num_subinterrupts)) { - if ((mode == METAL_SELECTIVE_VECTOR_MODE) && - __metal_controller_interrupt_is_selective_vectored()) { - __metal_clic0_interrupt_set_vector(clic, id, METAL_ENABLE); - return 0; - } - + if (id < num_subinterrupts) { + __metal_clic0_interrupt_set_vector_mode(clic, id, METAL_ENABLE); + return 0; } return -1; } @@ -483,20 +579,84 @@ int __metal_driver_sifive_clic0_disable_interrupt_vector(struct metal_interrupt struct __metal_driver_sifive_clic0 *clic = (struct __metal_driver_sifive_clic0 *)(controller); - if (id == METAL_INTERRUPT_ID_BASE) { - __metal_controller_interrupt_vector(METAL_SELECTIVE_VECTOR_MODE, (void *)(uintptr_t)&__metal_clic0_handler); - return 0; - } num_subinterrupts = __metal_driver_sifive_clic0_num_subinterrupts(controller); - if ((id >= METAL_INTERRUPT_ID_LC0) && (id < num_subinterrupts)) { - if (__metal_controller_interrupt_is_selective_vectored()) { - __metal_clic0_interrupt_set_vector(clic, id, METAL_DISABLE); - return 0; - } + if (id < num_subinterrupts) { + __metal_clic0_interrupt_set_vector_mode(clic, id, METAL_DISABLE); + return 0; } return -1; } +metal_vector_mode __metal_driver_sifive_clic0_get_vector_mode (struct metal_interrupt *controller) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_get_vector_mode(clic); +} + +int __metal_driver_sifive_clic0_set_vector_mode (struct metal_interrupt *controller, metal_vector_mode mode) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_set_vector_mode(clic, mode); +} + +metal_intr_priv_mode __metal_driver_sifive_clic0_get_privilege (struct metal_interrupt *controller) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_get_privilege(clic); +} + +int __metal_driver_sifive_clic0_set_privilege (struct metal_interrupt *controller, metal_intr_priv_mode priv) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_set_privilege(clic, priv); +} + +unsigned int __metal_driver_sifive_clic0_get_threshold (struct metal_interrupt *controller) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_get_level(clic); +} + +int __metal_driver_sifive_clic0_set_threshold (struct metal_interrupt *controller, unsigned int level) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_configure_set_level(clic, level); +} + +unsigned int __metal_driver_sifive_clic0_get_priority (struct metal_interrupt *controller, int id) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_get_priority(clic, id); +} + +int __metal_driver_sifive_clic0_set_priority (struct metal_interrupt *controller, int id, unsigned int priority) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_set_priority(clic, id, priority); +} + +int __metal_driver_sifive_clic0_clear_interrupt (struct metal_interrupt *controller, int id) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_clear(clic, id); +} + +int __metal_driver_sifive_clic0_set_interrupt (struct metal_interrupt *controller, int id) +{ + struct __metal_driver_sifive_clic0 *clic = + (struct __metal_driver_sifive_clic0 *)(controller); + return __metal_clic0_interrupt_set(clic, id); +} + int __metal_driver_sifive_clic0_command_request (struct metal_interrupt *controller, int command, void *data) { @@ -552,10 +712,21 @@ int __metal_driver_sifive_clic0_command_request (struct metal_interrupt *control __METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_clic0) = { .clic_vtable.interrupt_init = __metal_driver_sifive_clic0_init, .clic_vtable.interrupt_register = __metal_driver_sifive_clic0_register, + .clic_vtable.interrupt_vector_register = __metal_driver_sifive_clic0_vector_register, .clic_vtable.interrupt_enable = __metal_driver_sifive_clic0_enable, .clic_vtable.interrupt_disable = __metal_driver_sifive_clic0_disable, .clic_vtable.interrupt_vector_enable = __metal_driver_sifive_clic0_enable_interrupt_vector, .clic_vtable.interrupt_vector_disable = __metal_driver_sifive_clic0_disable_interrupt_vector, + .clic_vtable.interrupt_get_vector_mode = __metal_driver_sifive_clic0_get_vector_mode, + .clic_vtable.interrupt_set_vector_mode = __metal_driver_sifive_clic0_set_vector_mode, + .clic_vtable.interrupt_get_privilege = __metal_driver_sifive_clic0_get_privilege, + .clic_vtable.interrupt_set_privilege = __metal_driver_sifive_clic0_set_privilege, + .clic_vtable.interrupt_get_threshold = __metal_driver_sifive_clic0_get_threshold, + .clic_vtable.interrupt_set_threshold = __metal_driver_sifive_clic0_set_threshold, + .clic_vtable.interrupt_get_priority = __metal_driver_sifive_clic0_get_priority, + .clic_vtable.interrupt_set_priority = __metal_driver_sifive_clic0_set_priority, + .clic_vtable.interrupt_clear = __metal_driver_sifive_clic0_clear_interrupt, + .clic_vtable.interrupt_set = __metal_driver_sifive_clic0_set_interrupt, .clic_vtable.command_request = __metal_driver_sifive_clic0_command_request, .clic_vtable.mtimecmp_set = __metal_driver_sifive_clic0_mtimecmp_set, }; diff --git a/src/drivers/sifive_local-external-interrupts0.c b/src/drivers/sifive_local-external-interrupts0.c index 36cb7db9..1c34ca44 100644 --- a/src/drivers/sifive_local-external-interrupts0.c +++ b/src/drivers/sifive_local-external-interrupts0.c @@ -82,6 +82,33 @@ int __metal_driver_sifive_local_external_interrupt_disable(struct metal_interrup return rc; } +int __metal_driver_sifive_local_external_interrupt_set_threshold(struct metal_interrupt *controller, + unsigned int threshold) +{ + /* Core controller does not support threshold configuration */ + return -1; +} + +unsigned int __metal_driver_sifive_local_external_interrupt_get_threshold(struct metal_interrupt *controller) +{ + /* Core controller does not support threshold configuration */ + return 0; +} + + +int __metal_driver_sifive_local_external_interrupt_set_priority(struct metal_interrupt *controller, + int id, unsigned int priority) +{ + /* Core controller does not support priority configuration */ + return -1; +} + +unsigned int __metal_driver_sifive_local_external_interrupt_get_priority(struct metal_interrupt *controller, int id) +{ + /* Core controller does not support priority configuration */ + return 0; +} + int __metal_driver_sifive_local_external_command_request (struct metal_interrupt *controller, int command, void *data) { @@ -111,6 +138,10 @@ __METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_local_external_interrupts0) = .local0_vtable.interrupt_register = __metal_driver_sifive_local_external_interrupt_register, .local0_vtable.interrupt_enable = __metal_driver_sifive_local_external_interrupt_enable, .local0_vtable.interrupt_disable = __metal_driver_sifive_local_external_interrupt_disable, + .local0_vtable.interrupt_get_threshold = __metal_driver_sifive_local_external_interrupt_get_threshold, + .local0_vtable.interrupt_set_threshold = __metal_driver_sifive_local_external_interrupt_set_threshold, + .local0_vtable.interrupt_get_priority = __metal_driver_sifive_local_external_interrupt_get_priority, + .local0_vtable.interrupt_set_priority = __metal_driver_sifive_local_external_interrupt_set_priority, .local0_vtable.command_request = __metal_driver_sifive_local_external_command_request, }; diff --git a/src/interrupt.c b/src/interrupt.c index 855f31e5..eeb88b26 100644 --- a/src/interrupt.c +++ b/src/interrupt.c @@ -1,6 +1,7 @@ /* Copyright 2018 SiFive, Inc */ /* SPDX-License-Identifier: Apache-2.0 */ +#include #include #include @@ -31,11 +32,36 @@ struct metal_interrupt* metal_interrupt_get_controller (metal_intr_cntrl_type cn extern __inline__ void metal_interrupt_init(struct metal_interrupt *controller); +extern __inline__ int metal_interrupt_set_vector_mode(struct metal_interrupt *controller, + metal_vector_mode mode); +extern __inline__ metal_vector_mode metal_interrupt_get_vector_mode(struct metal_interrupt *controller); + +extern __inline__ int metal_interrupt_set_privilege(struct metal_interrupt *controller, + metal_intr_priv_mode mode); +extern __inline__ metal_intr_priv_mode metal_interrupt_get_privilege(struct metal_interrupt *controller); + +extern __inline__ int metal_interrupt_set_threshold(struct metal_interrupt *controller, + unsigned int level); +extern __inline__ unsigned int metal_interrupt_get_threshold(struct metal_interrupt *controller); + +extern __inline__ unsigned int metal_interrupt_get_priority(struct metal_interrupt *controller, int id); + +extern __inline__ int metal_interrupt_set_priority(struct metal_interrupt *controller, int id, unsigned int priority); + +extern __inline__ int metal_interrupt_clear(struct metal_interrupt *controller, int id); + +extern __inline__ int metal_interrupt_set(struct metal_interrupt *controller, int id); + extern __inline__ int metal_interrupt_register_handler(struct metal_interrupt *controller, int id, metal_interrupt_handler_t handler, void *priv); +extern __inline__ int metal_interrupt_register_vector_handler(struct metal_interrupt *controller, + int id, + metal_interrupt_vector_handler_t handler, + void *priv_data); + extern __inline__ int metal_interrupt_enable(struct metal_interrupt *controller, int id); extern __inline__ int metal_interrupt_disable(struct metal_interrupt *controller, int id); @@ -48,8 +74,7 @@ extern __inline__ unsigned int metal_interrupt_get_priority(struct metal_interru extern __inline__ int metal_interrupt_set_priority(struct metal_interrupt *controller, int id, unsigned int priority); -extern __inline__ int metal_interrupt_vector_enable(struct metal_interrupt *controller, - int id, metal_vector_mode mode); +extern __inline__ int metal_interrupt_vector_enable(struct metal_interrupt *controller, int id); extern __inline__ int metal_interrupt_vector_disable(struct metal_interrupt *controller, int id);