summaryrefslogtreecommitdiff
path: root/drivers/ptp
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ptp')
-rw-r--r--drivers/ptp/Kconfig28
-rw-r--r--drivers/ptp/Makefile2
-rw-r--r--drivers/ptp/ptp_chardev.c15
-rw-r--r--drivers/ptp/ptp_clock.c13
-rw-r--r--drivers/ptp/ptp_clockmatrix.c8
-rw-r--r--drivers/ptp/ptp_dte.c4
-rw-r--r--drivers/ptp/ptp_fc3.c9
-rw-r--r--drivers/ptp/ptp_idt82p33.c14
-rw-r--r--drivers/ptp/ptp_ines.c9
-rw-r--r--drivers/ptp/ptp_kvm_x86.c6
-rw-r--r--drivers/ptp/ptp_ocp.c212
-rw-r--r--drivers/ptp/ptp_pch.c6
-rw-r--r--drivers/ptp/ptp_qoriq.c3
-rw-r--r--drivers/ptp/ptp_s390.c129
-rw-r--r--drivers/ptp/ptp_sysfs.c3
-rw-r--r--drivers/ptp/ptp_vmclock.c610
-rw-r--r--drivers/ptp/ptp_vmw.c13
17 files changed, 941 insertions, 143 deletions
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 604541dcb320..07bf7f9aae01 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -131,6 +131,23 @@ config PTP_1588_CLOCK_KVM
To compile this driver as a module, choose M here: the module
will be called ptp_kvm.
+config PTP_1588_CLOCK_VMCLOCK
+ tristate "Virtual machine PTP clock"
+ depends on X86_TSC || ARM_ARCH_TIMER
+ depends on PTP_1588_CLOCK && ACPI && ARCH_SUPPORTS_INT128
+ default PTP_1588_CLOCK_KVM
+ help
+ This driver adds support for using a virtual precision clock
+ advertised by the hypervisor. This clock is only useful in virtual
+ machines where such a device is present.
+
+ Unlike the KVM virtual PTP clock, the VMCLOCK device offers support
+ for reliable timekeeping even across live migration. So this driver
+ is enabled by default whenever the KVM PTP clock is.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ptp_vmclock.
+
config PTP_1588_CLOCK_IDT82P33
tristate "IDT 82P33xxx PTP clock"
depends on PTP_1588_CLOCK && I2C
@@ -224,4 +241,15 @@ config PTP_DFL_TOD
To compile this driver as a module, choose M here: the module
will be called ptp_dfl_tod.
+config PTP_S390
+ tristate "S390 PTP driver"
+ depends on PTP_1588_CLOCK
+ depends on S390
+ help
+ This driver adds support for S390 time steering via the PtP
+ interface. This works by adding a in-kernel clock delta value,
+ which is always added to time values used in the kernel. The PtP
+ driver provides the raw clock value without the delta to
+ userspace. That way userspace programs like chrony could steer
+ the kernel clock.
endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 68bf02078053..25f846fe48c9 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_PTP_1588_CLOCK_DTE) += ptp_dte.o
obj-$(CONFIG_PTP_1588_CLOCK_INES) += ptp_ines.o
obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o
obj-$(CONFIG_PTP_1588_CLOCK_KVM) += ptp_kvm.o
+obj-$(CONFIG_PTP_1588_CLOCK_VMCLOCK) += ptp_vmclock.o
obj-$(CONFIG_PTP_1588_CLOCK_QORIQ) += ptp-qoriq.o
ptp-qoriq-y += ptp_qoriq.o
ptp-qoriq-$(CONFIG_DEBUG_FS) += ptp_qoriq_debugfs.o
@@ -21,3 +22,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_MOCK) += ptp_mock.o
obj-$(CONFIG_PTP_1588_CLOCK_VMW) += ptp_vmw.o
obj-$(CONFIG_PTP_1588_CLOCK_OCP) += ptp_ocp.o
obj-$(CONFIG_PTP_DFL_TOD) += ptp_dfl_tod.o
+obj-$(CONFIG_PTP_S390) += ptp_s390.o
diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c
index 7513018c9f9a..bf6468c56419 100644
--- a/drivers/ptp/ptp_chardev.c
+++ b/drivers/ptp/ptp_chardev.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2010 OMICRON electronics GmbH
*/
+#include <linux/compat.h>
#include <linux/module.h>
#include <linux/posix-clock.h>
#include <linux/poll.h>
@@ -85,7 +86,8 @@ int ptp_set_pinfunc(struct ptp_clock *ptp, unsigned int pin,
}
if (info->verify(info, pin, func, chan)) {
- pr_err("driver cannot use function %u on pin %u\n", func, chan);
+ pr_err("driver cannot use function %u and channel %u on pin %u\n",
+ func, chan, pin);
return -EOPNOTSUPP;
}
@@ -175,6 +177,9 @@ long ptp_ioctl(struct posix_clock_context *pccontext, unsigned int cmd,
struct timespec64 ts;
int enable, err = 0;
+ if (in_compat_syscall() && cmd != PTP_ENABLE_PPS && cmd != PTP_ENABLE_PPS2)
+ arg = (unsigned long)compat_ptr(arg);
+
tsevq = pccontext->private_clkdata;
switch (cmd) {
@@ -358,11 +363,15 @@ long ptp_ioctl(struct posix_clock_context *pccontext, unsigned int cmd,
extoff = NULL;
break;
}
- if (extoff->n_samples > PTP_MAX_SAMPLES
- || extoff->rsv[0] || extoff->rsv[1] || extoff->rsv[2]) {
+ if (extoff->n_samples > PTP_MAX_SAMPLES ||
+ extoff->rsv[0] || extoff->rsv[1] ||
+ (extoff->clockid != CLOCK_REALTIME &&
+ extoff->clockid != CLOCK_MONOTONIC &&
+ extoff->clockid != CLOCK_MONOTONIC_RAW)) {
err = -EINVAL;
break;
}
+ sts.clockid = extoff->clockid;
for (i = 0; i < extoff->n_samples; i++) {
err = ptp->info->gettimex64(ptp->info, &ts, &sts);
if (err)
diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c
index c56cd0f63909..35a5994bf64f 100644
--- a/drivers/ptp/ptp_clock.c
+++ b/drivers/ptp/ptp_clock.c
@@ -150,7 +150,8 @@ static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx)
if (ppb > ops->max_adj || ppb < -ops->max_adj)
return -ERANGE;
err = ops->adjfine(ops, tx->freq);
- ptp->dialed_frequency = tx->freq;
+ if (!err)
+ ptp->dialed_frequency = tx->freq;
} else if (tx->modes & ADJ_OFFSET) {
if (ops->adjphase) {
s32 max_phase_adj = ops->getmaxphase(ops);
@@ -216,6 +217,11 @@ static int ptp_getcycles64(struct ptp_clock_info *info, struct timespec64 *ts)
return info->gettime64(info, ts);
}
+static int ptp_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *request, int on)
+{
+ return -EOPNOTSUPP;
+}
+
static void ptp_aux_kworker(struct kthread_work *work)
{
struct ptp_clock *ptp = container_of(work, struct ptp_clock,
@@ -293,9 +299,12 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info,
ptp->info->getcrosscycles = ptp->info->getcrosststamp;
}
+ if (!ptp->info->enable)
+ ptp->info->enable = ptp_enable;
+
if (ptp->info->do_aux_work) {
kthread_init_delayed_work(&ptp->aux_work, ptp_aux_kworker);
- ptp->kworker = kthread_create_worker(0, "ptp%d", ptp->index);
+ ptp->kworker = kthread_run_worker(0, "ptp%d", ptp->index);
if (IS_ERR(ptp->kworker)) {
err = PTR_ERR(ptp->kworker);
pr_err("failed to create ptp aux_worker %d\n", err);
diff --git a/drivers/ptp/ptp_clockmatrix.c b/drivers/ptp/ptp_clockmatrix.c
index f6f9d4adce04..fbb3fa8fc60b 100644
--- a/drivers/ptp/ptp_clockmatrix.c
+++ b/drivers/ptp/ptp_clockmatrix.c
@@ -17,7 +17,7 @@
#include <linux/of.h>
#include <linux/mfd/rsmu.h>
#include <linux/mfd/idt8a340_reg.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "ptp_private.h"
#include "ptp_clockmatrix.h"
@@ -2457,15 +2457,13 @@ static int idtcm_probe(struct platform_device *pdev)
return 0;
}
-static int idtcm_remove(struct platform_device *pdev)
+static void idtcm_remove(struct platform_device *pdev)
{
struct idtcm *idtcm = platform_get_drvdata(pdev);
idtcm->extts_mask = 0;
ptp_clock_unregister_all(idtcm);
cancel_delayed_work_sync(&idtcm->extts_work);
-
- return 0;
}
static struct platform_driver idtcm_driver = {
@@ -2473,7 +2471,7 @@ static struct platform_driver idtcm_driver = {
.name = "8a3400x-phc",
},
.probe = idtcm_probe,
- .remove = idtcm_remove,
+ .remove = idtcm_remove,
};
module_platform_driver(idtcm_driver);
diff --git a/drivers/ptp/ptp_dte.c b/drivers/ptp/ptp_dte.c
index 7cc5a00e625b..847276c69008 100644
--- a/drivers/ptp/ptp_dte.c
+++ b/drivers/ptp/ptp_dte.c
@@ -258,7 +258,7 @@ static int ptp_dte_probe(struct platform_device *pdev)
return 0;
}
-static int ptp_dte_remove(struct platform_device *pdev)
+static void ptp_dte_remove(struct platform_device *pdev)
{
struct ptp_dte *ptp_dte = platform_get_drvdata(pdev);
u8 i;
@@ -267,8 +267,6 @@ static int ptp_dte_remove(struct platform_device *pdev)
for (i = 0; i < DTE_NUM_REGS_TO_RESTORE; i++)
writel(0, ptp_dte->regs + (i * sizeof(u32)));
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/ptp/ptp_fc3.c b/drivers/ptp/ptp_fc3.c
index 6ef982862e27..cfced36c70bc 100644
--- a/drivers/ptp/ptp_fc3.c
+++ b/drivers/ptp/ptp_fc3.c
@@ -18,7 +18,7 @@
#include <linux/bitfield.h>
#include <linux/mfd/rsmu.h>
#include <linux/mfd/idtRC38xxx_reg.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "ptp_private.h"
#include "ptp_fc3.h"
@@ -986,11 +986,6 @@ static int idtfc3_probe(struct platform_device *pdev)
mutex_unlock(idtfc3->lock);
- if (err) {
- ptp_clock_unregister(idtfc3->ptp_clock);
- return err;
- }
-
platform_set_drvdata(pdev, idtfc3);
return 0;
@@ -1008,7 +1003,7 @@ static struct platform_driver idtfc3_driver = {
.name = "rc38xxx-phc",
},
.probe = idtfc3_probe,
- .remove_new = idtfc3_remove,
+ .remove = idtfc3_remove,
};
module_platform_driver(idtfc3_driver);
diff --git a/drivers/ptp/ptp_idt82p33.c b/drivers/ptp/ptp_idt82p33.c
index 057190b9cd3d..b2fd94d4f863 100644
--- a/drivers/ptp/ptp_idt82p33.c
+++ b/drivers/ptp/ptp_idt82p33.c
@@ -1171,10 +1171,10 @@ static void idt82p33_caps_init(u32 index, struct ptp_clock_info *caps,
caps->owner = THIS_MODULE;
caps->max_adj = DCO_MAX_PPB;
caps->n_per_out = MAX_PER_OUT;
- caps->n_ext_ts = MAX_PHC_PLL,
- caps->n_pins = max_pins,
- caps->adjphase = idt82p33_adjwritephase,
- caps->getmaxphase = idt82p33_getmaxphase,
+ caps->n_ext_ts = MAX_PHC_PLL;
+ caps->n_pins = max_pins;
+ caps->adjphase = idt82p33_adjwritephase;
+ caps->getmaxphase = idt82p33_getmaxphase;
caps->adjfine = idt82p33_adjfine;
caps->adjtime = idt82p33_adjtime;
caps->gettime64 = idt82p33_gettime;
@@ -1447,15 +1447,13 @@ static int idt82p33_probe(struct platform_device *pdev)
return 0;
}
-static int idt82p33_remove(struct platform_device *pdev)
+static void idt82p33_remove(struct platform_device *pdev)
{
struct idt82p33 *idt82p33 = platform_get_drvdata(pdev);
cancel_delayed_work_sync(&idt82p33->extts_work);
idt82p33_ptp_clock_unregister_all(idt82p33);
-
- return 0;
}
static struct platform_driver idt82p33_driver = {
@@ -1463,7 +1461,7 @@ static struct platform_driver idt82p33_driver = {
.name = "82p33x1x-phc",
},
.probe = idt82p33_probe,
- .remove = idt82p33_remove,
+ .remove = idt82p33_remove,
};
module_platform_driver(idt82p33_driver);
diff --git a/drivers/ptp/ptp_ines.c b/drivers/ptp/ptp_ines.c
index 1d2940a78455..68f1f7fdaa9d 100644
--- a/drivers/ptp/ptp_ines.c
+++ b/drivers/ptp/ptp_ines.c
@@ -556,18 +556,14 @@ static bool ines_timestamp_expired(struct ines_timestamp *ts)
}
static int ines_ts_info(struct mii_timestamper *mii_ts,
- struct ethtool_ts_info *info)
+ struct kernel_ethtool_ts_info *info)
{
info->so_timestamping =
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
- SOF_TIMESTAMPING_RX_SOFTWARE |
- SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
- info->phc_index = -1;
-
info->tx_types =
(1 << HWTSTAMP_TX_OFF) |
(1 << HWTSTAMP_TX_ON) |
@@ -765,7 +761,7 @@ out:
return err;
}
-static int ines_ptp_ctrl_remove(struct platform_device *pld)
+static void ines_ptp_ctrl_remove(struct platform_device *pld)
{
struct ines_clock *clock = dev_get_drvdata(&pld->dev);
@@ -775,7 +771,6 @@ static int ines_ptp_ctrl_remove(struct platform_device *pld)
mutex_unlock(&ines_clocks_lock);
ines_clock_cleanup(clock);
kfree(clock);
- return 0;
}
static const struct of_device_id ines_ptp_ctrl_of_match[] = {
diff --git a/drivers/ptp/ptp_kvm_x86.c b/drivers/ptp/ptp_kvm_x86.c
index 617c8d6706d3..6cea4fe39bcf 100644
--- a/drivers/ptp/ptp_kvm_x86.c
+++ b/drivers/ptp/ptp_kvm_x86.c
@@ -26,7 +26,7 @@ int kvm_arch_ptp_init(void)
long ret;
if (!kvm_para_available())
- return -ENODEV;
+ return -EOPNOTSUPP;
if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
p = alloc_page(GFP_KERNEL | __GFP_ZERO);
@@ -46,14 +46,14 @@ int kvm_arch_ptp_init(void)
clock_pair_gpa = slow_virt_to_phys(clock_pair);
if (!pvclock_get_pvti_cpu0_va()) {
- ret = -ENODEV;
+ ret = -EOPNOTSUPP;
goto err;
}
ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa,
KVM_CLOCK_PAIRING_WALLCLOCK);
if (ret == -KVM_ENOSYS) {
- ret = -ENODEV;
+ ret = -EOPNOTSUPP;
goto err;
}
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 6506cfb89aa9..b651087f426f 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -316,6 +316,15 @@ struct ptp_ocp_serial_port {
#define OCP_SERIAL_LEN 6
#define OCP_SMA_NUM 4
+enum {
+ PORT_GNSS,
+ PORT_GNSS2,
+ PORT_MAC, /* miniature atomic clock */
+ PORT_NMEA,
+
+ __PORT_COUNT,
+};
+
struct ptp_ocp {
struct pci_dev *pdev;
struct device dev;
@@ -357,10 +366,7 @@ struct ptp_ocp {
struct delayed_work sync_work;
int id;
int n_irqs;
- struct ptp_ocp_serial_port gnss_port;
- struct ptp_ocp_serial_port gnss2_port;
- struct ptp_ocp_serial_port mac_port; /* miniature atomic clock */
- struct ptp_ocp_serial_port nmea_port;
+ struct ptp_ocp_serial_port port[__PORT_COUNT];
bool fw_loader;
u8 fw_tag;
u16 fw_version;
@@ -655,28 +661,28 @@ static struct ocp_resource ocp_fb_resource[] = {
},
},
{
- OCP_SERIAL_RESOURCE(gnss_port),
+ OCP_SERIAL_RESOURCE(port[PORT_GNSS]),
.offset = 0x00160000 + 0x1000, .irq_vec = 3,
.extra = &(struct ptp_ocp_serial_port) {
.baud = 115200,
},
},
{
- OCP_SERIAL_RESOURCE(gnss2_port),
+ OCP_SERIAL_RESOURCE(port[PORT_GNSS2]),
.offset = 0x00170000 + 0x1000, .irq_vec = 4,
.extra = &(struct ptp_ocp_serial_port) {
.baud = 115200,
},
},
{
- OCP_SERIAL_RESOURCE(mac_port),
+ OCP_SERIAL_RESOURCE(port[PORT_MAC]),
.offset = 0x00180000 + 0x1000, .irq_vec = 5,
.extra = &(struct ptp_ocp_serial_port) {
.baud = 57600,
},
},
{
- OCP_SERIAL_RESOURCE(nmea_port),
+ OCP_SERIAL_RESOURCE(port[PORT_NMEA]),
.offset = 0x00190000 + 0x1000, .irq_vec = 10,
},
{
@@ -740,7 +746,7 @@ static struct ocp_resource ocp_art_resource[] = {
.offset = 0x01000000, .size = 0x10000,
},
{
- OCP_SERIAL_RESOURCE(gnss_port),
+ OCP_SERIAL_RESOURCE(port[PORT_GNSS]),
.offset = 0x00160000 + 0x1000, .irq_vec = 3,
.extra = &(struct ptp_ocp_serial_port) {
.baud = 115200,
@@ -839,7 +845,7 @@ static struct ocp_resource ocp_art_resource[] = {
},
},
{
- OCP_SERIAL_RESOURCE(mac_port),
+ OCP_SERIAL_RESOURCE(port[PORT_MAC]),
.offset = 0x00190000, .irq_vec = 7,
.extra = &(struct ptp_ocp_serial_port) {
.baud = 9600,
@@ -950,14 +956,14 @@ static struct ocp_resource ocp_adva_resource[] = {
.offset = 0x00220000, .size = 0x1000,
},
{
- OCP_SERIAL_RESOURCE(gnss_port),
+ OCP_SERIAL_RESOURCE(port[PORT_GNSS]),
.offset = 0x00160000 + 0x1000, .irq_vec = 3,
.extra = &(struct ptp_ocp_serial_port) {
.baud = 9600,
},
},
{
- OCP_SERIAL_RESOURCE(mac_port),
+ OCP_SERIAL_RESOURCE(port[PORT_MAC]),
.offset = 0x00180000 + 0x1000, .irq_vec = 5,
.extra = &(struct ptp_ocp_serial_port) {
.baud = 115200,
@@ -1552,22 +1558,24 @@ ptp_ocp_watchdog(struct timer_list *t)
static void
ptp_ocp_estimate_pci_timing(struct ptp_ocp *bp)
{
- ktime_t start, end;
- ktime_t delay;
+ ktime_t start, end, delay = U64_MAX;
u32 ctrl;
+ int i;
- ctrl = ioread32(&bp->reg->ctrl);
- ctrl = OCP_CTRL_READ_TIME_REQ | OCP_CTRL_ENABLE;
+ for (i = 0; i < 3; i++) {
+ ctrl = ioread32(&bp->reg->ctrl);
+ ctrl = OCP_CTRL_READ_TIME_REQ | OCP_CTRL_ENABLE;
- iowrite32(ctrl, &bp->reg->ctrl);
+ iowrite32(ctrl, &bp->reg->ctrl);
- start = ktime_get_ns();
+ start = ktime_get_raw_ns();
- ctrl = ioread32(&bp->reg->ctrl);
+ ctrl = ioread32(&bp->reg->ctrl);
- end = ktime_get_ns();
+ end = ktime_get_raw_ns();
- delay = end - start;
+ delay = min(delay, end - start);
+ }
bp->ts_window_adjust = (delay >> 5) * 3;
}
@@ -1649,6 +1657,15 @@ ptp_ocp_tod_gnss_name(int idx)
return gnss_name[idx];
}
+static const char *
+ptp_ocp_tty_port_name(int idx)
+{
+ static const char * const tty_name[] = {
+ "GNSS", "GNSS2", "MAC", "NMEA"
+ };
+ return tty_name[idx];
+}
+
struct ptp_ocp_nvmem_match_info {
struct ptp_ocp *bp;
const void * const tag;
@@ -3347,6 +3364,54 @@ static EXT_ATTR_RO(freq, frequency, 2);
static EXT_ATTR_RO(freq, frequency, 3);
static ssize_t
+ptp_ocp_tty_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *ea = to_ext_attr(attr);
+ struct ptp_ocp *bp = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "ttyS%d", bp->port[(uintptr_t)ea->var].line);
+}
+
+static umode_t
+ptp_ocp_timecard_tty_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+ struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+ struct ptp_ocp_serial_port *port;
+ struct device_attribute *dattr;
+ struct dev_ext_attribute *ea;
+
+ if (strncmp(attr->name, "tty", 3))
+ return attr->mode;
+
+ dattr = container_of(attr, struct device_attribute, attr);
+ ea = container_of(dattr, struct dev_ext_attribute, attr);
+ port = &bp->port[(uintptr_t)ea->var];
+ return port->line == -1 ? 0 : 0444;
+}
+
+#define EXT_TTY_ATTR_RO(_name, _val) \
+ struct dev_ext_attribute dev_attr_tty##_name = \
+ { __ATTR(tty##_name, 0444, ptp_ocp_tty_show, NULL), (void *)_val }
+
+static EXT_TTY_ATTR_RO(GNSS, PORT_GNSS);
+static EXT_TTY_ATTR_RO(GNSS2, PORT_GNSS2);
+static EXT_TTY_ATTR_RO(MAC, PORT_MAC);
+static EXT_TTY_ATTR_RO(NMEA, PORT_NMEA);
+static struct attribute *ptp_ocp_timecard_tty_attrs[] = {
+ &dev_attr_ttyGNSS.attr.attr,
+ &dev_attr_ttyGNSS2.attr.attr,
+ &dev_attr_ttyMAC.attr.attr,
+ &dev_attr_ttyNMEA.attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ptp_ocp_timecard_tty_group = {
+ .name = "tty",
+ .attrs = ptp_ocp_timecard_tty_attrs,
+ .is_visible = ptp_ocp_timecard_tty_is_visible,
+};
+
+static ssize_t
serialnum_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
@@ -3627,7 +3692,7 @@ DEVICE_FREQ_GROUP(freq4, 3);
static ssize_t
disciplining_config_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
@@ -3662,7 +3727,7 @@ out:
static ssize_t
disciplining_config_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
@@ -3685,11 +3750,11 @@ disciplining_config_write(struct file *filp, struct kobject *kobj,
return err;
}
-static BIN_ATTR_RW(disciplining_config, OCP_ART_CONFIG_SIZE);
+static const BIN_ATTR_RW(disciplining_config, OCP_ART_CONFIG_SIZE);
static ssize_t
temperature_table_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
@@ -3724,7 +3789,7 @@ out:
static ssize_t
temperature_table_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
@@ -3747,7 +3812,7 @@ temperature_table_write(struct file *filp, struct kobject *kobj,
return err;
}
-static BIN_ATTR_RW(temperature_table, OCP_ART_TEMP_TABLE_SIZE);
+static const BIN_ATTR_RW(temperature_table, OCP_ART_TEMP_TABLE_SIZE);
static struct attribute *fb_timecard_attrs[] = {
&dev_attr_serialnum.attr,
@@ -3775,6 +3840,7 @@ static const struct attribute_group fb_timecard_group = {
static const struct ocp_attr_group fb_timecard_groups[] = {
{ .cap = OCP_CAP_BASIC, .group = &fb_timecard_group },
+ { .cap = OCP_CAP_BASIC, .group = &ptp_ocp_timecard_tty_group },
{ .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal0_group },
{ .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal1_group },
{ .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal2_group },
@@ -3801,7 +3867,7 @@ static struct attribute *art_timecard_attrs[] = {
NULL,
};
-static struct bin_attribute *bin_art_timecard_attrs[] = {
+static const struct bin_attribute *const bin_art_timecard_attrs[] = {
&bin_attr_disciplining_config,
&bin_attr_temperature_table,
NULL,
@@ -3809,11 +3875,12 @@ static struct bin_attribute *bin_art_timecard_attrs[] = {
static const struct attribute_group art_timecard_group = {
.attrs = art_timecard_attrs,
- .bin_attrs = bin_art_timecard_attrs,
+ .bin_attrs_new = bin_art_timecard_attrs,
};
static const struct ocp_attr_group art_timecard_groups[] = {
{ .cap = OCP_CAP_BASIC, .group = &art_timecard_group },
+ { .cap = OCP_CAP_BASIC, .group = &ptp_ocp_timecard_tty_group },
{ },
};
@@ -3841,6 +3908,7 @@ static const struct attribute_group adva_timecard_group = {
static const struct ocp_attr_group adva_timecard_groups[] = {
{ .cap = OCP_CAP_BASIC, .group = &adva_timecard_group },
+ { .cap = OCP_CAP_BASIC, .group = &ptp_ocp_timecard_tty_group },
{ .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal0_group },
{ .cap = OCP_CAP_SIGNAL, .group = &fb_timecard_signal1_group },
{ .cap = OCP_CAP_FREQ, .group = &fb_timecard_freq0_group },
@@ -3960,16 +4028,11 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
bp = dev_get_drvdata(dev);
seq_printf(s, "%7s: /dev/ptp%d\n", "PTP", ptp_clock_index(bp->ptp));
- if (bp->gnss_port.line != -1)
- seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS1",
- bp->gnss_port.line);
- if (bp->gnss2_port.line != -1)
- seq_printf(s, "%7s: /dev/ttyS%d\n", "GNSS2",
- bp->gnss2_port.line);
- if (bp->mac_port.line != -1)
- seq_printf(s, "%7s: /dev/ttyS%d\n", "MAC", bp->mac_port.line);
- if (bp->nmea_port.line != -1)
- seq_printf(s, "%7s: /dev/ttyS%d\n", "NMEA", bp->nmea_port.line);
+ for (i = 0; i < __PORT_COUNT; i++) {
+ if (bp->port[i].line != -1)
+ seq_printf(s, "%7s: /dev/ttyS%d\n", ptp_ocp_tty_port_name(i),
+ bp->port[i].line);
+ }
memset(sma_val, 0xff, sizeof(sma_val));
if (bp->sma_map1) {
@@ -4279,7 +4342,7 @@ ptp_ocp_dev_release(struct device *dev)
static int
ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev)
{
- int err;
+ int i, err;
mutex_lock(&ptp_ocp_lock);
err = idr_alloc(&ptp_ocp_idr, bp, 0, 0, GFP_KERNEL);
@@ -4292,10 +4355,10 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev)
bp->ptp_info = ptp_ocp_clock_info;
spin_lock_init(&bp->lock);
- bp->gnss_port.line = -1;
- bp->gnss2_port.line = -1;
- bp->mac_port.line = -1;
- bp->nmea_port.line = -1;
+
+ for (i = 0; i < __PORT_COUNT; i++)
+ bp->port[i].line = -1;
+
bp->pdev = pdev;
device_initialize(&bp->dev);
@@ -4352,28 +4415,12 @@ ptp_ocp_complete(struct ptp_ocp *bp)
struct pps_device *pps;
char buf[32];
- if (bp->gnss_port.line != -1) {
- sprintf(buf, "ttyS%d", bp->gnss_port.line);
- ptp_ocp_link_child(bp, buf, "ttyGNSS");
- }
- if (bp->gnss2_port.line != -1) {
- sprintf(buf, "ttyS%d", bp->gnss2_port.line);
- ptp_ocp_link_child(bp, buf, "ttyGNSS2");
- }
- if (bp->mac_port.line != -1) {
- sprintf(buf, "ttyS%d", bp->mac_port.line);
- ptp_ocp_link_child(bp, buf, "ttyMAC");
- }
- if (bp->nmea_port.line != -1) {
- sprintf(buf, "ttyS%d", bp->nmea_port.line);
- ptp_ocp_link_child(bp, buf, "ttyNMEA");
- }
sprintf(buf, "ptp%d", ptp_clock_index(bp->ptp));
ptp_ocp_link_child(bp, buf, "ptp");
pps = pps_lookup_dev(bp->ptp);
if (pps)
- ptp_ocp_symlink(bp, pps->dev, "pps");
+ ptp_ocp_symlink(bp, &pps->dev, "pps");
ptp_ocp_debugfs_add_device(bp);
@@ -4416,23 +4463,20 @@ ptp_ocp_info(struct ptp_ocp *bp)
};
struct device *dev = &bp->pdev->dev;
u32 reg;
+ int i;
ptp_ocp_phc_info(bp);
- ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port.line,
- bp->gnss_port.baud);
- ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port.line,
- bp->gnss2_port.baud);
- ptp_ocp_serial_info(dev, "MAC", bp->mac_port.line, bp->mac_port.baud);
- if (bp->nmea_out && bp->nmea_port.line != -1) {
- bp->nmea_port.baud = -1;
+ for (i = 0; i < __PORT_COUNT; i++) {
+ if (i == PORT_NMEA && bp->nmea_out && bp->port[PORT_NMEA].line != -1) {
+ bp->port[PORT_NMEA].baud = -1;
- reg = ioread32(&bp->nmea_out->uart_baud);
- if (reg < ARRAY_SIZE(nmea_baud))
- bp->nmea_port.baud = nmea_baud[reg];
-
- ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port.line,
- bp->nmea_port.baud);
+ reg = ioread32(&bp->nmea_out->uart_baud);
+ if (reg < ARRAY_SIZE(nmea_baud))
+ bp->port[PORT_NMEA].baud = nmea_baud[reg];
+ }
+ ptp_ocp_serial_info(dev, ptp_ocp_tty_port_name(i), bp->port[i].line,
+ bp->port[i].baud);
}
}
@@ -4441,9 +4485,6 @@ ptp_ocp_detach_sysfs(struct ptp_ocp *bp)
{
struct device *dev = &bp->dev;
- sysfs_remove_link(&dev->kobj, "ttyGNSS");
- sysfs_remove_link(&dev->kobj, "ttyGNSS2");
- sysfs_remove_link(&dev->kobj, "ttyMAC");
sysfs_remove_link(&dev->kobj, "ptp");
sysfs_remove_link(&dev->kobj, "pps");
}
@@ -4473,14 +4514,9 @@ ptp_ocp_detach(struct ptp_ocp *bp)
for (i = 0; i < 4; i++)
if (bp->signal_out[i])
ptp_ocp_unregister_ext(bp->signal_out[i]);
- if (bp->gnss_port.line != -1)
- serial8250_unregister_port(bp->gnss_port.line);
- if (bp->gnss2_port.line != -1)
- serial8250_unregister_port(bp->gnss2_port.line);
- if (bp->mac_port.line != -1)
- serial8250_unregister_port(bp->mac_port.line);
- if (bp->nmea_port.line != -1)
- serial8250_unregister_port(bp->nmea_port.line);
+ for (i = 0; i < __PORT_COUNT; i++)
+ if (bp->port[i].line != -1)
+ serial8250_unregister_port(bp->port[i].line);
platform_device_unregister(bp->spi_flash);
platform_device_unregister(bp->i2c_ctrl);
if (bp->i2c_clk)
@@ -4562,7 +4598,7 @@ static int ptp_ocp_dpll_direction_set(const struct dpll_pin *pin,
return -EOPNOTSUPP;
mode = direction == DPLL_PIN_DIRECTION_INPUT ?
SMA_MODE_IN : SMA_MODE_OUT;
- return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
+ return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr + 1);
}
static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
@@ -4583,7 +4619,7 @@ static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
tbl = bp->sma_op->tbl[sma->mode];
for (i = 0; tbl[i].name; i++)
if (tbl[i].frequency == frequency)
- return ptp_ocp_sma_store_val(bp, i, sma->mode, sma_nr);
+ return ptp_ocp_sma_store_val(bp, i, sma->mode, sma_nr + 1);
return -EINVAL;
}
@@ -4600,7 +4636,7 @@ static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
u32 val;
int i;
- val = bp->sma_op->get(bp, sma_nr);
+ val = bp->sma_op->get(bp, sma_nr + 1);
tbl = bp->sma_op->tbl[sma->mode];
for (i = 0; tbl[i].name; i++)
if (val == tbl[i].value) {
diff --git a/drivers/ptp/ptp_pch.c b/drivers/ptp/ptp_pch.c
index 33355d5eb033..b8a9a54a176c 100644
--- a/drivers/ptp/ptp_pch.c
+++ b/drivers/ptp/ptp_pch.c
@@ -462,14 +462,14 @@ pch_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return ret;
}
- ret = pcim_iomap_regions(pdev, BIT(IO_MEM_BAR), "1588_regs");
+ /* get the virtual address to the 1588 registers */
+ chip->regs = pcim_iomap_region(pdev, IO_MEM_BAR, KBUILD_MODNAME);
+ ret = PTR_ERR_OR_ZERO(chip->regs);
if (ret) {
dev_err(&pdev->dev, "could not locate IO memory address\n");
return ret;
}
- /* get the virtual address to the 1588 registers */
- chip->regs = pcim_iomap_table(pdev)[IO_MEM_BAR];
chip->caps = ptp_pch_caps;
chip->ptp_clock = ptp_clock_register(&chip->caps, &pdev->dev);
if (IS_ERR(chip->ptp_clock))
diff --git a/drivers/ptp/ptp_qoriq.c b/drivers/ptp/ptp_qoriq.c
index a52859d024f0..4d488c1f1941 100644
--- a/drivers/ptp/ptp_qoriq.c
+++ b/drivers/ptp/ptp_qoriq.c
@@ -648,14 +648,13 @@ no_memory:
return err;
}
-static int ptp_qoriq_remove(struct platform_device *dev)
+static void ptp_qoriq_remove(struct platform_device *dev)
{
struct ptp_qoriq *ptp_qoriq = platform_get_drvdata(dev);
ptp_qoriq_free(ptp_qoriq);
release_resource(ptp_qoriq->rsrc);
kfree(ptp_qoriq);
- return 0;
}
static const struct of_device_id match_table[] = {
diff --git a/drivers/ptp/ptp_s390.c b/drivers/ptp/ptp_s390.c
new file mode 100644
index 000000000000..29618eb9bf44
--- /dev/null
+++ b/drivers/ptp/ptp_s390.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * s390 PTP clock driver
+ *
+ */
+
+#include "ptp_private.h"
+#include <linux/time.h>
+#include <asm/stp.h>
+
+static struct ptp_clock *ptp_stcke_clock, *ptp_qpt_clock;
+
+static int ptp_s390_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ return -EOPNOTSUPP;
+}
+
+static int ptp_s390_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct timespec64 eitod_to_timespec64(union tod_clock *clk)
+{
+ return ns_to_timespec64(eitod_to_ns(clk->eitod - TOD_UNIX_EPOCH));
+}
+
+static struct timespec64 tod_to_timespec64(unsigned long tod)
+{
+ return ns_to_timespec64(tod_to_ns(tod - TOD_UNIX_EPOCH));
+}
+
+static int ptp_s390_stcke_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ union tod_clock tod;
+
+ if (!stp_enabled())
+ return -EOPNOTSUPP;
+
+ store_tod_clock_ext(&tod);
+ *ts = eitod_to_timespec64(&tod);
+ return 0;
+}
+
+static int ptp_s390_qpt_gettime(struct ptp_clock_info *ptp,
+ struct timespec64 *ts)
+{
+ unsigned long tod;
+
+ ptff(&tod, sizeof(tod), PTFF_QPT);
+ *ts = tod_to_timespec64(tod);
+ return 0;
+}
+
+static int ptp_s390_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ return -EOPNOTSUPP;
+}
+
+static int s390_arch_ptp_get_crosststamp(ktime_t *device_time,
+ struct system_counterval_t *system_counter,
+ void *ctx)
+{
+ union tod_clock clk;
+
+ store_tod_clock_ext(&clk);
+ *device_time = ns_to_ktime(tod_to_ns(clk.tod - TOD_UNIX_EPOCH));
+ system_counter->cycles = clk.tod;
+ system_counter->cs_id = CSID_S390_TOD;
+ return 0;
+}
+
+static int ptp_s390_getcrosststamp(struct ptp_clock_info *ptp,
+ struct system_device_crosststamp *xtstamp)
+{
+ if (!stp_enabled())
+ return -EOPNOTSUPP;
+ return get_device_system_crosststamp(s390_arch_ptp_get_crosststamp, NULL, NULL, xtstamp);
+}
+
+static struct ptp_clock_info ptp_s390_stcke_info = {
+ .owner = THIS_MODULE,
+ .name = "s390 STCKE Clock",
+ .max_adj = 0,
+ .adjfine = ptp_s390_adjfine,
+ .adjtime = ptp_s390_adjtime,
+ .gettime64 = ptp_s390_stcke_gettime,
+ .settime64 = ptp_s390_settime,
+ .getcrosststamp = ptp_s390_getcrosststamp,
+};
+
+static struct ptp_clock_info ptp_s390_qpt_info = {
+ .owner = THIS_MODULE,
+ .name = "s390 Physical Clock",
+ .max_adj = 0,
+ .adjfine = ptp_s390_adjfine,
+ .adjtime = ptp_s390_adjtime,
+ .gettime64 = ptp_s390_qpt_gettime,
+ .settime64 = ptp_s390_settime,
+};
+
+static __init int ptp_s390_init(void)
+{
+ ptp_stcke_clock = ptp_clock_register(&ptp_s390_stcke_info, NULL);
+ if (IS_ERR(ptp_stcke_clock))
+ return PTR_ERR(ptp_stcke_clock);
+
+ ptp_qpt_clock = ptp_clock_register(&ptp_s390_qpt_info, NULL);
+ if (IS_ERR(ptp_qpt_clock)) {
+ ptp_clock_unregister(ptp_stcke_clock);
+ return PTR_ERR(ptp_qpt_clock);
+ }
+ return 0;
+}
+
+static __exit void ptp_s390_exit(void)
+{
+ ptp_clock_unregister(ptp_qpt_clock);
+ ptp_clock_unregister(ptp_stcke_clock);
+}
+
+module_init(ptp_s390_init);
+module_exit(ptp_s390_exit);
+
+MODULE_AUTHOR("Sven Schnelle <svens@linux.ibm.com>");
+MODULE_DESCRIPTION("s390 Physical/STCKE Clock PtP Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ptp/ptp_sysfs.c b/drivers/ptp/ptp_sysfs.c
index a15460aaa03b..6b1b8f57cd95 100644
--- a/drivers/ptp/ptp_sysfs.c
+++ b/drivers/ptp/ptp_sysfs.c
@@ -296,8 +296,7 @@ static ssize_t max_vclocks_store(struct device *dev,
if (max < ptp->n_vclocks)
goto out;
- size = sizeof(int) * max;
- vclock_index = kzalloc(size, GFP_KERNEL);
+ vclock_index = kcalloc(max, sizeof(int), GFP_KERNEL);
if (!vclock_index) {
err = -ENOMEM;
goto out;
diff --git a/drivers/ptp/ptp_vmclock.c b/drivers/ptp/ptp_vmclock.c
new file mode 100644
index 000000000000..b3a83b03d9c1
--- /dev/null
+++ b/drivers/ptp/ptp_vmclock.c
@@ -0,0 +1,610 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Virtual PTP 1588 clock for use with LM-safe VMclock device.
+ *
+ * Copyright © 2024 Amazon.com, Inc. or its affiliates.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <uapi/linux/vmclock-abi.h>
+
+#include <linux/ptp_clock_kernel.h>
+
+#ifdef CONFIG_X86
+#include <asm/pvclock.h>
+#include <asm/kvmclock.h>
+#endif
+
+#ifdef CONFIG_KVM_GUEST
+#define SUPPORT_KVMCLOCK
+#endif
+
+static DEFINE_IDA(vmclock_ida);
+
+ACPI_MODULE_NAME("vmclock");
+
+struct vmclock_state {
+ struct resource res;
+ struct vmclock_abi *clk;
+ struct miscdevice miscdev;
+ struct ptp_clock_info ptp_clock_info;
+ struct ptp_clock *ptp_clock;
+ enum clocksource_ids cs_id, sys_cs_id;
+ int index;
+ char *name;
+};
+
+#define VMCLOCK_MAX_WAIT ms_to_ktime(100)
+
+/* Require at least the flags field to be present. All else can be optional. */
+#define VMCLOCK_MIN_SIZE offsetof(struct vmclock_abi, pad)
+
+#define VMCLOCK_FIELD_PRESENT(_c, _f) \
+ (le32_to_cpu((_c)->size) >= (offsetof(struct vmclock_abi, _f) + \
+ sizeof((_c)->_f)))
+
+/*
+ * Multiply a 64-bit count by a 64-bit tick 'period' in units of seconds >> 64
+ * and add the fractional second part of the reference time.
+ *
+ * The result is a 128-bit value, the top 64 bits of which are seconds, and
+ * the low 64 bits are (seconds >> 64).
+ */
+static uint64_t mul_u64_u64_shr_add_u64(uint64_t *res_hi, uint64_t delta,
+ uint64_t period, uint8_t shift,
+ uint64_t frac_sec)
+{
+ unsigned __int128 res = (unsigned __int128)delta * period;
+
+ res >>= shift;
+ res += frac_sec;
+ *res_hi = res >> 64;
+ return (uint64_t)res;
+}
+
+static bool tai_adjust(struct vmclock_abi *clk, uint64_t *sec)
+{
+ if (likely(clk->time_type == VMCLOCK_TIME_UTC))
+ return true;
+
+ if (clk->time_type == VMCLOCK_TIME_TAI &&
+ (le64_to_cpu(clk->flags) & VMCLOCK_FLAG_TAI_OFFSET_VALID)) {
+ if (sec)
+ *sec += (int16_t)le16_to_cpu(clk->tai_offset_sec);
+ return true;
+ }
+ return false;
+}
+
+static int vmclock_get_crosststamp(struct vmclock_state *st,
+ struct ptp_system_timestamp *sts,
+ struct system_counterval_t *system_counter,
+ struct timespec64 *tspec)
+{
+ ktime_t deadline = ktime_add(ktime_get(), VMCLOCK_MAX_WAIT);
+ struct system_time_snapshot systime_snapshot;
+ uint64_t cycle, delta, seq, frac_sec;
+
+#ifdef CONFIG_X86
+ /*
+ * We'd expect the hypervisor to know this and to report the clock
+ * status as VMCLOCK_STATUS_UNRELIABLE. But be paranoid.
+ */
+ if (check_tsc_unstable())
+ return -EINVAL;
+#endif
+
+ while (1) {
+ seq = le32_to_cpu(st->clk->seq_count) & ~1ULL;
+
+ /*
+ * This pairs with a write barrier in the hypervisor
+ * which populates this structure.
+ */
+ virt_rmb();
+
+ if (st->clk->clock_status == VMCLOCK_STATUS_UNRELIABLE)
+ return -EINVAL;
+
+ /*
+ * When invoked for gettimex64(), fill in the pre/post system
+ * times. The simple case is when system time is based on the
+ * same counter as st->cs_id, in which case all three times
+ * will be derived from the *same* counter value.
+ *
+ * If the system isn't using the same counter, then the value
+ * from ktime_get_snapshot() will still be used as pre_ts, and
+ * ptp_read_system_postts() is called to populate postts after
+ * calling get_cycles().
+ *
+ * The conversion to timespec64 happens further down, outside
+ * the seq_count loop.
+ */
+ if (sts) {
+ ktime_get_snapshot(&systime_snapshot);
+ if (systime_snapshot.cs_id == st->cs_id) {
+ cycle = systime_snapshot.cycles;
+ } else {
+ cycle = get_cycles();
+ ptp_read_system_postts(sts);
+ }
+ } else {
+ cycle = get_cycles();
+ }
+
+ delta = cycle - le64_to_cpu(st->clk->counter_value);
+
+ frac_sec = mul_u64_u64_shr_add_u64(&tspec->tv_sec, delta,
+ le64_to_cpu(st->clk->counter_period_frac_sec),
+ st->clk->counter_period_shift,
+ le64_to_cpu(st->clk->time_frac_sec));
+ tspec->tv_nsec = mul_u64_u64_shr(frac_sec, NSEC_PER_SEC, 64);
+ tspec->tv_sec += le64_to_cpu(st->clk->time_sec);
+
+ if (!tai_adjust(st->clk, &tspec->tv_sec))
+ return -EINVAL;
+
+ /*
+ * This pairs with a write barrier in the hypervisor
+ * which populates this structure.
+ */
+ virt_rmb();
+ if (seq == le32_to_cpu(st->clk->seq_count))
+ break;
+
+ if (ktime_after(ktime_get(), deadline))
+ return -ETIMEDOUT;
+ }
+
+ if (system_counter) {
+ system_counter->cycles = cycle;
+ system_counter->cs_id = st->cs_id;
+ }
+
+ if (sts) {
+ sts->pre_ts = ktime_to_timespec64(systime_snapshot.real);
+ if (systime_snapshot.cs_id == st->cs_id)
+ sts->post_ts = sts->pre_ts;
+ }
+
+ return 0;
+}
+
+#ifdef SUPPORT_KVMCLOCK
+/*
+ * In the case where the system is using the KVM clock for timekeeping, convert
+ * the TSC value into a KVM clock time in order to return a paired reading that
+ * get_device_system_crosststamp() can cope with.
+ */
+static int vmclock_get_crosststamp_kvmclock(struct vmclock_state *st,
+ struct ptp_system_timestamp *sts,
+ struct system_counterval_t *system_counter,
+ struct timespec64 *tspec)
+{
+ struct pvclock_vcpu_time_info *pvti = this_cpu_pvti();
+ unsigned int pvti_ver;
+ int ret;
+
+ preempt_disable_notrace();
+
+ do {
+ pvti_ver = pvclock_read_begin(pvti);
+
+ ret = vmclock_get_crosststamp(st, sts, system_counter, tspec);
+ if (ret)
+ break;
+
+ system_counter->cycles = __pvclock_read_cycles(pvti,
+ system_counter->cycles);
+ system_counter->cs_id = CSID_X86_KVM_CLK;
+
+ /*
+ * This retry should never really happen; if the TSC is
+ * stable and reliable enough across vCPUS that it is sane
+ * for the hypervisor to expose a VMCLOCK device which uses
+ * it as the reference counter, then the KVM clock sohuld be
+ * in 'master clock mode' and basically never changed. But
+ * the KVM clock is a fickle and often broken thing, so do
+ * it "properly" just in case.
+ */
+ } while (pvclock_read_retry(pvti, pvti_ver));
+
+ preempt_enable_notrace();
+
+ return ret;
+}
+#endif
+
+static int ptp_vmclock_get_time_fn(ktime_t *device_time,
+ struct system_counterval_t *system_counter,
+ void *ctx)
+{
+ struct vmclock_state *st = ctx;
+ struct timespec64 tspec;
+ int ret;
+
+#ifdef SUPPORT_KVMCLOCK
+ if (READ_ONCE(st->sys_cs_id) == CSID_X86_KVM_CLK)
+ ret = vmclock_get_crosststamp_kvmclock(st, NULL, system_counter,
+ &tspec);
+ else
+#endif
+ ret = vmclock_get_crosststamp(st, NULL, system_counter, &tspec);
+
+ if (!ret)
+ *device_time = timespec64_to_ktime(tspec);
+
+ return ret;
+}
+
+static int ptp_vmclock_getcrosststamp(struct ptp_clock_info *ptp,
+ struct system_device_crosststamp *xtstamp)
+{
+ struct vmclock_state *st = container_of(ptp, struct vmclock_state,
+ ptp_clock_info);
+ int ret = get_device_system_crosststamp(ptp_vmclock_get_time_fn, st,
+ NULL, xtstamp);
+#ifdef SUPPORT_KVMCLOCK
+ /*
+ * On x86, the KVM clock may be used for the system time. We can
+ * actually convert a TSC reading to that, and return a paired
+ * timestamp that get_device_system_crosststamp() *can* handle.
+ */
+ if (ret == -ENODEV) {
+ struct system_time_snapshot systime_snapshot;
+
+ ktime_get_snapshot(&systime_snapshot);
+
+ if (systime_snapshot.cs_id == CSID_X86_TSC ||
+ systime_snapshot.cs_id == CSID_X86_KVM_CLK) {
+ WRITE_ONCE(st->sys_cs_id, systime_snapshot.cs_id);
+ ret = get_device_system_crosststamp(ptp_vmclock_get_time_fn,
+ st, NULL, xtstamp);
+ }
+ }
+#endif
+ return ret;
+}
+
+/*
+ * PTP clock operations
+ */
+
+static int ptp_vmclock_adjfine(struct ptp_clock_info *ptp, long delta)
+{
+ return -EOPNOTSUPP;
+}
+
+static int ptp_vmclock_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ return -EOPNOTSUPP;
+}
+
+static int ptp_vmclock_settime(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ return -EOPNOTSUPP;
+}
+
+static int ptp_vmclock_gettimex(struct ptp_clock_info *ptp, struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct vmclock_state *st = container_of(ptp, struct vmclock_state,
+ ptp_clock_info);
+
+ return vmclock_get_crosststamp(st, sts, NULL, ts);
+}
+
+static int ptp_vmclock_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ return -EOPNOTSUPP;
+}
+
+static const struct ptp_clock_info ptp_vmclock_info = {
+ .owner = THIS_MODULE,
+ .max_adj = 0,
+ .n_ext_ts = 0,
+ .n_pins = 0,
+ .pps = 0,
+ .adjfine = ptp_vmclock_adjfine,
+ .adjtime = ptp_vmclock_adjtime,
+ .gettimex64 = ptp_vmclock_gettimex,
+ .settime64 = ptp_vmclock_settime,
+ .enable = ptp_vmclock_enable,
+ .getcrosststamp = ptp_vmclock_getcrosststamp,
+};
+
+static struct ptp_clock *vmclock_ptp_register(struct device *dev,
+ struct vmclock_state *st)
+{
+ enum clocksource_ids cs_id;
+
+ if (IS_ENABLED(CONFIG_ARM64) &&
+ st->clk->counter_id == VMCLOCK_COUNTER_ARM_VCNT) {
+ /* Can we check it's the virtual counter? */
+ cs_id = CSID_ARM_ARCH_COUNTER;
+ } else if (IS_ENABLED(CONFIG_X86) &&
+ st->clk->counter_id == VMCLOCK_COUNTER_X86_TSC) {
+ cs_id = CSID_X86_TSC;
+ } else {
+ return NULL;
+ }
+
+ /* Only UTC, or TAI with offset */
+ if (!tai_adjust(st->clk, NULL)) {
+ dev_info(dev, "vmclock does not provide unambiguous UTC\n");
+ return NULL;
+ }
+
+ st->sys_cs_id = cs_id;
+ st->cs_id = cs_id;
+ st->ptp_clock_info = ptp_vmclock_info;
+ strscpy(st->ptp_clock_info.name, st->name);
+
+ return ptp_clock_register(&st->ptp_clock_info, dev);
+}
+
+static int vmclock_miscdev_mmap(struct file *fp, struct vm_area_struct *vma)
+{
+ struct vmclock_state *st = container_of(fp->private_data,
+ struct vmclock_state, miscdev);
+
+ if ((vma->vm_flags & (VM_READ|VM_WRITE)) != VM_READ)
+ return -EROFS;
+
+ if (vma->vm_end - vma->vm_start != PAGE_SIZE || vma->vm_pgoff)
+ return -EINVAL;
+
+ if (io_remap_pfn_range(vma, vma->vm_start,
+ st->res.start >> PAGE_SHIFT, PAGE_SIZE,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static ssize_t vmclock_miscdev_read(struct file *fp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct vmclock_state *st = container_of(fp->private_data,
+ struct vmclock_state, miscdev);
+ ktime_t deadline = ktime_add(ktime_get(), VMCLOCK_MAX_WAIT);
+ size_t max_count;
+ uint32_t seq;
+
+ if (*ppos >= PAGE_SIZE)
+ return 0;
+
+ max_count = PAGE_SIZE - *ppos;
+ if (count > max_count)
+ count = max_count;
+
+ while (1) {
+ seq = le32_to_cpu(st->clk->seq_count) & ~1U;
+ /* Pairs with hypervisor wmb */
+ virt_rmb();
+
+ if (copy_to_user(buf, ((char *)st->clk) + *ppos, count))
+ return -EFAULT;
+
+ /* Pairs with hypervisor wmb */
+ virt_rmb();
+ if (seq == le32_to_cpu(st->clk->seq_count))
+ break;
+
+ if (ktime_after(ktime_get(), deadline))
+ return -ETIMEDOUT;
+ }
+
+ *ppos += count;
+ return count;
+}
+
+static const struct file_operations vmclock_miscdev_fops = {
+ .owner = THIS_MODULE,
+ .mmap = vmclock_miscdev_mmap,
+ .read = vmclock_miscdev_read,
+};
+
+/* module operations */
+
+static void vmclock_remove(void *data)
+{
+ struct vmclock_state *st = data;
+
+ if (st->ptp_clock)
+ ptp_clock_unregister(st->ptp_clock);
+
+ if (st->miscdev.minor != MISC_DYNAMIC_MINOR)
+ misc_deregister(&st->miscdev);
+}
+
+static acpi_status vmclock_acpi_resources(struct acpi_resource *ares, void *data)
+{
+ struct vmclock_state *st = data;
+ struct resource_win win;
+ struct resource *res = &win.res;
+
+ if (ares->type == ACPI_RESOURCE_TYPE_END_TAG)
+ return AE_OK;
+
+ /* There can be only one */
+ if (resource_type(&st->res) == IORESOURCE_MEM)
+ return AE_ERROR;
+
+ if (acpi_dev_resource_memory(ares, res) ||
+ acpi_dev_resource_address_space(ares, &win)) {
+
+ if (resource_type(res) != IORESOURCE_MEM ||
+ resource_size(res) < sizeof(st->clk))
+ return AE_ERROR;
+
+ st->res = *res;
+ return AE_OK;
+ }
+
+ return AE_ERROR;
+}
+
+static int vmclock_probe_acpi(struct device *dev, struct vmclock_state *st)
+{
+ struct acpi_device *adev = ACPI_COMPANION(dev);
+ acpi_status status;
+
+ /*
+ * This should never happen as this function is only called when
+ * has_acpi_companion(dev) is true, but the logic is sufficiently
+ * complex that Coverity can't see the tautology.
+ */
+ if (!adev)
+ return -ENODEV;
+
+ status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+ vmclock_acpi_resources, st);
+ if (ACPI_FAILURE(status) || resource_type(&st->res) != IORESOURCE_MEM) {
+ dev_err(dev, "failed to get resources\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void vmclock_put_idx(void *data)
+{
+ struct vmclock_state *st = data;
+
+ ida_free(&vmclock_ida, st->index);
+}
+
+static int vmclock_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vmclock_state *st;
+ int ret;
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ if (has_acpi_companion(dev))
+ ret = vmclock_probe_acpi(dev, st);
+ else
+ ret = -EINVAL; /* Only ACPI for now */
+
+ if (ret) {
+ dev_info(dev, "Failed to obtain physical address: %d\n", ret);
+ return ret;
+ }
+
+ if (resource_size(&st->res) < VMCLOCK_MIN_SIZE) {
+ dev_info(dev, "Region too small (0x%llx)\n",
+ resource_size(&st->res));
+ return -EINVAL;
+ }
+ st->clk = devm_memremap(dev, st->res.start, resource_size(&st->res),
+ MEMREMAP_WB | MEMREMAP_DEC);
+ if (IS_ERR(st->clk)) {
+ ret = PTR_ERR(st->clk);
+ dev_info(dev, "failed to map shared memory\n");
+ st->clk = NULL;
+ return ret;
+ }
+
+ if (le32_to_cpu(st->clk->magic) != VMCLOCK_MAGIC ||
+ le32_to_cpu(st->clk->size) > resource_size(&st->res) ||
+ le16_to_cpu(st->clk->version) != 1) {
+ dev_info(dev, "vmclock magic fields invalid\n");
+ return -EINVAL;
+ }
+
+ ret = ida_alloc(&vmclock_ida, GFP_KERNEL);
+ if (ret < 0)
+ return ret;
+
+ st->index = ret;
+ ret = devm_add_action_or_reset(&pdev->dev, vmclock_put_idx, st);
+ if (ret)
+ return ret;
+
+ st->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "vmclock%d", st->index);
+ if (!st->name)
+ return -ENOMEM;
+
+ st->miscdev.minor = MISC_DYNAMIC_MINOR;
+
+ ret = devm_add_action_or_reset(&pdev->dev, vmclock_remove, st);
+ if (ret)
+ return ret;
+
+ /*
+ * If the structure is big enough, it can be mapped to userspace.
+ * Theoretically a guest OS even using larger pages could still
+ * use 4KiB PTEs to map smaller MMIO regions like this, but let's
+ * cross that bridge if/when we come to it.
+ */
+ if (le32_to_cpu(st->clk->size) >= PAGE_SIZE) {
+ st->miscdev.fops = &vmclock_miscdev_fops;
+ st->miscdev.name = st->name;
+
+ ret = misc_register(&st->miscdev);
+ if (ret)
+ return ret;
+ }
+
+ /* If there is valid clock information, register a PTP clock */
+ if (VMCLOCK_FIELD_PRESENT(st->clk, time_frac_sec)) {
+ /* Can return a silent NULL, or an error. */
+ st->ptp_clock = vmclock_ptp_register(dev, st);
+ if (IS_ERR(st->ptp_clock)) {
+ ret = PTR_ERR(st->ptp_clock);
+ st->ptp_clock = NULL;
+ return ret;
+ }
+ }
+
+ if (!st->miscdev.minor && !st->ptp_clock) {
+ /* Neither miscdev nor PTP registered */
+ dev_info(dev, "vmclock: Neither miscdev nor PTP available; not registering\n");
+ return -ENODEV;
+ }
+
+ dev_info(dev, "%s: registered %s%s%s\n", st->name,
+ st->miscdev.minor ? "miscdev" : "",
+ (st->miscdev.minor && st->ptp_clock) ? ", " : "",
+ st->ptp_clock ? "PTP" : "");
+
+ return 0;
+}
+
+static const struct acpi_device_id vmclock_acpi_ids[] = {
+ { "AMZNC10C", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, vmclock_acpi_ids);
+
+static struct platform_driver vmclock_platform_driver = {
+ .probe = vmclock_probe,
+ .driver = {
+ .name = "vmclock",
+ .acpi_match_table = vmclock_acpi_ids,
+ },
+};
+
+module_platform_driver(vmclock_platform_driver)
+
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("PTP clock using VMCLOCK");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ptp/ptp_vmw.c b/drivers/ptp/ptp_vmw.c
index 27c5547aa8a9..20ab05c4daa8 100644
--- a/drivers/ptp/ptp_vmw.c
+++ b/drivers/ptp/ptp_vmw.c
@@ -14,7 +14,6 @@
#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)
@@ -24,15 +23,10 @@ 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");
+ u32 ret, nsec_hi, nsec_lo;
+ ret = vmware_hypercall3(VMWARE_CMD_PCLK_GETTIME, 0,
+ &nsec_hi, &nsec_lo);
if (ret == 0)
*ns = ((u64)nsec_hi << 32) | nsec_lo;
return ret;
@@ -120,7 +114,6 @@ static struct acpi_driver ptp_vmw_acpi_driver = {
.add = ptp_vmw_acpi_add,
.remove = ptp_vmw_acpi_remove
},
- .owner = THIS_MODULE
};
static int __init ptp_vmw_init(void)