xref: /openbmc/linux/drivers/ptp/ptp_dfl_tod.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1*615927f1STianfei Zhang // SPDX-License-Identifier: GPL-2.0-only
2*615927f1STianfei Zhang /*
3*615927f1STianfei Zhang  * DFL device driver for Time-of-Day (ToD) private feature
4*615927f1STianfei Zhang  *
5*615927f1STianfei Zhang  * Copyright (C) 2023 Intel Corporation
6*615927f1STianfei Zhang  */
7*615927f1STianfei Zhang 
8*615927f1STianfei Zhang #include <linux/bitfield.h>
9*615927f1STianfei Zhang #include <linux/delay.h>
10*615927f1STianfei Zhang #include <linux/dfl.h>
11*615927f1STianfei Zhang #include <linux/gcd.h>
12*615927f1STianfei Zhang #include <linux/iopoll.h>
13*615927f1STianfei Zhang #include <linux/module.h>
14*615927f1STianfei Zhang #include <linux/ptp_clock_kernel.h>
15*615927f1STianfei Zhang #include <linux/spinlock.h>
16*615927f1STianfei Zhang #include <linux/units.h>
17*615927f1STianfei Zhang 
18*615927f1STianfei Zhang #define FME_FEATURE_ID_TOD		0x22
19*615927f1STianfei Zhang 
20*615927f1STianfei Zhang /* ToD clock register space. */
21*615927f1STianfei Zhang #define TOD_CLK_FREQ			0x038
22*615927f1STianfei Zhang 
23*615927f1STianfei Zhang /*
24*615927f1STianfei Zhang  * The read sequence of ToD timestamp registers: TOD_NANOSEC, TOD_SECONDSL and
25*615927f1STianfei Zhang  * TOD_SECONDSH, because there is a hardware snapshot whenever the TOD_NANOSEC
26*615927f1STianfei Zhang  * register is read.
27*615927f1STianfei Zhang  *
28*615927f1STianfei Zhang  * The ToD IP requires writing registers in the reverse order to the read sequence.
29*615927f1STianfei Zhang  * The timestamp is corrected when the TOD_NANOSEC register is written, so the
30*615927f1STianfei Zhang  * sequence of write TOD registers: TOD_SECONDSH, TOD_SECONDSL and TOD_NANOSEC.
31*615927f1STianfei Zhang  */
32*615927f1STianfei Zhang #define TOD_SECONDSH			0x100
33*615927f1STianfei Zhang #define TOD_SECONDSL			0x104
34*615927f1STianfei Zhang #define TOD_NANOSEC			0x108
35*615927f1STianfei Zhang #define TOD_PERIOD			0x110
36*615927f1STianfei Zhang #define TOD_ADJUST_PERIOD		0x114
37*615927f1STianfei Zhang #define TOD_ADJUST_COUNT		0x118
38*615927f1STianfei Zhang #define TOD_DRIFT_ADJUST		0x11c
39*615927f1STianfei Zhang #define TOD_DRIFT_ADJUST_RATE		0x120
40*615927f1STianfei Zhang #define PERIOD_FRAC_OFFSET		16
41*615927f1STianfei Zhang #define SECONDS_MSB			GENMASK_ULL(47, 32)
42*615927f1STianfei Zhang #define SECONDS_LSB			GENMASK_ULL(31, 0)
43*615927f1STianfei Zhang #define TOD_SECONDSH_SEC_MSB		GENMASK_ULL(15, 0)
44*615927f1STianfei Zhang 
45*615927f1STianfei Zhang #define CAL_SECONDS(m, l)		((FIELD_GET(TOD_SECONDSH_SEC_MSB, (m)) << 32) | (l))
46*615927f1STianfei Zhang 
47*615927f1STianfei Zhang #define TOD_PERIOD_MASK		GENMASK_ULL(19, 0)
48*615927f1STianfei Zhang #define TOD_PERIOD_MAX			FIELD_MAX(TOD_PERIOD_MASK)
49*615927f1STianfei Zhang #define TOD_PERIOD_MIN			0
50*615927f1STianfei Zhang #define TOD_DRIFT_ADJUST_MASK		GENMASK_ULL(15, 0)
51*615927f1STianfei Zhang #define TOD_DRIFT_ADJUST_FNS_MAX	FIELD_MAX(TOD_DRIFT_ADJUST_MASK)
52*615927f1STianfei Zhang #define TOD_DRIFT_ADJUST_RATE_MAX	TOD_DRIFT_ADJUST_FNS_MAX
53*615927f1STianfei Zhang #define TOD_ADJUST_COUNT_MASK		GENMASK_ULL(19, 0)
54*615927f1STianfei Zhang #define TOD_ADJUST_COUNT_MAX		FIELD_MAX(TOD_ADJUST_COUNT_MASK)
55*615927f1STianfei Zhang #define TOD_ADJUST_INTERVAL_US		10
56*615927f1STianfei Zhang #define TOD_ADJUST_MS			\
57*615927f1STianfei Zhang 		(((TOD_PERIOD_MAX >> 16) + 1) * (TOD_ADJUST_COUNT_MAX + 1))
58*615927f1STianfei Zhang #define TOD_ADJUST_MS_MAX		(TOD_ADJUST_MS / MICRO)
59*615927f1STianfei Zhang #define TOD_ADJUST_MAX_US		(TOD_ADJUST_MS_MAX * USEC_PER_MSEC)
60*615927f1STianfei Zhang #define TOD_MAX_ADJ			(500 * MEGA)
61*615927f1STianfei Zhang 
62*615927f1STianfei Zhang struct dfl_tod {
63*615927f1STianfei Zhang 	struct ptp_clock_info ptp_clock_ops;
64*615927f1STianfei Zhang 	struct device *dev;
65*615927f1STianfei Zhang 	struct ptp_clock *ptp_clock;
66*615927f1STianfei Zhang 
67*615927f1STianfei Zhang 	/* ToD Clock address space */
68*615927f1STianfei Zhang 	void __iomem *tod_ctrl;
69*615927f1STianfei Zhang 
70*615927f1STianfei Zhang 	/* ToD clock registers protection */
71*615927f1STianfei Zhang 	spinlock_t tod_lock;
72*615927f1STianfei Zhang };
73*615927f1STianfei Zhang 
74*615927f1STianfei Zhang /*
75*615927f1STianfei Zhang  * A fine ToD HW clock offset adjustment. To perform the fine offset adjustment, the
76*615927f1STianfei Zhang  * adjust_period and adjust_count argument are used to update the TOD_ADJUST_PERIOD
77*615927f1STianfei Zhang  * and TOD_ADJUST_COUNT register for in hardware. The dt->tod_lock spinlock must be
78*615927f1STianfei Zhang  * held when calling this function.
79*615927f1STianfei Zhang  */
fine_adjust_tod_clock(struct dfl_tod * dt,u32 adjust_period,u32 adjust_count)80*615927f1STianfei Zhang static int fine_adjust_tod_clock(struct dfl_tod *dt, u32 adjust_period,
81*615927f1STianfei Zhang 				 u32 adjust_count)
82*615927f1STianfei Zhang {
83*615927f1STianfei Zhang 	void __iomem *base = dt->tod_ctrl;
84*615927f1STianfei Zhang 	u32 val;
85*615927f1STianfei Zhang 
86*615927f1STianfei Zhang 	writel(adjust_period, base + TOD_ADJUST_PERIOD);
87*615927f1STianfei Zhang 	writel(adjust_count, base + TOD_ADJUST_COUNT);
88*615927f1STianfei Zhang 
89*615927f1STianfei Zhang 	/* Wait for present offset adjustment update to complete */
90*615927f1STianfei Zhang 	return readl_poll_timeout_atomic(base + TOD_ADJUST_COUNT, val, !val, TOD_ADJUST_INTERVAL_US,
91*615927f1STianfei Zhang 				  TOD_ADJUST_MAX_US);
92*615927f1STianfei Zhang }
93*615927f1STianfei Zhang 
94*615927f1STianfei Zhang /*
95*615927f1STianfei Zhang  * A coarse ToD HW clock offset adjustment. The coarse time adjustment performs by
96*615927f1STianfei Zhang  * adding or subtracting the delta value from the current ToD HW clock time.
97*615927f1STianfei Zhang  */
coarse_adjust_tod_clock(struct dfl_tod * dt,s64 delta)98*615927f1STianfei Zhang static int coarse_adjust_tod_clock(struct dfl_tod *dt, s64 delta)
99*615927f1STianfei Zhang {
100*615927f1STianfei Zhang 	u32 seconds_msb, seconds_lsb, nanosec;
101*615927f1STianfei Zhang 	void __iomem *base = dt->tod_ctrl;
102*615927f1STianfei Zhang 	u64 seconds, now;
103*615927f1STianfei Zhang 
104*615927f1STianfei Zhang 	if (delta == 0)
105*615927f1STianfei Zhang 		return 0;
106*615927f1STianfei Zhang 
107*615927f1STianfei Zhang 	nanosec = readl(base + TOD_NANOSEC);
108*615927f1STianfei Zhang 	seconds_lsb = readl(base + TOD_SECONDSL);
109*615927f1STianfei Zhang 	seconds_msb = readl(base + TOD_SECONDSH);
110*615927f1STianfei Zhang 
111*615927f1STianfei Zhang 	/* Calculate new time */
112*615927f1STianfei Zhang 	seconds = CAL_SECONDS(seconds_msb, seconds_lsb);
113*615927f1STianfei Zhang 	now = seconds * NSEC_PER_SEC + nanosec + delta;
114*615927f1STianfei Zhang 
115*615927f1STianfei Zhang 	seconds = div_u64_rem(now, NSEC_PER_SEC, &nanosec);
116*615927f1STianfei Zhang 	seconds_msb = FIELD_GET(SECONDS_MSB, seconds);
117*615927f1STianfei Zhang 	seconds_lsb = FIELD_GET(SECONDS_LSB, seconds);
118*615927f1STianfei Zhang 
119*615927f1STianfei Zhang 	writel(seconds_msb, base + TOD_SECONDSH);
120*615927f1STianfei Zhang 	writel(seconds_lsb, base + TOD_SECONDSL);
121*615927f1STianfei Zhang 	writel(nanosec, base + TOD_NANOSEC);
122*615927f1STianfei Zhang 
123*615927f1STianfei Zhang 	return 0;
124*615927f1STianfei Zhang }
125*615927f1STianfei Zhang 
dfl_tod_adjust_fine(struct ptp_clock_info * ptp,long scaled_ppm)126*615927f1STianfei Zhang static int dfl_tod_adjust_fine(struct ptp_clock_info *ptp, long scaled_ppm)
127*615927f1STianfei Zhang {
128*615927f1STianfei Zhang 	struct dfl_tod *dt = container_of(ptp, struct dfl_tod, ptp_clock_ops);
129*615927f1STianfei Zhang 	u32 tod_period, tod_rem, tod_drift_adjust_fns, tod_drift_adjust_rate;
130*615927f1STianfei Zhang 	void __iomem *base = dt->tod_ctrl;
131*615927f1STianfei Zhang 	unsigned long flags, rate;
132*615927f1STianfei Zhang 	u64 ppb;
133*615927f1STianfei Zhang 
134*615927f1STianfei Zhang 	/* Get the clock rate from clock frequency register offset */
135*615927f1STianfei Zhang 	rate = readl(base + TOD_CLK_FREQ);
136*615927f1STianfei Zhang 
137*615927f1STianfei Zhang 	/* add GIGA as nominal ppb */
138*615927f1STianfei Zhang 	ppb = scaled_ppm_to_ppb(scaled_ppm) + GIGA;
139*615927f1STianfei Zhang 
140*615927f1STianfei Zhang 	tod_period = div_u64_rem(ppb << PERIOD_FRAC_OFFSET, rate, &tod_rem);
141*615927f1STianfei Zhang 	if (tod_period > TOD_PERIOD_MAX)
142*615927f1STianfei Zhang 		return -ERANGE;
143*615927f1STianfei Zhang 
144*615927f1STianfei Zhang 	/*
145*615927f1STianfei Zhang 	 * The drift of ToD adjusted periodically by adding a drift_adjust_fns
146*615927f1STianfei Zhang 	 * correction value every drift_adjust_rate count of clock cycles.
147*615927f1STianfei Zhang 	 */
148*615927f1STianfei Zhang 	tod_drift_adjust_fns = tod_rem / gcd(tod_rem, rate);
149*615927f1STianfei Zhang 	tod_drift_adjust_rate = rate / gcd(tod_rem, rate);
150*615927f1STianfei Zhang 
151*615927f1STianfei Zhang 	while ((tod_drift_adjust_fns > TOD_DRIFT_ADJUST_FNS_MAX) ||
152*615927f1STianfei Zhang 	       (tod_drift_adjust_rate > TOD_DRIFT_ADJUST_RATE_MAX)) {
153*615927f1STianfei Zhang 		tod_drift_adjust_fns >>= 1;
154*615927f1STianfei Zhang 		tod_drift_adjust_rate >>= 1;
155*615927f1STianfei Zhang 	}
156*615927f1STianfei Zhang 
157*615927f1STianfei Zhang 	if (tod_drift_adjust_fns == 0)
158*615927f1STianfei Zhang 		tod_drift_adjust_rate = 0;
159*615927f1STianfei Zhang 
160*615927f1STianfei Zhang 	spin_lock_irqsave(&dt->tod_lock, flags);
161*615927f1STianfei Zhang 	writel(tod_period, base + TOD_PERIOD);
162*615927f1STianfei Zhang 	writel(0, base + TOD_ADJUST_PERIOD);
163*615927f1STianfei Zhang 	writel(0, base + TOD_ADJUST_COUNT);
164*615927f1STianfei Zhang 	writel(tod_drift_adjust_fns, base + TOD_DRIFT_ADJUST);
165*615927f1STianfei Zhang 	writel(tod_drift_adjust_rate, base + TOD_DRIFT_ADJUST_RATE);
166*615927f1STianfei Zhang 	spin_unlock_irqrestore(&dt->tod_lock, flags);
167*615927f1STianfei Zhang 
168*615927f1STianfei Zhang 	return 0;
169*615927f1STianfei Zhang }
170*615927f1STianfei Zhang 
dfl_tod_adjust_time(struct ptp_clock_info * ptp,s64 delta)171*615927f1STianfei Zhang static int dfl_tod_adjust_time(struct ptp_clock_info *ptp, s64 delta)
172*615927f1STianfei Zhang {
173*615927f1STianfei Zhang 	struct dfl_tod *dt = container_of(ptp, struct dfl_tod, ptp_clock_ops);
174*615927f1STianfei Zhang 	u32 period, diff, rem, rem_period, adj_period;
175*615927f1STianfei Zhang 	void __iomem *base = dt->tod_ctrl;
176*615927f1STianfei Zhang 	unsigned long flags;
177*615927f1STianfei Zhang 	bool neg_adj;
178*615927f1STianfei Zhang 	u64 count;
179*615927f1STianfei Zhang 	int ret;
180*615927f1STianfei Zhang 
181*615927f1STianfei Zhang 	neg_adj = delta < 0;
182*615927f1STianfei Zhang 	if (neg_adj)
183*615927f1STianfei Zhang 		delta = -delta;
184*615927f1STianfei Zhang 
185*615927f1STianfei Zhang 	spin_lock_irqsave(&dt->tod_lock, flags);
186*615927f1STianfei Zhang 
187*615927f1STianfei Zhang 	/*
188*615927f1STianfei Zhang 	 * Get the maximum possible value of the Period register offset
189*615927f1STianfei Zhang 	 * adjustment in nanoseconds scale. This depends on the current
190*615927f1STianfei Zhang 	 * Period register setting and the maximum and minimum possible
191*615927f1STianfei Zhang 	 * values of the Period register.
192*615927f1STianfei Zhang 	 */
193*615927f1STianfei Zhang 	period = readl(base + TOD_PERIOD);
194*615927f1STianfei Zhang 
195*615927f1STianfei Zhang 	if (neg_adj) {
196*615927f1STianfei Zhang 		diff = (period - TOD_PERIOD_MIN) >> PERIOD_FRAC_OFFSET;
197*615927f1STianfei Zhang 		adj_period = period - (diff << PERIOD_FRAC_OFFSET);
198*615927f1STianfei Zhang 		count = div_u64_rem(delta, diff, &rem);
199*615927f1STianfei Zhang 		rem_period = period - (rem << PERIOD_FRAC_OFFSET);
200*615927f1STianfei Zhang 	} else {
201*615927f1STianfei Zhang 		diff = (TOD_PERIOD_MAX - period) >> PERIOD_FRAC_OFFSET;
202*615927f1STianfei Zhang 		adj_period = period + (diff << PERIOD_FRAC_OFFSET);
203*615927f1STianfei Zhang 		count = div_u64_rem(delta, diff, &rem);
204*615927f1STianfei Zhang 		rem_period = period + (rem << PERIOD_FRAC_OFFSET);
205*615927f1STianfei Zhang 	}
206*615927f1STianfei Zhang 
207*615927f1STianfei Zhang 	ret = 0;
208*615927f1STianfei Zhang 
209*615927f1STianfei Zhang 	if (count > TOD_ADJUST_COUNT_MAX) {
210*615927f1STianfei Zhang 		ret = coarse_adjust_tod_clock(dt, delta);
211*615927f1STianfei Zhang 	} else {
212*615927f1STianfei Zhang 		/* Adjust the period by count cycles to adjust the time */
213*615927f1STianfei Zhang 		if (count)
214*615927f1STianfei Zhang 			ret = fine_adjust_tod_clock(dt, adj_period, count);
215*615927f1STianfei Zhang 
216*615927f1STianfei Zhang 		/* If there is a remainder, adjust the period for an additional cycle */
217*615927f1STianfei Zhang 		if (rem)
218*615927f1STianfei Zhang 			ret = fine_adjust_tod_clock(dt, rem_period, 1);
219*615927f1STianfei Zhang 	}
220*615927f1STianfei Zhang 
221*615927f1STianfei Zhang 	spin_unlock_irqrestore(&dt->tod_lock, flags);
222*615927f1STianfei Zhang 
223*615927f1STianfei Zhang 	return ret;
224*615927f1STianfei Zhang }
225*615927f1STianfei Zhang 
dfl_tod_get_timex(struct ptp_clock_info * ptp,struct timespec64 * ts,struct ptp_system_timestamp * sts)226*615927f1STianfei Zhang static int dfl_tod_get_timex(struct ptp_clock_info *ptp, struct timespec64 *ts,
227*615927f1STianfei Zhang 			     struct ptp_system_timestamp *sts)
228*615927f1STianfei Zhang {
229*615927f1STianfei Zhang 	struct dfl_tod *dt = container_of(ptp, struct dfl_tod, ptp_clock_ops);
230*615927f1STianfei Zhang 	u32 seconds_msb, seconds_lsb, nanosec;
231*615927f1STianfei Zhang 	void __iomem *base = dt->tod_ctrl;
232*615927f1STianfei Zhang 	unsigned long flags;
233*615927f1STianfei Zhang 	u64 seconds;
234*615927f1STianfei Zhang 
235*615927f1STianfei Zhang 	spin_lock_irqsave(&dt->tod_lock, flags);
236*615927f1STianfei Zhang 	ptp_read_system_prets(sts);
237*615927f1STianfei Zhang 	nanosec = readl(base + TOD_NANOSEC);
238*615927f1STianfei Zhang 	seconds_lsb = readl(base + TOD_SECONDSL);
239*615927f1STianfei Zhang 	seconds_msb = readl(base + TOD_SECONDSH);
240*615927f1STianfei Zhang 	ptp_read_system_postts(sts);
241*615927f1STianfei Zhang 	spin_unlock_irqrestore(&dt->tod_lock, flags);
242*615927f1STianfei Zhang 
243*615927f1STianfei Zhang 	seconds = CAL_SECONDS(seconds_msb, seconds_lsb);
244*615927f1STianfei Zhang 
245*615927f1STianfei Zhang 	ts->tv_nsec = nanosec;
246*615927f1STianfei Zhang 	ts->tv_sec = seconds;
247*615927f1STianfei Zhang 
248*615927f1STianfei Zhang 	return 0;
249*615927f1STianfei Zhang }
250*615927f1STianfei Zhang 
dfl_tod_set_time(struct ptp_clock_info * ptp,const struct timespec64 * ts)251*615927f1STianfei Zhang static int dfl_tod_set_time(struct ptp_clock_info *ptp,
252*615927f1STianfei Zhang 			    const struct timespec64 *ts)
253*615927f1STianfei Zhang {
254*615927f1STianfei Zhang 	struct dfl_tod *dt = container_of(ptp, struct dfl_tod, ptp_clock_ops);
255*615927f1STianfei Zhang 	u32 seconds_msb = FIELD_GET(SECONDS_MSB, ts->tv_sec);
256*615927f1STianfei Zhang 	u32 seconds_lsb = FIELD_GET(SECONDS_LSB, ts->tv_sec);
257*615927f1STianfei Zhang 	u32 nanosec = FIELD_GET(SECONDS_LSB, ts->tv_nsec);
258*615927f1STianfei Zhang 	void __iomem *base = dt->tod_ctrl;
259*615927f1STianfei Zhang 	unsigned long flags;
260*615927f1STianfei Zhang 
261*615927f1STianfei Zhang 	spin_lock_irqsave(&dt->tod_lock, flags);
262*615927f1STianfei Zhang 	writel(seconds_msb, base + TOD_SECONDSH);
263*615927f1STianfei Zhang 	writel(seconds_lsb, base + TOD_SECONDSL);
264*615927f1STianfei Zhang 	writel(nanosec, base + TOD_NANOSEC);
265*615927f1STianfei Zhang 	spin_unlock_irqrestore(&dt->tod_lock, flags);
266*615927f1STianfei Zhang 
267*615927f1STianfei Zhang 	return 0;
268*615927f1STianfei Zhang }
269*615927f1STianfei Zhang 
270*615927f1STianfei Zhang static struct ptp_clock_info dfl_tod_clock_ops = {
271*615927f1STianfei Zhang 	.owner = THIS_MODULE,
272*615927f1STianfei Zhang 	.name = "dfl_tod",
273*615927f1STianfei Zhang 	.max_adj = TOD_MAX_ADJ,
274*615927f1STianfei Zhang 	.adjfine = dfl_tod_adjust_fine,
275*615927f1STianfei Zhang 	.adjtime = dfl_tod_adjust_time,
276*615927f1STianfei Zhang 	.gettimex64 = dfl_tod_get_timex,
277*615927f1STianfei Zhang 	.settime64 = dfl_tod_set_time,
278*615927f1STianfei Zhang };
279*615927f1STianfei Zhang 
dfl_tod_probe(struct dfl_device * ddev)280*615927f1STianfei Zhang static int dfl_tod_probe(struct dfl_device *ddev)
281*615927f1STianfei Zhang {
282*615927f1STianfei Zhang 	struct device *dev = &ddev->dev;
283*615927f1STianfei Zhang 	struct dfl_tod *dt;
284*615927f1STianfei Zhang 
285*615927f1STianfei Zhang 	dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
286*615927f1STianfei Zhang 	if (!dt)
287*615927f1STianfei Zhang 		return -ENOMEM;
288*615927f1STianfei Zhang 
289*615927f1STianfei Zhang 	dt->tod_ctrl = devm_ioremap_resource(dev, &ddev->mmio_res);
290*615927f1STianfei Zhang 	if (IS_ERR(dt->tod_ctrl))
291*615927f1STianfei Zhang 		return PTR_ERR(dt->tod_ctrl);
292*615927f1STianfei Zhang 
293*615927f1STianfei Zhang 	dt->dev = dev;
294*615927f1STianfei Zhang 	spin_lock_init(&dt->tod_lock);
295*615927f1STianfei Zhang 	dev_set_drvdata(dev, dt);
296*615927f1STianfei Zhang 
297*615927f1STianfei Zhang 	dt->ptp_clock_ops = dfl_tod_clock_ops;
298*615927f1STianfei Zhang 
299*615927f1STianfei Zhang 	dt->ptp_clock = ptp_clock_register(&dt->ptp_clock_ops, dev);
300*615927f1STianfei Zhang 	if (IS_ERR(dt->ptp_clock))
301*615927f1STianfei Zhang 		return dev_err_probe(dt->dev, PTR_ERR(dt->ptp_clock),
302*615927f1STianfei Zhang 				     "Unable to register PTP clock\n");
303*615927f1STianfei Zhang 
304*615927f1STianfei Zhang 	return 0;
305*615927f1STianfei Zhang }
306*615927f1STianfei Zhang 
dfl_tod_remove(struct dfl_device * ddev)307*615927f1STianfei Zhang static void dfl_tod_remove(struct dfl_device *ddev)
308*615927f1STianfei Zhang {
309*615927f1STianfei Zhang 	struct dfl_tod *dt = dev_get_drvdata(&ddev->dev);
310*615927f1STianfei Zhang 
311*615927f1STianfei Zhang 	ptp_clock_unregister(dt->ptp_clock);
312*615927f1STianfei Zhang }
313*615927f1STianfei Zhang 
314*615927f1STianfei Zhang static const struct dfl_device_id dfl_tod_ids[] = {
315*615927f1STianfei Zhang 	{ FME_ID, FME_FEATURE_ID_TOD },
316*615927f1STianfei Zhang 	{ }
317*615927f1STianfei Zhang };
318*615927f1STianfei Zhang MODULE_DEVICE_TABLE(dfl, dfl_tod_ids);
319*615927f1STianfei Zhang 
320*615927f1STianfei Zhang static struct dfl_driver dfl_tod_driver = {
321*615927f1STianfei Zhang 	.drv = {
322*615927f1STianfei Zhang 		.name = "dfl-tod",
323*615927f1STianfei Zhang 	},
324*615927f1STianfei Zhang 	.id_table = dfl_tod_ids,
325*615927f1STianfei Zhang 	.probe = dfl_tod_probe,
326*615927f1STianfei Zhang 	.remove = dfl_tod_remove,
327*615927f1STianfei Zhang };
328*615927f1STianfei Zhang module_dfl_driver(dfl_tod_driver);
329*615927f1STianfei Zhang 
330*615927f1STianfei Zhang MODULE_DESCRIPTION("FPGA DFL ToD driver");
331*615927f1STianfei Zhang MODULE_AUTHOR("Intel Corporation");
332*615927f1STianfei Zhang MODULE_LICENSE("GPL");
333