162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * Definitions for the clocksource provided by the Hyper-V 562306a36Sopenharmony_ci * hypervisor to guest VMs, as described in the Hyper-V Top 662306a36Sopenharmony_ci * Level Functional Spec (TLFS). 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2019, Microsoft, Inc. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Author: Michael Kelley <mikelley@microsoft.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#ifndef __CLKSOURCE_HYPERV_TIMER_H 1462306a36Sopenharmony_ci#define __CLKSOURCE_HYPERV_TIMER_H 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/clocksource.h> 1762306a36Sopenharmony_ci#include <linux/math64.h> 1862306a36Sopenharmony_ci#include <asm/hyperv-tlfs.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define HV_MAX_MAX_DELTA_TICKS 0xffffffff 2162306a36Sopenharmony_ci#define HV_MIN_DELTA_TICKS 1 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#ifdef CONFIG_HYPERV_TIMER 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/hyperv_timer.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Routines called by the VMbus driver */ 2862306a36Sopenharmony_ciextern int hv_stimer_alloc(bool have_percpu_irqs); 2962306a36Sopenharmony_ciextern int hv_stimer_cleanup(unsigned int cpu); 3062306a36Sopenharmony_ciextern void hv_stimer_legacy_init(unsigned int cpu, int sint); 3162306a36Sopenharmony_ciextern void hv_stimer_legacy_cleanup(unsigned int cpu); 3262306a36Sopenharmony_ciextern void hv_stimer_global_cleanup(void); 3362306a36Sopenharmony_ciextern void hv_stimer0_isr(void); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ciextern void hv_init_clocksource(void); 3662306a36Sopenharmony_ciextern void hv_remap_tsc_clocksource(void); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciextern unsigned long hv_get_tsc_pfn(void); 3962306a36Sopenharmony_ciextern struct ms_hyperv_tsc_page *hv_get_tsc_page(void); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic __always_inline bool 4262306a36Sopenharmony_cihv_read_tsc_page_tsc(const struct ms_hyperv_tsc_page *tsc_pg, 4362306a36Sopenharmony_ci u64 *cur_tsc, u64 *time) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci u64 scale, offset; 4662306a36Sopenharmony_ci u32 sequence; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * The protocol for reading Hyper-V TSC page is specified in Hypervisor 5062306a36Sopenharmony_ci * Top-Level Functional Specification ver. 3.0 and above. To get the 5162306a36Sopenharmony_ci * reference time we must do the following: 5262306a36Sopenharmony_ci * - READ ReferenceTscSequence 5362306a36Sopenharmony_ci * A special '0' value indicates the time source is unreliable and we 5462306a36Sopenharmony_ci * need to use something else. The currently published specification 5562306a36Sopenharmony_ci * versions (up to 4.0b) contain a mistake and wrongly claim '-1' 5662306a36Sopenharmony_ci * instead of '0' as the special value, see commit c35b82ef0294. 5762306a36Sopenharmony_ci * - ReferenceTime = 5862306a36Sopenharmony_ci * ((RDTSC() * ReferenceTscScale) >> 64) + ReferenceTscOffset 5962306a36Sopenharmony_ci * - READ ReferenceTscSequence again. In case its value has changed 6062306a36Sopenharmony_ci * since our first reading we need to discard ReferenceTime and repeat 6162306a36Sopenharmony_ci * the whole sequence as the hypervisor was updating the page in 6262306a36Sopenharmony_ci * between. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci do { 6562306a36Sopenharmony_ci sequence = READ_ONCE(tsc_pg->tsc_sequence); 6662306a36Sopenharmony_ci if (!sequence) 6762306a36Sopenharmony_ci return false; 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Make sure we read sequence before we read other values from 7062306a36Sopenharmony_ci * TSC page. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci smp_rmb(); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci scale = READ_ONCE(tsc_pg->tsc_scale); 7562306a36Sopenharmony_ci offset = READ_ONCE(tsc_pg->tsc_offset); 7662306a36Sopenharmony_ci *cur_tsc = hv_get_raw_timer(); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* 7962306a36Sopenharmony_ci * Make sure we read sequence after we read all other values 8062306a36Sopenharmony_ci * from TSC page. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci smp_rmb(); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci } while (READ_ONCE(tsc_pg->tsc_sequence) != sequence); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci *time = mul_u64_u64_shr(*cur_tsc, scale, 64) + offset; 8762306a36Sopenharmony_ci return true; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#else /* CONFIG_HYPERV_TIMER */ 9162306a36Sopenharmony_cistatic inline unsigned long hv_get_tsc_pfn(void) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic inline struct ms_hyperv_tsc_page *hv_get_tsc_page(void) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci return NULL; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic __always_inline bool 10262306a36Sopenharmony_cihv_read_tsc_page_tsc(const struct ms_hyperv_tsc_page *tsc_pg, u64 *cur_tsc, u64 *time) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci return false; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic inline int hv_stimer_cleanup(unsigned int cpu) { return 0; } 10862306a36Sopenharmony_cistatic inline void hv_stimer_legacy_init(unsigned int cpu, int sint) {} 10962306a36Sopenharmony_cistatic inline void hv_stimer_legacy_cleanup(unsigned int cpu) {} 11062306a36Sopenharmony_cistatic inline void hv_stimer_global_cleanup(void) {} 11162306a36Sopenharmony_cistatic inline void hv_stimer0_isr(void) {} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#endif /* CONFIG_HYPERV_TIMER */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#endif 116