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