Commit | Line | Data |
---|---|---|
dc899971 KS |
1 | /* |
2 | * A clocksource for Linux running on HyperV. | |
3 | * | |
4 | * | |
5 | * Copyright (C) 2010, Novell, Inc. | |
6 | * Author : K. Y. Srinivasan <ksrinivasan@novell.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
15 | * NON INFRINGEMENT. See the GNU General Public License for more | |
16 | * details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | * | |
22 | */ | |
23 | ||
24 | #include <linux/version.h> | |
25 | #include <linux/clocksource.h> | |
26 | #include <linux/init.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/pci.h> | |
29 | #include <linux/dmi.h> | |
30 | #include <asm/hyperv.h> | |
31 | #include <asm/mshyperv.h> | |
32 | #include <asm/hypervisor.h> | |
33 | ||
34 | #define HV_CLOCK_SHIFT 22 | |
35 | ||
36 | static cycle_t read_hv_clock(struct clocksource *arg) | |
37 | { | |
38 | cycle_t current_tick; | |
39 | /* | |
40 | * Read the partition counter to get the current tick count. This count | |
41 | * is set to 0 when the partition is created and is incremented in | |
42 | * 100 nanosecond units. | |
43 | */ | |
44 | rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); | |
45 | return current_tick; | |
46 | } | |
47 | ||
48 | static struct clocksource hyperv_cs = { | |
49 | .name = "hyperv_clocksource", | |
50 | .rating = 400, /* use this when running on Hyperv*/ | |
51 | .read = read_hv_clock, | |
52 | .mask = CLOCKSOURCE_MASK(64), | |
53 | /* | |
54 | * The time ref counter in HyperV is in 100ns units. | |
55 | * The definition of mult is: | |
56 | * mult/2^shift = ns/cyc = 100 | |
57 | * mult = (100 << shift) | |
58 | */ | |
59 | .mult = (100 << HV_CLOCK_SHIFT), | |
60 | .shift = HV_CLOCK_SHIFT, | |
61 | }; | |
62 | ||
63 | static const struct dmi_system_id __initconst | |
64 | hv_timesource_dmi_table[] __maybe_unused = { | |
65 | { | |
66 | .ident = "Hyper-V", | |
67 | .matches = { | |
68 | DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), | |
69 | DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"), | |
70 | DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"), | |
71 | }, | |
72 | }, | |
73 | { }, | |
74 | }; | |
75 | MODULE_DEVICE_TABLE(dmi, hv_timesource_dmi_table); | |
76 | ||
77 | static const struct pci_device_id __initconst | |
78 | hv_timesource_pci_table[] __maybe_unused = { | |
79 | { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ | |
80 | { 0 } | |
81 | }; | |
82 | MODULE_DEVICE_TABLE(pci, hv_timesource_pci_table); | |
83 | ||
84 | ||
85 | static int __init init_hv_clocksource(void) | |
86 | { | |
87 | if ((x86_hyper != &x86_hyper_ms_hyperv) || | |
88 | !(ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)) | |
89 | return -ENODEV; | |
90 | ||
91 | if (!dmi_check_system(hv_timesource_dmi_table)) | |
92 | return -ENODEV; | |
93 | ||
94 | printk(KERN_INFO "Registering HyperV clock source\n"); | |
95 | return clocksource_register(&hyperv_cs); | |
96 | } | |
97 | ||
98 | module_init(init_hv_clocksource); | |
99 | MODULE_DESCRIPTION("HyperV based clocksource"); | |
100 | MODULE_AUTHOR("K. Y. Srinivasan <ksrinivasan@novell.com>"); | |
101 | MODULE_LICENSE("GPL"); |