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