1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2015 Imagination Technologies
4 * Author: Alex Smith <alex.smith@imgtec.com>
9 #include <linux/compiler.h>
10 #include <linux/time.h>
12 #include <asm/clocksource.h>
14 #include <asm/unistd.h>
17 #ifdef CONFIG_MIPS_CLOCK_VSYSCALL
19 static __always_inline
long gettimeofday_fallback(struct timeval
*_tv
,
22 register struct timezone
*tz
asm("a1") = _tz
;
23 register struct timeval
*tv
asm("a0") = _tv
;
24 register long ret
asm("v0");
25 register long nr
asm("v0") = __NR_gettimeofday
;
26 register long error
asm("a3");
30 : "=r" (ret
), "=r" (error
)
31 : "r" (tv
), "r" (tz
), "r" (nr
)
32 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
33 "$14", "$15", "$24", "$25", "hi", "lo", "memory");
35 return error
? -ret
: ret
;
40 static __always_inline
long clock_gettime_fallback(clockid_t _clkid
,
43 register struct timespec
*ts
asm("a1") = _ts
;
44 register clockid_t clkid
asm("a0") = _clkid
;
45 register long ret
asm("v0");
46 register long nr
asm("v0") = __NR_clock_gettime
;
47 register long error
asm("a3");
51 : "=r" (ret
), "=r" (error
)
52 : "r" (clkid
), "r" (ts
), "r" (nr
)
53 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
54 "$14", "$15", "$24", "$25", "hi", "lo", "memory");
56 return error
? -ret
: ret
;
59 static __always_inline
int do_realtime_coarse(struct timespec
*ts
,
60 const union mips_vdso_data
*data
)
65 start_seq
= vdso_data_read_begin(data
);
67 ts
->tv_sec
= data
->xtime_sec
;
68 ts
->tv_nsec
= data
->xtime_nsec
>> data
->cs_shift
;
69 } while (vdso_data_read_retry(data
, start_seq
));
74 static __always_inline
int do_monotonic_coarse(struct timespec
*ts
,
75 const union mips_vdso_data
*data
)
82 start_seq
= vdso_data_read_begin(data
);
84 ts
->tv_sec
= data
->xtime_sec
;
85 ts
->tv_nsec
= data
->xtime_nsec
>> data
->cs_shift
;
87 to_mono_sec
= data
->wall_to_mono_sec
;
88 to_mono_nsec
= data
->wall_to_mono_nsec
;
89 } while (vdso_data_read_retry(data
, start_seq
));
91 ts
->tv_sec
+= to_mono_sec
;
92 timespec_add_ns(ts
, to_mono_nsec
);
97 #ifdef CONFIG_CSRC_R4K
99 static __always_inline u64
read_r4k_count(void)
103 __asm__
__volatile__(
115 #ifdef CONFIG_CLKSRC_MIPS_GIC
117 static __always_inline u64
read_gic_count(const union mips_vdso_data
*data
)
119 void __iomem
*gic
= get_gic(data
);
123 hi
= __raw_readl(gic
+ sizeof(lo
));
124 lo
= __raw_readl(gic
);
125 hi2
= __raw_readl(gic
+ sizeof(lo
));
128 return (((u64
)hi
) << 32) + lo
;
133 static __always_inline u64
get_ns(const union mips_vdso_data
*data
)
135 u64 cycle_now
, delta
, nsec
;
137 switch (data
->clock_mode
) {
138 #ifdef CONFIG_CSRC_R4K
140 cycle_now
= read_r4k_count();
143 #ifdef CONFIG_CLKSRC_MIPS_GIC
145 cycle_now
= read_gic_count(data
);
152 delta
= (cycle_now
- data
->cs_cycle_last
) & data
->cs_mask
;
154 nsec
= (delta
* data
->cs_mult
) + data
->xtime_nsec
;
155 nsec
>>= data
->cs_shift
;
160 static __always_inline
int do_realtime(struct timespec
*ts
,
161 const union mips_vdso_data
*data
)
167 start_seq
= vdso_data_read_begin(data
);
169 if (data
->clock_mode
== VDSO_CLOCK_NONE
)
172 ts
->tv_sec
= data
->xtime_sec
;
174 } while (vdso_data_read_retry(data
, start_seq
));
177 timespec_add_ns(ts
, ns
);
182 static __always_inline
int do_monotonic(struct timespec
*ts
,
183 const union mips_vdso_data
*data
)
191 start_seq
= vdso_data_read_begin(data
);
193 if (data
->clock_mode
== VDSO_CLOCK_NONE
)
196 ts
->tv_sec
= data
->xtime_sec
;
199 to_mono_sec
= data
->wall_to_mono_sec
;
200 to_mono_nsec
= data
->wall_to_mono_nsec
;
201 } while (vdso_data_read_retry(data
, start_seq
));
203 ts
->tv_sec
+= to_mono_sec
;
205 timespec_add_ns(ts
, ns
+ to_mono_nsec
);
210 #ifdef CONFIG_MIPS_CLOCK_VSYSCALL
213 * This is behind the ifdef so that we don't provide the symbol when there's no
214 * possibility of there being a usable clocksource, because there's nothing we
215 * can do without it. When libc fails the symbol lookup it should fall back on
216 * the standard syscall path.
218 int __vdso_gettimeofday(struct timeval
*tv
, struct timezone
*tz
)
220 const union mips_vdso_data
*data
= get_vdso_data();
224 ret
= do_realtime(&ts
, data
);
226 return gettimeofday_fallback(tv
, tz
);
229 tv
->tv_sec
= ts
.tv_sec
;
230 tv
->tv_usec
= ts
.tv_nsec
/ 1000;
234 tz
->tz_minuteswest
= data
->tz_minuteswest
;
235 tz
->tz_dsttime
= data
->tz_dsttime
;
241 #endif /* CONFIG_MIPS_CLOCK_VSYSCALL */
243 int __vdso_clock_gettime(clockid_t clkid
, struct timespec
*ts
)
245 const union mips_vdso_data
*data
= get_vdso_data();
249 case CLOCK_REALTIME_COARSE
:
250 ret
= do_realtime_coarse(ts
, data
);
252 case CLOCK_MONOTONIC_COARSE
:
253 ret
= do_monotonic_coarse(ts
, data
);
256 ret
= do_realtime(ts
, data
);
258 case CLOCK_MONOTONIC
:
259 ret
= do_monotonic(ts
, data
);
266 ret
= clock_gettime_fallback(clkid
, ts
);