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