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