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-03-23     Bernard      the first version
9  * 2010-11-10     Bernard      add cleanup callback function in thread exit.
10  * 2012-12-29     Bernard      fix compiling warning.
11  * 2013-12-21     Grissiom     let rt_thread_idle_excute loop until there is no
12  *                             dead thread.
13  * 2016-08-09     ArdaFu       add method to get the handler of the idle thread.
14  * 2018-02-07     Bernard      lock scheduler to protect tid->cleanup.
15  * 2018-07-14     armink       add idle hook list
16  * 2018-11-22     Jesven       add per cpu idle task
17  *                             combine the code of primary and secondary cpu
18  * 2021-11-15     THEWON       Remove duplicate work between idle and _thread_exit
19  * 2023-09-15     xqyjlj       perf rt_hw_interrupt_disable/enable
20  * 2023-11-07     xqyjlj       fix thread exit
21  * 2023-12-10     xqyjlj       add _hook_spinlock
22  */
23 
24 #include <rthw.h>
25 #include <rtthread.h>
26 
27 #ifdef RT_USING_MODULE
28 #include <dlmodule.h>
29 #endif /* RT_USING_MODULE */
30 
31 #ifdef RT_USING_HOOK
32 #ifndef RT_USING_IDLE_HOOK
33 #define RT_USING_IDLE_HOOK
34 #endif /* RT_USING_IDLE_HOOK */
35 #endif /* RT_USING_HOOK */
36 
37 #ifndef IDLE_THREAD_STACK_SIZE
38 #if defined (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP)
39 #define IDLE_THREAD_STACK_SIZE  256
40 #else
41 #define IDLE_THREAD_STACK_SIZE  128
42 #endif /* (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP) */
43 #endif /* IDLE_THREAD_STACK_SIZE */
44 
45 #define _CPUS_NR                RT_CPUS_NR
46 
47 static struct rt_thread idle_thread[_CPUS_NR];
48 rt_align(RT_ALIGN_SIZE)
49 static rt_uint8_t idle_thread_stack[_CPUS_NR][IDLE_THREAD_STACK_SIZE];
50 
51 #ifdef RT_USING_IDLE_HOOK
52 #ifndef RT_IDLE_HOOK_LIST_SIZE
53 #define RT_IDLE_HOOK_LIST_SIZE  4
54 #endif /* RT_IDLE_HOOK_LIST_SIZE */
55 
56 static void (*idle_hook_list[RT_IDLE_HOOK_LIST_SIZE])(void);
57 static struct rt_spinlock _hook_spinlock;
58 
59 /**
60  * @brief This function sets a hook function to idle thread loop. When the system performs
61  *        idle loop, this hook function should be invoked.
62  *
63  * @param hook the specified hook function.
64  *
65  * @return RT_EOK: set OK.
66  *         -RT_EFULL: hook list is full.
67  *
68  * @note the hook function must be simple and never be blocked or suspend.
69  */
rt_thread_idle_sethook(void (* hook)(void))70 rt_err_t rt_thread_idle_sethook(void (*hook)(void))
71 {
72     rt_size_t i;
73     rt_err_t ret = -RT_EFULL;
74     rt_base_t level;
75 
76     level = rt_spin_lock_irqsave(&_hook_spinlock);
77 
78     for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
79     {
80         if (idle_hook_list[i] == RT_NULL)
81         {
82             idle_hook_list[i] = hook;
83             ret = RT_EOK;
84             break;
85         }
86     }
87 
88     rt_spin_unlock_irqrestore(&_hook_spinlock, level);
89 
90     return ret;
91 }
92 
93 /**
94  * @brief delete the idle hook on hook list.
95  *
96  * @param hook the specified hook function.
97  *
98  * @return RT_EOK: delete OK.
99  *         -RT_ENOSYS: hook was not found.
100  */
rt_thread_idle_delhook(void (* hook)(void))101 rt_err_t rt_thread_idle_delhook(void (*hook)(void))
102 {
103     rt_size_t i;
104     rt_err_t ret = -RT_ENOSYS;
105     rt_base_t level;
106 
107     level = rt_spin_lock_irqsave(&_hook_spinlock);
108 
109     for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
110     {
111         if (idle_hook_list[i] == hook)
112         {
113             idle_hook_list[i] = RT_NULL;
114             ret = RT_EOK;
115             break;
116         }
117     }
118 
119     rt_spin_unlock_irqrestore(&_hook_spinlock, level);
120 
121     return ret;
122 }
123 
124 #endif /* RT_USING_IDLE_HOOK */
125 
idle_thread_entry(void * parameter)126 static void idle_thread_entry(void *parameter)
127 {
128     RT_UNUSED(parameter);
129 #ifdef RT_USING_SMP
130     if (rt_cpu_get_id() != 0)
131     {
132         while (1)
133         {
134             rt_hw_secondary_cpu_idle_exec();
135         }
136     }
137 #endif /* RT_USING_SMP */
138 
139     while (1)
140     {
141 #ifdef RT_USING_IDLE_HOOK
142         rt_size_t i;
143         void (*idle_hook)(void);
144 
145         for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
146         {
147             idle_hook = idle_hook_list[i];
148             if (idle_hook != RT_NULL)
149             {
150                 idle_hook();
151             }
152         }
153 #endif /* RT_USING_IDLE_HOOK */
154 
155 #if !defined(RT_USING_SMP) && !defined(RT_USING_SMART)
156     rt_defunct_execute();
157 #endif
158 
159 #ifdef RT_USING_PM
160         void rt_system_power_manager(void);
161         rt_system_power_manager();
162 #endif /* RT_USING_PM */
163     }
164 }
165 
166 /**
167  * @brief This function will initialize idle thread, then start it.
168  *
169  * @note this function must be invoked when system init.
170  */
rt_thread_idle_init(void)171 void rt_thread_idle_init(void)
172 {
173     rt_ubase_t i;
174 #if RT_NAME_MAX > 0
175     char idle_thread_name[RT_NAME_MAX];
176 #endif /* RT_NAME_MAX > 0 */
177 
178 #ifdef RT_USING_IDLE_HOOK
179     rt_spin_lock_init(&_hook_spinlock);
180 #endif
181 
182     for (i = 0; i < _CPUS_NR; i++)
183     {
184 #if RT_NAME_MAX > 0
185         rt_snprintf(idle_thread_name, RT_NAME_MAX, "tidle%d", i);
186 #endif /* RT_NAME_MAX > 0 */
187         rt_thread_init(&idle_thread[i],
188 #if RT_NAME_MAX > 0
189                 idle_thread_name,
190 #else
191                 "tidle",
192 #endif /* RT_NAME_MAX > 0 */
193                 idle_thread_entry,
194                 RT_NULL,
195                 &idle_thread_stack[i][0],
196                 sizeof(idle_thread_stack[i]),
197                 RT_THREAD_PRIORITY_MAX - 1,
198                 32);
199 #ifdef RT_USING_SMP
200         rt_thread_control(&idle_thread[i], RT_THREAD_CTRL_BIND_CPU, (void*)i);
201 #endif /* RT_USING_SMP */
202 
203         /* update */
204         rt_cpu_index(i)->idle_thread = &idle_thread[i];
205 
206         /* startup */
207         rt_thread_startup(&idle_thread[i]);
208     }
209 }
210 
211 /**
212  * @brief This function will get the handler of the idle thread.
213  */
rt_thread_idle_gethandler(void)214 rt_thread_t rt_thread_idle_gethandler(void)
215 {
216     int id = rt_cpu_get_id();
217 
218     return (rt_thread_t)(&idle_thread[id]);
219 }
220