1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2005-2017 Andes Technology Corporation
4 #include <linux/compiler.h>
5 #include <linux/hrtimer.h>
6 #include <linux/time.h>
8 #include <asm/barrier.h>
11 #include <asm/unistd.h>
12 #include <asm/vdso_datapage.h>
13 #include <asm/vdso_timer_info.h>
14 #include <asm/asm-offsets.h>
19 extern struct vdso_data *__get_datapage(void);
20 extern struct vdso_data *__get_timerpage(void);
22 static notrace unsigned int __vdso_read_begin(const struct vdso_data *vdata)
26 seq = READ_ONCE(vdata->seq_count);
34 static notrace unsigned int vdso_read_begin(const struct vdso_data *vdata)
38 seq = __vdso_read_begin(vdata);
40 smp_rmb(); /* Pairs with smp_wmb in vdso_write_end */
44 static notrace int vdso_read_retry(const struct vdso_data *vdata, u32 start)
46 smp_rmb(); /* Pairs with smp_wmb in vdso_write_begin */
47 return vdata->seq_count != start;
50 static notrace long clock_gettime_fallback(clockid_t _clkid,
53 register struct timespec *ts asm("$r1") = _ts;
54 register clockid_t clkid asm("$r0") = _clkid;
55 register long ret asm("$r0");
57 asm volatile ("movi $r15, %3\n"
60 :"r"(clkid), "r"(ts), "i"(__NR_clock_gettime)
66 static notrace int do_realtime_coarse(struct timespec *ts,
67 struct vdso_data *vdata)
72 seq = vdso_read_begin(vdata);
74 ts->tv_sec = vdata->xtime_coarse_sec;
75 ts->tv_nsec = vdata->xtime_coarse_nsec;
77 } while (vdso_read_retry(vdata, seq));
81 static notrace int do_monotonic_coarse(struct timespec *ts,
82 struct vdso_data *vdata)
84 struct timespec tomono;
88 seq = vdso_read_begin(vdata);
90 ts->tv_sec = vdata->xtime_coarse_sec;
91 ts->tv_nsec = vdata->xtime_coarse_nsec;
93 tomono.tv_sec = vdata->wtm_clock_sec;
94 tomono.tv_nsec = vdata->wtm_clock_nsec;
96 } while (vdso_read_retry(vdata, seq));
98 ts->tv_sec += tomono.tv_sec;
99 timespec_add_ns(ts, tomono.tv_nsec);
103 static notrace inline u64 vgetsns(struct vdso_data *vdso)
107 u32 *timer_cycle_base;
110 (u32 *) ((char *)__get_timerpage() + vdso->cycle_count_offset);
111 cycle_now = readl_relaxed(timer_cycle_base);
112 if (true == vdso->cycle_count_down)
113 cycle_now = ~(*timer_cycle_base);
114 cycle_delta = cycle_now - (u32) vdso->cs_cycle_last;
115 return ((u64) cycle_delta & vdso->cs_mask) * vdso->cs_mult;
118 static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata)
123 count = vdso_read_begin(vdata);
124 ts->tv_sec = vdata->xtime_clock_sec;
125 ns = vdata->xtime_clock_nsec;
126 ns += vgetsns(vdata);
127 ns >>= vdata->cs_shift;
128 } while (vdso_read_retry(vdata, count));
130 ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
136 static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata)
138 struct timespec tomono;
143 seq = vdso_read_begin(vdata);
145 ts->tv_sec = vdata->xtime_clock_sec;
146 nsecs = vdata->xtime_clock_nsec;
147 nsecs += vgetsns(vdata);
148 nsecs >>= vdata->cs_shift;
150 tomono.tv_sec = vdata->wtm_clock_sec;
151 tomono.tv_nsec = vdata->wtm_clock_nsec;
153 } while (vdso_read_retry(vdata, seq));
155 ts->tv_sec += tomono.tv_sec;
157 timespec_add_ns(ts, nsecs + tomono.tv_nsec);
161 notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts)
163 struct vdso_data *vdata;
166 vdata = __get_datapage();
167 if (vdata->cycle_count_offset == EMPTY_REG_OFFSET)
168 return clock_gettime_fallback(clkid, ts);
171 case CLOCK_REALTIME_COARSE:
172 ret = do_realtime_coarse(ts, vdata);
174 case CLOCK_MONOTONIC_COARSE:
175 ret = do_monotonic_coarse(ts, vdata);
178 ret = do_realtime(ts, vdata);
180 case CLOCK_MONOTONIC:
181 ret = do_monotonic(ts, vdata);
188 ret = clock_gettime_fallback(clkid, ts);
193 static notrace int clock_getres_fallback(clockid_t _clk_id,
194 struct timespec *_res)
196 register clockid_t clk_id asm("$r0") = _clk_id;
197 register struct timespec *res asm("$r1") = _res;
198 register int ret asm("$r0");
200 asm volatile ("movi $r15, %3\n"
203 :"r"(clk_id), "r"(res), "i"(__NR_clock_getres)
209 notrace int __vdso_clock_getres(clockid_t clk_id, struct timespec *res)
215 case CLOCK_MONOTONIC:
216 case CLOCK_MONOTONIC_RAW:
218 res->tv_nsec = CLOCK_REALTIME_RES;
220 case CLOCK_REALTIME_COARSE:
221 case CLOCK_MONOTONIC_COARSE:
223 res->tv_nsec = CLOCK_COARSE_RES;
226 return clock_getres_fallback(clk_id, res);
231 static notrace inline int gettimeofday_fallback(struct timeval *_tv,
232 struct timezone *_tz)
234 register struct timeval *tv asm("$r0") = _tv;
235 register struct timezone *tz asm("$r1") = _tz;
236 register int ret asm("$r0");
238 asm volatile ("movi $r15, %3\n"
241 :"r"(tv), "r"(tz), "i"(__NR_gettimeofday)
247 notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
250 struct vdso_data *vdata;
253 vdata = __get_datapage();
255 if (vdata->cycle_count_offset == EMPTY_REG_OFFSET)
256 return gettimeofday_fallback(tv, tz);
258 ret = do_realtime(&ts, vdata);
261 tv->tv_sec = ts.tv_sec;
262 tv->tv_usec = ts.tv_nsec / 1000;
265 tz->tz_minuteswest = vdata->tz_minuteswest;
266 tz->tz_dsttime = vdata->tz_dsttime;