Blame view
drivers/ptp/ptp_vmw.c
3.05 KB
7d10001e2
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* * Copyright (C) 2020 VMware, Inc., Palo Alto, CA., USA * * PTP clock driver for VMware precision clock virtual device. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/ptp_clock_kernel.h> #include <asm/hypervisor.h> #include <asm/vmware.h> #define VMWARE_MAGIC 0x564D5868 #define VMWARE_CMD_PCLK(nr) ((nr << 16) | 97) #define VMWARE_CMD_PCLK_GETTIME VMWARE_CMD_PCLK(0) static struct acpi_device *ptp_vmw_acpi_device; static struct ptp_clock *ptp_vmw_clock; static int ptp_vmw_pclk_read(u64 *ns) { u32 ret, nsec_hi, nsec_lo, unused1, unused2, unused3; asm volatile (VMWARE_HYPERCALL : "=a"(ret), "=b"(nsec_hi), "=c"(nsec_lo), "=d"(unused1), "=S"(unused2), "=D"(unused3) : "a"(VMWARE_MAGIC), "b"(0), "c"(VMWARE_CMD_PCLK_GETTIME), "d"(0) : "memory"); if (ret == 0) *ns = ((u64)nsec_hi << 32) | nsec_lo; return ret; } /* * PTP clock ops. */ static int ptp_vmw_adjtime(struct ptp_clock_info *info, s64 delta) { return -EOPNOTSUPP; } static int ptp_vmw_adjfreq(struct ptp_clock_info *info, s32 delta) { return -EOPNOTSUPP; } static int ptp_vmw_gettime(struct ptp_clock_info *info, struct timespec64 *ts) { u64 ns; if (ptp_vmw_pclk_read(&ns) != 0) return -EIO; *ts = ns_to_timespec64(ns); return 0; } static int ptp_vmw_settime(struct ptp_clock_info *info, const struct timespec64 *ts) { return -EOPNOTSUPP; } static int ptp_vmw_enable(struct ptp_clock_info *info, struct ptp_clock_request *request, int on) { return -EOPNOTSUPP; } static struct ptp_clock_info ptp_vmw_clock_info = { .owner = THIS_MODULE, .name = "ptp_vmw", .max_adj = 0, .adjtime = ptp_vmw_adjtime, .adjfreq = ptp_vmw_adjfreq, .gettime64 = ptp_vmw_gettime, .settime64 = ptp_vmw_settime, .enable = ptp_vmw_enable, }; /* * ACPI driver ops for VMware "precision clock" virtual device. */ static int ptp_vmw_acpi_add(struct acpi_device *device) { ptp_vmw_clock = ptp_clock_register(&ptp_vmw_clock_info, NULL); if (IS_ERR(ptp_vmw_clock)) { pr_err("failed to register ptp clock "); return PTR_ERR(ptp_vmw_clock); } ptp_vmw_acpi_device = device; return 0; } static int ptp_vmw_acpi_remove(struct acpi_device *device) { ptp_clock_unregister(ptp_vmw_clock); return 0; } static const struct acpi_device_id ptp_vmw_acpi_device_ids[] = { { "VMW0005", 0 }, { "", 0 }, }; MODULE_DEVICE_TABLE(acpi, ptp_vmw_acpi_device_ids); static struct acpi_driver ptp_vmw_acpi_driver = { .name = "ptp_vmw", .ids = ptp_vmw_acpi_device_ids, .ops = { .add = ptp_vmw_acpi_add, .remove = ptp_vmw_acpi_remove }, .owner = THIS_MODULE }; static int __init ptp_vmw_init(void) { if (x86_hyper_type != X86_HYPER_VMWARE) return -1; return acpi_bus_register_driver(&ptp_vmw_acpi_driver); } static void __exit ptp_vmw_exit(void) { acpi_bus_unregister_driver(&ptp_vmw_acpi_driver); } module_init(ptp_vmw_init); module_exit(ptp_vmw_exit); MODULE_DESCRIPTION("VMware virtual PTP clock driver"); MODULE_AUTHOR("VMware, Inc."); MODULE_LICENSE("Dual BSD/GPL"); |