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