diff mbox series

[net-next,v5,1/2] net: add support for Cavium PTP coprocessor

Message ID 20171211141435.2915-2-aleksey.makarov@cavium.com
State Changes Requested, archived
Delegated to: David Miller
Headers show
Series net: thunderx: add support for PTP clock | expand

Commit Message

Aleksey Makarov Dec. 11, 2017, 2:14 p.m. UTC
From: Radoslaw Biernacki <rad@semihalf.com>

This patch adds support for the Precision Time Protocol
Clocks and Timestamping hardware found on Cavium ThunderX
processors.

Signed-off-by: Radoslaw Biernacki <rad@semihalf.com>
Signed-off-by: Aleksey Makarov <aleksey.makarov@cavium.com>
---
 drivers/net/ethernet/cavium/Kconfig             |  12 +
 drivers/net/ethernet/cavium/Makefile            |   1 +
 drivers/net/ethernet/cavium/common/Makefile     |   1 +
 drivers/net/ethernet/cavium/common/cavium_ptp.c | 329 ++++++++++++++++++++++++
 drivers/net/ethernet/cavium/common/cavium_ptp.h |  70 +++++
 5 files changed, 413 insertions(+)
 create mode 100644 drivers/net/ethernet/cavium/common/Makefile
 create mode 100644 drivers/net/ethernet/cavium/common/cavium_ptp.c
 create mode 100644 drivers/net/ethernet/cavium/common/cavium_ptp.h

Comments

Richard Cochran Dec. 11, 2017, 10:59 p.m. UTC | #1
Sorry I didn't finish reviewing before...

On Mon, Dec 11, 2017 at 05:14:30PM +0300, Aleksey Makarov wrote:
> +/**
> + * cavium_ptp_adjfreq() - Adjust ptp frequency
> + * @ptp: PTP clock info
> + * @ppb: how much to adjust by, in parts-per-billion
> + */
> +static int cavium_ptp_adjfreq(struct ptp_clock_info *ptp_info, s32 ppb)

adjfreq() is deprecated.  See ptp_clock_kernel.h.  Please re-work this
to implement the adjfine() method instead.

> +/**
> + * cavium_ptp_enable() - Check if PTP is enabled

Nit - comment is not correct. This method is for the auxiliary PHC
functions.

> + * @ptp: PTP clock info
> + * @rq:  request
> + * @on:  is it on
> + */
> +static int cavium_ptp_enable(struct ptp_clock_info *ptp_info,
> +			     struct ptp_clock_request *rq, int on)
> +{
> +	return -EOPNOTSUPP;
> +}

...

