1 /*
2  * Copyright (C) 2017-2019 Alibaba Group Holding Limited
3  */
4 
5 /******************************************************************************
6  * @file     clock_gettime.c
7  * @brief    clock_gettime()
8  * @version  V1.0
9  * @date     08. May 2019
10  ******************************************************************************/
11 
12 #include <stdio.h>
13 #include "sys_freq.h"
14 #include "drv_timer.h"
15 #include "soc.h"
16 #include <csi_config.h>
17 #include "time.h"
18 #include "pin.h"
19 
20 #define MILLION 1000000
21 
22 int clock_gettime(clockid_t clk_id, struct timespec *tp);
23 int clock_timer_stop(void);
24 
25 /* APB frequence definition */
26 static uint32_t APB_FREQ;
27 static uint32_t TIMER_LOADCOUNT;
28 
29 static timer_handle_t timer_handle;
30 static unsigned int Timer_LoopCount;    /* Count unit is 10 seconds */
31 static uint8_t timer_count_rise = 0;    /*1: timer cont increasing, 0: timer cont diminishing*/
32 
timer_cb_fun(int32_t idx,timer_event_e event)33 static void timer_cb_fun(int32_t idx, timer_event_e event)
34 {
35     if (TIMER_EVENT_TIMEOUT == event) {
36         Timer_LoopCount++;
37     }
38 }
39 
timer_current_value(void)40 static unsigned long long timer_current_value(void)
41 {
42     unsigned int cv;
43 
44     int ret = csi_timer_get_current_value(timer_handle, &cv);
45 
46     if (ret != 0) {
47         return 0;
48     }
49 
50     if (timer_count_rise) {
51         return (unsigned long long)(Timer_LoopCount) * (TIMER_LOADCOUNT + 1) + cv;
52     }
53 
54     return (unsigned long long)(Timer_LoopCount + 1) * (TIMER_LOADCOUNT + 1) - cv - 1;
55 }
56 
clock_timer_init(void)57 int clock_timer_init(void)
58 {
59     if (CLOCK_GETTIME_USE_TIMER_ID > CONFIG_TIMER_NUM) {
60         return EPERM;
61     }
62 
63     uint32_t timer_loadtimer;
64     timer_handle = csi_timer_initialize(CLOCK_GETTIME_USE_TIMER_ID, timer_cb_fun);
65 
66     if (timer_handle == NULL) {
67         return -1;
68     }
69 
70     APB_FREQ = drv_get_timer_freq(CLOCK_GETTIME_USE_TIMER_ID);
71     timer_loadtimer = 10 * MILLION; /*10Mus=10s */
72     TIMER_LOADCOUNT = timer_loadtimer * (APB_FREQ / MILLION);
73 
74     int ret = csi_timer_config(timer_handle, TIMER_MODE_RELOAD);
75 
76     if (ret != 0) {
77         return -1;
78     }
79 
80     ret = csi_timer_set_timeout(timer_handle, timer_loadtimer);
81 
82     if (ret != 0) {
83         return -1;
84     }
85 
86     unsigned int cv1, cv2;
87     csi_timer_get_current_value(timer_handle, &cv1);
88     csi_timer_get_current_value(timer_handle, &cv2);
89 
90     if (cv2 > cv1) {
91         timer_count_rise = 1;
92     }
93 
94     return 0;
95 }
96 
clock_timer_uninit(void)97 int clock_timer_uninit(void)
98 {
99     if (clock_timer_stop() != 0) {
100         return -1;
101     }
102 
103     if (csi_timer_uninitialize(timer_handle) != 0) {
104         return -1;
105     }
106 
107     timer_handle = NULL;
108 
109     return 0;
110 }
111 
clock_timer_start(void)112 int clock_timer_start(void)
113 {
114     int ret = -1;
115     Timer_LoopCount = 0;
116 
117     ret = csi_timer_start(timer_handle);
118 
119     if (ret != 0) {
120         return -1;
121     }
122 
123 #ifdef CONFIG_SYSLOG_LEVEL_DEBUG
124     struct timespec ts_begin, ts_end;
125     ret = clock_gettime(CLOCK_MONOTONIC, &ts_begin);
126 
127     if (ret != 0) {
128         return -1;
129     }
130 
131     ret = clock_gettime(CLOCK_MONOTONIC, &ts_end);
132 
133     if (ret != 0) {
134         return -1;
135     }
136 
137     unsigned long long error_margin_ns =
138         (ts_end.tv_sec * 1000000000 + ts_end.tv_nsec) -
139         (ts_begin.tv_sec * 1000000000 + ts_begin.tv_nsec);
140     printf("clock_gettime() timing deviation is +%llu ns\n", error_margin_ns);
141 #endif
142 
143     return 0;
144 }
145 
clock_timer_stop(void)146 int clock_timer_stop(void)
147 {
148     if (csi_timer_stop(timer_handle) != 0) {
149         return -1;
150     }
151 
152     return 0;
153 }
154 
clock_gettime(clockid_t clk_id,struct timespec * tp)155 int clock_gettime(clockid_t clk_id, struct timespec *tp)
156 {
157     if (clk_id != CLOCK_MONOTONIC) {
158         return EINVAL;
159     }
160 
161     if (timer_handle == 0) {
162         return EPERM;
163     }
164 
165     unsigned long long systimer_val = timer_current_value();
166     tp->tv_sec = systimer_val / APB_FREQ;
167     tp->tv_nsec = (systimer_val % APB_FREQ) * 1000 * MILLION / APB_FREQ;
168 
169     return  0;
170 }
171