18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 28c2ecf20Sopenharmony_ci 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Definitions for the clocksource provided by the Hyper-V 58c2ecf20Sopenharmony_ci * hypervisor to guest VMs, as described in the Hyper-V Top 68c2ecf20Sopenharmony_ci * Level Functional Spec (TLFS). 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2019, Microsoft, Inc. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Author: Michael Kelley <mikelley@microsoft.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#ifndef __CLKSOURCE_HYPERV_TIMER_H 148c2ecf20Sopenharmony_ci#define __CLKSOURCE_HYPERV_TIMER_H 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 178c2ecf20Sopenharmony_ci#include <linux/math64.h> 188c2ecf20Sopenharmony_ci#include <asm/mshyperv.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define HV_MAX_MAX_DELTA_TICKS 0xffffffff 218c2ecf20Sopenharmony_ci#define HV_MIN_DELTA_TICKS 1 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Routines called by the VMbus driver */ 248c2ecf20Sopenharmony_ciextern int hv_stimer_alloc(void); 258c2ecf20Sopenharmony_ciextern void hv_stimer_free(void); 268c2ecf20Sopenharmony_ciextern int hv_stimer_cleanup(unsigned int cpu); 278c2ecf20Sopenharmony_ciextern void hv_stimer_legacy_init(unsigned int cpu, int sint); 288c2ecf20Sopenharmony_ciextern void hv_stimer_legacy_cleanup(unsigned int cpu); 298c2ecf20Sopenharmony_ciextern void hv_stimer_global_cleanup(void); 308c2ecf20Sopenharmony_ciextern void hv_stimer0_isr(void); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#ifdef CONFIG_HYPERV_TIMER 338c2ecf20Sopenharmony_ciextern u64 (*hv_read_reference_counter)(void); 348c2ecf20Sopenharmony_ciextern void hv_init_clocksource(void); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ciextern struct ms_hyperv_tsc_page *hv_get_tsc_page(void); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline notrace u64 398c2ecf20Sopenharmony_cihv_read_tsc_page_tsc(const struct ms_hyperv_tsc_page *tsc_pg, u64 *cur_tsc) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci u64 scale, offset; 428c2ecf20Sopenharmony_ci u32 sequence; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* 458c2ecf20Sopenharmony_ci * The protocol for reading Hyper-V TSC page is specified in Hypervisor 468c2ecf20Sopenharmony_ci * Top-Level Functional Specification ver. 3.0 and above. To get the 478c2ecf20Sopenharmony_ci * reference time we must do the following: 488c2ecf20Sopenharmony_ci * - READ ReferenceTscSequence 498c2ecf20Sopenharmony_ci * A special '0' value indicates the time source is unreliable and we 508c2ecf20Sopenharmony_ci * need to use something else. The currently published specification 518c2ecf20Sopenharmony_ci * versions (up to 4.0b) contain a mistake and wrongly claim '-1' 528c2ecf20Sopenharmony_ci * instead of '0' as the special value, see commit c35b82ef0294. 538c2ecf20Sopenharmony_ci * - ReferenceTime = 548c2ecf20Sopenharmony_ci * ((RDTSC() * ReferenceTscScale) >> 64) + ReferenceTscOffset 558c2ecf20Sopenharmony_ci * - READ ReferenceTscSequence again. In case its value has changed 568c2ecf20Sopenharmony_ci * since our first reading we need to discard ReferenceTime and repeat 578c2ecf20Sopenharmony_ci * the whole sequence as the hypervisor was updating the page in 588c2ecf20Sopenharmony_ci * between. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci do { 618c2ecf20Sopenharmony_ci sequence = READ_ONCE(tsc_pg->tsc_sequence); 628c2ecf20Sopenharmony_ci if (!sequence) 638c2ecf20Sopenharmony_ci return U64_MAX; 648c2ecf20Sopenharmony_ci /* 658c2ecf20Sopenharmony_ci * Make sure we read sequence before we read other values from 668c2ecf20Sopenharmony_ci * TSC page. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci smp_rmb(); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci scale = READ_ONCE(tsc_pg->tsc_scale); 718c2ecf20Sopenharmony_ci offset = READ_ONCE(tsc_pg->tsc_offset); 728c2ecf20Sopenharmony_ci *cur_tsc = hv_get_raw_timer(); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * Make sure we read sequence after we read all other values 768c2ecf20Sopenharmony_ci * from TSC page. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci smp_rmb(); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci } while (READ_ONCE(tsc_pg->tsc_sequence) != sequence); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return mul_u64_u64_shr(*cur_tsc, scale, 64) + offset; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic inline notrace u64 868c2ecf20Sopenharmony_cihv_read_tsc_page(const struct ms_hyperv_tsc_page *tsc_pg) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci u64 cur_tsc; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return hv_read_tsc_page_tsc(tsc_pg, &cur_tsc); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#else /* CONFIG_HYPERV_TIMER */ 948c2ecf20Sopenharmony_cistatic inline struct ms_hyperv_tsc_page *hv_get_tsc_page(void) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci return NULL; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic inline u64 hv_read_tsc_page_tsc(const struct ms_hyperv_tsc_page *tsc_pg, 1008c2ecf20Sopenharmony_ci u64 *cur_tsc) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci return U64_MAX; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci#endif /* CONFIG_HYPERV_TIMER */ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#endif 107