18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 VMware, Inc., Palo Alto, CA., USA 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * PTP clock driver for VMware precision clock virtual device. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/acpi.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 148c2ecf20Sopenharmony_ci#include <asm/hypervisor.h> 158c2ecf20Sopenharmony_ci#include <asm/vmware.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define VMWARE_MAGIC 0x564D5868 188c2ecf20Sopenharmony_ci#define VMWARE_CMD_PCLK(nr) ((nr << 16) | 97) 198c2ecf20Sopenharmony_ci#define VMWARE_CMD_PCLK_GETTIME VMWARE_CMD_PCLK(0) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic struct acpi_device *ptp_vmw_acpi_device; 228c2ecf20Sopenharmony_cistatic struct ptp_clock *ptp_vmw_clock; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int ptp_vmw_pclk_read(u64 *ns) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci u32 ret, nsec_hi, nsec_lo, unused1, unused2, unused3; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci asm volatile (VMWARE_HYPERCALL : 308c2ecf20Sopenharmony_ci "=a"(ret), "=b"(nsec_hi), "=c"(nsec_lo), "=d"(unused1), 318c2ecf20Sopenharmony_ci "=S"(unused2), "=D"(unused3) : 328c2ecf20Sopenharmony_ci "a"(VMWARE_MAGIC), "b"(0), 338c2ecf20Sopenharmony_ci "c"(VMWARE_CMD_PCLK_GETTIME), "d"(0) : 348c2ecf20Sopenharmony_ci "memory"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (ret == 0) 378c2ecf20Sopenharmony_ci *ns = ((u64)nsec_hi << 32) | nsec_lo; 388c2ecf20Sopenharmony_ci return ret; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * PTP clock ops. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int ptp_vmw_adjtime(struct ptp_clock_info *info, s64 delta) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int ptp_vmw_adjfreq(struct ptp_clock_info *info, s32 delta) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int ptp_vmw_gettime(struct ptp_clock_info *info, struct timespec64 *ts) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci u64 ns; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (ptp_vmw_pclk_read(&ns) != 0) 608c2ecf20Sopenharmony_ci return -EIO; 618c2ecf20Sopenharmony_ci *ts = ns_to_timespec64(ns); 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic int ptp_vmw_settime(struct ptp_clock_info *info, 668c2ecf20Sopenharmony_ci const struct timespec64 *ts) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int ptp_vmw_enable(struct ptp_clock_info *info, 728c2ecf20Sopenharmony_ci struct ptp_clock_request *request, int on) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic struct ptp_clock_info ptp_vmw_clock_info = { 788c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 798c2ecf20Sopenharmony_ci .name = "ptp_vmw", 808c2ecf20Sopenharmony_ci .max_adj = 0, 818c2ecf20Sopenharmony_ci .adjtime = ptp_vmw_adjtime, 828c2ecf20Sopenharmony_ci .adjfreq = ptp_vmw_adjfreq, 838c2ecf20Sopenharmony_ci .gettime64 = ptp_vmw_gettime, 848c2ecf20Sopenharmony_ci .settime64 = ptp_vmw_settime, 858c2ecf20Sopenharmony_ci .enable = ptp_vmw_enable, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * ACPI driver ops for VMware "precision clock" virtual device. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int ptp_vmw_acpi_add(struct acpi_device *device) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci ptp_vmw_clock = ptp_clock_register(&ptp_vmw_clock_info, NULL); 958c2ecf20Sopenharmony_ci if (IS_ERR(ptp_vmw_clock)) { 968c2ecf20Sopenharmony_ci pr_err("failed to register ptp clock\n"); 978c2ecf20Sopenharmony_ci return PTR_ERR(ptp_vmw_clock); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ptp_vmw_acpi_device = device; 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int ptp_vmw_acpi_remove(struct acpi_device *device) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci ptp_clock_unregister(ptp_vmw_clock); 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic const struct acpi_device_id ptp_vmw_acpi_device_ids[] = { 1118c2ecf20Sopenharmony_ci { "VMW0005", 0 }, 1128c2ecf20Sopenharmony_ci { "", 0 }, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, ptp_vmw_acpi_device_ids); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic struct acpi_driver ptp_vmw_acpi_driver = { 1188c2ecf20Sopenharmony_ci .name = "ptp_vmw", 1198c2ecf20Sopenharmony_ci .ids = ptp_vmw_acpi_device_ids, 1208c2ecf20Sopenharmony_ci .ops = { 1218c2ecf20Sopenharmony_ci .add = ptp_vmw_acpi_add, 1228c2ecf20Sopenharmony_ci .remove = ptp_vmw_acpi_remove 1238c2ecf20Sopenharmony_ci }, 1248c2ecf20Sopenharmony_ci .owner = THIS_MODULE 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int __init ptp_vmw_init(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci if (x86_hyper_type != X86_HYPER_VMWARE) 1308c2ecf20Sopenharmony_ci return -1; 1318c2ecf20Sopenharmony_ci return acpi_bus_register_driver(&ptp_vmw_acpi_driver); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void __exit ptp_vmw_exit(void) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci acpi_bus_unregister_driver(&ptp_vmw_acpi_driver); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cimodule_init(ptp_vmw_init); 1408c2ecf20Sopenharmony_cimodule_exit(ptp_vmw_exit); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VMware virtual PTP clock driver"); 1438c2ecf20Sopenharmony_ciMODULE_AUTHOR("VMware, Inc."); 1448c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 145