162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 VMware, Inc., Palo Alto, CA., USA 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * PTP clock driver for VMware precision clock virtual device. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/acpi.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 1462306a36Sopenharmony_ci#include <asm/hypervisor.h> 1562306a36Sopenharmony_ci#include <asm/vmware.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define VMWARE_MAGIC 0x564D5868 1862306a36Sopenharmony_ci#define VMWARE_CMD_PCLK(nr) ((nr << 16) | 97) 1962306a36Sopenharmony_ci#define VMWARE_CMD_PCLK_GETTIME VMWARE_CMD_PCLK(0) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct acpi_device *ptp_vmw_acpi_device; 2262306a36Sopenharmony_cistatic struct ptp_clock *ptp_vmw_clock; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int ptp_vmw_pclk_read(u64 *ns) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci u32 ret, nsec_hi, nsec_lo, unused1, unused2, unused3; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci asm volatile (VMWARE_HYPERCALL : 3062306a36Sopenharmony_ci "=a"(ret), "=b"(nsec_hi), "=c"(nsec_lo), "=d"(unused1), 3162306a36Sopenharmony_ci "=S"(unused2), "=D"(unused3) : 3262306a36Sopenharmony_ci "a"(VMWARE_MAGIC), "b"(0), 3362306a36Sopenharmony_ci "c"(VMWARE_CMD_PCLK_GETTIME), "d"(0) : 3462306a36Sopenharmony_ci "memory"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (ret == 0) 3762306a36Sopenharmony_ci *ns = ((u64)nsec_hi << 32) | nsec_lo; 3862306a36Sopenharmony_ci return ret; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * PTP clock ops. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int ptp_vmw_adjtime(struct ptp_clock_info *info, s64 delta) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci return -EOPNOTSUPP; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int ptp_vmw_adjfine(struct ptp_clock_info *info, long delta) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci return -EOPNOTSUPP; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int ptp_vmw_gettime(struct ptp_clock_info *info, struct timespec64 *ts) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci u64 ns; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (ptp_vmw_pclk_read(&ns) != 0) 6062306a36Sopenharmony_ci return -EIO; 6162306a36Sopenharmony_ci *ts = ns_to_timespec64(ns); 6262306a36Sopenharmony_ci return 0; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int ptp_vmw_settime(struct ptp_clock_info *info, 6662306a36Sopenharmony_ci const struct timespec64 *ts) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci return -EOPNOTSUPP; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int ptp_vmw_enable(struct ptp_clock_info *info, 7262306a36Sopenharmony_ci struct ptp_clock_request *request, int on) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci return -EOPNOTSUPP; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct ptp_clock_info ptp_vmw_clock_info = { 7862306a36Sopenharmony_ci .owner = THIS_MODULE, 7962306a36Sopenharmony_ci .name = "ptp_vmw", 8062306a36Sopenharmony_ci .max_adj = 0, 8162306a36Sopenharmony_ci .adjtime = ptp_vmw_adjtime, 8262306a36Sopenharmony_ci .adjfine = ptp_vmw_adjfine, 8362306a36Sopenharmony_ci .gettime64 = ptp_vmw_gettime, 8462306a36Sopenharmony_ci .settime64 = ptp_vmw_settime, 8562306a36Sopenharmony_ci .enable = ptp_vmw_enable, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci * ACPI driver ops for VMware "precision clock" virtual device. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int ptp_vmw_acpi_add(struct acpi_device *device) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci ptp_vmw_clock = ptp_clock_register(&ptp_vmw_clock_info, NULL); 9562306a36Sopenharmony_ci if (IS_ERR(ptp_vmw_clock)) { 9662306a36Sopenharmony_ci pr_err("failed to register ptp clock\n"); 9762306a36Sopenharmony_ci return PTR_ERR(ptp_vmw_clock); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci ptp_vmw_acpi_device = device; 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void ptp_vmw_acpi_remove(struct acpi_device *device) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci ptp_clock_unregister(ptp_vmw_clock); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const struct acpi_device_id ptp_vmw_acpi_device_ids[] = { 11062306a36Sopenharmony_ci { "VMW0005", 0 }, 11162306a36Sopenharmony_ci { "", 0 }, 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, ptp_vmw_acpi_device_ids); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic struct acpi_driver ptp_vmw_acpi_driver = { 11762306a36Sopenharmony_ci .name = "ptp_vmw", 11862306a36Sopenharmony_ci .ids = ptp_vmw_acpi_device_ids, 11962306a36Sopenharmony_ci .ops = { 12062306a36Sopenharmony_ci .add = ptp_vmw_acpi_add, 12162306a36Sopenharmony_ci .remove = ptp_vmw_acpi_remove 12262306a36Sopenharmony_ci }, 12362306a36Sopenharmony_ci .owner = THIS_MODULE 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int __init ptp_vmw_init(void) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci if (x86_hyper_type != X86_HYPER_VMWARE) 12962306a36Sopenharmony_ci return -1; 13062306a36Sopenharmony_ci return acpi_bus_register_driver(&ptp_vmw_acpi_driver); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void __exit ptp_vmw_exit(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci acpi_bus_unregister_driver(&ptp_vmw_acpi_driver); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cimodule_init(ptp_vmw_init); 13962306a36Sopenharmony_cimodule_exit(ptp_vmw_exit); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ciMODULE_DESCRIPTION("VMware virtual PTP clock driver"); 14262306a36Sopenharmony_ciMODULE_AUTHOR("VMware, Inc."); 14362306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 144