| /* |
| * Copyright 2012 Tilera Corporation. All Rights Reserved. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation, version 2. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or |
| * NON INFRINGEMENT. See the GNU General Public License for |
| * more details. |
| */ |
| |
| #define VDSO_BUILD /* avoid some shift warnings for -m32 in <asm/page.h> */ |
| #include <linux/time.h> |
| #include <asm/timex.h> |
| #include <asm/vdso.h> |
| |
| #if CHIP_HAS_SPLIT_CYCLE() |
| static inline cycles_t get_cycles_inline(void) |
| { |
| unsigned int high = __insn_mfspr(SPR_CYCLE_HIGH); |
| unsigned int low = __insn_mfspr(SPR_CYCLE_LOW); |
| unsigned int high2 = __insn_mfspr(SPR_CYCLE_HIGH); |
| |
| while (unlikely(high != high2)) { |
| low = __insn_mfspr(SPR_CYCLE_LOW); |
| high = high2; |
| high2 = __insn_mfspr(SPR_CYCLE_HIGH); |
| } |
| |
| return (((cycles_t)high) << 32) | low; |
| } |
| #define get_cycles get_cycles_inline |
| #endif |
| |
| /* |
| * Find out the vDSO data page address in the process address space. |
| */ |
| inline unsigned long get_datapage(void) |
| { |
| unsigned long ret; |
| |
| /* vdso data page located in the 2nd vDSO page. */ |
| asm volatile ("lnk %0" : "=r"(ret)); |
| ret &= ~(PAGE_SIZE - 1); |
| ret += PAGE_SIZE; |
| |
| return ret; |
| } |
| |
| int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) |
| { |
| cycles_t cycles; |
| unsigned long count, sec, ns; |
| volatile struct vdso_data *vdso_data; |
| |
| vdso_data = (struct vdso_data *)get_datapage(); |
| /* The use of the timezone is obsolete, normally tz is NULL. */ |
| if (unlikely(tz != NULL)) { |
| while (1) { |
| /* Spin until the update finish. */ |
| count = vdso_data->tz_update_count; |
| if (count & 1) |
| continue; |
| |
| tz->tz_minuteswest = vdso_data->tz_minuteswest; |
| tz->tz_dsttime = vdso_data->tz_dsttime; |
| |
| /* Check whether updated, read again if so. */ |
| if (count == vdso_data->tz_update_count) |
| break; |
| } |
| } |
| |
| if (unlikely(tv == NULL)) |
| return 0; |
| |
| while (1) { |
| /* Spin until the update finish. */ |
| count = vdso_data->tb_update_count; |
| if (count & 1) |
| continue; |
| |
| cycles = (get_cycles() - vdso_data->xtime_tod_stamp); |
| ns = (cycles * vdso_data->mult) >> vdso_data->shift; |
| sec = vdso_data->xtime_clock_sec; |
| ns += vdso_data->xtime_clock_nsec; |
| if (ns >= NSEC_PER_SEC) { |
| ns -= NSEC_PER_SEC; |
| sec += 1; |
| } |
| |
| /* Check whether updated, read again if so. */ |
| if (count == vdso_data->tb_update_count) |
| break; |
| } |
| |
| tv->tv_sec = sec; |
| tv->tv_usec = ns / 1000; |
| |
| return 0; |
| } |
| |
| int gettimeofday(struct timeval *tv, struct timezone *tz) |
| __attribute__((weak, alias("__vdso_gettimeofday"))); |