diff options
Diffstat (limited to 'FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_plic0.c')
-rw-r--r-- | FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_plic0.c | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_plic0.c b/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_plic0.c new file mode 100644 index 000000000..ed9782450 --- /dev/null +++ b/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_plic0.c @@ -0,0 +1,172 @@ +/* Copyright 2018 SiFive, Inc */ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include <metal/machine/platform.h> + +#ifdef METAL_RISCV_PLIC0 + +#include <metal/io.h> +#include <metal/shutdown.h> +#include <metal/drivers/riscv_plic0.h> +#include <metal/machine.h> + +unsigned int __metal_plic0_claim_interrupt (struct __metal_driver_riscv_plic0 *plic) +{ + unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic); + return __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + + METAL_RISCV_PLIC0_CLAIM)); +} + +void __metal_plic0_complete_interrupt(struct __metal_driver_riscv_plic0 *plic, + unsigned int id) +{ + unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic); + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + + METAL_RISCV_PLIC0_CLAIM)) = id; +} + +void __metal_plic0_set_threshold(struct __metal_driver_riscv_plic0 *plic, + unsigned int threshold) +{ + unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic); + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + + METAL_RISCV_PLIC0_THRESHOLD)) = threshold; +} + +void __metal_plic0_set_priority(struct __metal_driver_riscv_plic0 *plic, + int id, unsigned int priority) +{ + unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic); + int max_priority = __metal_driver_sifive_plic0_max_priority((struct metal_interrupt *)plic); + if ( (max_priority) && (priority < max_priority) ) { + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + + METAL_RISCV_PLIC0_PRIORITY_BASE + + (id << METAL_PLIC_SOURCE_PRIORITY_SHIFT))) = priority; + } +} + +void __metal_plic0_enable(struct __metal_driver_riscv_plic0 *plic, int id, int enable) +{ + unsigned int current; + unsigned long hartid = __metal_myhart_id(); + unsigned long control_base = __metal_driver_sifive_plic0_control_base((struct metal_interrupt *)plic); + + current = __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + + METAL_RISCV_PLIC0_ENABLE_BASE + + (id >> METAL_PLIC_SOURCE_SHIFT) * 4)); + __METAL_ACCESS_ONCE((__metal_io_u32 *)(control_base + + METAL_RISCV_PLIC0_ENABLE_BASE + + ((id >> METAL_PLIC_SOURCE_SHIFT) * 4))) = + enable ? (current | (1 << (id & METAL_PLIC_SOURCE_MASK))) + : (current & ~(1 << (id & METAL_PLIC_SOURCE_MASK))); +} + +void __metal_plic0_default_handler (int id, void *priv) { + metal_shutdown(300); +} + +void __metal_plic0_handler (int id, void *priv) +{ + struct __metal_driver_riscv_plic0 *plic = priv; + unsigned int idx = __metal_plic0_claim_interrupt(plic); + int num_interrupts = __metal_driver_sifive_plic0_num_interrupts((struct metal_interrupt *)plic); + + if ( (idx < num_interrupts) && (plic->metal_exint_table[idx]) ) { + plic->metal_exint_table[idx](idx, + plic->metal_exdata_table[idx].exint_data); + } + + __metal_plic0_complete_interrupt(plic, idx); +} + +void __metal_driver_riscv_plic0_init (struct metal_interrupt *controller) +{ + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if ( !plic->init_done ) { + int num_interrupts, line; + struct metal_interrupt *intc; + + for(int parent = 0; parent < __METAL_PLIC_NUM_PARENTS; parent++) { + num_interrupts = __metal_driver_sifive_plic0_num_interrupts(controller); + intc = __metal_driver_sifive_plic0_interrupt_parents(controller, parent); + line = __metal_driver_sifive_plic0_interrupt_lines(controller, parent); + + /* Initialize ist parent controller, aka cpu_intc. */ + intc->vtable->interrupt_init(intc); + + for (int i = 0; i < num_interrupts; i++) { + __metal_plic0_enable(plic, i, METAL_DISABLE); + __metal_plic0_set_priority(plic, i, 0); + plic->metal_exint_table[i] = NULL; + plic->metal_exdata_table[i].sub_int = NULL; + plic->metal_exdata_table[i].exint_data = NULL; + } + + __metal_plic0_set_threshold(plic, 0); + + /* Register plic (ext) interrupt with with parent controller */ + intc->vtable->interrupt_register(intc, line, NULL, plic); + /* Register plic handler for dispatching its device interrupts */ + intc->vtable->interrupt_register(intc, line, __metal_plic0_handler, plic); + /* Enable plic (ext) interrupt with with parent controller */ + intc->vtable->interrupt_enable(intc, line); + } + plic->init_done = 1; + } +} + +int __metal_driver_riscv_plic0_register (struct metal_interrupt *controller, + int id, metal_interrupt_handler_t isr, + void *priv) +{ + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) { + return -1; + } + + if (isr) { + __metal_plic0_set_priority(plic ,id, 2); + plic->metal_exint_table[id] = isr; + plic->metal_exdata_table[id].exint_data = priv; + } else { + __metal_plic0_set_priority(plic, id, 1); + plic->metal_exint_table[id] = __metal_plic0_default_handler; + plic->metal_exdata_table[id].sub_int = priv; + } + + return 0; +} + +int __metal_driver_riscv_plic0_enable (struct metal_interrupt *controller, int id) +{ + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) { + return -1; + } + + __metal_plic0_enable(plic, id, METAL_ENABLE); + return 0; +} + +int __metal_driver_riscv_plic0_disable (struct metal_interrupt *controller, int id) +{ + struct __metal_driver_riscv_plic0 *plic = (void *)(controller); + + if (id >= __metal_driver_sifive_plic0_num_interrupts(controller)) { + return -1; + } + __metal_plic0_enable(plic, id, METAL_DISABLE); + return 0; +} + +__METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_plic0) = { + .plic_vtable.interrupt_init = __metal_driver_riscv_plic0_init, + .plic_vtable.interrupt_register = __metal_driver_riscv_plic0_register, + .plic_vtable.interrupt_enable = __metal_driver_riscv_plic0_enable, + .plic_vtable.interrupt_disable = __metal_driver_riscv_plic0_disable, +}; + +#endif /* METAL_RISCV_PLIC0 */ |