18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2018 ARM Limited
38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Imagination Technologies
48c2ecf20Sopenharmony_ci * Author: Alex Smith <alex.smith@imgtec.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it
78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the
88c2ecf20Sopenharmony_ci * Free Software Foundation;  either version 2 of the  License, or (at your
98c2ecf20Sopenharmony_ci * option) any later version.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#ifndef __ASM_VDSO_GETTIMEOFDAY_H
128c2ecf20Sopenharmony_ci#define __ASM_VDSO_GETTIMEOFDAY_H
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#ifndef __ASSEMBLY__
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <asm/vdso/vdso.h>
178c2ecf20Sopenharmony_ci#include <asm/clocksource.h>
188c2ecf20Sopenharmony_ci#include <asm/unistd.h>
198c2ecf20Sopenharmony_ci#include <asm/vdso.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define VDSO_HAS_CLOCK_GETRES		1
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#if MIPS_ISA_REV < 6
248c2ecf20Sopenharmony_ci#define VDSO_SYSCALL_CLOBBERS "hi", "lo",
258c2ecf20Sopenharmony_ci#else
268c2ecf20Sopenharmony_ci#define VDSO_SYSCALL_CLOBBERS
278c2ecf20Sopenharmony_ci#endif
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic __always_inline long gettimeofday_fallback(
308c2ecf20Sopenharmony_ci				struct __kernel_old_timeval *_tv,
318c2ecf20Sopenharmony_ci				struct timezone *_tz)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	register struct timezone *tz asm("a1") = _tz;
348c2ecf20Sopenharmony_ci	register struct __kernel_old_timeval *tv asm("a0") = _tv;
358c2ecf20Sopenharmony_ci	register long ret asm("v0");
368c2ecf20Sopenharmony_ci	register long nr asm("v0") = __NR_gettimeofday;
378c2ecf20Sopenharmony_ci	register long error asm("a3");
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	asm volatile(
408c2ecf20Sopenharmony_ci	"       syscall\n"
418c2ecf20Sopenharmony_ci	: "=r" (ret), "=r" (error)
428c2ecf20Sopenharmony_ci	: "r" (tv), "r" (tz), "r" (nr)
438c2ecf20Sopenharmony_ci	: "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
448c2ecf20Sopenharmony_ci	  "$14", "$15", "$24", "$25",
458c2ecf20Sopenharmony_ci	  VDSO_SYSCALL_CLOBBERS
468c2ecf20Sopenharmony_ci	  "memory");
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return error ? -ret : ret;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic __always_inline long clock_gettime_fallback(
528c2ecf20Sopenharmony_ci					clockid_t _clkid,
538c2ecf20Sopenharmony_ci					struct __kernel_timespec *_ts)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	register struct __kernel_timespec *ts asm("a1") = _ts;
568c2ecf20Sopenharmony_ci	register clockid_t clkid asm("a0") = _clkid;
578c2ecf20Sopenharmony_ci	register long ret asm("v0");
588c2ecf20Sopenharmony_ci#if _MIPS_SIM == _MIPS_SIM_ABI64
598c2ecf20Sopenharmony_ci	register long nr asm("v0") = __NR_clock_gettime;
608c2ecf20Sopenharmony_ci#else
618c2ecf20Sopenharmony_ci	register long nr asm("v0") = __NR_clock_gettime64;
628c2ecf20Sopenharmony_ci#endif
638c2ecf20Sopenharmony_ci	register long error asm("a3");
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	asm volatile(
668c2ecf20Sopenharmony_ci	"       syscall\n"
678c2ecf20Sopenharmony_ci	: "=r" (ret), "=r" (error)
688c2ecf20Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
698c2ecf20Sopenharmony_ci	: "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
708c2ecf20Sopenharmony_ci	  "$14", "$15", "$24", "$25",
718c2ecf20Sopenharmony_ci	  VDSO_SYSCALL_CLOBBERS
728c2ecf20Sopenharmony_ci	  "memory");
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return error ? -ret : ret;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic __always_inline int clock_getres_fallback(
788c2ecf20Sopenharmony_ci					clockid_t _clkid,
798c2ecf20Sopenharmony_ci					struct __kernel_timespec *_ts)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	register struct __kernel_timespec *ts asm("a1") = _ts;
828c2ecf20Sopenharmony_ci	register clockid_t clkid asm("a0") = _clkid;
838c2ecf20Sopenharmony_ci	register long ret asm("v0");
848c2ecf20Sopenharmony_ci#if _MIPS_SIM == _MIPS_SIM_ABI64
858c2ecf20Sopenharmony_ci	register long nr asm("v0") = __NR_clock_getres;
868c2ecf20Sopenharmony_ci#else
878c2ecf20Sopenharmony_ci	register long nr asm("v0") = __NR_clock_getres_time64;
888c2ecf20Sopenharmony_ci#endif
898c2ecf20Sopenharmony_ci	register long error asm("a3");
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	asm volatile(
928c2ecf20Sopenharmony_ci	"       syscall\n"
938c2ecf20Sopenharmony_ci	: "=r" (ret), "=r" (error)
948c2ecf20Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
958c2ecf20Sopenharmony_ci	: "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
968c2ecf20Sopenharmony_ci	  "$14", "$15", "$24", "$25",
978c2ecf20Sopenharmony_ci	  VDSO_SYSCALL_CLOBBERS
988c2ecf20Sopenharmony_ci	  "memory");
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return error ? -ret : ret;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#if _MIPS_SIM != _MIPS_SIM_ABI64
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic __always_inline long clock_gettime32_fallback(
1068c2ecf20Sopenharmony_ci					clockid_t _clkid,
1078c2ecf20Sopenharmony_ci					struct old_timespec32 *_ts)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	register struct old_timespec32 *ts asm("a1") = _ts;
1108c2ecf20Sopenharmony_ci	register clockid_t clkid asm("a0") = _clkid;
1118c2ecf20Sopenharmony_ci	register long ret asm("v0");
1128c2ecf20Sopenharmony_ci	register long nr asm("v0") = __NR_clock_gettime;
1138c2ecf20Sopenharmony_ci	register long error asm("a3");
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	asm volatile(
1168c2ecf20Sopenharmony_ci	"       syscall\n"
1178c2ecf20Sopenharmony_ci	: "=r" (ret), "=r" (error)
1188c2ecf20Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
1198c2ecf20Sopenharmony_ci	: "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
1208c2ecf20Sopenharmony_ci	  "$14", "$15", "$24", "$25",
1218c2ecf20Sopenharmony_ci	  VDSO_SYSCALL_CLOBBERS
1228c2ecf20Sopenharmony_ci	  "memory");
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return error ? -ret : ret;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic __always_inline int clock_getres32_fallback(
1288c2ecf20Sopenharmony_ci					clockid_t _clkid,
1298c2ecf20Sopenharmony_ci					struct old_timespec32 *_ts)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	register struct old_timespec32 *ts asm("a1") = _ts;
1328c2ecf20Sopenharmony_ci	register clockid_t clkid asm("a0") = _clkid;
1338c2ecf20Sopenharmony_ci	register long ret asm("v0");
1348c2ecf20Sopenharmony_ci	register long nr asm("v0") = __NR_clock_getres;
1358c2ecf20Sopenharmony_ci	register long error asm("a3");
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	asm volatile(
1388c2ecf20Sopenharmony_ci	"       syscall\n"
1398c2ecf20Sopenharmony_ci	: "=r" (ret), "=r" (error)
1408c2ecf20Sopenharmony_ci	: "r" (clkid), "r" (ts), "r" (nr)
1418c2ecf20Sopenharmony_ci	: "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
1428c2ecf20Sopenharmony_ci	  "$14", "$15", "$24", "$25",
1438c2ecf20Sopenharmony_ci	  VDSO_SYSCALL_CLOBBERS
1448c2ecf20Sopenharmony_ci	  "memory");
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return error ? -ret : ret;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci#endif
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci#ifdef CONFIG_CSRC_R4K
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic __always_inline u64 read_r4k_count(void)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	unsigned int count;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	__asm__ __volatile__(
1578c2ecf20Sopenharmony_ci	"	.set push\n"
1588c2ecf20Sopenharmony_ci	"	.set mips32r2\n"
1598c2ecf20Sopenharmony_ci	"	rdhwr	%0, $2\n"
1608c2ecf20Sopenharmony_ci	"	.set pop\n"
1618c2ecf20Sopenharmony_ci	: "=r" (count));
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return count;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#endif
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci#ifdef CONFIG_CLKSRC_MIPS_GIC
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic __always_inline u64 read_gic_count(const struct vdso_data *data)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	void __iomem *gic = get_gic(data);
1738c2ecf20Sopenharmony_ci	u32 hi, hi2, lo;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	do {
1768c2ecf20Sopenharmony_ci		hi = __raw_readl(gic + sizeof(lo));
1778c2ecf20Sopenharmony_ci		lo = __raw_readl(gic);
1788c2ecf20Sopenharmony_ci		hi2 = __raw_readl(gic + sizeof(lo));
1798c2ecf20Sopenharmony_ci	} while (hi2 != hi);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return (((u64)hi) << 32) + lo;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci#endif
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
1878c2ecf20Sopenharmony_ci						 const struct vdso_data *vd)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci#ifdef CONFIG_CSRC_R4K
1908c2ecf20Sopenharmony_ci	if (clock_mode == VDSO_CLOCKMODE_R4K)
1918c2ecf20Sopenharmony_ci		return read_r4k_count();
1928c2ecf20Sopenharmony_ci#endif
1938c2ecf20Sopenharmony_ci#ifdef CONFIG_CLKSRC_MIPS_GIC
1948c2ecf20Sopenharmony_ci	if (clock_mode == VDSO_CLOCKMODE_GIC)
1958c2ecf20Sopenharmony_ci		return read_gic_count(vd);
1968c2ecf20Sopenharmony_ci#endif
1978c2ecf20Sopenharmony_ci	/*
1988c2ecf20Sopenharmony_ci	 * Core checks mode already. So this raced against a concurrent
1998c2ecf20Sopenharmony_ci	 * update. Return something. Core will do another round see the
2008c2ecf20Sopenharmony_ci	 * change and fallback to syscall.
2018c2ecf20Sopenharmony_ci	 */
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic inline bool mips_vdso_hres_capable(void)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	return IS_ENABLED(CONFIG_CSRC_R4K) ||
2088c2ecf20Sopenharmony_ci	       IS_ENABLED(CONFIG_CLKSRC_MIPS_GIC);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci#define __arch_vdso_hres_capable mips_vdso_hres_capable
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic __always_inline const struct vdso_data *__arch_get_vdso_data(void)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	return get_vdso_data();
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci#endif /* !__ASSEMBLY__ */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
220