> +static int cavium_ptp_probe(struct pci_dev *pdev,
> +			    const struct pci_device_id *ent)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct cavium_ptp *clock;
> +	struct cyclecounter *cc;
> +	u64 clock_cfg;
> +	u64 clock_comp;
> +	int err;
> +
> +	clock = devm_kzalloc(dev, sizeof(*clock), GFP_KERNEL);
> +	if (!clock)
> +		return -ENOMEM;
> +
> +	clock->pdev = pdev;
> +
> +	err = pcim_enable_device(pdev);
> +	if (err)
> +		return err;
> +
> +	err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev));
> +	if (err)
> +		return err;
> +
> +	clock->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO];
> +
> +	spin_lock_init(&clock->spin_lock);
> +
> +	cc = &clock->cycle_counter;
> +	cc->read = cavium_ptp_cc_read;
> +	cc->mask = CYCLECOUNTER_MASK(64);
> +	cc->mult = 1;
> +	cc->shift = 0;
> +
> +	timecounter_init(&clock->time_counter, &clock->cycle_counter,
> +			 ktime_to_ns(ktime_get_real()));
> +
> +	clock->clock_rate = ptp_cavium_clock_get();
> +
> +	clock->ptp_info = (struct ptp_clock_info) {
> +		.owner		= THIS_MODULE,
> +		.name		= "ThunderX PTP",
> +		.max_adj	= 1000000000ull,
> +		.n_ext_ts	= 0,
> +		.n_pins		= 0,
> +		.pps		= 0,
> +		.adjfreq	= cavium_ptp_adjfreq,
> +		.adjtime	= cavium_ptp_adjtime,
> +		.gettime64	= cavium_ptp_gettime,
> +		.settime64	= cavium_ptp_settime,
> +		.enable		= cavium_ptp_enable,
> +	};
> +
> +	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
> +	clock_cfg |= PTP_CLOCK_CFG_PTP_EN;
> +	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
> +
> +	clock_comp = ((u64)1000000000ull << 32) / clock->clock_rate;
> +	writeq(clock_comp, clock->reg_base + PTP_CLOCK_COMP);
> +
> +	clock->ptp_clock = ptp_clock_register(&clock->ptp_info, dev);
> +	if (IS_ERR(clock->ptp_clock)) {

You need to handle the case when ptp_clock_register() returns NULL.

from ptp_clock_kernel.h:

/**
 * ptp_clock_register() - register a PTP hardware clock driver
 *
 * @info:   Structure describing the new clock.
 * @parent: Pointer to the parent device of the new clock.
 *
 * Returns a valid pointer on success or PTR_ERR on failure.  If PHC
 * support is missing at the configuration level, this function
 * returns NULL, and drivers are expected to gracefully handle that
 * case separately.
 */

> +		clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
> +		clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
> +		writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
> +		return PTR_ERR(clock->ptp_clock);
> +	}
> +
> +	pci_set_drvdata(pdev, clock);
> +	return 0;
> +}

Thanks,
Richard
Aleksey Makarov Dec. 12, 2017, 9:41 a.m. UTC | #2
Hi Richard,

On 12/12/2017 01:59 AM, Richard Cochran wrote:
> 
> Sorry I didn't finish reviewing before...
> 
> On Mon, Dec 11, 2017 at 05:14:30PM +0300, Aleksey Makarov wrote:

[ ... ]

