Merge tag 'xtensa-20180225' of git://github.com/jcmvbkbc/linux-xtensa
[cris-mirror.git] / arch / tile / kernel / vdso / vgettimeofday.c
blobe63310c49742ca3036f9ed50556ff382bc36722b
1 /*
2 * Copyright 2012 Tilera Corporation. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation, version 2.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11 * NON INFRINGEMENT. See the GNU General Public License for
12 * more details.
15 #define VDSO_BUILD /* avoid some shift warnings for -m32 in <asm/page.h> */
16 #include <linux/time.h>
17 #include <asm/timex.h>
18 #include <asm/unistd.h>
19 #include <asm/vdso.h>
21 #if CHIP_HAS_SPLIT_CYCLE()
22 static inline cycles_t get_cycles_inline(void)
24 unsigned int high = __insn_mfspr(SPR_CYCLE_HIGH);
25 unsigned int low = __insn_mfspr(SPR_CYCLE_LOW);
26 unsigned int high2 = __insn_mfspr(SPR_CYCLE_HIGH);
28 while (unlikely(high != high2)) {
29 low = __insn_mfspr(SPR_CYCLE_LOW);
30 high = high2;
31 high2 = __insn_mfspr(SPR_CYCLE_HIGH);
34 return (((cycles_t)high) << 32) | low;
36 #define get_cycles get_cycles_inline
37 #endif
39 struct syscall_return_value {
40 long value;
41 long error;
45 * Find out the vDSO data page address in the process address space.
47 inline unsigned long get_datapage(void)
49 unsigned long ret;
51 /* vdso data page located in the 2nd vDSO page. */
52 asm volatile ("lnk %0" : "=r"(ret));
53 ret &= ~(PAGE_SIZE - 1);
54 ret += PAGE_SIZE;
56 return ret;
59 static inline u64 vgetsns(struct vdso_data *vdso)
61 return ((get_cycles() - vdso->cycle_last) & vdso->mask) * vdso->mult;
64 static inline int do_realtime(struct vdso_data *vdso, struct timespec *ts)
66 unsigned count;
67 u64 ns;
69 do {
70 count = raw_read_seqcount_begin(&vdso->tb_seq);
71 ts->tv_sec = vdso->wall_time_sec;
72 ns = vdso->wall_time_snsec;
73 ns += vgetsns(vdso);
74 ns >>= vdso->shift;
75 } while (unlikely(read_seqcount_retry(&vdso->tb_seq, count)));
77 ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
78 ts->tv_nsec = ns;
80 return 0;
83 static inline int do_monotonic(struct vdso_data *vdso, struct timespec *ts)
85 unsigned count;
86 u64 ns;
88 do {
89 count = raw_read_seqcount_begin(&vdso->tb_seq);
90 ts->tv_sec = vdso->monotonic_time_sec;
91 ns = vdso->monotonic_time_snsec;
92 ns += vgetsns(vdso);
93 ns >>= vdso->shift;
94 } while (unlikely(read_seqcount_retry(&vdso->tb_seq, count)));
96 ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
97 ts->tv_nsec = ns;
99 return 0;
102 static inline int do_realtime_coarse(struct vdso_data *vdso,
103 struct timespec *ts)
105 unsigned count;
107 do {
108 count = raw_read_seqcount_begin(&vdso->tb_seq);
109 ts->tv_sec = vdso->wall_time_coarse_sec;
110 ts->tv_nsec = vdso->wall_time_coarse_nsec;
111 } while (unlikely(read_seqcount_retry(&vdso->tb_seq, count)));
113 return 0;
116 static inline int do_monotonic_coarse(struct vdso_data *vdso,
117 struct timespec *ts)
119 unsigned count;
121 do {
122 count = raw_read_seqcount_begin(&vdso->tb_seq);
123 ts->tv_sec = vdso->monotonic_time_coarse_sec;
124 ts->tv_nsec = vdso->monotonic_time_coarse_nsec;
125 } while (unlikely(read_seqcount_retry(&vdso->tb_seq, count)));
127 return 0;
130 struct syscall_return_value __vdso_gettimeofday(struct timeval *tv,
131 struct timezone *tz)
133 struct syscall_return_value ret = { 0, 0 };
134 unsigned count;
135 struct vdso_data *vdso = (struct vdso_data *)get_datapage();
137 /* The use of the timezone is obsolete, normally tz is NULL. */
138 if (unlikely(tz != NULL)) {
139 do {
140 count = raw_read_seqcount_begin(&vdso->tz_seq);
141 tz->tz_minuteswest = vdso->tz_minuteswest;
142 tz->tz_dsttime = vdso->tz_dsttime;
143 } while (unlikely(read_seqcount_retry(&vdso->tz_seq, count)));
146 if (unlikely(tv == NULL))
147 return ret;
149 do_realtime(vdso, (struct timespec *)tv);
150 tv->tv_usec /= 1000;
152 return ret;
155 int gettimeofday(struct timeval *tv, struct timezone *tz)
156 __attribute__((weak, alias("__vdso_gettimeofday")));
158 static struct syscall_return_value vdso_fallback_gettime(long clock,
159 struct timespec *ts)
161 struct syscall_return_value ret;
162 __asm__ __volatile__ (
163 "swint1"
164 : "=R00" (ret.value), "=R01" (ret.error)
165 : "R10" (__NR_clock_gettime), "R00" (clock), "R01" (ts)
166 : "r2", "r3", "r4", "r5", "r6", "r7",
167 "r8", "r9", "r11", "r12", "r13", "r14", "r15",
168 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
169 "r24", "r25", "r26", "r27", "r28", "r29", "memory");
170 return ret;
173 struct syscall_return_value __vdso_clock_gettime(clockid_t clock,
174 struct timespec *ts)
176 struct vdso_data *vdso = (struct vdso_data *)get_datapage();
177 struct syscall_return_value ret = { 0, 0 };
179 switch (clock) {
180 case CLOCK_REALTIME:
181 do_realtime(vdso, ts);
182 return ret;
183 case CLOCK_MONOTONIC:
184 do_monotonic(vdso, ts);
185 return ret;
186 case CLOCK_REALTIME_COARSE:
187 do_realtime_coarse(vdso, ts);
188 return ret;
189 case CLOCK_MONOTONIC_COARSE:
190 do_monotonic_coarse(vdso, ts);
191 return ret;
192 default:
193 return vdso_fallback_gettime(clock, ts);
197 int clock_gettime(clockid_t clock, struct timespec *ts)
198 __attribute__((weak, alias("__vdso_clock_gettime")));