1 /*
2  * File      : interrupt.c
3  * This file is part of RT-Thread RTOS
4  * COPYRIGHT (C) 2018, RT-Thread Development Team
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License along
17  *  with this program; if not, write to the Free Software Foundation, Inc.,
18  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Change Logs:
21  * Date           Author       Notes
22  */
23 
24 #include <rthw.h>
25 #include <plic_driver.h>
26 #include <platform.h>
27 #include <encoding.h>
28 #include <interrupt.h>
29 
30 #define MAX_HANDLERS    PLIC_NUM_INTERRUPTS
31 
32 /* exception and interrupt handler table */
33 static struct rt_irq_desc irq_desc[MAX_HANDLERS];
34 
35 static plic_instance_t g_plic;
36 
37 /**
38  * This function will mask a interrupt.
39  * @param vector the interrupt number
40  */
rt_hw_interrupt_mask(int irq)41 void rt_hw_interrupt_mask(int irq)
42 {
43     PLIC_disable_interrupt(&g_plic, irq);
44 }
45 
46 /**
47  * This function will un-mask a interrupt.
48  * @param vector the interrupt number
49  */
rt_hw_interrupt_unmask(int irq)50 void rt_hw_interrupt_unmask(int irq)
51 {
52     PLIC_enable_interrupt(&g_plic, irq);
53     PLIC_set_priority(&g_plic, irq, 1);
54 }
55 
rt_hw_interrupt_handle(rt_uint32_t vector,void * param)56 rt_isr_handler_t rt_hw_interrupt_handle(rt_uint32_t vector, void *param)
57 {
58     rt_kprintf("UN-handled interrupt %d occurred!!!\n", vector);
59     return RT_NULL;
60 }
61 
rt_hw_interrupt_init(void)62 void rt_hw_interrupt_init(void)
63 {
64     int idx;
65 
66     /*  config interrupt vector*/
67     asm volatile(
68         "la t0, SW_handler\n"
69         "csrw mtvec, t0"
70     );
71 
72     /*  enable global interrupt*/
73     PLIC_init(&g_plic,
74             PLIC_CTRL_ADDR,
75             PLIC_NUM_INTERRUPTS,
76             PLIC_NUM_PRIORITIES);
77 
78     /* init exceptions table */
79     for (idx = 0; idx < MAX_HANDLERS; idx++)
80     {
81         rt_hw_interrupt_mask(idx);
82         irq_desc[idx].handler = (rt_isr_handler_t)rt_hw_interrupt_handle;
83         irq_desc[idx].param = RT_NULL;
84 #ifdef RT_USING_INTERRUPT_INFO
85         rt_snprintf(irq_desc[idx].name, RT_NAME_MAX - 1, "default");
86         irq_desc[idx].counter = 0;
87 #endif
88     }
89 
90     // enable machine external interrupt
91     set_csr(mie, MIP_MEIP);
92 }
93 
rt_hw_interrupt_get_active(rt_uint32_t fiq_irq)94 rt_uint32_t rt_hw_interrupt_get_active(rt_uint32_t fiq_irq)
95 {
96     return (rt_uint32_t)PLIC_claim_interrupt(&g_plic);;
97 }
98 
rt_hw_interrupt_ack(rt_uint32_t fiq_irq,rt_uint32_t id)99 void rt_hw_interrupt_ack(rt_uint32_t fiq_irq, rt_uint32_t id)
100 {
101     PLIC_complete_interrupt(&g_plic, id);
102 }
103 
104 /**
105  * This function will install a interrupt service routine to a interrupt.
106  * @param vector the interrupt number
107  * @param handler the interrupt service routine to be installed
108  * @param param the interrupt service function parameter
109  * @param name the interrupt name
110  * @return old handler
111  */
rt_hw_interrupt_install(int vector,rt_isr_handler_t handler,void * param,const char * name)112 rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler,
113         void *param, const char *name)
114 {
115     rt_isr_handler_t old_handler = RT_NULL;
116 
117     if(vector < MAX_HANDLERS)
118     {
119         old_handler = irq_desc[vector].handler;
120         if (handler != RT_NULL)
121         {
122             irq_desc[vector].handler = (rt_isr_handler_t)handler;
123             irq_desc[vector].param = param;
124 #ifdef RT_USING_INTERRUPT_INFO
125             rt_snprintf(irq_desc[vector].name, RT_NAME_MAX - 1, "%s", name);
126             irq_desc[vector].counter = 0;
127 #endif
128         }
129     }
130 
131     return old_handler;
132 }
133 
134 /**
135  * This function will be call when external machine-level
136  * interrupt from PLIC occurred.
137  */
handle_m_ext_interrupt(void)138 void handle_m_ext_interrupt(void)
139 {
140     rt_isr_handler_t isr_func;
141     rt_uint32_t irq;
142     void *param;
143 
144     /* get irq number */
145     irq = rt_hw_interrupt_get_active(0);
146 
147     /* get interrupt service routine */
148     isr_func = irq_desc[irq].handler;
149     param = irq_desc[irq].param;
150 
151     /* turn to interrupt service routine */
152     isr_func(irq, param);
153     rt_hw_interrupt_ack(0, irq);
154 
155 #ifdef RT_USING_INTERRUPT_INFO
156     irq_desc[irq].counter ++;
157 #endif
158 }
159