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 timespec *_ts)
  52{
  53        register struct 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 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 timespec *ts,
  82                                       struct vdso_data *vdata)
  83{
  84        struct timespec tomono;
  85        u32 seq;
  86
  87        do {
  88                seq = vdso_read_begin(vdata);
  89
  90                ts->tv_sec = vdata->xtime_coarse_sec;
  91                ts->tv_nsec = vdata->xtime_coarse_nsec;
  92
  93                tomono.tv_sec = vdata->wtm_clock_sec;
  94                tomono.tv_nsec = vdata->wtm_clock_nsec;
  95
  96        } while (vdso_read_retry(vdata, seq));
  97
  98        ts->tv_sec += tomono.tv_sec;
  99        timespec_add_ns(ts, tomono.tv_nsec);
 100        return 0;
 101}
 102
 103static notrace inline u64 vgetsns(struct vdso_data *vdso)
 104{
 105        u32 cycle_now;
 106        u32 cycle_delta;
 107        u32 *timer_cycle_base;
 108
 109        timer_cycle_base =
 110            (u32 *) ((char *)__get_timerpage() + vdso->cycle_count_offset);
 111        cycle_now = readl_relaxed(timer_cycle_base);
 112        if (true == vdso->cycle_count_down)
 113                cycle_now = ~(*timer_cycle_base);
 114        cycle_delta = cycle_now - (u32) vdso->cs_cycle_last;
 115        return ((u64) cycle_delta & vdso->cs_mask) * vdso->cs_mult;
 116}
 117
 118static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata)
 119{
 120        unsigned count;
 121        u64 ns;
 122        do {
 123                count = vdso_read_begin(vdata);
 124                ts->tv_sec = vdata->xtime_clock_sec;
 125                ns = vdata->xtime_clock_nsec;
 126                ns += vgetsns(vdata);
 127                ns >>= vdata->cs_shift;
 128        } while (vdso_read_retry(vdata, count));
 129
 130        ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
 131        ts->tv_nsec = ns;
 132
 133        return 0;
 134}
 135
 136static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata)
 137{
 138        struct timespec tomono;
 139        u64 nsecs;
 140        u32 seq;
 141
 142        do {
 143                seq = vdso_read_begin(vdata);
 144
 145                ts->tv_sec = vdata->xtime_clock_sec;
 146                nsecs = vdata->xtime_clock_nsec;
 147                nsecs += vgetsns(vdata);
 148                nsecs >>= vdata->cs_shift;
 149
 150                tomono.tv_sec = vdata->wtm_clock_sec;
 151                tomono.tv_nsec = vdata->wtm_clock_nsec;
 152
 153        } while (vdso_read_retry(vdata, seq));
 154
 155        ts->tv_sec += tomono.tv_sec;
 156        ts->tv_nsec = 0;
 157        timespec_add_ns(ts, nsecs + tomono.tv_nsec);
 158        return 0;
 159}
 160
 161notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts)
 162{
 163        struct vdso_data *vdata;
 164        int ret = -1;
 165
 166        vdata = __get_datapage();
 167        if (vdata->cycle_count_offset == EMPTY_REG_OFFSET)
 168                return clock_gettime_fallback(clkid, ts);
 169
 170        switch (clkid) {
 171        case CLOCK_REALTIME_COARSE:
 172                ret = do_realtime_coarse(ts, vdata);
 173                break;
 174        case CLOCK_MONOTONIC_COARSE:
 175                ret = do_monotonic_coarse(ts, vdata);
 176                break;
 177        case CLOCK_REALTIME:
 178                ret = do_realtime(ts, vdata);
 179                break;
 180        case CLOCK_MONOTONIC:
 181                ret = do_monotonic(ts, vdata);
 182                break;
 183        default:
 184                break;
 185        }
 186
 187        if (ret)
 188                ret = clock_gettime_fallback(clkid, ts);
 189
 190        return ret;
 191}
 192
 193static notrace int clock_getres_fallback(clockid_t _clk_id,
 194                                          struct timespec *_res)
 195{
 196        register clockid_t clk_id asm("$r0") = _clk_id;
 197        register struct timespec *res asm("$r1") = _res;
 198        register int ret asm("$r0");
 199
 200        asm volatile ("movi     $r15, %3\n"
 201                      "syscall  0x0\n"
 202                      :"=r" (ret)
 203                      :"r"(clk_id), "r"(res), "i"(__NR_clock_getres)
 204                      :"$r15", "memory");
 205
 206        return ret;
 207}
 208
 209notrace int __vdso_clock_getres(clockid_t clk_id, struct timespec *res)
 210{
 211        struct vdso_data *vdata = __get_datapage();
 212
 213        if (res == NULL)
 214                return 0;
 215        switch (clk_id) {
 216        case CLOCK_REALTIME:
 217        case CLOCK_MONOTONIC:
 218        case CLOCK_MONOTONIC_RAW:
 219                res->tv_sec = 0;
 220                res->tv_nsec = vdata->hrtimer_res;
 221                break;
 222        case CLOCK_REALTIME_COARSE:
 223        case CLOCK_MONOTONIC_COARSE:
 224                res->tv_sec = 0;
 225                res->tv_nsec = CLOCK_COARSE_RES;
 226                break;
 227        default:
 228                return clock_getres_fallback(clk_id, res);
 229        }
 230        return 0;
 231}
 232
 233static notrace inline int gettimeofday_fallback(struct timeval *_tv,
 234                                                struct timezone *_tz)
 235{
 236        register struct timeval *tv asm("$r0") = _tv;
 237        register struct timezone *tz asm("$r1") = _tz;
 238        register int ret asm("$r0");
 239
 240        asm volatile ("movi     $r15, %3\n"
 241                      "syscall  0x0\n"
 242                      :"=r" (ret)
 243                      :"r"(tv), "r"(tz), "i"(__NR_gettimeofday)
 244                      :"$r15", "memory");
 245
 246        return ret;
 247}
 248
 249notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
 250{
 251        struct timespec ts;
 252        struct vdso_data *vdata;
 253        int ret;
 254
 255        vdata = __get_datapage();
 256
 257        if (vdata->cycle_count_offset == EMPTY_REG_OFFSET)
 258                return gettimeofday_fallback(tv, tz);
 259
 260        ret = do_realtime(&ts, vdata);
 261
 262        if (tv) {
 263                tv->tv_sec = ts.tv_sec;
 264                tv->tv_usec = ts.tv_nsec / 1000;
 265        }
 266        if (tz) {
 267                tz->tz_minuteswest = vdata->tz_minuteswest;
 268                tz->tz_dsttime = vdata->tz_dsttime;
 269        }
 270
 271        return ret;
 272}
 273