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.
6 #include <linux/device.h>
7 #include <linux/module.h>
8 #include <linux/timecounter.h>
11 #include "cavium_ptp.h"
13 #define DRV_NAME "cavium_ptp"
15 #define PCI_DEVICE_ID_CAVIUM_PTP 0xA00C
16 #define PCI_SUBSYS_DEVID_88XX_PTP 0xA10C
17 #define PCI_SUBSYS_DEVID_81XX_PTP 0XA20C
18 #define PCI_SUBSYS_DEVID_83XX_PTP 0xA30C
19 #define PCI_DEVICE_ID_CAVIUM_RST 0xA00E
21 #define PCI_PTP_BAR_NO 0
22 #define PCI_RST_BAR_NO 0
24 #define PTP_CLOCK_CFG 0xF00ULL
25 #define PTP_CLOCK_CFG_PTP_EN BIT(0)
26 #define PTP_CLOCK_LO 0xF08ULL
27 #define PTP_CLOCK_HI 0xF10ULL
28 #define PTP_CLOCK_COMP 0xF18ULL
30 #define RST_BOOT 0x1600ULL
31 #define CLOCK_BASE_RATE 50000000ULL
33 static u64
ptp_cavium_clock_get(void)
37 u64 ret
= CLOCK_BASE_RATE
* 16;
39 pdev
= pci_get_device(PCI_VENDOR_ID_CAVIUM
,
40 PCI_DEVICE_ID_CAVIUM_RST
, NULL
);
44 base
= pci_ioremap_bar(pdev
, PCI_RST_BAR_NO
);
48 ret
= CLOCK_BASE_RATE
* ((readq(base
+ RST_BOOT
) >> 33) & 0x3f);
59 struct cavium_ptp
*cavium_ptp_get(void)
61 struct cavium_ptp
*ptp
;
64 pdev
= pci_get_device(PCI_VENDOR_ID_CAVIUM
,
65 PCI_DEVICE_ID_CAVIUM_PTP
, NULL
);
67 return ERR_PTR(-ENODEV
);
69 ptp
= pci_get_drvdata(pdev
);
71 ptp
= ERR_PTR(-EPROBE_DEFER
);
77 EXPORT_SYMBOL(cavium_ptp_get
);
79 void cavium_ptp_put(struct cavium_ptp
*ptp
)
83 pci_dev_put(ptp
->pdev
);
85 EXPORT_SYMBOL(cavium_ptp_put
);
88 * cavium_ptp_adjfine() - Adjust ptp frequency
89 * @ptp_info: PTP clock info
90 * @scaled_ppm: how much to adjust by, in parts per million, but with a
91 * 16 bit binary fractional field
93 static int cavium_ptp_adjfine(struct ptp_clock_info
*ptp_info
, long scaled_ppm
)
95 struct cavium_ptp
*clock
=
96 container_of(ptp_info
, struct cavium_ptp
, ptp_info
);
100 bool neg_adj
= false;
102 if (scaled_ppm
< 0) {
104 scaled_ppm
= -scaled_ppm
;
107 /* The hardware adds the clock compensation value to the PTP clock
108 * on every coprocessor clock cycle. Typical convention is that it
109 * represent number of nanosecond betwen each cycle. In this
110 * convention compensation value is in 64 bit fixed-point
111 * representation where upper 32 bits are number of nanoseconds
112 * and lower is fractions of nanosecond.
113 * The scaled_ppm represent the ratio in "parts per bilion" by which the
114 * compensation value should be corrected.
115 * To calculate new compenstation value we use 64bit fixed point
116 * arithmetic on following formula
117 * comp = tbase + tbase * scaled_ppm / (1M * 2^16)
118 * where tbase is the basic compensation value calculated initialy
119 * in cavium_ptp_init() -> tbase = 1/Hz. Then we use endian
120 * independent structure definition to write data to PTP register.
122 comp
= ((u64
)1000000000ull << 32) / clock
->clock_rate
;
123 adj
= comp
* scaled_ppm
;
125 adj
= div_u64(adj
, 1000000ull);
126 comp
= neg_adj
? comp
- adj
: comp
+ adj
;
128 spin_lock_irqsave(&clock
->spin_lock
, flags
);
129 writeq(comp
, clock
->reg_base
+ PTP_CLOCK_COMP
);
130 spin_unlock_irqrestore(&clock
->spin_lock
, flags
);
136 * cavium_ptp_adjtime() - Adjust ptp time
137 * @ptp_info: PTP clock info
138 * @delta: how much to adjust by, in nanosecs
140 static int cavium_ptp_adjtime(struct ptp_clock_info
*ptp_info
, s64 delta
)
142 struct cavium_ptp
*clock
=
143 container_of(ptp_info
, struct cavium_ptp
, ptp_info
);
146 spin_lock_irqsave(&clock
->spin_lock
, flags
);
147 timecounter_adjtime(&clock
->time_counter
, delta
);
148 spin_unlock_irqrestore(&clock
->spin_lock
, flags
);
150 /* Sync, for network driver to get latest value */
157 * cavium_ptp_gettime() - Get hardware clock time with adjustment
158 * @ptp_info: PTP clock info
161 static int cavium_ptp_gettime(struct ptp_clock_info
*ptp_info
,
162 struct timespec64
*ts
)
164 struct cavium_ptp
*clock
=
165 container_of(ptp_info
, struct cavium_ptp
, ptp_info
);
169 spin_lock_irqsave(&clock
->spin_lock
, flags
);
170 nsec
= timecounter_read(&clock
->time_counter
);
171 spin_unlock_irqrestore(&clock
->spin_lock
, flags
);
173 *ts
= ns_to_timespec64(nsec
);
179 * cavium_ptp_settime() - Set hardware clock time. Reset adjustment
180 * @ptp_info: PTP clock info
183 static int cavium_ptp_settime(struct ptp_clock_info
*ptp_info
,
184 const struct timespec64
*ts
)
186 struct cavium_ptp
*clock
=
187 container_of(ptp_info
, struct cavium_ptp
, ptp_info
);
191 nsec
= timespec64_to_ns(ts
);
193 spin_lock_irqsave(&clock
->spin_lock
, flags
);
194 timecounter_init(&clock
->time_counter
, &clock
->cycle_counter
, nsec
);
195 spin_unlock_irqrestore(&clock
->spin_lock
, flags
);
201 * cavium_ptp_enable() - Request to enable or disable an ancillary feature.
202 * @ptp_info: PTP clock info
206 static int cavium_ptp_enable(struct ptp_clock_info
*ptp_info
,
207 struct ptp_clock_request
*rq
, int on
)
212 static u64
cavium_ptp_cc_read(const struct cyclecounter
*cc
)
214 struct cavium_ptp
*clock
=
215 container_of(cc
, struct cavium_ptp
, cycle_counter
);
217 return readq(clock
->reg_base
+ PTP_CLOCK_HI
);
220 static int cavium_ptp_probe(struct pci_dev
*pdev
,
221 const struct pci_device_id
*ent
)
223 struct device
*dev
= &pdev
->dev
;
224 struct cavium_ptp
*clock
;
225 struct cyclecounter
*cc
;
230 clock
= devm_kzalloc(dev
, sizeof(*clock
), GFP_KERNEL
);
238 err
= pcim_enable_device(pdev
);
242 err
= pcim_iomap_regions(pdev
, 1 << PCI_PTP_BAR_NO
, pci_name(pdev
));
246 clock
->reg_base
= pcim_iomap_table(pdev
)[PCI_PTP_BAR_NO
];
248 spin_lock_init(&clock
->spin_lock
);
250 cc
= &clock
->cycle_counter
;
251 cc
->read
= cavium_ptp_cc_read
;
252 cc
->mask
= CYCLECOUNTER_MASK(64);
256 timecounter_init(&clock
->time_counter
, &clock
->cycle_counter
,
257 ktime_to_ns(ktime_get_real()));
259 clock
->clock_rate
= ptp_cavium_clock_get();
261 clock
->ptp_info
= (struct ptp_clock_info
) {
262 .owner
= THIS_MODULE
,
263 .name
= "ThunderX PTP",
264 .max_adj
= 1000000000ull,
268 .adjfine
= cavium_ptp_adjfine
,
269 .adjtime
= cavium_ptp_adjtime
,
270 .gettime64
= cavium_ptp_gettime
,
271 .settime64
= cavium_ptp_settime
,
272 .enable
= cavium_ptp_enable
,
275 clock_cfg
= readq(clock
->reg_base
+ PTP_CLOCK_CFG
);
276 clock_cfg
|= PTP_CLOCK_CFG_PTP_EN
;
277 writeq(clock_cfg
, clock
->reg_base
+ PTP_CLOCK_CFG
);
279 clock_comp
= ((u64
)1000000000ull << 32) / clock
->clock_rate
;
280 writeq(clock_comp
, clock
->reg_base
+ PTP_CLOCK_COMP
);
282 clock
->ptp_clock
= ptp_clock_register(&clock
->ptp_info
, dev
);
283 if (IS_ERR(clock
->ptp_clock
)) {
284 err
= PTR_ERR(clock
->ptp_clock
);
288 pci_set_drvdata(pdev
, clock
);
292 clock_cfg
= readq(clock
->reg_base
+ PTP_CLOCK_CFG
);
293 clock_cfg
&= ~PTP_CLOCK_CFG_PTP_EN
;
294 writeq(clock_cfg
, clock
->reg_base
+ PTP_CLOCK_CFG
);
295 pcim_iounmap_regions(pdev
, 1 << PCI_PTP_BAR_NO
);
298 devm_kfree(dev
, clock
);
301 /* For `cavium_ptp_get()` we need to differentiate between the case
302 * when the core has not tried to probe this device and the case when
303 * the probe failed. In the later case we pretend that the
304 * initialization was successful and keep the error in
305 * `dev->driver_data`.
307 pci_set_drvdata(pdev
, ERR_PTR(err
));
311 static void cavium_ptp_remove(struct pci_dev
*pdev
)
313 struct cavium_ptp
*clock
= pci_get_drvdata(pdev
);
316 if (IS_ERR_OR_NULL(clock
))
319 ptp_clock_unregister(clock
->ptp_clock
);
321 clock_cfg
= readq(clock
->reg_base
+ PTP_CLOCK_CFG
);
322 clock_cfg
&= ~PTP_CLOCK_CFG_PTP_EN
;
323 writeq(clock_cfg
, clock
->reg_base
+ PTP_CLOCK_CFG
);
326 static const struct pci_device_id cavium_ptp_id_table
[] = {
327 { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM
, PCI_DEVICE_ID_CAVIUM_PTP
,
328 PCI_VENDOR_ID_CAVIUM
, PCI_SUBSYS_DEVID_88XX_PTP
) },
329 { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM
, PCI_DEVICE_ID_CAVIUM_PTP
,
330 PCI_VENDOR_ID_CAVIUM
, PCI_SUBSYS_DEVID_81XX_PTP
) },
331 { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM
, PCI_DEVICE_ID_CAVIUM_PTP
,
332 PCI_VENDOR_ID_CAVIUM
, PCI_SUBSYS_DEVID_83XX_PTP
) },
336 static struct pci_driver cavium_ptp_driver
= {
338 .id_table
= cavium_ptp_id_table
,
339 .probe
= cavium_ptp_probe
,
340 .remove
= cavium_ptp_remove
,
343 module_pci_driver(cavium_ptp_driver
);
345 MODULE_DESCRIPTION(DRV_NAME
);
346 MODULE_AUTHOR("Cavium Networks <support@cavium.com>");
347 MODULE_LICENSE("GPL v2");
348 MODULE_DEVICE_TABLE(pci
, cavium_ptp_id_table
);