Skip to content

Commit 3b039b4

Browse files
committed
Merge branch 'net-thunderx-add-support-for-PTP-clock'
Aleksey Makarov says: ==================== net: thunderx: add support for PTP clock This series adds support for IEEE 1588 Precision Time Protocol to Cavium ethernet driver. The first patch adds support for the Precision Time Protocol Clocks and Timestamping coprocessor (PTP) found on Cavium processors. It registers a new PTP clock in the PTP core and provides functions to use the counter in BGX, TNS, GTI, and NIC blocks. The second patch introduces support for the PTP protocol to the Cavium ThunderX ethernet driver. v6: - check if ptp_clock_register() returns NULL (Richard Cochran) - fix doc comment for cavium_ptp_enable() (Richard Cochran) - fix a function call formatting; use defined constant (Richard Cochran) - add comments for `tx_ptp_skbs` and `ptp_skb` (Richard Cochran) - use adjfine() instead of adjfreq() (Richard Cochran) - add Acked-by: Philippe Ombredanne <[email protected]> v5: https://lkml.kernel.org/r/[email protected] - fix the file headers (add SPDX tags, remove advertisment) (Philippe Ombredanne) - use "imply" instead of "select" (Richard Cochran) - add some code in cavium_ptp_get() for the case when the PTP driver has not been registered with the PTP core v4: https://lkml.kernel.org/r/[email protected] - use IS_ENABLED. This fixes compilation of the ptp as a module (David Miller) - select PTP_1588_CLOCK, not depend on it. This fixes a build warning. - change u64 to __be64. This fixes the sparse warning "warning: cast to restricted __be64" - make nicvf_config_hwtstamp() static. This fixes the sparse warning "warning: symbol 'nicvf_config_hwtstamp' was not declared. Should it be static?" v3: https://lkml.kernel.org/r/[email protected] - rebase to net-next v2: https://lkml.kernel.org/r/[email protected] - use readq()/writeq() in place of cavium_ptp_reg_read()/cavium_ptp_reg_write(), don't use readq_relaxed()/writeq_relaxed() (David Daney) v1: https://lkml.kernel.org/r/[email protected] ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents e02f08a + 4a87550 commit 3b039b4

File tree

13 files changed

+782
-6
lines changed

13 files changed

+782
-6
lines changed

drivers/net/ethernet/cavium/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ config THUNDER_NIC_PF
2727

2828
config THUNDER_NIC_VF
2929
tristate "Thunder Virtual function driver"
30+
imply CAVIUM_PTP
3031
depends on 64BIT
3132
---help---
3233
This driver supports Thunder's NIC virtual function
@@ -50,6 +51,18 @@ config THUNDER_NIC_RGX
5051
This driver supports configuring XCV block of RGX interface
5152
present on CN81XX chip.
5253

54+
config CAVIUM_PTP
55+
tristate "Cavium PTP coprocessor as PTP clock"
56+
depends on 64BIT
57+
imply PTP_1588_CLOCK
58+
default y
59+
---help---
60+
This driver adds support for the Precision Time Protocol Clocks and
61+
Timestamping coprocessor (PTP) found on Cavium processors.
62+
PTP provides timestamping mechanism that is suitable for use in IEEE 1588
63+
Precision Time Protocol or other purposes. Timestamps can be used in
64+
BGX, TNS, GTI, and NIC blocks.
65+
5366
config LIQUIDIO
5467
tristate "Cavium LiquidIO support"
5568
depends on 64BIT

