| /* 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> |
| |
| extern void __metal_vector_table(); |
| 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); |
| } |
| |
| /* The metal_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_interrupt_vector_handler (void) { |
| metal_shutdown(300); |
| } |
| |
| /* The metal_software_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_software_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_SW].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_SW].handler(METAL_INTERRUPT_ID_SW, priv); |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| /* The metal_timer_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_timer_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_TMR].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_TMR].handler(METAL_INTERRUPT_ID_TMR, priv); |
| } |
| } |
| |
| 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); |
| } |
| |
| /* The metal_external_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_external_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_EXT].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_EXT].handler(METAL_INTERRUPT_ID_EXT, priv); |
| } |
| } |
| |
| 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_CSW) || |
| ((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, 0x307" : "=r"(mtvt)); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_SW].sub_int; |
| mtvt_handler = (metal_interrupt_handler_t)*(uintptr_t *)mtvt; |
| mtvt_handler(id, priv); |
| return; |
| } |
| } else { |
| intc->metal_exception_table[id]((struct metal_cpu *)cpu, id); |
| } |
| } |
| } |
| |
| /* The metal_lc0_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc0_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC0].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC0].handler(METAL_INTERRUPT_ID_LC0, priv); |
| } |
| } |
| |
| /* The metal_lc1_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc1_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC1].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC1].handler(METAL_INTERRUPT_ID_LC1, priv); |
| } |
| } |
| |
| /* The metal_lc2_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc2_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC2].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC2].handler(METAL_INTERRUPT_ID_LC2, priv); |
| } |
| } |
| |
| /* The metal_lc3_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc3_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC3].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC3].handler(METAL_INTERRUPT_ID_LC3, priv); |
| } |
| } |
| |
| /* The metal_lc4_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc4_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC4].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC4].handler(METAL_INTERRUPT_ID_LC4, priv); |
| } |
| } |
| |
| /* The metal_lc5_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc5_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC5].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC5].handler(METAL_INTERRUPT_ID_LC5, priv); |
| } |
| } |
| |
| /* The metal_lc6_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc6_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC6].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC6].handler(METAL_INTERRUPT_ID_LC6, priv); |
| } |
| } |
| |
| /* The metal_lc7_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc7_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC7].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC7].handler(METAL_INTERRUPT_ID_LC7, priv); |
| } |
| } |
| |
| /* The metal_lc8_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc8_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC8].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC8].handler(METAL_INTERRUPT_ID_LC8, priv); |
| } |
| } |
| |
| /* The metal_lc9_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc9_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC9].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC9].handler(METAL_INTERRUPT_ID_LC9, priv); |
| } |
| } |
| |
| /* The metal_lc10_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc10_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC10].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC10].handler(METAL_INTERRUPT_ID_LC10, priv); |
| } |
| } |
| |
| /* The metal_lc11_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc11_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC11].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC11].handler(METAL_INTERRUPT_ID_LC11, priv); |
| } |
| } |
| |
| /* The metal_lc12_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc12_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC12].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC12].handler(METAL_INTERRUPT_ID_LC12, priv); |
| } |
| } |
| |
| /* The metal_lc13_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc13_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC13].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC13].handler(METAL_INTERRUPT_ID_LC13, priv); |
| } |
| } |
| |
| /* The metal_lc14_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc14_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC14].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC14].handler(METAL_INTERRUPT_ID_LC14, priv); |
| } |
| } |
| |
| /* The metal_lc15_interrupt_vector_handler() function can be redefined. */ |
| void __attribute__((weak, interrupt)) metal_lc15_interrupt_vector_handler (void) { |
| void *priv; |
| struct __metal_driver_riscv_cpu_intc *intc; |
| struct __metal_driver_cpu *cpu = __metal_cpu_table[__metal_myhart_id()]; |
| |
| if ( cpu ) { |
| intc = (struct __metal_driver_riscv_cpu_intc *) |
| __metal_driver_cpu_interrupt_controller((struct metal_cpu *)cpu); |
| priv = intc->metal_int_table[METAL_INTERRUPT_ID_LC15].exint_data; |
| intc->metal_int_table[METAL_INTERRUPT_ID_LC15].handler(METAL_INTERRUPT_ID_LC15, priv); |
| } |
| } |
| |
| 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; |
| |
| __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_NONVECTOR_MODE: |
| case METAL_SELECTIVE_VECTOR_MODE: |
| __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)); |
| __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; |
| default: |
| 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, (void *)(uintptr_t)&__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; |
| default: |
| 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, (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_interrupt_disable_vector(struct metal_interrupt *controller, |
| int id) |
| { |
| if (id == METAL_INTERRUPT_ID_BASE) { |
| __metal_controller_interrupt_vector(METAL_DIRECT_MODE, (void *)(uintptr_t)&__metal_exception_handler); |
| return 0; |
| } |
| 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); |
| ( void ) intc; |
| |
| 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 *)__metal_vector_table); |
| 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; |
| } |
| |
| /* CPU driver !!! */ |
| |
| unsigned long long __metal_driver_cpu_mcycle_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); |
| |
| 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); |
| |
| 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 |
| #pragma message("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 |
| #pragma message("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); |
| |
| 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); |
| |
| 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); |
| |
| 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. |
| * The encoding '00' '01' '10' are used for compressed instruction. |
| * Only enconding '11' isn't regarded as compressed instruction (>16b). |
| */ |
| return ((*(unsigned short*)epc & METAL_INSN_LENGTH_MASK) |
| == METAL_INSN_NOT_COMPRESSED) ? 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_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, |
| }; |
| |
| __METAL_DEFINE_VTABLE(__metal_driver_vtable_cpu) = { |
| .cpu_vtable.mcycle_get = __metal_driver_cpu_mcycle_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, |
| }; |
| |