>> +static int cavium_ptp_probe(struct pci_dev *pdev,
>> +			    const struct pci_device_id *ent)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct cavium_ptp *clock;
>> +	struct cyclecounter *cc;
>> +	u64 clock_cfg;
>> +	u64 clock_comp;
>> +	int err;
>> +
>> +	clock = devm_kzalloc(dev, sizeof(*clock), GFP_KERNEL);
>> +	if (!clock)
>> +		return -ENOMEM;
>> +
>> +	clock->pdev = pdev;
>> +
>> +	err = pcim_enable_device(pdev);
>> +	if (err)
>> +		return err;
>> +
>> +	err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev));
>> +	if (err)
>> +		return err;
>> +
>> +	clock->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO];
>> +
>> +	spin_lock_init(&clock->spin_lock);
>> +
>> +	cc = &clock->cycle_counter;
>> +	cc->read = cavium_ptp_cc_read;
>> +	cc->mask = CYCLECOUNTER_MASK(64);
>> +	cc->mult = 1;
>> +	cc->shift = 0;
>> +
>> +	timecounter_init(&clock->time_counter, &clock->cycle_counter,
>> +			 ktime_to_ns(ktime_get_real()));
>> +
>> +	clock->clock_rate = ptp_cavium_clock_get();
>> +
>> +	clock->ptp_info = (struct ptp_clock_info) {
>> +		.owner		= THIS_MODULE,
>> +		.name		= "ThunderX PTP",
>> +		.max_adj	= 1000000000ull,
>> +		.n_ext_ts	= 0,
>> +		.n_pins		= 0,
>> +		.pps		= 0,
>> +		.adjfreq	= cavium_ptp_adjfreq,
>> +		.adjtime	= cavium_ptp_adjtime,
>> +		.gettime64	= cavium_ptp_gettime,
>> +		.settime64	= cavium_ptp_settime,
>> +		.enable		= cavium_ptp_enable,
>> +	};
>> +
>> +	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
>> +	clock_cfg |= PTP_CLOCK_CFG_PTP_EN;
>> +	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
>> +
>> +	clock_comp = ((u64)1000000000ull << 32) / clock->clock_rate;
>> +	writeq(clock_comp, clock->reg_base + PTP_CLOCK_COMP);
>> +
>> +	clock->ptp_clock = ptp_clock_register(&clock->ptp_info, dev);
>> +	if (IS_ERR(clock->ptp_clock)) {
> 
> You need to handle the case when ptp_clock_register() returns NULL.
> 
> from ptp_clock_kernel.h:
> 
> /**
>   * ptp_clock_register() - register a PTP hardware clock driver
>   *
>   * @info:   Structure describing the new clock.
>   * @parent: Pointer to the parent device of the new clock.
>   *
>   * Returns a valid pointer on success or PTR_ERR on failure.  If PHC
>   * support is missing at the configuration level, this function
>   * returns NULL, and drivers are expected to gracefully handle that
>   * case separately.
>   */

If ptp_clock_register() returns NULL, the device is still paired with the driver,
but the driver is not registered in the PTP core.  When ethernet driver needs
the reference to this cavium PTP driver, it calls cavium_ptp_get() that checks
if ptp->ptp_clock is NULL and, if so, returns -ENODEV.

I need this behavior because I need to differentiate between two cases:

- the state when the driver is not initialized for the device because of PTP core
   has not registered it.  In this case function cavium_ptp_get() returns -ENODEV
   and ethernet driver proceeds without PTP device.

- the state when the driver is not initialized because kernel has not tired
   to initialize it yet.  In this case function cavium_ptp_get() returns -EPROBE_DEFER
   that is used in ethernet driver to defer initialization.

If you know how to do the same in more smoothly please share it.  Or else I would
prefer to insert a comment about it and leave it as is.

Richard, thank you for review.  I am going to address your comments in my next series.

Thank you
Aleksey Makarov
  
>> +		clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
>> +		clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
>> +		writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
>> +		return PTR_ERR(clock->ptp_clock);
>> +	}
>> +
>> +	pci_set_drvdata(pdev, clock);
>> +	return 0;
>> +}
> 
> Thanks,
> Richard
>
Richard Cochran Dec. 12, 2017, 5:12 p.m. UTC | #3
On Tue, Dec 12, 2017 at 12:41:35PM +0300, Aleksey Makarov wrote:
> If ptp_clock_register() returns NULL, the device is still paired with the driver,
> but the driver is not registered in the PTP core.  When ethernet driver needs
> the reference to this cavium PTP driver, it calls cavium_ptp_get() that checks
> if ptp->ptp_clock is NULL and, if so, returns -ENODEV.

The pointer clock->ptp_clock can be NULL.

Yet you de-reference it here:

> +static void cavium_ptp_remove(struct pci_dev *pdev)
> +{
> +	struct cavium_ptp *clock = pci_get_drvdata(pdev);
> +	u64 clock_cfg;
> +
> +	pci_set_drvdata(pdev, NULL);
> +
> +	ptp_clock_unregister(clock->ptp_clock);
> +
> +	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
> +	clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
> +	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
> +}

and here:

> +static inline int cavium_ptp_clock_index(struct cavium_ptp *clock)
> +{
> +	return ptp_clock_index(clock->ptp_clock);
> +}

That needs to be fixed.

Thanks,
Richard
diff mbox series

Patch

diff --git a/drivers/net/ethernet/cavium/Kconfig b/drivers/net/ethernet/cavium/Kconfig
index 63be75eb34d2..96586c0b4490 100644
--- a/drivers/net/ethernet/cavium/Kconfig
+++ b/drivers/net/ethernet/cavium/Kconfig
@@ -50,6 +50,18 @@  config	THUNDER_NIC_RGX
 	  This driver supports configuring XCV block of RGX interface
 	  present on CN81XX chip.
 
