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