gettimeofday.c 5.8 KB

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