1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2006-02-24     Bernard      first version
9  * 2006-05-03     Bernard      add IRQ_DEBUG
10  * 2016-08-09     ArdaFu       add interrupt enter and leave hook.
11  * 2018-11-22     Jesven       rt_interrupt_get_nest function add disable irq
12  * 2021-08-15     Supperthomas fix the comment
13  * 2022-01-07     Gabriel      Moving __on_rt_xxxxx_hook to irq.c
14  * 2022-07-04     Yunjie       fix RT_DEBUG_LOG
15  * 2023-09-15     xqyjlj       perf rt_hw_interrupt_disable/enable
16  * 2024-01-05     Shell        Fixup of data racing in rt_interrupt_get_nest
17  * 2024-01-03     Shell        Support for interrupt context
18  */
19 
20 #include <rthw.h>
21 #include <rtthread.h>
22 
23 #define DBG_TAG           "kernel.irq"
24 #define DBG_LVL           DBG_INFO
25 #include <rtdbg.h>
26 
27 #if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
28 
29 static void (*rt_interrupt_enter_hook)(void);
30 static void (*rt_interrupt_leave_hook)(void);
31 
32 /**
33  * @ingroup group_hook
34  *
35  * @brief This function set a hook function when the system enter a interrupt
36  *
37  * @note The hook function must be simple and never be blocked or suspend.
38  *
39  * @param hook the function point to be called
40  */
rt_interrupt_enter_sethook(void (* hook)(void))41 void rt_interrupt_enter_sethook(void (*hook)(void))
42 {
43     rt_interrupt_enter_hook = hook;
44 }
45 
46 /**
47  * @ingroup group_hook
48  *
49  * @brief This function set a hook function when the system exit a interrupt.
50  *
51  * @note The hook function must be simple and never be blocked or suspend.
52  *
53  * @param hook the function point to be called
54  */
rt_interrupt_leave_sethook(void (* hook)(void))55 void rt_interrupt_leave_sethook(void (*hook)(void))
56 {
57     rt_interrupt_leave_hook = hook;
58 }
59 #endif /* RT_USING_HOOK */
60 
61 /**
62  * @addtogroup group_kernel_core
63  */
64 
65 /**@{*/
66 
67 #ifdef RT_USING_SMP
68 #define rt_interrupt_nest rt_cpu_self()->irq_nest
69 #else
70 volatile rt_atomic_t rt_interrupt_nest = 0;
71 #endif /* RT_USING_SMP */
72 
73 #ifdef ARCH_USING_IRQ_CTX_LIST
rt_interrupt_context_push(rt_interrupt_context_t this_ctx)74 void rt_interrupt_context_push(rt_interrupt_context_t this_ctx)
75 {
76     struct rt_cpu *this_cpu = rt_cpu_self();
77     rt_slist_insert(&this_cpu->irq_ctx_head, &this_ctx->node);
78 }
79 
rt_interrupt_context_pop(void)80 void rt_interrupt_context_pop(void)
81 {
82     struct rt_cpu *this_cpu = rt_cpu_self();
83     rt_slist_pop(&this_cpu->irq_ctx_head);
84 }
85 
rt_interrupt_context_get(void)86 void *rt_interrupt_context_get(void)
87 {
88     struct rt_cpu *this_cpu = rt_cpu_self();
89     return rt_slist_first_entry(&this_cpu->irq_ctx_head, struct rt_interrupt_context, node)->context;
90 }
91 #endif /* ARCH_USING_IRQ_CTX_LIST */
92 
93 /**
94  * @brief This function will be invoked by BSP, when enter interrupt service routine
95  *
96  * @note Please don't invoke this routine in application
97  *
98  * @see rt_interrupt_leave
99  */
rt_interrupt_enter(void)100 rt_weak void rt_interrupt_enter(void)
101 {
102     rt_atomic_add(&(rt_interrupt_nest), 1);
103     RT_OBJECT_HOOK_CALL(rt_interrupt_enter_hook,());
104     LOG_D("irq has come..., irq current nest:%d",
105           (rt_int32_t)rt_atomic_load(&(rt_interrupt_nest)));
106 }
107 RTM_EXPORT(rt_interrupt_enter);
108 
109 
110 /**
111  * @brief This function will be invoked by BSP, when leave interrupt service routine
112  *
113  * @note Please don't invoke this routine in application
114  *
115  * @see rt_interrupt_enter
116  */
rt_interrupt_leave(void)117 rt_weak void rt_interrupt_leave(void)
118 {
119     LOG_D("irq is going to leave, irq current nest:%d",
120                  (rt_int32_t)rt_atomic_load(&(rt_interrupt_nest)));
121     RT_OBJECT_HOOK_CALL(rt_interrupt_leave_hook,());
122     rt_atomic_sub(&(rt_interrupt_nest), 1);
123 
124 }
125 RTM_EXPORT(rt_interrupt_leave);
126 
127 
128 /**
129  * @brief This function will return the nest of interrupt.
130  *
131  * User application can invoke this function to get whether current
132  * context is interrupt context.
133  *
134  * @return the number of nested interrupts.
135  */
rt_interrupt_get_nest(void)136 rt_weak rt_uint8_t rt_interrupt_get_nest(void)
137 {
138     rt_uint8_t ret;
139     rt_base_t level;
140 
141     level = rt_hw_local_irq_disable();
142     ret = rt_atomic_load(&rt_interrupt_nest);
143     rt_hw_local_irq_enable(level);
144     return ret;
145 }
146 RTM_EXPORT(rt_interrupt_get_nest);
147 
148 RTM_EXPORT(rt_hw_interrupt_disable);
149 RTM_EXPORT(rt_hw_interrupt_enable);
150 
rt_hw_interrupt_is_disabled(void)151 rt_weak rt_bool_t rt_hw_interrupt_is_disabled(void)
152 {
153     return RT_FALSE;
154 }
155 RTM_EXPORT(rt_hw_interrupt_is_disabled);
156 /**@}*/
157 
158