GNU Linux-libre 5.10.217-gnu1
[releases.git] / arch / nds32 / kernel / vdso / gettimeofday.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2005-2017 Andes Technology Corporation
3
4 #include <linux/compiler.h>
5 #include <linux/hrtimer.h>
6 #include <linux/time.h>
7 #include <asm/io.h>
8 #include <asm/barrier.h>
9 #include <asm/bug.h>
10 #include <asm/page.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>
15
16 #define X(x) #x
17 #define Y(x) X(x)
18
19 extern struct vdso_data *__get_datapage(void);
20 extern struct vdso_data *__get_timerpage(void);
21
22 static notrace unsigned int __vdso_read_begin(const struct vdso_data *vdata)
23 {
24         u32 seq;
25 repeat:
26         seq = READ_ONCE(vdata->seq_count);
27         if (seq & 1) {
28                 cpu_relax();
29                 goto repeat;
30         }
31         return seq;
32 }
33
34 static notrace unsigned int vdso_read_begin(const struct vdso_data *vdata)
35 {
36         unsigned int seq;
37
38         seq = __vdso_read_begin(vdata);
39
40         smp_rmb();              /* Pairs with smp_wmb in vdso_write_end */
41         return seq;
42 }
43
44 static notrace int vdso_read_retry(const struct vdso_data *vdata, u32 start)
45 {
46         smp_rmb();              /* Pairs with smp_wmb in vdso_write_begin */
47         return vdata->seq_count != start;
48 }
49
50 static notrace long clock_gettime_fallback(clockid_t _clkid,
51                                            struct __kernel_old_timespec *_ts)
52 {
53         register struct __kernel_old_timespec *ts asm("$r1") = _ts;
54         register clockid_t clkid asm("$r0") = _clkid;
55         register long ret asm("$r0");
56
57         asm volatile ("movi     $r15, %3\n"
58                       "syscall  0x0\n"
59                       :"=r" (ret)
60                       :"r"(clkid), "r"(ts), "i"(__NR_clock_gettime)
61                       :"$r15", "memory");
62
63         return ret;
64 }
65
66 static notrace int do_realtime_coarse(struct __kernel_old_timespec *ts,
67                                       struct vdso_data *vdata)
68 {
69         u32 seq;
70
71         do {
72                 seq = vdso_read_begin(vdata);
73
74                 ts->tv_sec = vdata->xtime_coarse_sec;
75                 ts->tv_nsec = vdata->xtime_coarse_nsec;
76
77         } while (vdso_read_retry(vdata, seq));
78         return 0;
79 }
80
81 static notrace int do_monotonic_coarse(struct __kernel_old_timespec *ts,
82                                        struct vdso_data *vdata)
83 {
84         u32 seq;
85         u64 ns;
86
87         do {
88                 seq = vdso_read_begin(vdata);
89
90                 ts->tv_sec = vdata->xtime_coarse_sec + vdata->wtm_clock_sec;
91                 ns = vdata->xtime_coarse_nsec + vdata->wtm_clock_nsec;
92
93         } while (vdso_read_retry(vdata, seq));
94
95         ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
96         ts->tv_nsec = ns;
97
98         return 0;
99 }
100
101 static notrace inline u64 vgetsns(struct vdso_data *vdso)
102 {
103         u32 cycle_now;
104         u32 cycle_delta;
105         u32 *timer_cycle_base;
106
107         timer_cycle_base =
108             (u32 *) ((char *)__get_timerpage() + vdso->cycle_count_offset);
109         cycle_now = readl_relaxed(timer_cycle_base);
110         if (true == vdso->cycle_count_down)
111                 cycle_now = ~(*timer_cycle_base);
112         cycle_delta = cycle_now - (u32) vdso->cs_cycle_last;
113         return ((u64) cycle_delta & vdso->cs_mask) * vdso->cs_mult;
114 }
115
116 static notrace int do_realtime(struct __kernel_old_timespec *ts, struct vdso_data *vdata)
117 {
118         unsigned count;
119         u64 ns;
120         do {
121                 count = vdso_read_begin(vdata);
122                 ts->tv_sec = vdata->xtime_clock_sec;
123                 ns = vdata->xtime_clock_nsec;
124                 ns += vgetsns(vdata);
125                 ns >>= vdata->cs_shift;
126         } while (vdso_read_retry(vdata, count));
127
128         ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
129         ts->tv_nsec = ns;
130
131         return 0;
132 }
133
134 static notrace int do_monotonic(struct __kernel_old_timespec *ts, struct vdso_data *vdata)
135 {
136         u64 ns;
137         u32 seq;
138
139         do {
140                 seq = vdso_read_begin(vdata);
141
142                 ts->tv_sec = vdata->xtime_clock_sec;
143                 ns = vdata->xtime_clock_nsec;
144                 ns += vgetsns(vdata);
145                 ns >>= vdata->cs_shift;
146
147                 ts->tv_sec += vdata->wtm_clock_sec;
148                 ns += vdata->wtm_clock_nsec;
149
150         } while (vdso_read_retry(vdata, seq));
151
152         ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
153         ts->tv_nsec = ns;
154
155         return 0;
156 }
157
158 notrace int __vdso_clock_gettime(clockid_t clkid, struct __kernel_old_timespec *ts)
159 {
160         struct vdso_data *vdata;
161         int ret = -1;
162
163         vdata = __get_datapage();
164         if (vdata->cycle_count_offset == EMPTY_REG_OFFSET)
165                 return clock_gettime_fallback(clkid, ts);
166
167         switch (clkid) {
168         case CLOCK_REALTIME_COARSE:
169                 ret = do_realtime_coarse(ts, vdata);
170                 break;
171         case CLOCK_MONOTONIC_COARSE:
172                 ret = do_monotonic_coarse(ts, vdata);
173                 break;
174         case CLOCK_REALTIME:
175                 ret = do_realtime(ts, vdata);
176                 break;
177         case CLOCK_MONOTONIC:
178                 ret = do_monotonic(ts, vdata);
179                 break;
180         default:
181                 break;
182         }
183
184         if (ret)
185                 ret = clock_gettime_fallback(clkid, ts);
186
187         return ret;
188 }
189
190 static notrace int clock_getres_fallback(clockid_t _clk_id,
191                                           struct __kernel_old_timespec *_res)
192 {
193         register clockid_t clk_id asm("$r0") = _clk_id;
194         register struct __kernel_old_timespec *res asm("$r1") = _res;
195         register int ret asm("$r0");
196
197         asm volatile ("movi     $r15, %3\n"
198                       "syscall  0x0\n"
199                       :"=r" (ret)
200                       :"r"(clk_id), "r"(res), "i"(__NR_clock_getres)
201                       :"$r15", "memory");
202
203         return ret;
204 }
205
206 notrace int __vdso_clock_getres(clockid_t clk_id, struct __kernel_old_timespec *res)
207 {
208         struct vdso_data *vdata = __get_datapage();
209
210         if (res == NULL)
211                 return 0;
212         switch (clk_id) {
213         case CLOCK_REALTIME:
214         case CLOCK_MONOTONIC:
215         case CLOCK_MONOTONIC_RAW:
216                 res->tv_sec = 0;
217                 res->tv_nsec = vdata->hrtimer_res;
218                 break;
219         case CLOCK_REALTIME_COARSE:
220         case CLOCK_MONOTONIC_COARSE:
221                 res->tv_sec = 0;
222                 res->tv_nsec = CLOCK_COARSE_RES;
223                 break;
224         default:
225                 return clock_getres_fallback(clk_id, res);
226         }
227         return 0;
228 }
229
230 static notrace inline int gettimeofday_fallback(struct __kernel_old_timeval *_tv,
231                                                 struct timezone *_tz)
232 {
233         register struct __kernel_old_timeval *tv asm("$r0") = _tv;
234         register struct timezone *tz asm("$r1") = _tz;
235         register int ret asm("$r0");
236
237         asm volatile ("movi     $r15, %3\n"
238                       "syscall  0x0\n"
239                       :"=r" (ret)
240                       :"r"(tv), "r"(tz), "i"(__NR_gettimeofday)
241                       :"$r15", "memory");
242
243         return ret;
244 }
245
246 notrace int __vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz)
247 {
248         struct __kernel_old_timespec ts;
249         struct vdso_data *vdata;
250         int ret;
251
252         vdata = __get_datapage();
253
254         if (vdata->cycle_count_offset == EMPTY_REG_OFFSET)
255                 return gettimeofday_fallback(tv, tz);
256
257         ret = do_realtime(&ts, vdata);
258
259         if (tv) {
260                 tv->tv_sec = ts.tv_sec;
261                 tv->tv_usec = ts.tv_nsec / 1000;
262         }
263         if (tz) {
264                 tz->tz_minuteswest = vdata->tz_minuteswest;
265                 tz->tz_dsttime = vdata->tz_dsttime;
266         }
267
268         return ret;
269 }