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-07-10     xqyjlj       The first version.
9  * 2023-09-15     xqyjlj       perf rt_hw_interrupt_disable/enable
10  */
11 
12 #include <rtdevice.h>
13 #include <rthw.h>
14 #include <rtthread.h>
15 
16 #define DBG_SECTION_NAME               "drv.ktime"
17 #define DBG_LEVEL                      DBG_INFO
18 #include <rtdbg.h>
19 
20 #include "ktime.h"
21 
22 #ifdef ARCH_CPU_64BIT
23 #define _HRTIMER_MAX_CNT UINT64_MAX
24 #else
25 #define _HRTIMER_MAX_CNT UINT32_MAX
26 #endif
27 
28 static rt_list_t          _timer_list   = RT_LIST_OBJECT_INIT(_timer_list);
29 static RT_DEFINE_SPINLOCK(_spinlock);
30 
_first_hrtimer(void)31 rt_inline rt_ktime_hrtimer_t _first_hrtimer(void)
32 {
33     return rt_list_isempty(&_timer_list) ? RT_NULL : rt_list_first_entry(&_timer_list, struct rt_ktime_hrtimer, node);
34 }
35 
rt_ktime_hrtimer_getres(void)36 rt_weak rt_uint64_t rt_ktime_hrtimer_getres(void)
37 {
38     return ((1000ULL * 1000 * 1000) * RT_KTIME_RESMUL) / RT_TICK_PER_SECOND;
39 }
40 
rt_ktime_hrtimer_getfrq(void)41 rt_weak unsigned long rt_ktime_hrtimer_getfrq(void)
42 {
43     return RT_TICK_PER_SECOND;
44 }
45 
rt_ktime_hrtimer_getcnt(void)46 rt_weak unsigned long rt_ktime_hrtimer_getcnt(void)
47 {
48     return rt_tick_get();
49 }
50 
rt_ktime_hrtimer_settimeout(unsigned long cnt)51 rt_weak rt_err_t rt_ktime_hrtimer_settimeout(unsigned long cnt)
52 {
53     static rt_timer_t timer = RT_NULL;
54     static struct rt_timer _sh_rtimer;
55 
56     RT_ASSERT(cnt > 0);
57 
58     if (timer == RT_NULL)
59     {
60         timer = &_sh_rtimer;
61         rt_timer_init(timer, "shrtimer", (void (*)(void *))rt_ktime_hrtimer_process, RT_NULL, cnt, RT_TIMER_FLAG_ONE_SHOT);
62     }
63     else
64     {
65         rt_tick_t tick = cnt;
66         rt_timer_control(timer, RT_TIMER_CTRL_SET_TIME, &tick);
67         rt_timer_control(timer, RT_TIMER_CTRL_SET_PARM, RT_NULL);
68     }
69 
70     if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
71     {
72         rt_timer_stop(timer);
73     }
74 
75     rt_timer_start(timer);
76     return RT_EOK;
77 }
78 
79 /**
80  * @brief convert cnt from cputimer cnt to hrtimer cnt
81  *
82  * @param cnt
83  * @return unsigned long
84  */
_cnt_convert(unsigned long cnt)85 static unsigned long _cnt_convert(unsigned long cnt)
86 {
87     unsigned long rtn   = 0;
88     unsigned long count = cnt - rt_ktime_cputimer_getcnt();
89     if (count > (_HRTIMER_MAX_CNT / 2))
90         return 0;
91 
92     rtn = (count * rt_ktime_cputimer_getres()) / rt_ktime_hrtimer_getres();
93     return rtn == 0 ? 1 : rtn; /* at least 1 */
94 }
95 
_sleep_timeout(void * parameter)96 static void _sleep_timeout(void *parameter)
97 {
98     struct rt_ktime_hrtimer *timer = parameter;
99     rt_completion_done(&timer->completion);
100 }
101 
_insert_timer_to_list_locked(rt_ktime_hrtimer_t timer)102 static void _insert_timer_to_list_locked(rt_ktime_hrtimer_t timer)
103 {
104     rt_ktime_hrtimer_t iter;
105 
106     rt_list_for_each_entry(iter, &_timer_list, node)
107     {
108         if (iter->timeout_cnt > timer->timeout_cnt)
109         {
110             break;
111         }
112     }
113     rt_list_insert_before(&iter->node, &(timer->node));
114 
115     timer->flag |= RT_TIMER_FLAG_ACTIVATED;
116 }
117 
_hrtimer_process_locked(void)118 static void _hrtimer_process_locked(void)
119 {
120     rt_ktime_hrtimer_t timer;
121 
122     for (timer = _first_hrtimer();
123         (timer != RT_NULL) && (timer->timeout_cnt <= rt_ktime_cputimer_getcnt());
124         timer = _first_hrtimer())
125     {
126         rt_list_remove(&(timer->node));
127 
128         if (timer->flag & RT_TIMER_FLAG_PERIODIC)
129         {
130             timer->timeout_cnt = timer->delay_cnt + rt_ktime_cputimer_getcnt();
131             _insert_timer_to_list_locked(timer);
132         }
133         else
134         {
135             timer->flag &= ~RT_TIMER_FLAG_ACTIVATED;
136         }
137 
138         if (timer->timeout_func)
139         {
140             timer->timeout_func(timer->parameter);
141         }
142     }
143 }
144 
_set_next_timeout_locked(void)145 static void _set_next_timeout_locked(void)
146 {
147     rt_ktime_hrtimer_t timer;
148     rt_ubase_t next_timeout_hrtimer_cnt;
149     rt_bool_t find_next;
150 
151     do
152     {
153         find_next = RT_FALSE;
154         if ((timer = _first_hrtimer()) != RT_NULL)
155         {
156             next_timeout_hrtimer_cnt = _cnt_convert(timer->timeout_cnt);
157             if (next_timeout_hrtimer_cnt > 0)
158             {
159                 rt_ktime_hrtimer_settimeout(next_timeout_hrtimer_cnt);
160             }
161             else
162             {
163                 _hrtimer_process_locked();
164                 find_next = RT_TRUE;
165             }
166         }
167     }
168     while (find_next);
169 }
170 
rt_ktime_hrtimer_process(void)171 void rt_ktime_hrtimer_process(void)
172 {
173     rt_base_t level = rt_spin_lock_irqsave(&_spinlock);
174 
175     _hrtimer_process_locked();
176     _set_next_timeout_locked();
177 
178     rt_spin_unlock_irqrestore(&_spinlock, level);
179 }
180 
rt_ktime_hrtimer_init(rt_ktime_hrtimer_t timer,const char * name,rt_uint8_t flag,void (* timeout)(void * parameter),void * parameter)181 void rt_ktime_hrtimer_init(rt_ktime_hrtimer_t timer,
182                            const char        *name,
183                            rt_uint8_t         flag,
184                            void (*timeout)(void *parameter),
185                            void *parameter)
186 {
187     /* parameter check */
188     RT_ASSERT(timer != RT_NULL);
189     RT_ASSERT(timeout != RT_NULL);
190 
191     rt_memset(timer, 0, sizeof(struct rt_ktime_hrtimer));
192 
193     timer->flag         = flag & ~RT_TIMER_FLAG_ACTIVATED;
194     timer->timeout_func = timeout;
195     timer->parameter    = parameter;
196     rt_strncpy(timer->name, name, RT_NAME_MAX - 1);
197     rt_list_init(&(timer->node));
198     rt_completion_init(&timer->completion);
199 }
200 
rt_ktime_hrtimer_start(rt_ktime_hrtimer_t timer,unsigned long delay_cnt)201 rt_err_t rt_ktime_hrtimer_start(rt_ktime_hrtimer_t timer, unsigned long delay_cnt)
202 {
203     rt_base_t  level;
204 
205     /* parameter check */
206     RT_ASSERT(timer != RT_NULL);
207     RT_ASSERT(delay_cnt < (_HRTIMER_MAX_CNT / 2));
208 
209     timer->delay_cnt    = delay_cnt;
210     timer->timeout_cnt  = timer->delay_cnt + rt_ktime_cputimer_getcnt();
211 
212     level = rt_spin_lock_irqsave(&_spinlock);
213 
214     if (timer->flag & RT_TIMER_FLAG_ACTIVATED)
215     {
216         rt_spin_unlock_irqrestore(&_spinlock, level);
217         return -RT_ERROR;
218     }
219 
220     _insert_timer_to_list_locked(timer);
221     _set_next_timeout_locked();
222 
223     rt_spin_unlock_irqrestore(&_spinlock, level);
224 
225     return RT_EOK;
226 }
227 
rt_ktime_hrtimer_stop(rt_ktime_hrtimer_t timer)228 rt_err_t rt_ktime_hrtimer_stop(rt_ktime_hrtimer_t timer)
229 {
230     rt_base_t level;
231 
232     RT_ASSERT(timer != RT_NULL); /* timer check */
233 
234     level = rt_spin_lock_irqsave(&_spinlock);
235 
236     if (!(timer->flag & RT_TIMER_FLAG_ACTIVATED))
237     {
238         rt_spin_unlock_irqrestore(&_spinlock, level);
239         return -RT_ERROR;
240     }
241 
242     rt_list_remove(&timer->node);
243     timer->flag &= ~RT_TIMER_FLAG_ACTIVATED;
244     _set_next_timeout_locked();
245 
246     rt_spin_unlock_irqrestore(&_spinlock, level);
247 
248     return RT_EOK;
249 }
250 
rt_ktime_hrtimer_control(rt_ktime_hrtimer_t timer,int cmd,void * arg)251 rt_err_t rt_ktime_hrtimer_control(rt_ktime_hrtimer_t timer, int cmd, void *arg)
252 {
253     rt_base_t level;
254 
255     /* parameter check */
256     RT_ASSERT(timer != RT_NULL);
257 
258     level = rt_spin_lock_irqsave(&_spinlock);
259     switch (cmd)
260     {
261 
262     case RT_TIMER_CTRL_GET_TIME:
263         *(unsigned long *)arg = timer->delay_cnt;
264         break;
265 
266     case RT_TIMER_CTRL_SET_TIME:
267         RT_ASSERT((*(unsigned long *)arg) < (_HRTIMER_MAX_CNT / 2));
268         timer->delay_cnt    = *(unsigned long *)arg;
269         timer->timeout_cnt  = *(unsigned long *)arg + rt_ktime_cputimer_getcnt();
270         break;
271 
272     case RT_TIMER_CTRL_SET_ONESHOT:
273         timer->flag &= ~RT_TIMER_FLAG_PERIODIC;
274         break;
275 
276     case RT_TIMER_CTRL_SET_PERIODIC:
277         timer->flag |= RT_TIMER_FLAG_PERIODIC;
278         break;
279 
280     case RT_TIMER_CTRL_GET_STATE:
281         if (timer->flag & RT_TIMER_FLAG_ACTIVATED)
282         {
283             /*timer is start and run*/
284             *(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED;
285         }
286         else
287         {
288             /*timer is stop*/
289             *(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED;
290         }
291         break;
292 
293     case RT_TIMER_CTRL_GET_REMAIN_TIME:
294         *(unsigned long *)arg = timer->timeout_cnt;
295         break;
296     case RT_TIMER_CTRL_GET_FUNC:
297         arg = (void *)timer->timeout_func;
298         break;
299 
300     case RT_TIMER_CTRL_SET_FUNC:
301         timer->timeout_func = (void (*)(void *))arg;
302         break;
303 
304     case RT_TIMER_CTRL_GET_PARM:
305         *(void **)arg = timer->parameter;
306         break;
307 
308     case RT_TIMER_CTRL_SET_PARM:
309         timer->parameter = arg;
310         break;
311 
312     default:
313         break;
314     }
315     rt_spin_unlock_irqrestore(&_spinlock, level);
316 
317     return RT_EOK;
318 }
319 
rt_ktime_hrtimer_detach(rt_ktime_hrtimer_t timer)320 rt_err_t rt_ktime_hrtimer_detach(rt_ktime_hrtimer_t timer)
321 {
322     rt_base_t level;
323 
324     /* parameter check */
325     RT_ASSERT(timer != RT_NULL);
326 
327     /* notify the timer stop event */
328     rt_completion_wakeup_by_errno(&timer->completion, RT_ERROR);
329 
330     level = rt_spin_lock_irqsave(&_spinlock);
331 
332     /* stop timer */
333     timer->flag &= ~RT_TIMER_FLAG_ACTIVATED;
334     /* when interrupted */
335     if (timer->error == -RT_EINTR || timer->error == RT_EINTR)
336     {
337         rt_list_remove(&timer->node);
338         _set_next_timeout_locked();
339     }
340 
341     rt_spin_unlock_irqrestore(&_spinlock, level);
342 
343     return RT_EOK;
344 }
345 
346 /************************** delay ***************************/
347 
rt_ktime_hrtimer_delay_init(struct rt_ktime_hrtimer * timer)348 void rt_ktime_hrtimer_delay_init(struct rt_ktime_hrtimer *timer)
349 {
350     rt_ktime_hrtimer_init(timer, "hrtimer_sleep", RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_HARD_TIMER,
351                           _sleep_timeout, timer);
352 }
353 
rt_ktime_hrtimer_delay_detach(struct rt_ktime_hrtimer * timer)354 void rt_ktime_hrtimer_delay_detach(struct rt_ktime_hrtimer *timer)
355 {
356     rt_ktime_hrtimer_detach(timer);
357 }
358 
rt_ktime_hrtimer_sleep(struct rt_ktime_hrtimer * timer,unsigned long cnt)359 rt_err_t rt_ktime_hrtimer_sleep(struct rt_ktime_hrtimer *timer, unsigned long cnt)
360 {
361     rt_err_t err;
362 
363     if (cnt == 0)
364         return -RT_EINVAL;
365 
366     err = rt_ktime_hrtimer_start(timer, cnt);
367     if (err)
368         return err;
369 
370     err = rt_completion_wait_flags(&(timer->completion), RT_WAITING_FOREVER,
371                                    RT_INTERRUPTIBLE);
372     rt_ktime_hrtimer_keep_errno(timer, err);
373 
374     return err;
375 }
376 
rt_ktime_hrtimer_ndelay(struct rt_ktime_hrtimer * timer,unsigned long ns)377 rt_err_t rt_ktime_hrtimer_ndelay(struct rt_ktime_hrtimer *timer, unsigned long ns)
378 {
379     rt_uint64_t res = rt_ktime_cputimer_getres();
380     return rt_ktime_hrtimer_sleep(timer, (ns * RT_KTIME_RESMUL) / res);
381 }
382 
rt_ktime_hrtimer_udelay(struct rt_ktime_hrtimer * timer,unsigned long us)383 rt_err_t rt_ktime_hrtimer_udelay(struct rt_ktime_hrtimer *timer, unsigned long us)
384 {
385     return rt_ktime_hrtimer_ndelay(timer, us * 1000);
386 }
387 
rt_ktime_hrtimer_mdelay(struct rt_ktime_hrtimer * timer,unsigned long ms)388 rt_err_t rt_ktime_hrtimer_mdelay(struct rt_ktime_hrtimer *timer, unsigned long ms)
389 {
390     return rt_ktime_hrtimer_ndelay(timer, ms * 1000000);
391 }
392