+config CAVIUM_PTP
+	tristate "Cavium PTP coprocessor as PTP clock"
+	depends on 64BIT
+	imply PTP_1588_CLOCK
+	default y
+	---help---
+	  This driver adds support for the Precision Time Protocol Clocks and
+	  Timestamping coprocessor (PTP) found on Cavium processors.
+	  PTP provides timestamping mechanism that is suitable for use in IEEE 1588
+	  Precision Time Protocol or other purposes.  Timestamps can be used in
+	  BGX, TNS, GTI, and NIC blocks.
+
 config LIQUIDIO
 	tristate "Cavium LiquidIO support"
 	depends on 64BIT
diff --git a/drivers/net/ethernet/cavium/Makefile b/drivers/net/ethernet/cavium/Makefile
index 872da9f7c31a..946bba84e81d 100644
--- a/drivers/net/ethernet/cavium/Makefile
+++ b/drivers/net/ethernet/cavium/Makefile
@@ -1,6 +1,7 @@ 
 #
 # Makefile for the Cavium ethernet device drivers.
 #
+obj-$(CONFIG_NET_VENDOR_CAVIUM) += common/
 obj-$(CONFIG_NET_VENDOR_CAVIUM) += thunder/
 obj-$(CONFIG_NET_VENDOR_CAVIUM) += liquidio/
 obj-$(CONFIG_NET_VENDOR_CAVIUM) += octeon/
