284 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2006-2018, RT-Thread Development Team
 | |
|  *
 | |
|  * SPDX-License-Identifier: Apache-2.0
 | |
|  *
 | |
|  * Change Logs:
 | |
|  * Date           Author       Notes
 | |
|  * 2018/10/01     Bernard      The first version
 | |
|  * 2018/12/27     Jesven       Change irq enable/disable to cpu0
 | |
|  */
 | |
| 
 | |
| #include <rthw.h>
 | |
| 
 | |
| #include "tick.h"
 | |
| 
 | |
| #include <plic.h>
 | |
| #include <clint.h>
 | |
| #include <interrupt.h>
 | |
| 
 | |
| #define CPU_NUM         2
 | |
| #define MAX_HANDLERS    IRQN_MAX
 | |
| 
 | |
| static struct rt_irq_desc irq_desc[MAX_HANDLERS];
 | |
| 
 | |
| static rt_isr_handler_t rt_hw_interrupt_handle(rt_uint32_t vector, void *param)
 | |
| {
 | |
|     rt_kprintf("UN-handled interrupt %d occurred!!!\n", vector);
 | |
|     return RT_NULL;
 | |
| }
 | |
| 
 | |
| int rt_hw_clint_ipi_enable(void)
 | |
| {
 | |
|     /* Set the Machine-Software bit in MIE */
 | |
|     set_csr(mie, MIP_MSIP);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int rt_hw_clint_ipi_disable(void)
 | |
| {
 | |
|     /* Clear the Machine-Software bit in MIE */
 | |
|     clear_csr(mie, MIP_MSIP);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int rt_hw_plic_irq_enable(plic_irq_t irq_number)
 | |
| {
 | |
|     unsigned long core_id = 0;
 | |
| 
 | |
|     /* Check parameters */
 | |
|     if (PLIC_NUM_SOURCES < irq_number || 0 > irq_number)
 | |
|         return -1;
 | |
|     /* Get current enable bit array by IRQ number */
 | |
|     uint32_t current = plic->target_enables.target[core_id].enable[irq_number / 32];
 | |
|     /* Set enable bit in enable bit array */
 | |
|     current |= (uint32_t)1 << (irq_number % 32);
 | |
|     /* Write back the enable bit array */
 | |
|     plic->target_enables.target[core_id].enable[irq_number / 32] = current;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int rt_hw_plic_irq_disable(plic_irq_t irq_number)
 | |
| {
 | |
|     unsigned long core_id = 0;
 | |
| 
 | |
|     /* Check parameters */
 | |
|     if (PLIC_NUM_SOURCES < irq_number || 0 > irq_number)
 | |
|         return -1;
 | |
|     /* Get current enable bit array by IRQ number */
 | |
|     uint32_t current = plic->target_enables.target[core_id].enable[irq_number / 32];
 | |
|     /* Clear enable bit in enable bit array */
 | |
|     current &= ~((uint32_t)1 << (irq_number % 32));
 | |
|     /* Write back the enable bit array */
 | |
|     plic->target_enables.target[core_id].enable[irq_number / 32] = current;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * This function will initialize hardware interrupt
 | |
|  */
 | |
| void rt_hw_interrupt_init(void)
 | |
| {
 | |
|     int idx;
 | |
|     int cpuid;
 | |
| 
 | |
|     cpuid = current_coreid();
 | |
| 
 | |
|     /* Disable all interrupts for the current core. */
 | |
|     for (idx = 0; idx < ((PLIC_NUM_SOURCES + 32u) / 32u); idx ++)
 | |
|         plic->target_enables.target[cpuid].enable[idx] = 0;
 | |
| 
 | |
|     /* Set priorities to zero. */
 | |
|     for (idx = 0; idx < PLIC_NUM_SOURCES; idx++)
 | |
|         plic->source_priorities.priority[idx] = 0;
 | |
| 
 | |
|     /* Set the threshold to zero. */
 | |
|     plic->targets.target[cpuid].priority_threshold = 0;
 | |
| 
 | |
|     /* init exceptions table */
 | |
|     for (idx = 0; idx < MAX_HANDLERS; idx++)
 | |
|     {
 | |
|         rt_hw_interrupt_mask(idx);
 | |
|         irq_desc[idx].handler = (rt_isr_handler_t)rt_hw_interrupt_handle;
 | |
|         irq_desc[idx].param = RT_NULL;
 | |
| #ifdef RT_USING_INTERRUPT_INFO
 | |
|         rt_snprintf(irq_desc[idx].name, RT_NAME_MAX - 1, "default");
 | |
|         irq_desc[idx].counter = 0;
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|     /* Enable machine external interrupts. */
 | |
|     set_csr(mie, MIP_MEIP);
 | |
| }
 | |
| 
 | |
| void rt_hw_scondary_interrupt_init(void)
 | |
| {
 | |
|     int idx;
 | |
|     int cpuid;
 | |
| 
 | |
|     cpuid = current_coreid();
 | |
| 
 | |
|     /* Disable all interrupts for the current core. */
 | |
|     for (idx = 0; idx < ((PLIC_NUM_SOURCES + 32u) / 32u); idx ++)
 | |
|         plic->target_enables.target[cpuid].enable[idx] = 0;
 | |
| 
 | |
|     /* Set the threshold to zero. */
 | |
|     plic->targets.target[cpuid].priority_threshold = 0;
 | |
| 
 | |
|     /* Enable machine external interrupts. */
 | |
|     set_csr(mie, MIP_MEIP);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * This function will mask a interrupt.
 | |
|  * @param vector the interrupt number
 | |
|  */
 | |
| void rt_hw_interrupt_mask(int vector)
 | |
| {
 | |
|     rt_hw_plic_irq_disable(vector);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * This function will un-mask a interrupt.
 | |
|  * @param vector the interrupt number
 | |
|  */
 | |
| void rt_hw_interrupt_umask(int vector)
 | |
| {
 | |
|     plic_set_priority(vector, 1);
 | |
|     rt_hw_plic_irq_enable(vector);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * This function will install a interrupt service routine to a interrupt.
 | |
|  * @param vector the interrupt number
 | |
|  * @param new_handler the interrupt service routine to be installed
 | |
|  * @param old_handler the old interrupt service routine
 | |
|  */
 | |
| rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler,
 | |
|         void *param, const char *name)
 | |
| {
 | |
|     rt_isr_handler_t old_handler = RT_NULL;
 | |
| 
 | |
|     if(vector < MAX_HANDLERS)
 | |
|     {
 | |
|         old_handler = irq_desc[vector].handler;
 | |
|         if (handler != RT_NULL)
 | |
|         {
 | |
|             irq_desc[vector].handler = (rt_isr_handler_t)handler;
 | |
|             irq_desc[vector].param = param;
 | |
| #ifdef RT_USING_INTERRUPT_INFO
 | |
|             rt_snprintf(irq_desc[vector].name, RT_NAME_MAX - 1, "%s", name);
 | |
|             irq_desc[vector].counter = 0;
 | |
| #endif
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return old_handler;
 | |
| }
 | |
| 
 | |
| RT_WEAK
 | |
| void plic_irq_handle(plic_irq_t irq)
 | |
| {
 | |
|     rt_kprintf("UN-handled interrupt %d occurred!!!\n", irq);
 | |
|     return ;
 | |
| }
 | |
| 
 | |
| uintptr_t handle_irq_m_ext(uintptr_t cause, uintptr_t epc)
 | |
| {
 | |
|     /*
 | |
|      * After the highest-priority pending interrupt is claimed by a target
 | |
|      * and the corresponding IP bit is cleared, other lower-priority
 | |
|      * pending interrupts might then become visible to the target, and so
 | |
|      * the PLIC EIP bit might not be cleared after a claim. The interrupt
 | |
|      * handler can check the local meip/heip/seip/ueip bits before exiting
 | |
|      * the handler, to allow more efficient service of other interrupts
 | |
|      * without first restoring the interrupted context and taking another
 | |
|      * interrupt trap.
 | |
|      */
 | |
|     if (read_csr(mip) & MIP_MEIP)
 | |
|     {
 | |
|         /* Get current core id */
 | |
|         uint64_t core_id = current_coreid();
 | |
|         /* Get primitive interrupt enable flag */
 | |
|         uint64_t ie_flag = read_csr(mie);
 | |
|         /* Get current IRQ num */
 | |
|         uint32_t int_num = plic->targets.target[core_id].claim_complete;
 | |
|         /* Get primitive IRQ threshold */
 | |
|         uint32_t int_threshold = plic->targets.target[core_id].priority_threshold;
 | |
|         /* Set new IRQ threshold = current IRQ threshold */
 | |
|         plic->targets.target[core_id].priority_threshold = plic->source_priorities.priority[int_num];
 | |
| 
 | |
|         /* Disable software interrupt and timer interrupt */
 | |
|         clear_csr(mie, MIP_MTIP | MIP_MSIP);
 | |
| 
 | |
|         if (irq_desc[int_num].handler == (rt_isr_handler_t)rt_hw_interrupt_handle)
 | |
|         {
 | |
|             /* default handler, route to kendryte bsp plic driver */
 | |
|             plic_irq_handle(int_num);
 | |
|         }
 | |
|         else if (irq_desc[int_num].handler)
 | |
|         {
 | |
|             irq_desc[int_num].handler(int_num, irq_desc[int_num].param);
 | |
|         }
 | |
| 
 | |
|         /* Perform IRQ complete */
 | |
|         plic->targets.target[core_id].claim_complete = int_num;
 | |
|         /* Set MPIE and MPP flag used to MRET instructions restore MIE flag */
 | |
|         set_csr(mstatus, MSTATUS_MPIE | MSTATUS_MPP);
 | |
|         /* Restore primitive interrupt enable flag */
 | |
|         write_csr(mie, ie_flag);
 | |
|         /* Restore primitive IRQ threshold */
 | |
|         plic->targets.target[core_id].priority_threshold = int_threshold;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         rt_kprintf("unhandled trap!\n");
 | |
|     }
 | |
| 
 | |
|     return epc;
 | |
| }
 | |
| 
 | |
| uintptr_t handle_trap(uintptr_t mcause, uintptr_t epc)
 | |
| {
 | |
|     int cause = mcause & CAUSE_MACHINE_IRQ_REASON_MASK;
 | |
| 
 | |
|     if (mcause & (1UL << 63))
 | |
|     {
 | |
|         switch (cause)
 | |
|         {
 | |
|             case IRQ_M_SOFT:
 | |
|                 {
 | |
|                     uint64_t core_id = current_coreid();
 | |
| 
 | |
|                     clint_ipi_clear(core_id);
 | |
|                     rt_schedule();
 | |
|                 }
 | |
|                 break;
 | |
|             case IRQ_M_EXT:
 | |
|                 handle_irq_m_ext(mcause, epc);
 | |
|                 break;
 | |
|             case IRQ_M_TIMER:
 | |
|                 tick_isr();
 | |
|                 break;
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         rt_thread_t tid;
 | |
|         extern long list_thread();
 | |
| 
 | |
|         rt_hw_interrupt_disable();
 | |
| 
 | |
|         tid = rt_thread_self();
 | |
|         rt_kprintf("\n");
 | |
|         rt_kprintf("unhandled trap, epc => 0x%08x, INT[%d]\n", epc, rt_interrupt_get_nest());
 | |
|         rt_kprintf("current thread: %.*s\n", RT_NAME_MAX, tid->name);
 | |
| #ifdef RT_USING_FINSH
 | |
|         list_thread();
 | |
| #endif
 | |
|         while(1);
 | |
|     }
 | |
| 
 | |
|     return epc;
 | |
| }
 |