Commit | Line | Data |
---|---|---|
fd1fea68 MK |
1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | ||
3 | /* | |
4 | * Definitions for the clocksource provided by the Hyper-V | |
5 | * hypervisor to guest VMs, as described in the Hyper-V Top | |
6 | * Level Functional Spec (TLFS). | |
7 | * | |
8 | * Copyright (C) 2019, Microsoft, Inc. | |
9 | * | |
10 | * Author: Michael Kelley <mikelley@microsoft.com> | |
11 | */ | |
12 | ||
13 | #ifndef __CLKSOURCE_HYPERV_TIMER_H | |
14 | #define __CLKSOURCE_HYPERV_TIMER_H | |
15 | ||
dd2cb348 MK |
16 | #include <linux/clocksource.h> |
17 | #include <linux/math64.h> | |
e5dfd093 | 18 | #include <asm/hyperv-tlfs.h> |
dd2cb348 | 19 | |
fd1fea68 MK |
20 | #define HV_MAX_MAX_DELTA_TICKS 0xffffffff |
21 | #define HV_MIN_DELTA_TICKS 1 | |
22 | ||
31e5e646 MK |
23 | #ifdef CONFIG_HYPERV_TIMER |
24 | ||
e5dfd093 TG |
25 | #include <asm/hyperv_timer.h> |
26 | ||
fd1fea68 | 27 | /* Routines called by the VMbus driver */ |
ec866be6 | 28 | extern int hv_stimer_alloc(bool have_percpu_irqs); |
4df4cb9e MK |
29 | extern int hv_stimer_cleanup(unsigned int cpu); |
30 | extern void hv_stimer_legacy_init(unsigned int cpu, int sint); | |
31 | extern void hv_stimer_legacy_cleanup(unsigned int cpu); | |
fd1fea68 MK |
32 | extern void hv_stimer_global_cleanup(void); |
33 | extern void hv_stimer0_isr(void); | |
34 | ||
dd2cb348 | 35 | extern void hv_init_clocksource(void); |
0408f16b | 36 | extern void hv_remap_tsc_clocksource(void); |
dd2cb348 | 37 | |
364adc45 | 38 | extern unsigned long hv_get_tsc_pfn(void); |
dd2cb348 MK |
39 | extern struct ms_hyperv_tsc_page *hv_get_tsc_page(void); |
40 | ||
41 | static inline notrace u64 | |
42 | hv_read_tsc_page_tsc(const struct ms_hyperv_tsc_page *tsc_pg, u64 *cur_tsc) | |
43 | { | |
44 | u64 scale, offset; | |
45 | u32 sequence; | |
46 | ||
47 | /* | |
48 | * The protocol for reading Hyper-V TSC page is specified in Hypervisor | |
49 | * Top-Level Functional Specification ver. 3.0 and above. To get the | |
50 | * reference time we must do the following: | |
51 | * - READ ReferenceTscSequence | |
52 | * A special '0' value indicates the time source is unreliable and we | |
53 | * need to use something else. The currently published specification | |
54 | * versions (up to 4.0b) contain a mistake and wrongly claim '-1' | |
55 | * instead of '0' as the special value, see commit c35b82ef0294. | |
56 | * - ReferenceTime = | |
57 | * ((RDTSC() * ReferenceTscScale) >> 64) + ReferenceTscOffset | |
58 | * - READ ReferenceTscSequence again. In case its value has changed | |
59 | * since our first reading we need to discard ReferenceTime and repeat | |
60 | * the whole sequence as the hypervisor was updating the page in | |
61 | * between. | |
62 | */ | |
63 | do { | |
64 | sequence = READ_ONCE(tsc_pg->tsc_sequence); | |
65 | if (!sequence) | |
66 | return U64_MAX; | |
67 | /* | |
68 | * Make sure we read sequence before we read other values from | |
69 | * TSC page. | |
70 | */ | |
71 | smp_rmb(); | |
72 | ||
73 | scale = READ_ONCE(tsc_pg->tsc_scale); | |
74 | offset = READ_ONCE(tsc_pg->tsc_offset); | |
75 | *cur_tsc = hv_get_raw_timer(); | |
76 | ||
77 | /* | |
78 | * Make sure we read sequence after we read all other values | |
79 | * from TSC page. | |
80 | */ | |
81 | smp_rmb(); | |
82 | ||
83 | } while (READ_ONCE(tsc_pg->tsc_sequence) != sequence); | |
84 | ||
85 | return mul_u64_u64_shr(*cur_tsc, scale, 64) + offset; | |
86 | } | |
87 | ||
88 | static inline notrace u64 | |
89 | hv_read_tsc_page(const struct ms_hyperv_tsc_page *tsc_pg) | |
90 | { | |
91 | u64 cur_tsc; | |
92 | ||
93 | return hv_read_tsc_page_tsc(tsc_pg, &cur_tsc); | |
94 | } | |
95 | ||
3e2d9453 | 96 | #else /* CONFIG_HYPERV_TIMER */ |
364adc45 SK |
97 | static inline unsigned long hv_get_tsc_pfn(void) |
98 | { | |
99 | return 0; | |
100 | } | |
101 | ||
dd2cb348 MK |
102 | static inline struct ms_hyperv_tsc_page *hv_get_tsc_page(void) |
103 | { | |
104 | return NULL; | |
105 | } | |
106 | ||
107 | static inline u64 hv_read_tsc_page_tsc(const struct ms_hyperv_tsc_page *tsc_pg, | |
108 | u64 *cur_tsc) | |
109 | { | |
110 | return U64_MAX; | |
111 | } | |
31e5e646 MK |
112 | |
113 | static inline int hv_stimer_cleanup(unsigned int cpu) { return 0; } | |
114 | static inline void hv_stimer_legacy_init(unsigned int cpu, int sint) {} | |
115 | static inline void hv_stimer_legacy_cleanup(unsigned int cpu) {} | |
116 | static inline void hv_stimer_global_cleanup(void) {} | |
117 | static inline void hv_stimer0_isr(void) {} | |
118 | ||
3e2d9453 | 119 | #endif /* CONFIG_HYPERV_TIMER */ |
dd2cb348 | 120 | |
fd1fea68 | 121 | #endif |