diff --git a/drivers/net/ethernet/cavium/common/Makefile b/drivers/net/ethernet/cavium/common/Makefile
new file mode 100644
index 000000000000..dd8561b8060b
--- /dev/null
+++ b/drivers/net/ethernet/cavium/common/Makefile
@@ -0,0 +1 @@ 
+obj-$(CONFIG_CAVIUM_PTP) += cavium_ptp.o
diff --git a/drivers/net/ethernet/cavium/common/cavium_ptp.c b/drivers/net/ethernet/cavium/common/cavium_ptp.c
new file mode 100644
index 000000000000..8b48863bfdec
--- /dev/null
+++ b/drivers/net/ethernet/cavium/common/cavium_ptp.c
@@ -0,0 +1,329 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* cavium_ptp.c - PTP 1588 clock on Cavium hardware
+ * Copyright (c) 2003-2015, 2017 Cavium, Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/timecounter.h>
+#include <linux/pci.h>
+
+#include "cavium_ptp.h"
+
+#define DRV_NAME	"Cavium PTP Driver"
+
+#define PCI_DEVICE_ID_CAVIUM_PTP	0xA00C
+#define PCI_DEVICE_ID_CAVIUM_RST	0xA00E
+
+#define PCI_PTP_BAR_NO	0
+#define PCI_RST_BAR_NO	0
+
+#define PTP_CLOCK_CFG		0xF00ULL
+#define  PTP_CLOCK_CFG_PTP_EN	BIT(0)
+#define PTP_CLOCK_LO		0xF08ULL
+#define PTP_CLOCK_HI		0xF10ULL
+#define PTP_CLOCK_COMP		0xF18ULL
+
+#define RST_BOOT	0x1600ULL
+#define CLOCK_BASE_RATE	50000000ULL
+
+static u64 ptp_cavium_clock_get(void)
+{
+	struct pci_dev *pdev;
+	void __iomem *base;
+	u64 ret = CLOCK_BASE_RATE * 16;
+
+	pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
+			      PCI_DEVICE_ID_CAVIUM_RST, NULL);
+	if (!pdev)
+		goto error;
+
+	base = pci_ioremap_bar(pdev, PCI_RST_BAR_NO);
+	if (!base)
+		goto error_put_pdev;
+
+	ret = CLOCK_BASE_RATE * ((readq(base + RST_BOOT) >> 33) & 0x3f);
+
+	iounmap(base);
+
+error_put_pdev:
+	pci_dev_put(pdev);
+
+error:
+	return ret;
+}
+
+struct cavium_ptp *cavium_ptp_get(void)
+{
+	struct cavium_ptp *ptp;
+	struct pci_dev *pdev;
+
+	pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
+			      PCI_DEVICE_ID_CAVIUM_PTP, NULL);
+	if (!pdev)
+		return ERR_PTR(-ENODEV);
+
+	ptp = pci_get_drvdata(pdev);
+	if (!ptp) {
+		pci_dev_put(pdev);
+		ptp = ERR_PTR(-EPROBE_DEFER);
+	} else if (!ptp->ptp_clock) {
+		pci_dev_put(pdev);
+		ptp = ERR_PTR(-ENODEV);
+	}
+
+	return ptp;
+}
+EXPORT_SYMBOL(cavium_ptp_get);
+
+void cavium_ptp_put(struct cavium_ptp *ptp)
+{
+	pci_dev_put(ptp->pdev);
+}
+EXPORT_SYMBOL(cavium_ptp_put);
+
+/**
+ * cavium_ptp_adjfreq() - Adjust ptp frequency
+ * @ptp: PTP clock info
+ * @ppb: how much to adjust by, in parts-per-billion
+ */
+static int cavium_ptp_adjfreq(struct ptp_clock_info *ptp_info, s32 ppb)
+{
+	struct cavium_ptp *clock =
+		container_of(ptp_info, struct cavium_ptp, ptp_info);
+	unsigned long flags;
+	u64 comp;
+	u64 adj;
+	bool neg_adj = false;
+
+	if (ppb < 0) {
+		neg_adj = true;
+		ppb = -ppb;
+	}
+
+	/* The hardware adds the clock compensation value to the PTP clock
+	 * on every coprocessor clock cycle. Typical convention is that it
+	 * represent number of nanosecond betwen each cycle. In this
+	 * convention compensation value is in 64 bit fixed-point
+	 * representation where upper 32 bits are number of nanoseconds
+	 * and lower is fractions of nanosecond.
+	 * The ppb represent the ratio in "parts per bilion" by which the
+	 * compensation value should be corrected.
+	 * To calculate new compenstation value we use 64bit fixed point
+	 * arithmetic on following formula comp = tbase + tbase * ppb / 1G
+	 * where tbase is the basic compensation value calculated initialy
+	 * in cavium_ptp_init() -> tbase = 1/Hz. Then we use endian
+	 * independent structure definition to write data to PTP register.
+	 */
+	comp = ((u64)1000000000ull << 32) / clock->clock_rate;
+	adj = comp * ppb;
+	adj = div_u64(adj, 1000000000ull);
+	comp = neg_adj ? comp - adj : comp + adj;
+
+	spin_lock_irqsave(&clock->spin_lock, flags);
+	writeq(comp, clock->reg_base + PTP_CLOCK_COMP);
+	spin_unlock_irqrestore(&clock->spin_lock, flags);
+
+	return 0;
+}
+
+/**
+ * cavium_ptp_adjtime() - Adjust ptp time
+ * @ptp:   PTP clock info
+ * @delta: how much to adjust by, in nanosecs
+ */
+static int cavium_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta)
+{
+	struct cavium_ptp *clock =
+		container_of(ptp_info, struct cavium_ptp, ptp_info);
+	unsigned long flags;
+
+	spin_lock_irqsave(&clock->spin_lock, flags);
+	timecounter_adjtime(&clock->time_counter, delta);
+	spin_unlock_irqrestore(&clock->spin_lock, flags);
+
+	/* Sync, for network driver to get latest value */
+	smp_mb();
+
+	return 0;
+}
+
+/**
+ * cavium_ptp_gettime() - Get hardware clock time with adjustment
+ * @ptp: PTP clock info
+ * @ts:  timespec
+ */
+static int cavium_ptp_gettime(struct ptp_clock_info *ptp_info,
+			      struct timespec64 *ts)
+{
+	struct cavium_ptp *clock =
+		container_of(ptp_info, struct cavium_ptp, ptp_info);
+	unsigned long flags;
+	u64 nsec;
+
+	spin_lock_irqsave(&clock->spin_lock, flags);
+	nsec = timecounter_read(&clock->time_counter);
+	spin_unlock_irqrestore(&clock->spin_lock, flags);
+
+	*ts = ns_to_timespec64(nsec);
+
+	return 0;
+}
+
+/**
+ * cavium_ptp_settime() - Set hardware clock time. Reset adjustment
+ * @ptp: PTP clock info
+ * @ts:  timespec
+ */
+static int cavium_ptp_settime(struct ptp_clock_info *ptp_info,
+			      const struct timespec64 *ts)
+{
+	struct cavium_ptp *clock =
+		container_of(ptp_info, struct cavium_ptp, ptp_info);
+	unsigned long flags;
+	u64 nsec;
+
+	nsec = timespec64_to_ns(ts);
+
+	spin_lock_irqsave(&clock->spin_lock, flags);
+	timecounter_init(&clock->time_counter, &clock->cycle_counter, nsec);
+	spin_unlock_irqrestore(&clock->spin_lock, flags);
+
+	return 0;
+}
+
+/**
+ * cavium_ptp_enable() - Check if PTP is enabled
+ * @ptp: PTP clock info
+ * @rq:  request
+ * @on:  is it on
+ */
+static int cavium_ptp_enable(struct ptp_clock_info *ptp_info,
+			     struct ptp_clock_request *rq, int on)
+{
+	return -EOPNOTSUPP;
+}
+
+static u64 cavium_ptp_cc_read(const struct cyclecounter *cc)
+{
+	struct cavium_ptp *clock =
+		container_of(cc, struct cavium_ptp, cycle_counter);
+
+	return readq(clock->reg_base + PTP_CLOCK_HI);
+}
+
+static int cavium_ptp_probe(struct pci_dev *pdev,
+			    const struct pci_device_id *ent)
+{
+	struct device *dev = &pdev->dev;
+	struct cavium_ptp *clock;
+	struct cyclecounter *cc;
+	u64 clock_cfg;
+	u64 clock_comp;
+	int err;
+
+	clock = devm_kzalloc(dev, sizeof(*clock), GFP_KERNEL);
+	if (!clock)
+		return -ENOMEM;
+
+	clock->pdev = pdev;
+
+	err = pcim_enable_device(pdev);
+	if (err)
+		return err;
+
+	err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev));
+	if (err)
+		return err;
+
+	clock->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO];
+
+	spin_lock_init(&clock->spin_lock);
+
+	cc = &clock->cycle_counter;
+	cc->read = cavium_ptp_cc_read;
+	cc->mask = CYCLECOUNTER_MASK(64);
+	cc->mult = 1;
+	cc->shift = 0;
+
+	timecounter_init(&clock->time_counter, &clock->cycle_counter,
+			 ktime_to_ns(ktime_get_real()));
+
+	clock->clock_rate = ptp_cavium_clock_get();
+
+	clock->ptp_info = (struct ptp_clock_info) {
+		.owner		= THIS_MODULE,
+		.name		= "ThunderX PTP",
+		.max_adj	= 1000000000ull,
+		.n_ext_ts	= 0,
+		.n_pins		= 0,
+		.pps		= 0,
+		.adjfreq	= cavium_ptp_adjfreq,
+		.adjtime	= cavium_ptp_adjtime,
+		.gettime64	= cavium_ptp_gettime,
+		.settime64	= cavium_ptp_settime,
+		.enable		= cavium_ptp_enable,
+	};
+
+	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
+	clock_cfg |= PTP_CLOCK_CFG_PTP_EN;
+	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
+
+	clock_comp = ((u64)1000000000ull << 32) / clock->clock_rate;
+	writeq(clock_comp, clock->reg_base + PTP_CLOCK_COMP);
+
+	clock->ptp_clock = ptp_clock_register(&clock->ptp_info, dev);
+	if (IS_ERR(clock->ptp_clock)) {
+		clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
+		clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
+		writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
+		return PTR_ERR(clock->ptp_clock);
+	}
+
+	pci_set_drvdata(pdev, clock);
+	return 0;
+}
+
+static void cavium_ptp_remove(struct pci_dev *pdev)
+{
+	struct cavium_ptp *clock = pci_get_drvdata(pdev);
+	u64 clock_cfg;
+
+	pci_set_drvdata(pdev, NULL);
+
+	ptp_clock_unregister(clock->ptp_clock);
+
+	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
+	clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
+	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
+}
+
+static const struct pci_device_id cavium_ptp_id_table[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP) },
+	{ 0, }
+};
+
+static struct pci_driver cavium_ptp_driver = {
+	.name = DRV_NAME,
+	.id_table = cavium_ptp_id_table,
+	.probe = cavium_ptp_probe,
+	.remove = cavium_ptp_remove,
+};
+
+static int __init cavium_ptp_init_module(void)
+{
+	return pci_register_driver(&cavium_ptp_driver);
+}
+
+static void __exit cavium_ptp_cleanup_module(void)
+{
+	pci_unregister_driver(&cavium_ptp_driver);
+}
+
+module_init(cavium_ptp_init_module);
+module_exit(cavium_ptp_cleanup_module);
+
+MODULE_DESCRIPTION(DRV_NAME);
+MODULE_AUTHOR("Cavium Networks <support@cavium.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(pci, cavium_ptp_id_table);
diff --git a/drivers/net/ethernet/cavium/common/cavium_ptp.h b/drivers/net/ethernet/cavium/common/cavium_ptp.h
new file mode 100644
index 000000000000..be2bafc7beeb
--- /dev/null
+++ b/drivers/net/ethernet/cavium/common/cavium_ptp.h
@@ -0,0 +1,70 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* cavium_ptp.h - PTP 1588 clock on Cavium hardware
+ * Copyright (c) 2003-2015, 2017 Cavium, Inc.
+ */
+
+#ifndef CAVIUM_PTP_H
+#define CAVIUM_PTP_H
+
+#include <linux/ptp_clock_kernel.h>
+#include <linux/timecounter.h>
+
+struct cavium_ptp {
+	struct pci_dev *pdev;
+
+	/* Serialize access to cycle_counter, time_counter and hw_registers */
+	spinlock_t spin_lock;
+	struct cyclecounter cycle_counter;
+	struct timecounter time_counter;
+	void __iomem *reg_base;
+
+	u32 clock_rate;
+
+	struct ptp_clock_info ptp_info;
+	struct ptp_clock *ptp_clock;
+};
+
+#if IS_ENABLED(CONFIG_CAVIUM_PTP)
+
+struct cavium_ptp *cavium_ptp_get(void);
+void cavium_ptp_put(struct cavium_ptp *ptp);
+
+static inline u64 cavium_ptp_tstamp2time(struct cavium_ptp *ptp, u64 tstamp)
+{
+	unsigned long flags;
+	u64 ret;
+
+	spin_lock_irqsave(&ptp->spin_lock, flags);
+	ret = timecounter_cyc2time(&ptp->time_counter, tstamp);
+	spin_unlock_irqrestore(&ptp->spin_lock, flags);
+
+	return ret;
+}
+
+static inline int cavium_ptp_clock_index(struct cavium_ptp *clock)
+{
+	return ptp_clock_index(clock->ptp_clock);
+}
+
+#else
+
+static inline struct cavium_ptp *cavium_ptp_get(void)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+static inline void cavium_ptp_put(struct cavium_ptp *ptp) {}
+
+static inline u64 cavium_ptp_tstamp2time(struct cavium_ptp *ptp, u64 tstamp)
+{
+	return 0;
+}
+
+static inline int cavium_ptp_clock_index(struct cavium_ptp *clock)
+{
+	return -1;
+}
+
+#endif
+
+#endif