summaryrefslogtreecommitdiff
path: root/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_cpu.c')
-rw-r--r--FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_cpu.c690
1 files changed, 690 insertions, 0 deletions
diff --git a/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_cpu.c b/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_cpu.c
new file mode 100644
index 000000000..88175eb18
--- /dev/null
+++ b/FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/riscv_cpu.c
@@ -0,0 +1,690 @@
+/* Copyright 2018 SiFive, Inc */
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include <stdint.h>
+#include <metal/io.h>
+#include <metal/shutdown.h>
+#include <metal/machine.h>
+
+unsigned long long __metal_driver_cpu_mtime_get(struct metal_cpu *cpu);
+int __metal_driver_cpu_mtimecmp_set(struct metal_cpu *cpu, unsigned long long time);
+
+struct metal_cpu *__metal_driver_cpu_get(int hartid)
+{
+ if (hartid < __METAL_DT_MAX_HARTS) {
+ return &(__metal_cpu_table[hartid]->cpu);
+ }
+ return (struct metal_cpu *)NULL;
+}
+
+uintptr_t __metal_myhart_id (void)
+{
+ uintptr_t myhart;
+ asm volatile ("csrr %0, mhartid" : "=r"(myhart));
+ return myhart;
+}
+
+void __metal_zero_memory (unsigned char *base, unsigned int size)
+{
+ volatile unsigned char *ptr;
+ for (ptr = base; ptr < (base + size); ptr++){
+ *ptr = 0;
+ }
+}
+
+void __metal_interrupt_global_enable (void) {
+ uintptr_t m;
+ asm volatile ("csrrs %0, mstatus, %1" : "=r"(m) : "r"(METAL_MIE_INTERRUPT));
+}
+
+void __metal_interrupt_global_disable (void) {
+ uintptr_t m;
+ asm volatile ("csrrc %0, mstatus, %1" : "=r"(m) : "r"(METAL_MIE_INTERRUPT));
+}
+
+void __metal_interrupt_software_enable (void) {
+ uintptr_t m;
+ asm volatile ("csrrs %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_SW));
+}
+
+void __metal_interrupt_software_disable (void) {
+ uintptr_t m;
+ asm volatile ("csrrc %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_SW));
+}
+
+void __metal_interrupt_timer_enable (void) {
+ uintptr_t m;
+ asm volatile ("csrrs %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_TMR));
+}
+
+void __metal_interrupt_timer_disable (void) {
+ uintptr_t m;
+ asm volatile ("csrrc %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_TMR));
+}
+
+void __metal_interrupt_external_enable (void) {
+ uintptr_t m;
+ asm volatile ("csrrs %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_EXT));
+}
+
+void __metal_interrupt_external_disable (void) {
+ unsigned long m;
+ asm volatile ("csrrc %0, mie, %1" : "=r"(m) : "r"(METAL_LOCAL_INTERRUPT_EXT));
+}
+
+void __metal_interrupt_local_enable (int id) {
+ uintptr_t b = 1 << id;
+ uintptr_t m;
+ asm volatile ("csrrs %0, mie, %1" : "=r"(m) : "r"(b));
+}
+
+void __metal_interrupt_local_disable (int id) {
+ uintptr_t b = 1 << id;
+ uintptr_t m;
+ asm volatile ("csrrc %0, mie, %1" : "=r"(m) : "r"(b));
+}
+
+void __metal_default_exception_handler (struct metal_cpu *cpu, int ecode) {
+ metal_shutdown(100);
+}
+
+void __metal_default_interrupt_handler (int id, void *priv) {
+ metal_shutdown(200);
+}
+
+void __metal_default_sw_handler (int id, void *priv) {
+ uintptr_t mcause;
+ struct __metal_driver_riscv_cpu_intc *intc;
+ struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
+
+ asm volatile ("csrr %0, mcause" : "=r"(mcause));
+ if ( cpu ) {
+ intc = (struct __metal_driver_riscv_cpu_intc *)
+ __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu);
+ intc->metal_exception_table[mcause & METAL_MCAUSE_CAUSE]((struct metal_cpu *)cpu, id);
+ }
+}
+
+void __metal_default_timer_handler (int id, void *priv) {
+ struct metal_cpu *cpu = __metal_driver_cpu_get(__metal_myhart_id());
+ unsigned long long time = __metal_driver_cpu_mtime_get(cpu);
+
+ /* Set a 10 cycle timer */
+ __metal_driver_cpu_mtimecmp_set(cpu, time + 10);
+}
+
+void __metal_exception_handler(void) __attribute__((interrupt, aligned(128)));
+void __metal_exception_handler (void) {
+ int id;
+ void *priv;
+ uintptr_t mcause, mepc, mtval, mtvec;
+ struct __metal_driver_riscv_cpu_intc *intc;
+ struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()];
+
+ asm volatile ("csrr %0, mcause" : "=r"(mcause));
+ asm volatile ("csrr %0, mepc" : "=r"(mepc));
+ asm volatile ("csrr %0, mtval" : "=r"(mtval));
+ asm volatile ("csrr %0, mtvec" : "=r"(mtvec));
+
+ if ( cpu ) {
+ intc = (struct __metal_driver_riscv_cpu_intc *)
+ __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) ||
+ ((mtvec & METAL_MTVEC_MASK) == METAL_MTVEC_DIRECT)) {
+ priv = intc->metal_int_table[id].exint_data;
+ intc->metal_int_table[id].handler(id, priv);
+ return;
+ }
+ if ((mtvec & METAL_MTVEC_MASK) == METAL_MTVEC_CLIC) {
+ uintptr_t mtvt;
+ metal_interrupt_handler_t mtvt_handler;
+
+ asm volatile ("csrr %0, mtvt" : "=r"(mtvt));
+ priv = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int;
+ mtvt_handler = (metal_interrupt_handler_t)mtvt;
+ mtvt_handler(id, priv);
+ return;
+ }
+ } else {
+ intc->metal_exception_table[id]((struct metal_cpu *)cpu, id);
+ }
+ }
+}
+
+void __metal_controller_interrupt_vector (metal_vector_mode mode, void *vec_table)
+{
+ uintptr_t trap_entry, val;
+
+ asm volatile ("csrr %0, mtvec" : "=r"(val));
+ val &= ~(METAL_MTVEC_CLIC_VECTORED | METAL_MTVEC_CLIC_RESERVED);
+ trap_entry = (uintptr_t)vec_table;
+
+ switch (mode) {
+ case METAL_SELECTIVE_VECTOR_MODE:
+ asm volatile ("csrw mtvt, %0" :: "r"(trap_entry | METAL_MTVEC_CLIC));
+ asm volatile ("csrw mtvec, %0" :: "r"(val | METAL_MTVEC_CLIC));
+ break;
+ case METAL_HARDWARE_VECTOR_MODE:
+ asm volatile ("csrw mtvt, %0" :: "r"(trap_entry | METAL_MTVEC_CLIC_VECTORED));
+ asm volatile ("csrw mtvec, %0" :: "r"(val | METAL_MTVEC_CLIC_VECTORED));
+ break;
+ case METAL_VECTOR_MODE:
+ asm volatile ("csrw mtvec, %0" :: "r"(trap_entry | METAL_MTVEC_VECTORED));
+ break;
+ case METAL_DIRECT_MODE:
+ asm volatile ("csrw mtvec, %0" :: "r"(trap_entry & ~METAL_MTVEC_CLIC_VECTORED));
+ break;
+ }
+}
+
+int __metal_valid_interrupt_id (int id)
+{
+ switch (id) {
+ case METAL_INTERRUPT_ID_SW:
+ case METAL_INTERRUPT_ID_TMR:
+ case METAL_INTERRUPT_ID_EXT:
+ case METAL_INTERRUPT_ID_LC0:
+ case METAL_INTERRUPT_ID_LC1:
+ case METAL_INTERRUPT_ID_LC2:
+ case METAL_INTERRUPT_ID_LC3:
+ case METAL_INTERRUPT_ID_LC4:
+ case METAL_INTERRUPT_ID_LC5:
+ case METAL_INTERRUPT_ID_LC6:
+ case METAL_INTERRUPT_ID_LC7:
+ case METAL_INTERRUPT_ID_LC8:
+ case METAL_INTERRUPT_ID_LC9:
+ case METAL_INTERRUPT_ID_LC10:
+ case METAL_INTERRUPT_ID_LC11:
+ case METAL_INTERRUPT_ID_LC12:
+ case METAL_INTERRUPT_ID_LC13:
+ case METAL_INTERRUPT_ID_LC14:
+ case METAL_INTERRUPT_ID_LC15:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+int __metal_local_interrupt_enable (struct metal_interrupt *controller,
+ metal_interrupt_id_e id, int enable)
+{
+ int rc = 0;
+
+ if ( !controller) {
+ return -1;
+ }
+
+ switch (id) {
+ case METAL_INTERRUPT_ID_BASE:
+ if (enable) {
+ __metal_interrupt_global_enable();
+ } else {
+ __metal_interrupt_global_disable();
+ }
+ break;
+ case METAL_INTERRUPT_ID_SW:
+ if (enable) {
+ __metal_interrupt_software_enable();
+ } else {
+ __metal_interrupt_software_disable();
+ }
+ break;
+ case METAL_INTERRUPT_ID_TMR:
+ if (enable) {
+ __metal_interrupt_timer_enable();
+ } else {
+ __metal_interrupt_timer_disable();
+ }
+ break;
+ case METAL_INTERRUPT_ID_EXT:
+ if (enable) {
+ __metal_interrupt_external_enable();
+ } else {
+ __metal_interrupt_external_disable();
+ }
+ break;
+ case METAL_INTERRUPT_ID_LC0:
+ case METAL_INTERRUPT_ID_LC1:
+ case METAL_INTERRUPT_ID_LC2:
+ case METAL_INTERRUPT_ID_LC3:
+ case METAL_INTERRUPT_ID_LC4:
+ case METAL_INTERRUPT_ID_LC5:
+ case METAL_INTERRUPT_ID_LC6:
+ case METAL_INTERRUPT_ID_LC7:
+ case METAL_INTERRUPT_ID_LC8:
+ case METAL_INTERRUPT_ID_LC9:
+ case METAL_INTERRUPT_ID_LC10:
+ case METAL_INTERRUPT_ID_LC11:
+ case METAL_INTERRUPT_ID_LC12:
+ case METAL_INTERRUPT_ID_LC13:
+ case METAL_INTERRUPT_ID_LC14:
+ case METAL_INTERRUPT_ID_LC15:
+ if (enable) {
+ __metal_interrupt_local_enable(id);
+ } else {
+ __metal_interrupt_local_disable(id);
+ }
+ break;
+ defaut:
+ rc = -1;
+ }
+ return rc;
+}
+
+int __metal_exception_register (struct metal_interrupt *controller,
+ int ecode, metal_exception_handler_t isr)
+{
+ struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
+
+ if ((ecode < METAL_MAX_EXCEPTION_CODE) && isr) {
+ intc->metal_exception_table[ecode] = isr;
+ return 0;
+ }
+ return -1;
+}
+
+void __metal_driver_riscv_cpu_controller_interrupt_init (struct metal_interrupt *controller)
+{
+ struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
+ uintptr_t val;
+
+ if ( !intc->init_done ) {
+ /* Disable and clear all interrupt sources */
+ asm volatile ("csrc mie, %0" :: "r"(-1));
+ asm volatile ("csrc mip, %0" :: "r"(-1));
+
+ /* Read the misa CSR to determine if the delegation registers exist */
+ uintptr_t misa;
+ asm volatile ("csrr %0, misa" : "=r" (misa));
+
+ /* The delegation CSRs exist if user mode interrupts (N extension) or
+ * supervisor mode (S extension) are supported */
+ if((misa & METAL_ISA_N_EXTENSIONS) || (misa & METAL_ISA_S_EXTENSIONS)) {
+ /* Disable interrupt and exception delegation */
+ asm volatile ("csrc mideleg, %0" :: "r"(-1));
+ asm volatile ("csrc medeleg, %0" :: "r"(-1));
+ }
+
+ /* The satp CSR exists if supervisor mode (S extension) is supported */
+ if(misa & METAL_ISA_S_EXTENSIONS) {
+ /* Clear the entire CSR to make sure that satp.MODE = 0 */
+ asm volatile ("csrc satp, %0" :: "r"(-1));
+ }
+
+ /* Default to use direct interrupt, setup sw cb table*/
+ for (int i = 0; i < METAL_MAX_MI; i++) {
+ intc->metal_int_table[i].handler = NULL;
+ intc->metal_int_table[i].sub_int = NULL;
+ intc->metal_int_table[i].exint_data = NULL;
+ }
+ for (int i = 0; i < METAL_MAX_ME; i++) {
+ intc->metal_exception_table[i] = __metal_default_exception_handler;
+ }
+ __metal_controller_interrupt_vector(METAL_DIRECT_MODE, &__metal_exception_handler);
+ asm volatile ("csrr %0, misa" : "=r"(val));
+ if (val & (METAL_ISA_D_EXTENSIONS | METAL_ISA_F_EXTENSIONS | METAL_ISA_Q_EXTENSIONS)) {
+ /* Floating point architecture, so turn on FP register saving*/
+ asm volatile ("csrr %0, mstatus" : "=r"(val));
+ asm volatile ("csrw mstatus, %0" :: "r"(val | METAL_MSTATUS_FS_INIT));
+ }
+ intc->init_done = 1;
+ }
+}
+
+int __metal_driver_riscv_cpu_controller_interrupt_register(struct metal_interrupt *controller,
+ int id, metal_interrupt_handler_t isr,
+ void *priv)
+{
+ int rc = 0;
+ struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
+
+ if ( !__metal_valid_interrupt_id(id) ) {
+ return -11;
+ }
+
+ if (isr) {
+ intc->metal_int_table[id].handler = isr;
+ intc->metal_int_table[id].exint_data = priv;
+ } else {
+ switch (id) {
+ case METAL_INTERRUPT_ID_SW:
+ intc->metal_int_table[id].handler = __metal_default_sw_handler;
+ intc->metal_int_table[id].sub_int = priv;
+ break;
+ case METAL_INTERRUPT_ID_TMR:
+ intc->metal_int_table[id].handler = __metal_default_timer_handler;
+ intc->metal_int_table[id].sub_int = priv;
+ break;
+ case METAL_INTERRUPT_ID_EXT:
+ case METAL_INTERRUPT_ID_LC0:
+ case METAL_INTERRUPT_ID_LC1:
+ case METAL_INTERRUPT_ID_LC2:
+ case METAL_INTERRUPT_ID_LC3:
+ case METAL_INTERRUPT_ID_LC4:
+ case METAL_INTERRUPT_ID_LC5:
+ case METAL_INTERRUPT_ID_LC6:
+ case METAL_INTERRUPT_ID_LC7:
+ case METAL_INTERRUPT_ID_LC8:
+ case METAL_INTERRUPT_ID_LC9:
+ case METAL_INTERRUPT_ID_LC10:
+ case METAL_INTERRUPT_ID_LC11:
+ case METAL_INTERRUPT_ID_LC12:
+ case METAL_INTERRUPT_ID_LC13:
+ case METAL_INTERRUPT_ID_LC14:
+ case METAL_INTERRUPT_ID_LC15:
+ intc->metal_int_table[id].handler = __metal_default_interrupt_handler;
+ intc->metal_int_table[id].sub_int = priv;
+ break;
+ defaut:
+ rc = -12;
+ }
+ }
+ return rc;
+}
+
+int __metal_driver_riscv_cpu_controller_interrupt_enable (struct metal_interrupt *controller,
+ int id)
+{
+ return __metal_local_interrupt_enable(controller, id, METAL_ENABLE);
+}
+
+int __metal_driver_riscv_cpu_controller_interrupt_disable (struct metal_interrupt *controller,
+ int id)
+{
+ return __metal_local_interrupt_enable(controller, id, METAL_DISABLE);
+}
+
+int __metal_driver_riscv_cpu_controller_interrupt_enable_vector(struct metal_interrupt *controller,
+ int id, metal_vector_mode mode)
+{
+ struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
+
+ if (id == METAL_INTERRUPT_ID_BASE) {
+ if (mode == METAL_DIRECT_MODE) {
+ __metal_controller_interrupt_vector(mode, &__metal_exception_handler);
+ return 0;
+ }
+ if (mode == METAL_VECTOR_MODE) {
+ __metal_controller_interrupt_vector(mode, &intc->metal_mtvec_table);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int __metal_driver_riscv_cpu_controller_interrupt_disable_vector(struct metal_interrupt *controller,
+ int id)
+{
+ struct __metal_driver_riscv_cpu_intc *intc = (void *)(controller);
+
+ if (id == METAL_INTERRUPT_ID_BASE) {
+ __metal_controller_interrupt_vector(METAL_DIRECT_MODE, &__metal_exception_handler);
+ return 0;
+ }
+ return -1;
+}
+
+int __metal_driver_riscv_cpu_controller_command_request (struct metal_interrupt *controller,
+ int cmd, void *data)
+{
+ /* NOP for now, unless local interrupt lines the like of clic, clint, plic */
+ return 0;
+}
+
+extern inline int __metal_controller_interrupt_is_selective_vectored(void);
+
+/* CPU driver !!! */
+
+unsigned long long __metal_driver_cpu_timer_get(struct metal_cpu *cpu)
+{
+ unsigned long long val = 0;
+
+#if __riscv_xlen == 32
+ unsigned long hi, hi1, lo;
+
+ asm volatile ("csrr %0, mcycleh" : "=r"(hi));
+ asm volatile ("csrr %0, mcycle" : "=r"(lo));
+ asm volatile ("csrr %0, mcycleh" : "=r"(hi1));
+ if (hi == hi1) {
+ val = ((unsigned long long)hi << 32) | lo;
+ }
+#else
+ asm volatile ("csrr %0, mcycle" : "=r"(val));
+#endif
+
+ return val;
+}
+
+unsigned long long __metal_driver_cpu_timebase_get(struct metal_cpu *cpu)
+{
+ int timebase;
+ if (!cpu) {
+ return 0;
+ }
+
+ timebase = __metal_driver_cpu_timebase((struct metal_cpu *)cpu);
+ return timebase;
+}
+
+unsigned long long __metal_driver_cpu_mtime_get (struct metal_cpu *cpu)
+{
+ unsigned long long time = 0;
+ struct metal_interrupt *tmr_intc;
+ struct __metal_driver_riscv_cpu_intc *intc =
+ (struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
+ struct __metal_driver_cpu *_cpu = (void *)cpu;
+
+ if (intc) {
+ tmr_intc = intc->metal_int_table[METAL_INTERRUPT_ID_TMR].sub_int;
+ if (tmr_intc) {
+ tmr_intc->vtable->command_request(tmr_intc,
+ METAL_TIMER_MTIME_GET, &time);
+ }
+ }
+ return time;
+}
+
+int __metal_driver_cpu_mtimecmp_set (struct metal_cpu *cpu, unsigned long long time)
+{
+ int rc = -1;
+ struct metal_interrupt *tmr_intc;
+ struct __metal_driver_riscv_cpu_intc *intc =
+ (struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
+ struct __metal_driver_cpu *_cpu = (void *)cpu;
+
+ if (intc) {
+ tmr_intc = intc->metal_int_table[METAL_INTERRUPT_ID_TMR].sub_int;
+ if (tmr_intc) {
+ rc = tmr_intc->vtable->mtimecmp_set(tmr_intc,
+ __metal_driver_cpu_hartid(cpu),
+ time);
+ }
+ }
+ return rc;
+}
+
+struct metal_interrupt *
+__metal_driver_cpu_timer_controller_interrupt(struct metal_cpu *cpu)
+{
+#ifdef __METAL_DT_RISCV_CLINT0_HANDLE
+ return __METAL_DT_RISCV_CLINT0_HANDLE;
+#else
+#ifdef __METAL_DT_SIFIVE_CLIC0_HANDLE
+ return __METAL_DT_SIFIVE_CLIC0_HANDLE;
+#else
+#warning "There is no interrupt controller for Timer interrupt"
+ return NULL;
+#endif
+#endif
+}
+
+int __metal_driver_cpu_get_timer_interrupt_id(struct metal_cpu *cpu)
+{
+ return METAL_INTERRUPT_ID_TMR;
+}
+
+struct metal_interrupt *
+__metal_driver_cpu_sw_controller_interrupt(struct metal_cpu *cpu)
+{
+#ifdef __METAL_DT_RISCV_CLINT0_HANDLE
+ return __METAL_DT_RISCV_CLINT0_HANDLE;
+#else
+#ifdef __METAL_DT_SIFIVE_CLIC0_HANDLE
+ return __METAL_DT_SIFIVE_CLIC0_HANDLE;
+#else
+#warning "There is no interrupt controller for Software interrupt"
+ return NULL;
+#endif
+#endif
+}
+
+int __metal_driver_cpu_get_sw_interrupt_id(struct metal_cpu *cpu)
+{
+ return METAL_INTERRUPT_ID_SW;
+}
+
+int __metal_driver_cpu_set_sw_ipi (struct metal_cpu *cpu, int hartid)
+{
+ int rc = -1;
+ struct metal_interrupt *sw_intc;
+ struct __metal_driver_riscv_cpu_intc *intc =
+ (struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
+ struct __metal_driver_cpu *_cpu = (void *)cpu;
+
+ if (intc) {
+ sw_intc = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int;
+ if (sw_intc) {
+ rc = sw_intc->vtable->command_request(sw_intc,
+ METAL_SOFTWARE_IPI_SET, &hartid);
+ }
+ }
+ return rc;
+}
+
+int __metal_driver_cpu_clear_sw_ipi (struct metal_cpu *cpu, int hartid)
+{
+ int rc = -1;
+ struct metal_interrupt *sw_intc;
+ struct __metal_driver_riscv_cpu_intc *intc =
+ (struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
+ struct __metal_driver_cpu *_cpu = (void *)cpu;
+
+ if (intc) {
+ sw_intc = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int;
+ if (sw_intc) {
+ rc = sw_intc->vtable->command_request(sw_intc,
+ METAL_SOFTWARE_IPI_CLEAR, &hartid);
+ }
+ }
+ return rc;
+}
+
+int __metal_driver_cpu_get_msip (struct metal_cpu *cpu, int hartid)
+{
+ int rc = 0;
+ struct metal_interrupt *sw_intc;
+ struct __metal_driver_riscv_cpu_intc *intc =
+ (struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
+ struct __metal_driver_cpu *_cpu = (void *)cpu;
+
+ if (intc) {
+ sw_intc = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int;
+ if (sw_intc) {
+ rc = sw_intc->vtable->command_request(sw_intc,
+ METAL_SOFTWARE_MSIP_GET, &hartid);
+ }
+ }
+ return rc;
+}
+
+struct metal_interrupt *
+__metal_driver_cpu_controller_interrupt(struct metal_cpu *cpu)
+{
+ return __metal_driver_cpu_interrupt_controller(cpu);
+}
+
+int __metal_driver_cpu_enable_interrupt(struct metal_cpu *cpu, void *priv)
+{
+ if ( __metal_driver_cpu_interrupt_controller(cpu) ) {
+ /* Only support machine mode for now */
+ __metal_interrupt_global_enable();
+ return 0;
+ }
+ return -1;
+}
+
+int __metal_driver_cpu_disable_interrupt(struct metal_cpu *cpu, void *priv)
+{
+ if ( __metal_driver_cpu_interrupt_controller(cpu) ) {
+ /* Only support machine mode for now */
+ __metal_interrupt_global_disable();
+ return 0;
+ }
+ return -1;
+}
+
+int __metal_driver_cpu_exception_register(struct metal_cpu *cpu, int ecode,
+ metal_exception_handler_t isr)
+{
+ struct __metal_driver_riscv_cpu_intc *intc =
+ (struct __metal_driver_riscv_cpu_intc *)__metal_driver_cpu_interrupt_controller(cpu);
+
+ if (intc) {
+ return __metal_exception_register((struct metal_interrupt *)intc, ecode, isr);
+ }
+ return -1;
+}
+
+int __metal_driver_cpu_get_instruction_length(struct metal_cpu *cpu, uintptr_t epc)
+{
+ /* Per ISA compressed instruction has last two bits of opcode set */
+ return (*(unsigned short*)epc & 3) ? 4 : 2;
+}
+
+uintptr_t __metal_driver_cpu_get_exception_pc(struct metal_cpu *cpu)
+{
+ uintptr_t mepc;
+ asm volatile ("csrr %0, mepc" : "=r"(mepc));
+ return mepc;
+}
+
+int __metal_driver_cpu_set_exception_pc(struct metal_cpu *cpu, uintptr_t mepc)
+{
+ asm volatile ("csrw mepc, %0" :: "r"(mepc));
+ return 0;
+}
+
+__METAL_DEFINE_VTABLE(__metal_driver_vtable_riscv_cpu_intc) = {
+ .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.command_request = __metal_driver_riscv_cpu_controller_command_request,
+};
+
+__METAL_DEFINE_VTABLE(__metal_driver_vtable_cpu) = {
+ .cpu_vtable.timer_get = __metal_driver_cpu_timer_get,
+ .cpu_vtable.timebase_get = __metal_driver_cpu_timebase_get,
+ .cpu_vtable.mtime_get = __metal_driver_cpu_mtime_get,
+ .cpu_vtable.mtimecmp_set = __metal_driver_cpu_mtimecmp_set,
+ .cpu_vtable.tmr_controller_interrupt = __metal_driver_cpu_timer_controller_interrupt,
+ .cpu_vtable.get_tmr_interrupt_id = __metal_driver_cpu_get_timer_interrupt_id,
+ .cpu_vtable.sw_controller_interrupt = __metal_driver_cpu_sw_controller_interrupt,
+ .cpu_vtable.get_sw_interrupt_id = __metal_driver_cpu_get_sw_interrupt_id,
+ .cpu_vtable.set_sw_ipi = __metal_driver_cpu_set_sw_ipi,
+ .cpu_vtable.clear_sw_ipi = __metal_driver_cpu_clear_sw_ipi,
+ .cpu_vtable.get_msip = __metal_driver_cpu_get_msip,
+ .cpu_vtable.controller_interrupt = __metal_driver_cpu_controller_interrupt,
+ .cpu_vtable.exception_register = __metal_driver_cpu_exception_register,
+ .cpu_vtable.get_ilen = __metal_driver_cpu_get_instruction_length,
+ .cpu_vtable.get_epc = __metal_driver_cpu_get_exception_pc,
+ .cpu_vtable.set_epc = __metal_driver_cpu_set_exception_pc,
+};
+