162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* cavium_ptp.c - PTP 1588 clock on Cavium hardware
362306a36Sopenharmony_ci * Copyright (c) 2003-2015, 2017 Cavium, Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/device.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/timecounter.h>
962306a36Sopenharmony_ci#include <linux/pci.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "cavium_ptp.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define DRV_NAME "cavium_ptp"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define PCI_DEVICE_ID_CAVIUM_PTP	0xA00C
1662306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_88XX_PTP	0xA10C
1762306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_81XX_PTP	0XA20C
1862306a36Sopenharmony_ci#define PCI_SUBSYS_DEVID_83XX_PTP	0xA30C
1962306a36Sopenharmony_ci#define PCI_DEVICE_ID_CAVIUM_RST	0xA00E
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define PCI_PTP_BAR_NO	0
2262306a36Sopenharmony_ci#define PCI_RST_BAR_NO	0
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define PTP_CLOCK_CFG		0xF00ULL
2562306a36Sopenharmony_ci#define  PTP_CLOCK_CFG_PTP_EN	BIT(0)
2662306a36Sopenharmony_ci#define PTP_CLOCK_LO		0xF08ULL
2762306a36Sopenharmony_ci#define PTP_CLOCK_HI		0xF10ULL
2862306a36Sopenharmony_ci#define PTP_CLOCK_COMP		0xF18ULL
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define RST_BOOT	0x1600ULL
3162306a36Sopenharmony_ci#define CLOCK_BASE_RATE	50000000ULL
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic u64 ptp_cavium_clock_get(void)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct pci_dev *pdev;
3662306a36Sopenharmony_ci	void __iomem *base;
3762306a36Sopenharmony_ci	u64 ret = CLOCK_BASE_RATE * 16;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
4062306a36Sopenharmony_ci			      PCI_DEVICE_ID_CAVIUM_RST, NULL);
4162306a36Sopenharmony_ci	if (!pdev)
4262306a36Sopenharmony_ci		goto error;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	base = pci_ioremap_bar(pdev, PCI_RST_BAR_NO);
4562306a36Sopenharmony_ci	if (!base)
4662306a36Sopenharmony_ci		goto error_put_pdev;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	ret = CLOCK_BASE_RATE * ((readq(base + RST_BOOT) >> 33) & 0x3f);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	iounmap(base);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cierror_put_pdev:
5362306a36Sopenharmony_ci	pci_dev_put(pdev);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cierror:
5662306a36Sopenharmony_ci	return ret;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct cavium_ptp *cavium_ptp_get(void)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct cavium_ptp *ptp;
6262306a36Sopenharmony_ci	struct pci_dev *pdev;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
6562306a36Sopenharmony_ci			      PCI_DEVICE_ID_CAVIUM_PTP, NULL);
6662306a36Sopenharmony_ci	if (!pdev)
6762306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	ptp = pci_get_drvdata(pdev);
7062306a36Sopenharmony_ci	if (!ptp)
7162306a36Sopenharmony_ci		ptp = ERR_PTR(-EPROBE_DEFER);
7262306a36Sopenharmony_ci	if (IS_ERR(ptp))
7362306a36Sopenharmony_ci		pci_dev_put(pdev);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return ptp;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ciEXPORT_SYMBOL(cavium_ptp_get);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_civoid cavium_ptp_put(struct cavium_ptp *ptp)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	if (!ptp)
8262306a36Sopenharmony_ci		return;
8362306a36Sopenharmony_ci	pci_dev_put(ptp->pdev);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ciEXPORT_SYMBOL(cavium_ptp_put);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/**
8862306a36Sopenharmony_ci * cavium_ptp_adjfine() - Adjust ptp frequency
8962306a36Sopenharmony_ci * @ptp_info: PTP clock info
9062306a36Sopenharmony_ci * @scaled_ppm: how much to adjust by, in parts per million, but with a
9162306a36Sopenharmony_ci *              16 bit binary fractional field
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_cistatic int cavium_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct cavium_ptp *clock =
9662306a36Sopenharmony_ci		container_of(ptp_info, struct cavium_ptp, ptp_info);
9762306a36Sopenharmony_ci	unsigned long flags;
9862306a36Sopenharmony_ci	u64 comp;
9962306a36Sopenharmony_ci	u64 adj;
10062306a36Sopenharmony_ci	bool neg_adj = false;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (scaled_ppm < 0) {
10362306a36Sopenharmony_ci		neg_adj = true;
10462306a36Sopenharmony_ci		scaled_ppm = -scaled_ppm;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* The hardware adds the clock compensation value to the PTP clock
10862306a36Sopenharmony_ci	 * on every coprocessor clock cycle. Typical convention is that it
10962306a36Sopenharmony_ci	 * represent number of nanosecond betwen each cycle. In this
11062306a36Sopenharmony_ci	 * convention compensation value is in 64 bit fixed-point
11162306a36Sopenharmony_ci	 * representation where upper 32 bits are number of nanoseconds
11262306a36Sopenharmony_ci	 * and lower is fractions of nanosecond.
11362306a36Sopenharmony_ci	 * The scaled_ppm represent the ratio in "parts per bilion" by which the
11462306a36Sopenharmony_ci	 * compensation value should be corrected.
11562306a36Sopenharmony_ci	 * To calculate new compenstation value we use 64bit fixed point
11662306a36Sopenharmony_ci	 * arithmetic on following formula
11762306a36Sopenharmony_ci	 * comp = tbase + tbase * scaled_ppm / (1M * 2^16)
11862306a36Sopenharmony_ci	 * where tbase is the basic compensation value calculated initialy
11962306a36Sopenharmony_ci	 * in cavium_ptp_init() -> tbase = 1/Hz. Then we use endian
12062306a36Sopenharmony_ci	 * independent structure definition to write data to PTP register.
12162306a36Sopenharmony_ci	 */
12262306a36Sopenharmony_ci	comp = ((u64)1000000000ull << 32) / clock->clock_rate;
12362306a36Sopenharmony_ci	adj = comp * scaled_ppm;
12462306a36Sopenharmony_ci	adj >>= 16;
12562306a36Sopenharmony_ci	adj = div_u64(adj, 1000000ull);
12662306a36Sopenharmony_ci	comp = neg_adj ? comp - adj : comp + adj;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	spin_lock_irqsave(&clock->spin_lock, flags);
12962306a36Sopenharmony_ci	writeq(comp, clock->reg_base + PTP_CLOCK_COMP);
13062306a36Sopenharmony_ci	spin_unlock_irqrestore(&clock->spin_lock, flags);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/**
13662306a36Sopenharmony_ci * cavium_ptp_adjtime() - Adjust ptp time
13762306a36Sopenharmony_ci * @ptp_info:   PTP clock info
13862306a36Sopenharmony_ci * @delta: how much to adjust by, in nanosecs
13962306a36Sopenharmony_ci */
14062306a36Sopenharmony_cistatic int cavium_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct cavium_ptp *clock =
14362306a36Sopenharmony_ci		container_of(ptp_info, struct cavium_ptp, ptp_info);
14462306a36Sopenharmony_ci	unsigned long flags;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	spin_lock_irqsave(&clock->spin_lock, flags);
14762306a36Sopenharmony_ci	timecounter_adjtime(&clock->time_counter, delta);
14862306a36Sopenharmony_ci	spin_unlock_irqrestore(&clock->spin_lock, flags);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Sync, for network driver to get latest value */
15162306a36Sopenharmony_ci	smp_mb();
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/**
15762306a36Sopenharmony_ci * cavium_ptp_gettime() - Get hardware clock time with adjustment
15862306a36Sopenharmony_ci * @ptp_info: PTP clock info
15962306a36Sopenharmony_ci * @ts:  timespec
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic int cavium_ptp_gettime(struct ptp_clock_info *ptp_info,
16262306a36Sopenharmony_ci			      struct timespec64 *ts)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct cavium_ptp *clock =
16562306a36Sopenharmony_ci		container_of(ptp_info, struct cavium_ptp, ptp_info);
16662306a36Sopenharmony_ci	unsigned long flags;
16762306a36Sopenharmony_ci	u64 nsec;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	spin_lock_irqsave(&clock->spin_lock, flags);
17062306a36Sopenharmony_ci	nsec = timecounter_read(&clock->time_counter);
17162306a36Sopenharmony_ci	spin_unlock_irqrestore(&clock->spin_lock, flags);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	*ts = ns_to_timespec64(nsec);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/**
17962306a36Sopenharmony_ci * cavium_ptp_settime() - Set hardware clock time. Reset adjustment
18062306a36Sopenharmony_ci * @ptp_info: PTP clock info
18162306a36Sopenharmony_ci * @ts:  timespec
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_cistatic int cavium_ptp_settime(struct ptp_clock_info *ptp_info,
18462306a36Sopenharmony_ci			      const struct timespec64 *ts)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct cavium_ptp *clock =
18762306a36Sopenharmony_ci		container_of(ptp_info, struct cavium_ptp, ptp_info);
18862306a36Sopenharmony_ci	unsigned long flags;
18962306a36Sopenharmony_ci	u64 nsec;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	nsec = timespec64_to_ns(ts);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	spin_lock_irqsave(&clock->spin_lock, flags);
19462306a36Sopenharmony_ci	timecounter_init(&clock->time_counter, &clock->cycle_counter, nsec);
19562306a36Sopenharmony_ci	spin_unlock_irqrestore(&clock->spin_lock, flags);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/**
20162306a36Sopenharmony_ci * cavium_ptp_enable() - Request to enable or disable an ancillary feature.
20262306a36Sopenharmony_ci * @ptp_info: PTP clock info
20362306a36Sopenharmony_ci * @rq:  request
20462306a36Sopenharmony_ci * @on:  is it on
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_cistatic int cavium_ptp_enable(struct ptp_clock_info *ptp_info,
20762306a36Sopenharmony_ci			     struct ptp_clock_request *rq, int on)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	return -EOPNOTSUPP;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic u64 cavium_ptp_cc_read(const struct cyclecounter *cc)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct cavium_ptp *clock =
21562306a36Sopenharmony_ci		container_of(cc, struct cavium_ptp, cycle_counter);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return readq(clock->reg_base + PTP_CLOCK_HI);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int cavium_ptp_probe(struct pci_dev *pdev,
22162306a36Sopenharmony_ci			    const struct pci_device_id *ent)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
22462306a36Sopenharmony_ci	struct cavium_ptp *clock;
22562306a36Sopenharmony_ci	struct cyclecounter *cc;
22662306a36Sopenharmony_ci	u64 clock_cfg;
22762306a36Sopenharmony_ci	u64 clock_comp;
22862306a36Sopenharmony_ci	int err;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	clock = devm_kzalloc(dev, sizeof(*clock), GFP_KERNEL);
23162306a36Sopenharmony_ci	if (!clock) {
23262306a36Sopenharmony_ci		err = -ENOMEM;
23362306a36Sopenharmony_ci		goto error;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	clock->pdev = pdev;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	err = pcim_enable_device(pdev);
23962306a36Sopenharmony_ci	if (err)
24062306a36Sopenharmony_ci		goto error_free;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev));
24362306a36Sopenharmony_ci	if (err)
24462306a36Sopenharmony_ci		goto error_free;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	clock->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO];
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	spin_lock_init(&clock->spin_lock);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	cc = &clock->cycle_counter;
25162306a36Sopenharmony_ci	cc->read = cavium_ptp_cc_read;
25262306a36Sopenharmony_ci	cc->mask = CYCLECOUNTER_MASK(64);
25362306a36Sopenharmony_ci	cc->mult = 1;
25462306a36Sopenharmony_ci	cc->shift = 0;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	timecounter_init(&clock->time_counter, &clock->cycle_counter,
25762306a36Sopenharmony_ci			 ktime_to_ns(ktime_get_real()));
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	clock->clock_rate = ptp_cavium_clock_get();
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	clock->ptp_info = (struct ptp_clock_info) {
26262306a36Sopenharmony_ci		.owner		= THIS_MODULE,
26362306a36Sopenharmony_ci		.name		= "ThunderX PTP",
26462306a36Sopenharmony_ci		.max_adj	= 1000000000ull,
26562306a36Sopenharmony_ci		.n_ext_ts	= 0,
26662306a36Sopenharmony_ci		.n_pins		= 0,
26762306a36Sopenharmony_ci		.pps		= 0,
26862306a36Sopenharmony_ci		.adjfine	= cavium_ptp_adjfine,
26962306a36Sopenharmony_ci		.adjtime	= cavium_ptp_adjtime,
27062306a36Sopenharmony_ci		.gettime64	= cavium_ptp_gettime,
27162306a36Sopenharmony_ci		.settime64	= cavium_ptp_settime,
27262306a36Sopenharmony_ci		.enable		= cavium_ptp_enable,
27362306a36Sopenharmony_ci	};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
27662306a36Sopenharmony_ci	clock_cfg |= PTP_CLOCK_CFG_PTP_EN;
27762306a36Sopenharmony_ci	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	clock_comp = ((u64)1000000000ull << 32) / clock->clock_rate;
28062306a36Sopenharmony_ci	writeq(clock_comp, clock->reg_base + PTP_CLOCK_COMP);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	clock->ptp_clock = ptp_clock_register(&clock->ptp_info, dev);
28362306a36Sopenharmony_ci	if (IS_ERR(clock->ptp_clock)) {
28462306a36Sopenharmony_ci		err = PTR_ERR(clock->ptp_clock);
28562306a36Sopenharmony_ci		goto error_stop;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	pci_set_drvdata(pdev, clock);
28962306a36Sopenharmony_ci	return 0;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cierror_stop:
29262306a36Sopenharmony_ci	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
29362306a36Sopenharmony_ci	clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
29462306a36Sopenharmony_ci	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
29562306a36Sopenharmony_ci	pcim_iounmap_regions(pdev, 1 << PCI_PTP_BAR_NO);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cierror_free:
29862306a36Sopenharmony_ci	devm_kfree(dev, clock);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cierror:
30162306a36Sopenharmony_ci	/* For `cavium_ptp_get()` we need to differentiate between the case
30262306a36Sopenharmony_ci	 * when the core has not tried to probe this device and the case when
30362306a36Sopenharmony_ci	 * the probe failed.  In the later case we pretend that the
30462306a36Sopenharmony_ci	 * initialization was successful and keep the error in
30562306a36Sopenharmony_ci	 * `dev->driver_data`.
30662306a36Sopenharmony_ci	 */
30762306a36Sopenharmony_ci	pci_set_drvdata(pdev, ERR_PTR(err));
30862306a36Sopenharmony_ci	return 0;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void cavium_ptp_remove(struct pci_dev *pdev)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	struct cavium_ptp *clock = pci_get_drvdata(pdev);
31462306a36Sopenharmony_ci	u64 clock_cfg;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(clock))
31762306a36Sopenharmony_ci		return;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	ptp_clock_unregister(clock->ptp_clock);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
32262306a36Sopenharmony_ci	clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
32362306a36Sopenharmony_ci	writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic const struct pci_device_id cavium_ptp_id_table[] = {
32762306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP,
32862306a36Sopenharmony_ci			PCI_VENDOR_ID_CAVIUM, PCI_SUBSYS_DEVID_88XX_PTP) },
32962306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP,
33062306a36Sopenharmony_ci			PCI_VENDOR_ID_CAVIUM, PCI_SUBSYS_DEVID_81XX_PTP) },
33162306a36Sopenharmony_ci	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP,
33262306a36Sopenharmony_ci			PCI_VENDOR_ID_CAVIUM, PCI_SUBSYS_DEVID_83XX_PTP) },
33362306a36Sopenharmony_ci	{ 0, }
33462306a36Sopenharmony_ci};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic struct pci_driver cavium_ptp_driver = {
33762306a36Sopenharmony_ci	.name = DRV_NAME,
33862306a36Sopenharmony_ci	.id_table = cavium_ptp_id_table,
33962306a36Sopenharmony_ci	.probe = cavium_ptp_probe,
34062306a36Sopenharmony_ci	.remove = cavium_ptp_remove,
34162306a36Sopenharmony_ci};
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cimodule_pci_driver(cavium_ptp_driver);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_NAME);
34662306a36Sopenharmony_ciMODULE_AUTHOR("Cavium Networks <support@cavium.com>");
34762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
34862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cavium_ptp_id_table);
349