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 36static 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 48static 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 63static const struct dmi_system_id __initconst 64hv_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}; 75MODULE_DEVICE_TABLE(dmi, hv_timesource_dmi_table); 76 77static const struct pci_device_id __initconst 78hv_timesource_pci_table[] __maybe_unused = { 79 { PCI_DEVICE(0x1414, 0x5353) }, /* VGA compatible controller */ 80 { 0 } 81}; 82MODULE_DEVICE_TABLE(pci, hv_timesource_pci_table); 83 84 85static 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 98module_init(init_hv_clocksource); 99MODULE_DESCRIPTION("HyperV based clocksource"); 100MODULE_AUTHOR("K. Y. Srinivasan <ksrinivasan@novell.com>"); 101MODULE_LICENSE("GPL"); 102