gettimeofday.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*
  2. * Copyright (C) 2015 Imagination Technologies
  3. * Author: Alex Smith <alex.smith@imgtec.com>
  4. *
  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.
  9. */
  10. #include "vdso.h"
  11. #include <linux/compiler.h>
  12. #include <linux/time.h>
  13. #include <asm/clocksource.h>
  14. #include <asm/io.h>
  15. #include <asm/unistd.h>
  16. #include <asm/vdso.h>
  17. #if MIPS_ISA_REV < 6
  18. #define VDSO_SYSCALL_CLOBBERS "hi", "lo",
  19. #else
  20. #define VDSO_SYSCALL_CLOBBERS
  21. #endif
  22. #ifdef CONFIG_MIPS_CLOCK_VSYSCALL
  23. static __always_inline long gettimeofday_fallback(struct timeval *_tv,
  24. struct timezone *_tz)
  25. {
  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");
  31. asm volatile(
  32. " syscall\n"
  33. : "=r" (ret), "=r" (error)
  34. : "r" (tv), "r" (tz), "r" (nr)
  35. : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
  36. "$14", "$15", "$24", "$25",
  37. VDSO_SYSCALL_CLOBBERS
  38. "memory");
  39. return error ? -ret : ret;
  40. }
  41. #endif
  42. static __always_inline long clock_gettime_fallback(clockid_t _clkid,
  43. struct timespec *_ts)
  44. {
  45. register struct timespec *ts asm("a1") = _ts;
  46. register clockid_t clkid asm("a0") = _clkid;
  47. register long ret asm("v0");
  48. register long nr asm("v0") = __NR_clock_gettime;
  49. register long error asm("a3");
  50. asm volatile(
  51. " syscall\n"
  52. : "=r" (ret), "=r" (error)
  53. : "r" (clkid), "r" (ts), "r" (nr)
  54. : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
  55. "$14", "$15", "$24", "$25",
  56. VDSO_SYSCALL_CLOBBERS
  57. "memory");
  58. return error ? -ret : ret;
  59. }
  60. static __always_inline int do_realtime_coarse(struct timespec *ts,
  61. const union mips_vdso_data *data)
  62. {
  63. u32 start_seq;
  64. do {
  65. start_seq = vdso_data_read_begin(data);
  66. ts->tv_sec = data->xtime_sec;
  67. ts->tv_nsec = data->xtime_nsec >> data->cs_shift;
  68. } while (vdso_data_read_retry(data, start_seq));
  69. return 0;
  70. }
  71. static __always_inline int do_monotonic_coarse(struct timespec *ts,
  72. const union mips_vdso_data *data)
  73. {
  74. u32 start_seq;
  75. u64 to_mono_sec;
  76. u64 to_mono_nsec;
  77. do {
  78. start_seq = vdso_data_read_begin(data);
  79. ts->tv_sec = data->xtime_sec;
  80. ts->tv_nsec = data->xtime_nsec >> data->cs_shift;
  81. to_mono_sec = data->wall_to_mono_sec;
  82. to_mono_nsec = data->wall_to_mono_nsec;
  83. } while (vdso_data_read_retry(data, start_seq));
  84. ts->tv_sec += to_mono_sec;
  85. timespec_add_ns(ts, to_mono_nsec);
  86. return 0;
  87. }
  88. #ifdef CONFIG_CSRC_R4K
  89. static __always_inline u64 read_r4k_count(void)
  90. {
  91. unsigned int count;
  92. __asm__ __volatile__(
  93. " .set push\n"
  94. " .set mips32r2\n"
  95. " rdhwr %0, $2\n"
  96. " .set pop\n"
  97. : "=r" (count));
  98. return count;
  99. }
  100. #endif
  101. #ifdef CONFIG_CLKSRC_MIPS_GIC
  102. static __always_inline u64 read_gic_count(const union mips_vdso_data *data)
  103. {
  104. void __iomem *gic = get_gic(data);
  105. u32 hi, hi2, lo;
  106. do {
  107. hi = __raw_readl(gic + sizeof(lo));
  108. lo = __raw_readl(gic);
  109. hi2 = __raw_readl(gic + sizeof(lo));
  110. } while (hi2 != hi);
  111. return (((u64)hi) << 32) + lo;
  112. }
  113. #endif
  114. static __always_inline u64 get_ns(const union mips_vdso_data *data)
  115. {
  116. u64 cycle_now, delta, nsec;
  117. switch (data->clock_mode) {
  118. #ifdef CONFIG_CSRC_R4K
  119. case VDSO_CLOCK_R4K:
  120. cycle_now = read_r4k_count();
  121. break;
  122. #endif
  123. #ifdef CONFIG_CLKSRC_MIPS_GIC
  124. case VDSO_CLOCK_GIC:
  125. cycle_now = read_gic_count(data);
  126. break;
  127. #endif
  128. default:
  129. return 0;
  130. }
  131. delta = (cycle_now - data->cs_cycle_last) & data->cs_mask;
  132. nsec = (delta * data->cs_mult) + data->xtime_nsec;
  133. nsec >>= data->cs_shift;
  134. return nsec;
  135. }
  136. static __always_inline int do_realtime(struct timespec *ts,
  137. const union mips_vdso_data *data)
  138. {
  139. u32 start_seq;
  140. u64 ns;
  141. do {
  142. start_seq = vdso_data_read_begin(data);
  143. if (data->clock_mode == VDSO_CLOCK_NONE)
  144. return -ENOSYS;
  145. ts->tv_sec = data->xtime_sec;
  146. ns = get_ns(data);
  147. } while (vdso_data_read_retry(data, start_seq));
  148. ts->tv_nsec = 0;
  149. timespec_add_ns(ts, ns);
  150. return 0;
  151. }
  152. static __always_inline int do_monotonic(struct timespec *ts,
  153. const union mips_vdso_data *data)
  154. {
  155. u32 start_seq;
  156. u64 ns;
  157. u64 to_mono_sec;
  158. u64 to_mono_nsec;
  159. do {
  160. start_seq = vdso_data_read_begin(data);
  161. if (data->clock_mode == VDSO_CLOCK_NONE)
  162. return -ENOSYS;
  163. ts->tv_sec = data->xtime_sec;
  164. ns = get_ns(data);
  165. to_mono_sec = data->wall_to_mono_sec;
  166. to_mono_nsec = data->wall_to_mono_nsec;
  167. } while (vdso_data_read_retry(data, start_seq));
  168. ts->tv_sec += to_mono_sec;
  169. ts->tv_nsec = 0;
  170. timespec_add_ns(ts, ns + to_mono_nsec);
  171. return 0;
  172. }
  173. #ifdef CONFIG_MIPS_CLOCK_VSYSCALL
  174. /*
  175. * This is behind the ifdef so that we don't provide the symbol when there's no
  176. * possibility of there being a usable clocksource, because there's nothing we
  177. * can do without it. When libc fails the symbol lookup it should fall back on
  178. * the standard syscall path.
  179. */
  180. int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
  181. {
  182. const union mips_vdso_data *data = get_vdso_data();
  183. struct timespec ts;
  184. int ret;
  185. ret = do_realtime(&ts, data);
  186. if (ret)
  187. return gettimeofday_fallback(tv, tz);
  188. if (tv) {
  189. tv->tv_sec = ts.tv_sec;
  190. tv->tv_usec = ts.tv_nsec / 1000;
  191. }
  192. if (tz) {
  193. tz->tz_minuteswest = data->tz_minuteswest;
  194. tz->tz_dsttime = data->tz_dsttime;
  195. }
  196. return 0;
  197. }
  198. #endif /* CONFIG_MIPS_CLOCK_VSYSCALL */
  199. int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts)
  200. {
  201. const union mips_vdso_data *data = get_vdso_data();
  202. int ret = -1;
  203. switch (clkid) {
  204. case CLOCK_REALTIME_COARSE:
  205. ret = do_realtime_coarse(ts, data);
  206. break;
  207. case CLOCK_MONOTONIC_COARSE:
  208. ret = do_monotonic_coarse(ts, data);
  209. break;
  210. case CLOCK_REALTIME:
  211. ret = do_realtime(ts, data);
  212. break;
  213. case CLOCK_MONOTONIC:
  214. ret = do_monotonic(ts, data);
  215. break;
  216. default:
  217. break;
  218. }
  219. if (ret)
  220. ret = clock_gettime_fallback(clkid, ts);
  221. return ret;
  222. }