drivers/net/ethernet/cavium/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#
22
# Makefile for the Cavium ethernet device drivers.
33
#
4+
obj-$(CONFIG_NET_VENDOR_CAVIUM) += common/
45
obj-$(CONFIG_NET_VENDOR_CAVIUM) += thunder/
56
obj-$(CONFIG_NET_VENDOR_CAVIUM) += liquidio/
67
obj-$(CONFIG_NET_VENDOR_CAVIUM) += octeon/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
obj-$(CONFIG_CAVIUM_PTP) += cavium_ptp.o
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* cavium_ptp.c - PTP 1588 clock on Cavium hardware
3+
* Copyright (c) 2003-2015, 2017 Cavium, Inc.
4+
*/
5+
6+
#include <linux/device.h>
7+
#include <linux/module.h>
8+
#include <linux/timecounter.h>
9+
#include <linux/pci.h>
10+
11+
#include "cavium_ptp.h"
12+
13+
#define DRV_NAME "Cavium PTP Driver"
14+
15+
#define PCI_DEVICE_ID_CAVIUM_PTP 0xA00C
16+
#define PCI_DEVICE_ID_CAVIUM_RST 0xA00E
17+
18+
#define PCI_PTP_BAR_NO 0
19+
#define PCI_RST_BAR_NO 0
20+
21+
#define PTP_CLOCK_CFG 0xF00ULL
22+
#define PTP_CLOCK_CFG_PTP_EN BIT(0)
23+
#define PTP_CLOCK_LO 0xF08ULL
24+
#define PTP_CLOCK_HI 0xF10ULL
25+
#define PTP_CLOCK_COMP 0xF18ULL
26+
27+
#define RST_BOOT 0x1600ULL
28+
#define CLOCK_BASE_RATE 50000000ULL
29+
30+
static u64 ptp_cavium_clock_get(void)
31+
{
32+
struct pci_dev *pdev;
33+
void __iomem *base;
34+
u64 ret = CLOCK_BASE_RATE * 16;
35+
36+
pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
37+
PCI_DEVICE_ID_CAVIUM_RST, NULL);
38+
if (!pdev)
39+
goto error;
40+
41+
base = pci_ioremap_bar(pdev, PCI_RST_BAR_NO);
42+
if (!base)
43+
goto error_put_pdev;
44+
45+
ret = CLOCK_BASE_RATE * ((readq(base + RST_BOOT) >> 33) & 0x3f);
46+
47+
iounmap(base);
48+
49+
error_put_pdev:
50+
pci_dev_put(pdev);
51+
52+
error:
53+
return ret;
54+
}
55+
56+
struct cavium_ptp *cavium_ptp_get(void)
57+
{
58+
struct cavium_ptp *ptp;
59+
struct pci_dev *pdev;
60+
61+
pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
62+
PCI_DEVICE_ID_CAVIUM_PTP, NULL);
63+
if (!pdev)
64+
return ERR_PTR(-ENODEV);
65+
66+
ptp = pci_get_drvdata(pdev);
67+
if (!ptp)
68+
ptp = ERR_PTR(-EPROBE_DEFER);
69+
if (IS_ERR(ptp))
70+
pci_dev_put(pdev);
71+
72+
return ptp;
73+
}
74+
EXPORT_SYMBOL(cavium_ptp_get);
75+
76+
void cavium_ptp_put(struct cavium_ptp *ptp)
77+
{
78+
pci_dev_put(ptp->pdev);
79+
}
80+
EXPORT_SYMBOL(cavium_ptp_put);
81+
82+
/**
83+
* cavium_ptp_adjfine() - Adjust ptp frequency
84+
* @ptp: PTP clock info
85+
* @scaled_ppm: how much to adjust by, in parts per million, but with a
86+
* 16 bit binary fractional field
87+
*/
88+
static int cavium_ptp_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
89+
{
90+
struct cavium_ptp *clock =
91+
container_of(ptp_info, struct cavium_ptp, ptp_info);
92+
unsigned long flags;
93+
u64 comp;
94+
u64 adj;
95+
bool neg_adj = false;
96+
97+
if (scaled_ppm < 0) {
98+
neg_adj = true;
99+
scaled_ppm = -scaled_ppm;
100+
}
101+
102+
/* The hardware adds the clock compensation value to the PTP clock
103+
* on every coprocessor clock cycle. Typical convention is that it
104+
* represent number of nanosecond betwen each cycle. In this
105+
* convention compensation value is in 64 bit fixed-point
106+
* representation where upper 32 bits are number of nanoseconds
107+
* and lower is fractions of nanosecond.
108+
* The scaled_ppm represent the ratio in "parts per bilion" by which the
109+
* compensation value should be corrected.
110+
* To calculate new compenstation value we use 64bit fixed point
111+
* arithmetic on following formula
112+
* comp = tbase + tbase * scaled_ppm / (1M * 2^16)
113+
* where tbase is the basic compensation value calculated initialy
114+
* in cavium_ptp_init() -> tbase = 1/Hz. Then we use endian
115+
* independent structure definition to write data to PTP register.
116+
*/
117+
comp = ((u64)1000000000ull << 32) / clock->clock_rate;
118+
adj = comp * scaled_ppm;
119+
adj >>= 16;
120+
adj = div_u64(adj, 1000000ull);
121+
comp = neg_adj ? comp - adj : comp + adj;
122+
123+
spin_lock_irqsave(&clock->spin_lock, flags);
124+
writeq(comp, clock->reg_base + PTP_CLOCK_COMP);
125+
spin_unlock_irqrestore(&clock->spin_lock, flags);
126+
127+
return 0;
128+
}
129+
130+
/**
131+
* cavium_ptp_adjtime() - Adjust ptp time
132+
* @ptp: PTP clock info
133+
* @delta: how much to adjust by, in nanosecs
134+
*/
135+
static int cavium_ptp_adjtime(struct ptp_clock_info *ptp_info, s64 delta)
136+
{
137+
struct cavium_ptp *clock =
138+
container_of(ptp_info, struct cavium_ptp, ptp_info);
139+
unsigned long flags;
140+
141+
spin_lock_irqsave(&clock->spin_lock, flags);
142+
timecounter_adjtime(&clock->time_counter, delta);
143+
spin_unlock_irqrestore(&clock->spin_lock, flags);
144+
145+
/* Sync, for network driver to get latest value */
146+
smp_mb();
147+
148+
return 0;
149+
}
150+
151+
/**
152+
* cavium_ptp_gettime() - Get hardware clock time with adjustment
153+
* @ptp: PTP clock info
154+
* @ts: timespec
155+
*/
156+
static int cavium_ptp_gettime(struct ptp_clock_info *ptp_info,
157+
struct timespec64 *ts)
158+
{
159+
struct cavium_ptp *clock =
160+
container_of(ptp_info, struct cavium_ptp, ptp_info);
161+
unsigned long flags;
162+
u64 nsec;
163+
164+
spin_lock_irqsave(&clock->spin_lock, flags);
165+
nsec = timecounter_read(&clock->time_counter);
166+
spin_unlock_irqrestore(&clock->spin_lock, flags);
167+
168+
*ts = ns_to_timespec64(nsec);
169+
170+
return 0;
171+
}
172+
173+
/**
174+
* cavium_ptp_settime() - Set hardware clock time. Reset adjustment
175+
* @ptp: PTP clock info
176+
* @ts: timespec
177+
*/
178+
static int cavium_ptp_settime(struct ptp_clock_info *ptp_info,
179+
const struct timespec64 *ts)
180+
{
181+
struct cavium_ptp *clock =
182+
container_of(ptp_info, struct cavium_ptp, ptp_info);
183+
unsigned long flags;
184+
u64 nsec;
185+
186+
nsec = timespec64_to_ns(ts);
187+
188+
spin_lock_irqsave(&clock->spin_lock, flags);
189+
timecounter_init(&clock->time_counter, &clock->cycle_counter, nsec);
190+
spin_unlock_irqrestore(&clock->spin_lock, flags);
191+
192+
return 0;
193+
}
194+
195+
/**
196+
* cavium_ptp_enable() - Request to enable or disable an ancillary feature.
197+
* @ptp: PTP clock info
198+
* @rq: request
199+
* @on: is it on
200+
*/
201+
static int cavium_ptp_enable(struct ptp_clock_info *ptp_info,
202+
struct ptp_clock_request *rq, int on)
203+
{
204+
return -EOPNOTSUPP;
205+
}
206+
207+
static u64 cavium_ptp_cc_read(const struct cyclecounter *cc)
208+
{
209+
struct cavium_ptp *clock =
210+
container_of(cc, struct cavium_ptp, cycle_counter);
211+
212+
return readq(clock->reg_base + PTP_CLOCK_HI);
213+
}
214+
215+
static int cavium_ptp_probe(struct pci_dev *pdev,
216+
const struct pci_device_id *ent)
217+
{
218+
struct device *dev = &pdev->dev;
219+
struct cavium_ptp *clock;
220+
struct cyclecounter *cc;
221+
u64 clock_cfg;
222+
u64 clock_comp;
223+
int err;
224+
225+
clock = devm_kzalloc(dev, sizeof(*clock), GFP_KERNEL);
226+
if (!clock) {
227+
err = -ENOMEM;
228+
goto error;
229+
}
230+
231+
clock->pdev = pdev;
232+
233+
err = pcim_enable_device(pdev);
234+
if (err)
235+
goto error_free;
236+
237+
err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev));
238+
if (err)
239+
goto error_free;
240+
241+
clock->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO];
242+
243+
spin_lock_init(&clock->spin_lock);
244+
245+
cc = &clock->cycle_counter;
246+
cc->read = cavium_ptp_cc_read;
247+
cc->mask = CYCLECOUNTER_MASK(64);
248+
cc->mult = 1;
249+
cc->shift = 0;
250+
251+
timecounter_init(&clock->time_counter, &clock->cycle_counter,
252+
ktime_to_ns(ktime_get_real()));
253+
254+
clock->clock_rate = ptp_cavium_clock_get();
255+
256+
clock->ptp_info = (struct ptp_clock_info) {
257+
.owner = THIS_MODULE,
258+
.name = "ThunderX PTP",
259+
.max_adj = 1000000000ull,
260+
.n_ext_ts = 0,
261+
.n_pins = 0,
262+
.pps = 0,
263+
.adjfine = cavium_ptp_adjfine,
264+
.adjtime = cavium_ptp_adjtime,
265+
.gettime64 = cavium_ptp_gettime,
266+
.settime64 = cavium_ptp_settime,
267+
.enable = cavium_ptp_enable,
268+
};
269+
270+
clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
271+
clock_cfg |= PTP_CLOCK_CFG_PTP_EN;
272+
writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
273+
274+
clock_comp = ((u64)1000000000ull << 32) / clock->clock_rate;
275+
writeq(clock_comp, clock->reg_base + PTP_CLOCK_COMP);
276+
277+
clock->ptp_clock = ptp_clock_register(&clock->ptp_info, dev);
278+
if (!clock->ptp_clock) {
279+
err = -ENODEV;
280+
goto error_stop;
281+
}
282+
if (IS_ERR(clock->ptp_clock)) {
283+
err = PTR_ERR(clock->ptp_clock);
284+
goto error_stop;
285+
}
286+
287+
pci_set_drvdata(pdev, clock);
288+
return 0;
289+
290+
error_stop:
291+
clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
292+
clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
293+
writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
294+
pcim_iounmap_regions(pdev, 1 << PCI_PTP_BAR_NO);
295+
296+
error_free:
297+
devm_kfree(dev, clock);
298+
299+
error:
300+
/* For `cavium_ptp_get()` we need to differentiate between the case
301+
* when the core has not tried to probe this device and the case when
302+
* the probe failed. In the later case we pretend that the
303+
* initialization was successful and keep the error in
304+
* `dev->driver_data`.
305+
*/
306+
pci_set_drvdata(pdev, ERR_PTR(err));
307+
return 0;
308+
}
309+
310+
static void cavium_ptp_remove(struct pci_dev *pdev)
311+
{
312+
struct cavium_ptp *clock = pci_get_drvdata(pdev);
313+
u64 clock_cfg;
314+
315+
if (IS_ERR_OR_NULL(clock))
316+
return;
317+
318+
ptp_clock_unregister(clock->ptp_clock);
319+
320+
clock_cfg = readq(clock->reg_base + PTP_CLOCK_CFG);
321+
clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
322+
writeq(clock_cfg, clock->reg_base + PTP_CLOCK_CFG);
323+
}
324+
325+
static const struct pci_device_id cavium_ptp_id_table[] = {
326+
{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_CAVIUM_PTP) },
327+
{ 0, }
328+
};
329+
330+
static struct pci_driver cavium_ptp_driver = {
331+
.name = DRV_NAME,
332+
.id_table = cavium_ptp_id_table,
333+
.probe = cavium_ptp_probe,
334+
.remove = cavium_ptp_remove,
335+
};
336+
337+
static int __init cavium_ptp_init_module(void)
338+
{
339+
return pci_register_driver(&cavium_ptp_driver);
340+
}
341+
342+
static void __exit cavium_ptp_cleanup_module(void)
343+
{
344+
pci_unregister_driver(&cavium_ptp_driver);
345+
}
346+
347+
module_init(cavium_ptp_init_module);
348+
module_exit(cavium_ptp_cleanup_module);
349+
350+
MODULE_DESCRIPTION(DRV_NAME);
351+
MODULE_AUTHOR("Cavium Networks <[email protected]>");
352+
MODULE_LICENSE("GPL v2");
353+
MODULE_DEVICE_TABLE(pci, cavium_ptp_id_table);

0 commit comments

Comments
 (0)