1 /* 2 * Copyright (C) 2018 ARM Limited 3 * Copyright (C) 2015 Imagination Technologies 4 * Author: Alex Smith <alex.smith@imgtec.com> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or (at your 9 * option) any later version. 10 */ 11 #ifndef __ASM_VDSO_GETTIMEOFDAY_H 12 #define __ASM_VDSO_GETTIMEOFDAY_H 13 14 #ifndef __ASSEMBLY__ 15 16 #include <linux/compiler.h> 17 #include <linux/time.h> 18 19 #include <asm/vdso/vdso.h> 20 #include <asm/clocksource.h> 21 #include <asm/io.h> 22 #include <asm/unistd.h> 23 #include <asm/vdso.h> 24 25 #define VDSO_HAS_CLOCK_GETRES 1 26 27 static __always_inline long gettimeofday_fallback( 28 struct __kernel_old_timeval *_tv, 29 struct timezone *_tz) 30 { 31 register struct timezone *tz asm("a1") = _tz; 32 register struct __kernel_old_timeval *tv asm("a0") = _tv; 33 register long ret asm("v0"); 34 register long nr asm("v0") = __NR_gettimeofday; 35 register long error asm("a3"); 36 37 asm volatile( 38 " syscall\n" 39 : "=r" (ret), "=r" (error) 40 : "r" (tv), "r" (tz), "r" (nr) 41 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", 42 "$14", "$15", "$24", "$25", "hi", "lo", "memory"); 43 44 return error ? -ret : ret; 45 } 46 47 static __always_inline long clock_gettime_fallback( 48 clockid_t _clkid, 49 struct __kernel_timespec *_ts) 50 { 51 register struct __kernel_timespec *ts asm("a1") = _ts; 52 register clockid_t clkid asm("a0") = _clkid; 53 register long ret asm("v0"); 54 #if _MIPS_SIM == _MIPS_SIM_ABI64 55 register long nr asm("v0") = __NR_clock_gettime; 56 #else 57 register long nr asm("v0") = __NR_clock_gettime64; 58 #endif 59 register long error asm("a3"); 60 61 asm volatile( 62 " syscall\n" 63 : "=r" (ret), "=r" (error) 64 : "r" (clkid), "r" (ts), "r" (nr) 65 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", 66 "$14", "$15", "$24", "$25", "hi", "lo", "memory"); 67 68 return error ? -ret : ret; 69 } 70 71 static __always_inline int clock_getres_fallback( 72 clockid_t _clkid, 73 struct __kernel_timespec *_ts) 74 { 75 register struct __kernel_timespec *ts asm("a1") = _ts; 76 register clockid_t clkid asm("a0") = _clkid; 77 register long ret asm("v0"); 78 #if _MIPS_SIM == _MIPS_SIM_ABI64 79 register long nr asm("v0") = __NR_clock_getres; 80 #else 81 register long nr asm("v0") = __NR_clock_getres_time64; 82 #endif 83 register long error asm("a3"); 84 85 asm volatile( 86 " syscall\n" 87 : "=r" (ret), "=r" (error) 88 : "r" (clkid), "r" (ts), "r" (nr) 89 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", 90 "$14", "$15", "$24", "$25", "hi", "lo", "memory"); 91 92 return error ? -ret : ret; 93 } 94 95 #if _MIPS_SIM != _MIPS_SIM_ABI64 96 97 static __always_inline long clock_gettime32_fallback( 98 clockid_t _clkid, 99 struct old_timespec32 *_ts) 100 { 101 register struct old_timespec32 *ts asm("a1") = _ts; 102 register clockid_t clkid asm("a0") = _clkid; 103 register long ret asm("v0"); 104 register long nr asm("v0") = __NR_clock_gettime; 105 register long error asm("a3"); 106 107 asm volatile( 108 " syscall\n" 109 : "=r" (ret), "=r" (error) 110 : "r" (clkid), "r" (ts), "r" (nr) 111 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", 112 "$14", "$15", "$24", "$25", "hi", "lo", "memory"); 113 114 return error ? -ret : ret; 115 } 116 117 static __always_inline int clock_getres32_fallback( 118 clockid_t _clkid, 119 struct old_timespec32 *_ts) 120 { 121 register struct old_timespec32 *ts asm("a1") = _ts; 122 register clockid_t clkid asm("a0") = _clkid; 123 register long ret asm("v0"); 124 register long nr asm("v0") = __NR_clock_getres; 125 register long error asm("a3"); 126 127 asm volatile( 128 " syscall\n" 129 : "=r" (ret), "=r" (error) 130 : "r" (clkid), "r" (ts), "r" (nr) 131 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", 132 "$14", "$15", "$24", "$25", "hi", "lo", "memory"); 133 134 return error ? -ret : ret; 135 } 136 #endif 137 138 #ifdef CONFIG_CSRC_R4K 139 140 static __always_inline u64 read_r4k_count(void) 141 { 142 unsigned int count; 143 144 __asm__ __volatile__( 145 " .set push\n" 146 " .set mips32r2\n" 147 " rdhwr %0, $2\n" 148 " .set pop\n" 149 : "=r" (count)); 150 151 return count; 152 } 153 154 #endif 155 156 #ifdef CONFIG_CLKSRC_MIPS_GIC 157 158 static __always_inline u64 read_gic_count(const struct vdso_data *data) 159 { 160 void __iomem *gic = get_gic(data); 161 u32 hi, hi2, lo; 162 163 do { 164 hi = __raw_readl(gic + sizeof(lo)); 165 lo = __raw_readl(gic); 166 hi2 = __raw_readl(gic + sizeof(lo)); 167 } while (hi2 != hi); 168 169 return (((u64)hi) << 32) + lo; 170 } 171 172 #endif 173 174 static __always_inline u64 __arch_get_hw_counter(s32 clock_mode) 175 { 176 #ifdef CONFIG_CSRC_R4K 177 if (clock_mode == VDSO_CLOCKMODE_R4K) 178 return read_r4k_count(); 179 #endif 180 #ifdef CONFIG_CLKSRC_MIPS_GIC 181 if (clock_mode == VDSO_CLOCKMODE_GIC) 182 return read_gic_count(get_vdso_data()); 183 #endif 184 /* 185 * Core checks mode already. So this raced against a concurrent 186 * update. Return something. Core will do another round see the 187 * change and fallback to syscall. 188 */ 189 return 0; 190 } 191 192 static inline bool mips_vdso_hres_capable(void) 193 { 194 return IS_ENABLED(CONFIG_CSRC_R4K) || 195 IS_ENABLED(CONFIG_CLKSRC_MIPS_GIC); 196 } 197 #define __arch_vdso_hres_capable mips_vdso_hres_capable 198 199 static __always_inline const struct vdso_data *__arch_get_vdso_data(void) 200 { 201 return get_vdso_data(); 202 } 203 204 #endif /* !__ASSEMBLY__ */ 205 206 #endif /* __ASM_VDSO_GETTIMEOFDAY_H */ 207