2 * Copyright (C) 2015 Imagination Technologies
3 * Author: Alex Smith <alex.smith@imgtec.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
13 #include <linux/compiler.h>
14 #include <linux/time.h>
16 #include <asm/clocksource.h>
18 #include <asm/unistd.h>
21 #ifdef CONFIG_MIPS_CLOCK_VSYSCALL
23 static __always_inline
long gettimeofday_fallback(struct timeval
*_tv
,
26 register struct timezone
*tz
asm("a1") = _tz
;
27 register struct timeval
*tv
asm("a0") = _tv
;
28 register long ret
asm("v0");
29 register long nr
asm("v0") = __NR_gettimeofday
;
30 register long error
asm("a3");
34 : "=r" (ret
), "=r" (error
)
35 : "r" (tv
), "r" (tz
), "r" (nr
)
36 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
37 "$14", "$15", "$24", "$25", "hi", "lo", "memory");
39 return error
? -ret
: ret
;
44 static __always_inline
long clock_gettime_fallback(clockid_t _clkid
,
47 register struct timespec
*ts
asm("a1") = _ts
;
48 register clockid_t clkid
asm("a0") = _clkid
;
49 register long ret
asm("v0");
50 register long nr
asm("v0") = __NR_clock_gettime
;
51 register long error
asm("a3");
55 : "=r" (ret
), "=r" (error
)
56 : "r" (clkid
), "r" (ts
), "r" (nr
)
57 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
58 "$14", "$15", "$24", "$25", "hi", "lo", "memory");
60 return error
? -ret
: ret
;
63 static __always_inline
int do_realtime_coarse(struct timespec
*ts
,
64 const union mips_vdso_data
*data
)
69 start_seq
= vdso_data_read_begin(data
);
71 ts
->tv_sec
= data
->xtime_sec
;
72 ts
->tv_nsec
= data
->xtime_nsec
>> data
->cs_shift
;
73 } while (vdso_data_read_retry(data
, start_seq
));
78 static __always_inline
int do_monotonic_coarse(struct timespec
*ts
,
79 const union mips_vdso_data
*data
)
86 start_seq
= vdso_data_read_begin(data
);
88 ts
->tv_sec
= data
->xtime_sec
;
89 ts
->tv_nsec
= data
->xtime_nsec
>> data
->cs_shift
;
91 to_mono_sec
= data
->wall_to_mono_sec
;
92 to_mono_nsec
= data
->wall_to_mono_nsec
;
93 } while (vdso_data_read_retry(data
, start_seq
));
95 ts
->tv_sec
+= to_mono_sec
;
96 timespec_add_ns(ts
, to_mono_nsec
);
101 #ifdef CONFIG_CSRC_R4K
103 static __always_inline u64
read_r4k_count(void)
107 __asm__
__volatile__(
119 #ifdef CONFIG_CLKSRC_MIPS_GIC
121 static __always_inline u64
read_gic_count(const union mips_vdso_data
*data
)
123 void __iomem
*gic
= get_gic(data
);
127 hi
= __raw_readl(gic
+ sizeof(lo
));
128 lo
= __raw_readl(gic
);
129 hi2
= __raw_readl(gic
+ sizeof(lo
));
132 return (((u64
)hi
) << 32) + lo
;
137 static __always_inline u64
get_ns(const union mips_vdso_data
*data
)
139 u64 cycle_now
, delta
, nsec
;
141 switch (data
->clock_mode
) {
142 #ifdef CONFIG_CSRC_R4K
144 cycle_now
= read_r4k_count();
147 #ifdef CONFIG_CLKSRC_MIPS_GIC
149 cycle_now
= read_gic_count(data
);
156 delta
= (cycle_now
- data
->cs_cycle_last
) & data
->cs_mask
;
158 nsec
= (delta
* data
->cs_mult
) + data
->xtime_nsec
;
159 nsec
>>= data
->cs_shift
;
164 static __always_inline
int do_realtime(struct timespec
*ts
,
165 const union mips_vdso_data
*data
)
171 start_seq
= vdso_data_read_begin(data
);
173 if (data
->clock_mode
== VDSO_CLOCK_NONE
)
176 ts
->tv_sec
= data
->xtime_sec
;
178 } while (vdso_data_read_retry(data
, start_seq
));
181 timespec_add_ns(ts
, ns
);
186 static __always_inline
int do_monotonic(struct timespec
*ts
,
187 const union mips_vdso_data
*data
)
195 start_seq
= vdso_data_read_begin(data
);
197 if (data
->clock_mode
== VDSO_CLOCK_NONE
)
200 ts
->tv_sec
= data
->xtime_sec
;
203 to_mono_sec
= data
->wall_to_mono_sec
;
204 to_mono_nsec
= data
->wall_to_mono_nsec
;
205 } while (vdso_data_read_retry(data
, start_seq
));
207 ts
->tv_sec
+= to_mono_sec
;
209 timespec_add_ns(ts
, ns
+ to_mono_nsec
);
214 #ifdef CONFIG_MIPS_CLOCK_VSYSCALL
217 * This is behind the ifdef so that we don't provide the symbol when there's no
218 * possibility of there being a usable clocksource, because there's nothing we
219 * can do without it. When libc fails the symbol lookup it should fall back on
220 * the standard syscall path.
222 int __vdso_gettimeofday(struct timeval
*tv
, struct timezone
*tz
)
224 const union mips_vdso_data
*data
= get_vdso_data();
228 ret
= do_realtime(&ts
, data
);
230 return gettimeofday_fallback(tv
, tz
);
233 tv
->tv_sec
= ts
.tv_sec
;
234 tv
->tv_usec
= ts
.tv_nsec
/ 1000;
238 tz
->tz_minuteswest
= data
->tz_minuteswest
;
239 tz
->tz_dsttime
= data
->tz_dsttime
;
245 #endif /* CONFIG_MIPS_CLOCK_VSYSCALL */
247 int __vdso_clock_gettime(clockid_t clkid
, struct timespec
*ts
)
249 const union mips_vdso_data
*data
= get_vdso_data();
253 case CLOCK_REALTIME_COARSE
:
254 ret
= do_realtime_coarse(ts
, data
);
256 case CLOCK_MONOTONIC_COARSE
:
257 ret
= do_monotonic_coarse(ts
, data
);
260 ret
= do_realtime(ts
, data
);
262 case CLOCK_MONOTONIC
:
263 ret
= do_monotonic(ts
, data
);
270 ret
= clock_gettime_fallback(clkid
, ts
);