1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-02-13 zhkag first version
9 * 2023-04-03 xqyjlj fix cputimer in multithreading
10 */
11
12 #include <rtdevice.h>
13 #include <rthw.h>
14 #include <rtthread.h>
15
16 static rt_list_t _cputimer_list = RT_LIST_OBJECT_INIT(_cputimer_list);
17 static struct rt_cputimer *_cputimer_nowtimer = RT_NULL;
18
_cputime_sleep_timeout(void * parameter)19 static void _cputime_sleep_timeout(void *parameter)
20 {
21 struct rt_semaphore *sem;
22 sem = (struct rt_semaphore *)parameter;
23 rt_sem_release(sem);
24 }
25
_cputime_timeout_callback(void * parameter)26 static void _cputime_timeout_callback(void *parameter)
27 {
28 struct rt_cputimer *timer;
29 timer = (struct rt_cputimer *)parameter;
30 rt_base_t level;
31 level = rt_hw_interrupt_disable();
32 _cputimer_nowtimer = RT_NULL;
33 rt_list_remove(&(timer->row));
34 rt_hw_interrupt_enable(level);
35 timer->timeout_func(timer->parameter);
36
37 if (&_cputimer_list != _cputimer_list.prev)
38 {
39 struct rt_cputimer *t;
40 t = rt_list_entry(_cputimer_list.next, struct rt_cputimer, row);
41 clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
42 }
43 else
44 {
45 clock_cpu_settimeout(RT_NULL, RT_NULL, RT_NULL);
46 }
47 }
48
_set_next_timeout()49 static void _set_next_timeout()
50 {
51 struct rt_cputimer *t;
52
53 if (&_cputimer_list != _cputimer_list.prev)
54 {
55 t = rt_list_entry((&_cputimer_list)->next, struct rt_cputimer, row);
56 if (_cputimer_nowtimer != RT_NULL)
57 {
58 if (t != _cputimer_nowtimer && t->timeout_tick < _cputimer_nowtimer->timeout_tick)
59 {
60 _cputimer_nowtimer = t;
61 clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
62 }
63 }
64 else
65 {
66 _cputimer_nowtimer = t;
67 clock_cpu_settimeout(t->timeout_tick, _cputime_timeout_callback, t);
68 }
69 }
70 else
71 {
72 _cputimer_nowtimer = RT_NULL;
73 clock_cpu_settimeout(RT_NULL, RT_NULL, RT_NULL);
74 }
75 }
76
rt_cputimer_init(rt_cputimer_t timer,const char * name,void (* timeout)(void * parameter),void * parameter,rt_uint64_t tick,rt_uint8_t flag)77 void rt_cputimer_init(rt_cputimer_t timer,
78 const char *name,
79 void (*timeout)(void *parameter),
80 void *parameter,
81 rt_uint64_t tick,
82 rt_uint8_t flag)
83 {
84 /* parameter check */
85 RT_ASSERT(timer != RT_NULL);
86 RT_ASSERT(timeout != RT_NULL);
87 RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
88
89 /* set flag */
90 timer->parent.flag = flag;
91
92 /* set deactivated */
93 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
94 timer->timeout_func = timeout;
95 timer->parameter = parameter;
96 timer->timeout_tick = tick + clock_cpu_gettime();
97 timer->init_tick = tick;
98
99 rt_list_init(&(timer->row));
100 rt_sem_init(&(timer->sem), "cputime", 0, RT_IPC_FLAG_PRIO);
101 }
102
rt_cputimer_delete(rt_cputimer_t timer)103 rt_err_t rt_cputimer_delete(rt_cputimer_t timer)
104 {
105 rt_base_t level;
106
107 /* parameter check */
108 RT_ASSERT(timer != RT_NULL);
109 RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
110
111 /* disable interrupt */
112 level = rt_hw_interrupt_disable();
113
114 rt_list_remove(&timer->row);
115 /* stop timer */
116 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
117
118 /* enable interrupt */
119 rt_hw_interrupt_enable(level);
120
121 _set_next_timeout();
122
123 return RT_EOK;
124 }
125
rt_cputimer_start(rt_cputimer_t timer)126 rt_err_t rt_cputimer_start(rt_cputimer_t timer)
127 {
128 rt_list_t *timer_list;
129 rt_base_t level;
130
131 /* parameter check */
132 RT_ASSERT(timer != RT_NULL);
133 RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
134
135 /* stop timer firstly */
136 level = rt_hw_interrupt_disable();
137 /* remove timer from list */
138
139 rt_list_remove(&timer->row);
140 /* change status of timer */
141 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
142
143 timer_list = &_cputimer_list;
144
145 for (; timer_list != _cputimer_list.prev;
146 timer_list = timer_list->next)
147 {
148 struct rt_cputimer *t;
149 rt_list_t *p = timer_list->next;
150
151 t = rt_list_entry(p, struct rt_cputimer, row);
152
153 if ((t->timeout_tick - timer->timeout_tick) == 0)
154 {
155 continue;
156 }
157 else if ((t->timeout_tick - timer->timeout_tick) < 0x7fffffffffffffff)
158 {
159 break;
160 }
161 }
162
163 rt_list_insert_after(timer_list, &(timer->row));
164
165 timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
166
167 _set_next_timeout();
168 /* enable interrupt */
169 rt_hw_interrupt_enable(level);
170
171 return RT_EOK;
172 }
173
rt_cputimer_stop(rt_cputimer_t timer)174 rt_err_t rt_cputimer_stop(rt_cputimer_t timer)
175 {
176 rt_base_t level;
177
178 /* disable interrupt */
179 level = rt_hw_interrupt_disable();
180
181 /* timer check */
182 RT_ASSERT(timer != RT_NULL);
183 RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
184
185 if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
186 {
187 rt_hw_interrupt_enable(level);
188 return -RT_ERROR;
189 }
190
191 rt_list_remove(&timer->row);
192 /* change status */
193 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
194
195 _set_next_timeout();
196 /* enable interrupt */
197 rt_hw_interrupt_enable(level);
198
199 return RT_EOK;
200 }
201
rt_cputimer_control(rt_cputimer_t timer,int cmd,void * arg)202 rt_err_t rt_cputimer_control(rt_cputimer_t timer, int cmd, void *arg)
203 {
204 rt_base_t level;
205
206 /* parameter check */
207 RT_ASSERT(timer != RT_NULL);
208 RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
209
210 level = rt_hw_interrupt_disable();
211 switch (cmd)
212 {
213 case RT_TIMER_CTRL_GET_TIME:
214 *(rt_uint64_t *)arg = timer->init_tick;
215 break;
216
217 case RT_TIMER_CTRL_SET_TIME:
218 RT_ASSERT((*(rt_uint64_t *)arg) < 0x7fffffffffffffff);
219 timer->init_tick = *(rt_uint64_t *)arg;
220 timer->timeout_tick = *(rt_uint64_t *)arg + clock_cpu_gettime();
221 break;
222
223 case RT_TIMER_CTRL_SET_ONESHOT:
224 timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
225 break;
226
227 case RT_TIMER_CTRL_SET_PERIODIC:
228 timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
229 break;
230
231 case RT_TIMER_CTRL_GET_STATE:
232 if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
233 {
234 /*timer is start and run*/
235 *(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED;
236 }
237 else
238 {
239 /*timer is stop*/
240 *(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED;
241 }
242 break;
243
244 case RT_TIMER_CTRL_GET_REMAIN_TIME:
245 *(rt_uint64_t *)arg = timer->timeout_tick;
246 break;
247 case RT_TIMER_CTRL_GET_FUNC:
248 arg = (void *)timer->timeout_func;
249 break;
250
251 case RT_TIMER_CTRL_SET_FUNC:
252 timer->timeout_func = (void (*)(void *))arg;
253 break;
254
255 case RT_TIMER_CTRL_GET_PARM:
256 *(void **)arg = timer->parameter;
257 break;
258
259 case RT_TIMER_CTRL_SET_PARM:
260 timer->parameter = arg;
261 break;
262
263 default:
264 break;
265 }
266 rt_hw_interrupt_enable(level);
267
268 return RT_EOK;
269 }
270
rt_cputimer_detach(rt_cputimer_t timer)271 rt_err_t rt_cputimer_detach(rt_cputimer_t timer)
272 {
273 rt_base_t level;
274
275 /* parameter check */
276 RT_ASSERT(timer != RT_NULL);
277 RT_ASSERT(clock_cpu_issettimeout() != RT_FALSE);
278
279 /* disable interrupt */
280 level = rt_hw_interrupt_disable();
281
282 rt_list_remove(&timer->row);
283 /* stop timer */
284 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
285
286 _set_next_timeout();
287 /* enable interrupt */
288 rt_hw_interrupt_enable(level);
289
290 rt_sem_detach(&(timer->sem));
291
292 return RT_EOK;
293 }
294
rt_cputime_sleep(rt_uint64_t tick)295 rt_err_t rt_cputime_sleep(rt_uint64_t tick)
296 {
297 rt_base_t level;
298 struct rt_cputimer cputimer;
299
300 if (!clock_cpu_issettimeout())
301 {
302 rt_int32_t ms = clock_cpu_millisecond(tick);
303 return rt_thread_delay(rt_tick_from_millisecond(ms));
304 }
305
306 if (tick == 0)
307 {
308 return -RT_EINVAL;
309 }
310
311 rt_cputimer_init(&cputimer, "cputime_sleep", _cputime_sleep_timeout, &(cputimer.sem), tick,
312 RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER);
313
314 /* disable interrupt */
315 level = rt_hw_interrupt_disable();
316
317 rt_cputimer_start(&cputimer); /* reset the timeout of thread timer and start it */
318 rt_hw_interrupt_enable(level);
319 rt_sem_take_interruptible(&(cputimer.sem), RT_WAITING_FOREVER);
320
321 rt_cputimer_detach(&cputimer);
322 return RT_EOK;
323 }
324
rt_cputime_ndelay(rt_uint64_t ns)325 rt_err_t rt_cputime_ndelay(rt_uint64_t ns)
326 {
327 uint64_t unit = clock_cpu_getres();
328 return rt_cputime_sleep(ns * (1000UL * 1000) / unit);
329 }
330
rt_cputime_udelay(rt_uint64_t us)331 rt_err_t rt_cputime_udelay(rt_uint64_t us)
332 {
333 return rt_cputime_ndelay(us * 1000);
334 }
335
rt_cputime_mdelay(rt_uint64_t ms)336 rt_err_t rt_cputime_mdelay(rt_uint64_t ms)
337 {
338 return rt_cputime_ndelay(ms * 1000000);
339 }
340