18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _ASM_X86_PVCLOCK_H
38c2ecf20Sopenharmony_ci#define _ASM_X86_PVCLOCK_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <asm/clocksource.h>
68c2ecf20Sopenharmony_ci#include <asm/pvclock-abi.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/* some helper functions for xen and kvm pv clock sources */
98c2ecf20Sopenharmony_ciu64 pvclock_clocksource_read(struct pvclock_vcpu_time_info *src);
108c2ecf20Sopenharmony_ciu8 pvclock_read_flags(struct pvclock_vcpu_time_info *src);
118c2ecf20Sopenharmony_civoid pvclock_set_flags(u8 flags);
128c2ecf20Sopenharmony_ciunsigned long pvclock_tsc_khz(struct pvclock_vcpu_time_info *src);
138c2ecf20Sopenharmony_civoid pvclock_read_wallclock(struct pvclock_wall_clock *wall,
148c2ecf20Sopenharmony_ci			    struct pvclock_vcpu_time_info *vcpu,
158c2ecf20Sopenharmony_ci			    struct timespec64 *ts);
168c2ecf20Sopenharmony_civoid pvclock_resume(void);
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_civoid pvclock_touch_watchdogs(void);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic __always_inline
218c2ecf20Sopenharmony_ciunsigned pvclock_read_begin(const struct pvclock_vcpu_time_info *src)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	unsigned version = src->version & ~1;
248c2ecf20Sopenharmony_ci	/* Make sure that the version is read before the data. */
258c2ecf20Sopenharmony_ci	virt_rmb();
268c2ecf20Sopenharmony_ci	return version;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic __always_inline
308c2ecf20Sopenharmony_cibool pvclock_read_retry(const struct pvclock_vcpu_time_info *src,
318c2ecf20Sopenharmony_ci			unsigned version)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	/* Make sure that the version is re-read after the data. */
348c2ecf20Sopenharmony_ci	virt_rmb();
358c2ecf20Sopenharmony_ci	return unlikely(version != src->version);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
408c2ecf20Sopenharmony_ci * yielding a 64-bit result.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_cistatic inline u64 pvclock_scale_delta(u64 delta, u32 mul_frac, int shift)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	u64 product;
458c2ecf20Sopenharmony_ci#ifdef __i386__
468c2ecf20Sopenharmony_ci	u32 tmp1, tmp2;
478c2ecf20Sopenharmony_ci#else
488c2ecf20Sopenharmony_ci	ulong tmp;
498c2ecf20Sopenharmony_ci#endif
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (shift < 0)
528c2ecf20Sopenharmony_ci		delta >>= -shift;
538c2ecf20Sopenharmony_ci	else
548c2ecf20Sopenharmony_ci		delta <<= shift;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#ifdef __i386__
578c2ecf20Sopenharmony_ci	__asm__ (
588c2ecf20Sopenharmony_ci		"mul  %5       ; "
598c2ecf20Sopenharmony_ci		"mov  %4,%%eax ; "
608c2ecf20Sopenharmony_ci		"mov  %%edx,%4 ; "
618c2ecf20Sopenharmony_ci		"mul  %5       ; "
628c2ecf20Sopenharmony_ci		"xor  %5,%5    ; "
638c2ecf20Sopenharmony_ci		"add  %4,%%eax ; "
648c2ecf20Sopenharmony_ci		"adc  %5,%%edx ; "
658c2ecf20Sopenharmony_ci		: "=A" (product), "=r" (tmp1), "=r" (tmp2)
668c2ecf20Sopenharmony_ci		: "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) );
678c2ecf20Sopenharmony_ci#elif defined(__x86_64__)
688c2ecf20Sopenharmony_ci	__asm__ (
698c2ecf20Sopenharmony_ci		"mulq %[mul_frac] ; shrd $32, %[hi], %[lo]"
708c2ecf20Sopenharmony_ci		: [lo]"=a"(product),
718c2ecf20Sopenharmony_ci		  [hi]"=d"(tmp)
728c2ecf20Sopenharmony_ci		: "0"(delta),
738c2ecf20Sopenharmony_ci		  [mul_frac]"rm"((u64)mul_frac));
748c2ecf20Sopenharmony_ci#else
758c2ecf20Sopenharmony_ci#error implement me!
768c2ecf20Sopenharmony_ci#endif
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return product;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic __always_inline
828c2ecf20Sopenharmony_ciu64 __pvclock_read_cycles(const struct pvclock_vcpu_time_info *src, u64 tsc)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	u64 delta = tsc - src->tsc_timestamp;
858c2ecf20Sopenharmony_ci	u64 offset = pvclock_scale_delta(delta, src->tsc_to_system_mul,
868c2ecf20Sopenharmony_ci					     src->tsc_shift);
878c2ecf20Sopenharmony_ci	return src->system_time + offset;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistruct pvclock_vsyscall_time_info {
918c2ecf20Sopenharmony_ci	struct pvclock_vcpu_time_info pvti;
928c2ecf20Sopenharmony_ci} __attribute__((__aligned__(SMP_CACHE_BYTES)));
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#define PVTI_SIZE sizeof(struct pvclock_vsyscall_time_info)
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#ifdef CONFIG_PARAVIRT_CLOCK
978c2ecf20Sopenharmony_civoid pvclock_set_pvti_cpu0_va(struct pvclock_vsyscall_time_info *pvti);
988c2ecf20Sopenharmony_cistruct pvclock_vsyscall_time_info *pvclock_get_pvti_cpu0_va(void);
998c2ecf20Sopenharmony_ci#else
1008c2ecf20Sopenharmony_cistatic inline struct pvclock_vsyscall_time_info *pvclock_get_pvti_cpu0_va(void)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	return NULL;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci#endif
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci#endif /* _ASM_X86_PVCLOCK_H */
107