xref: /openbmc/linux/drivers/net/phy/bcm-phy-ptp.c (revision 39db6be781cd7dfd44ea259acecaf23f8685550b)
1*39db6be7SJonathan Lemon // SPDX-License-Identifier: GPL-2.0
2*39db6be7SJonathan Lemon /*
3*39db6be7SJonathan Lemon  * Copyright (C) 2022 Meta Platforms Inc.
4*39db6be7SJonathan Lemon  * Copyright (C) 2022 Jonathan Lemon <jonathan.lemon@gmail.com>
5*39db6be7SJonathan Lemon  */
6*39db6be7SJonathan Lemon 
7*39db6be7SJonathan Lemon #include <asm/unaligned.h>
8*39db6be7SJonathan Lemon #include <linux/mii.h>
9*39db6be7SJonathan Lemon #include <linux/phy.h>
10*39db6be7SJonathan Lemon #include <linux/ptp_classify.h>
11*39db6be7SJonathan Lemon #include <linux/ptp_clock_kernel.h>
12*39db6be7SJonathan Lemon #include <linux/net_tstamp.h>
13*39db6be7SJonathan Lemon #include <linux/netdevice.h>
14*39db6be7SJonathan Lemon #include <linux/workqueue.h>
15*39db6be7SJonathan Lemon 
16*39db6be7SJonathan Lemon #include "bcm-phy-lib.h"
17*39db6be7SJonathan Lemon 
18*39db6be7SJonathan Lemon /* IEEE 1588 Expansion registers */
19*39db6be7SJonathan Lemon #define SLICE_CTRL		0x0810
20*39db6be7SJonathan Lemon #define  SLICE_TX_EN			BIT(0)
21*39db6be7SJonathan Lemon #define  SLICE_RX_EN			BIT(8)
22*39db6be7SJonathan Lemon #define TX_EVENT_MODE		0x0811
23*39db6be7SJonathan Lemon #define  MODE_TX_UPDATE_CF		BIT(0)
24*39db6be7SJonathan Lemon #define  MODE_TX_REPLACE_TS_CF		BIT(1)
25*39db6be7SJonathan Lemon #define  MODE_TX_REPLACE_TS		GENMASK(1, 0)
26*39db6be7SJonathan Lemon #define RX_EVENT_MODE		0x0819
27*39db6be7SJonathan Lemon #define  MODE_RX_UPDATE_CF		BIT(0)
28*39db6be7SJonathan Lemon #define  MODE_RX_INSERT_TS_48		BIT(1)
29*39db6be7SJonathan Lemon #define  MODE_RX_INSERT_TS_64		GENMASK(1, 0)
30*39db6be7SJonathan Lemon 
31*39db6be7SJonathan Lemon #define MODE_EVT_SHIFT_SYNC		0
32*39db6be7SJonathan Lemon #define MODE_EVT_SHIFT_DELAY_REQ	2
33*39db6be7SJonathan Lemon #define MODE_EVT_SHIFT_PDELAY_REQ	4
34*39db6be7SJonathan Lemon #define MODE_EVT_SHIFT_PDELAY_RESP	6
35*39db6be7SJonathan Lemon 
36*39db6be7SJonathan Lemon #define MODE_SEL_SHIFT_PORT		0
37*39db6be7SJonathan Lemon #define MODE_SEL_SHIFT_CPU		8
38*39db6be7SJonathan Lemon 
39*39db6be7SJonathan Lemon #define RX_MODE_SEL(sel, evt, act) \
40*39db6be7SJonathan Lemon 	(((MODE_RX_##act) << (MODE_EVT_SHIFT_##evt)) << (MODE_SEL_SHIFT_##sel))
41*39db6be7SJonathan Lemon 
42*39db6be7SJonathan Lemon #define TX_MODE_SEL(sel, evt, act) \
43*39db6be7SJonathan Lemon 	(((MODE_TX_##act) << (MODE_EVT_SHIFT_##evt)) << (MODE_SEL_SHIFT_##sel))
44*39db6be7SJonathan Lemon 
45*39db6be7SJonathan Lemon /* needs global TS capture first */
46*39db6be7SJonathan Lemon #define TX_TS_CAPTURE		0x0821
47*39db6be7SJonathan Lemon #define  TX_TS_CAP_EN			BIT(0)
48*39db6be7SJonathan Lemon #define RX_TS_CAPTURE		0x0822
49*39db6be7SJonathan Lemon #define  RX_TS_CAP_EN			BIT(0)
50*39db6be7SJonathan Lemon 
51*39db6be7SJonathan Lemon #define TIME_CODE_0		0x0854
52*39db6be7SJonathan Lemon #define TIME_CODE_1		0x0855
53*39db6be7SJonathan Lemon #define TIME_CODE_2		0x0856
54*39db6be7SJonathan Lemon #define TIME_CODE_3		0x0857
55*39db6be7SJonathan Lemon #define TIME_CODE_4		0x0858
56*39db6be7SJonathan Lemon 
57*39db6be7SJonathan Lemon #define DPLL_SELECT		0x085b
58*39db6be7SJonathan Lemon #define  DPLL_HB_MODE2			BIT(6)
59*39db6be7SJonathan Lemon 
60*39db6be7SJonathan Lemon #define SHADOW_CTRL		0x085c
61*39db6be7SJonathan Lemon #define SHADOW_LOAD		0x085d
62*39db6be7SJonathan Lemon #define  TIME_CODE_LOAD			BIT(10)
63*39db6be7SJonathan Lemon #define  SYNC_OUT_LOAD			BIT(9)
64*39db6be7SJonathan Lemon #define  NCO_TIME_LOAD			BIT(7)
65*39db6be7SJonathan Lemon #define  FREQ_LOAD			BIT(6)
66*39db6be7SJonathan Lemon #define INTR_MASK		0x085e
67*39db6be7SJonathan Lemon #define INTR_STATUS		0x085f
68*39db6be7SJonathan Lemon #define  INTC_FSYNC			BIT(0)
69*39db6be7SJonathan Lemon #define  INTC_SOP			BIT(1)
70*39db6be7SJonathan Lemon 
71*39db6be7SJonathan Lemon #define NCO_FREQ_LSB		0x0873
72*39db6be7SJonathan Lemon #define NCO_FREQ_MSB		0x0874
73*39db6be7SJonathan Lemon 
74*39db6be7SJonathan Lemon #define NCO_TIME_0		0x0875
75*39db6be7SJonathan Lemon #define NCO_TIME_1		0x0876
76*39db6be7SJonathan Lemon #define NCO_TIME_2_CTRL		0x0877
77*39db6be7SJonathan Lemon #define  FREQ_MDIO_SEL			BIT(14)
78*39db6be7SJonathan Lemon 
79*39db6be7SJonathan Lemon #define SYNC_OUT_0		0x0878
80*39db6be7SJonathan Lemon #define SYNC_OUT_1		0x0879
81*39db6be7SJonathan Lemon #define SYNC_OUT_2		0x087a
82*39db6be7SJonathan Lemon 
83*39db6be7SJonathan Lemon #define SYNOUT_TS_0		0x087c
84*39db6be7SJonathan Lemon #define SYNOUT_TS_1		0x087d
85*39db6be7SJonathan Lemon #define SYNOUT_TS_2		0x087e
86*39db6be7SJonathan Lemon 
87*39db6be7SJonathan Lemon #define NSE_CTRL		0x087f
88*39db6be7SJonathan Lemon #define  NSE_GMODE_EN			GENMASK(15, 14)
89*39db6be7SJonathan Lemon #define  NSE_CAPTURE_EN			BIT(13)
90*39db6be7SJonathan Lemon #define  NSE_INIT			BIT(12)
91*39db6be7SJonathan Lemon #define  NSE_CPU_FRAMESYNC		BIT(5)
92*39db6be7SJonathan Lemon #define  NSE_FRAMESYNC_MASK		GENMASK(5, 2)
93*39db6be7SJonathan Lemon #define  NSE_PEROUT_EN			BIT(1)
94*39db6be7SJonathan Lemon #define  NSE_ONESHOT_EN			BIT(0)
95*39db6be7SJonathan Lemon #define  NSE_SYNC_OUT_MASK		GENMASK(1, 0)
96*39db6be7SJonathan Lemon 
97*39db6be7SJonathan Lemon #define TS_READ_CTRL		0x0885
98*39db6be7SJonathan Lemon #define  TS_READ_START			BIT(0)
99*39db6be7SJonathan Lemon #define  TS_READ_END			BIT(1)
100*39db6be7SJonathan Lemon 
101*39db6be7SJonathan Lemon #define HB_REG_0		0x0886
102*39db6be7SJonathan Lemon #define HB_REG_1		0x0887
103*39db6be7SJonathan Lemon #define HB_REG_2		0x0888
104*39db6be7SJonathan Lemon #define HB_REG_3		0x08ec
105*39db6be7SJonathan Lemon #define HB_REG_4		0x08ed
106*39db6be7SJonathan Lemon #define HB_STAT_CTRL		0x088e
107*39db6be7SJonathan Lemon #define  HB_READ_START			BIT(10)
108*39db6be7SJonathan Lemon #define  HB_READ_END			BIT(11)
109*39db6be7SJonathan Lemon #define  HB_READ_MASK			GENMASK(11, 10)
110*39db6be7SJonathan Lemon 
111*39db6be7SJonathan Lemon #define TS_REG_0		0x0889
112*39db6be7SJonathan Lemon #define TS_REG_1		0x088a
113*39db6be7SJonathan Lemon #define TS_REG_2		0x088b
114*39db6be7SJonathan Lemon #define TS_REG_3		0x08c4
115*39db6be7SJonathan Lemon 
116*39db6be7SJonathan Lemon #define TS_INFO_0		0x088c
117*39db6be7SJonathan Lemon #define TS_INFO_1		0x088d
118*39db6be7SJonathan Lemon 
119*39db6be7SJonathan Lemon #define TIMECODE_CTRL		0x08c3
120*39db6be7SJonathan Lemon #define  TX_TIMECODE_SEL		GENMASK(7, 0)
121*39db6be7SJonathan Lemon #define  RX_TIMECODE_SEL		GENMASK(15, 8)
122*39db6be7SJonathan Lemon 
123*39db6be7SJonathan Lemon #define TIME_SYNC		0x0ff5
124*39db6be7SJonathan Lemon #define  TIME_SYNC_EN			BIT(0)
125*39db6be7SJonathan Lemon 
126*39db6be7SJonathan Lemon struct bcm_ptp_private {
127*39db6be7SJonathan Lemon 	struct phy_device *phydev;
128*39db6be7SJonathan Lemon 	struct mii_timestamper mii_ts;
129*39db6be7SJonathan Lemon 	struct ptp_clock *ptp_clock;
130*39db6be7SJonathan Lemon 	struct ptp_clock_info ptp_info;
131*39db6be7SJonathan Lemon 	struct mutex mutex;
132*39db6be7SJonathan Lemon 	struct sk_buff_head tx_queue;
133*39db6be7SJonathan Lemon 	int tx_type;
134*39db6be7SJonathan Lemon 	bool hwts_rx;
135*39db6be7SJonathan Lemon 	u16 nse_ctrl;
136*39db6be7SJonathan Lemon };
137*39db6be7SJonathan Lemon 
138*39db6be7SJonathan Lemon struct bcm_ptp_skb_cb {
139*39db6be7SJonathan Lemon 	unsigned long timeout;
140*39db6be7SJonathan Lemon 	u16 seq_id;
141*39db6be7SJonathan Lemon 	u8 msgtype;
142*39db6be7SJonathan Lemon 	bool discard;
143*39db6be7SJonathan Lemon };
144*39db6be7SJonathan Lemon 
145*39db6be7SJonathan Lemon struct bcm_ptp_capture {
146*39db6be7SJonathan Lemon 	ktime_t	hwtstamp;
147*39db6be7SJonathan Lemon 	u16 seq_id;
148*39db6be7SJonathan Lemon 	u8 msgtype;
149*39db6be7SJonathan Lemon 	bool tx_dir;
150*39db6be7SJonathan Lemon };
151*39db6be7SJonathan Lemon 
152*39db6be7SJonathan Lemon #define BCM_SKB_CB(skb)		((struct bcm_ptp_skb_cb *)(skb)->cb)
153*39db6be7SJonathan Lemon #define SKB_TS_TIMEOUT		10			/* jiffies */
154*39db6be7SJonathan Lemon 
155*39db6be7SJonathan Lemon #define BCM_MAX_PULSE_8NS	((1U << 9) - 1)
156*39db6be7SJonathan Lemon #define BCM_MAX_PERIOD_8NS	((1U << 30) - 1)
157*39db6be7SJonathan Lemon 
158*39db6be7SJonathan Lemon #define BRCM_PHY_MODEL(phydev) \
159*39db6be7SJonathan Lemon 	((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
160*39db6be7SJonathan Lemon 
161*39db6be7SJonathan Lemon static struct bcm_ptp_private *mii2priv(struct mii_timestamper *mii_ts)
162*39db6be7SJonathan Lemon {
163*39db6be7SJonathan Lemon 	return container_of(mii_ts, struct bcm_ptp_private, mii_ts);
164*39db6be7SJonathan Lemon }
165*39db6be7SJonathan Lemon 
166*39db6be7SJonathan Lemon static struct bcm_ptp_private *ptp2priv(struct ptp_clock_info *info)
167*39db6be7SJonathan Lemon {
168*39db6be7SJonathan Lemon 	return container_of(info, struct bcm_ptp_private, ptp_info);
169*39db6be7SJonathan Lemon }
170*39db6be7SJonathan Lemon 
171*39db6be7SJonathan Lemon static void bcm_ptp_get_framesync_ts(struct phy_device *phydev,
172*39db6be7SJonathan Lemon 				     struct timespec64 *ts)
173*39db6be7SJonathan Lemon {
174*39db6be7SJonathan Lemon 	u16 hb[4];
175*39db6be7SJonathan Lemon 
176*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, HB_STAT_CTRL, HB_READ_START);
177*39db6be7SJonathan Lemon 
178*39db6be7SJonathan Lemon 	hb[0] = bcm_phy_read_exp(phydev, HB_REG_0);
179*39db6be7SJonathan Lemon 	hb[1] = bcm_phy_read_exp(phydev, HB_REG_1);
180*39db6be7SJonathan Lemon 	hb[2] = bcm_phy_read_exp(phydev, HB_REG_2);
181*39db6be7SJonathan Lemon 	hb[3] = bcm_phy_read_exp(phydev, HB_REG_3);
182*39db6be7SJonathan Lemon 
183*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, HB_STAT_CTRL, HB_READ_END);
184*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, HB_STAT_CTRL, 0);
185*39db6be7SJonathan Lemon 
186*39db6be7SJonathan Lemon 	ts->tv_sec = (hb[3] << 16) | hb[2];
187*39db6be7SJonathan Lemon 	ts->tv_nsec = (hb[1] << 16) | hb[0];
188*39db6be7SJonathan Lemon }
189*39db6be7SJonathan Lemon 
190*39db6be7SJonathan Lemon static u16 bcm_ptp_framesync_disable(struct phy_device *phydev, u16 orig_ctrl)
191*39db6be7SJonathan Lemon {
192*39db6be7SJonathan Lemon 	u16 ctrl = orig_ctrl & ~(NSE_FRAMESYNC_MASK | NSE_CAPTURE_EN);
193*39db6be7SJonathan Lemon 
194*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NSE_CTRL, ctrl);
195*39db6be7SJonathan Lemon 
196*39db6be7SJonathan Lemon 	return ctrl;
197*39db6be7SJonathan Lemon }
198*39db6be7SJonathan Lemon 
199*39db6be7SJonathan Lemon static void bcm_ptp_framesync_restore(struct phy_device *phydev, u16 orig_ctrl)
200*39db6be7SJonathan Lemon {
201*39db6be7SJonathan Lemon 	if (orig_ctrl & NSE_FRAMESYNC_MASK)
202*39db6be7SJonathan Lemon 		bcm_phy_write_exp(phydev, NSE_CTRL, orig_ctrl);
203*39db6be7SJonathan Lemon }
204*39db6be7SJonathan Lemon 
205*39db6be7SJonathan Lemon static void bcm_ptp_framesync(struct phy_device *phydev, u16 ctrl)
206*39db6be7SJonathan Lemon {
207*39db6be7SJonathan Lemon 	/* trigger framesync - must have 0->1 transition. */
208*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NSE_CTRL, ctrl | NSE_CPU_FRAMESYNC);
209*39db6be7SJonathan Lemon }
210*39db6be7SJonathan Lemon 
211*39db6be7SJonathan Lemon static int bcm_ptp_framesync_ts(struct phy_device *phydev,
212*39db6be7SJonathan Lemon 				struct ptp_system_timestamp *sts,
213*39db6be7SJonathan Lemon 				struct timespec64 *ts,
214*39db6be7SJonathan Lemon 				u16 orig_ctrl)
215*39db6be7SJonathan Lemon {
216*39db6be7SJonathan Lemon 	u16 ctrl, reg;
217*39db6be7SJonathan Lemon 	int i;
218*39db6be7SJonathan Lemon 
219*39db6be7SJonathan Lemon 	ctrl = bcm_ptp_framesync_disable(phydev, orig_ctrl);
220*39db6be7SJonathan Lemon 
221*39db6be7SJonathan Lemon 	ptp_read_system_prets(sts);
222*39db6be7SJonathan Lemon 
223*39db6be7SJonathan Lemon 	/* trigger framesync + capture */
224*39db6be7SJonathan Lemon 	bcm_ptp_framesync(phydev, ctrl | NSE_CAPTURE_EN);
225*39db6be7SJonathan Lemon 
226*39db6be7SJonathan Lemon 	ptp_read_system_postts(sts);
227*39db6be7SJonathan Lemon 
228*39db6be7SJonathan Lemon 	/* poll for FSYNC interrupt from TS capture */
229*39db6be7SJonathan Lemon 	for (i = 0; i < 10; i++) {
230*39db6be7SJonathan Lemon 		reg = bcm_phy_read_exp(phydev, INTR_STATUS);
231*39db6be7SJonathan Lemon 		if (reg & INTC_FSYNC) {
232*39db6be7SJonathan Lemon 			bcm_ptp_get_framesync_ts(phydev, ts);
233*39db6be7SJonathan Lemon 			break;
234*39db6be7SJonathan Lemon 		}
235*39db6be7SJonathan Lemon 	}
236*39db6be7SJonathan Lemon 
237*39db6be7SJonathan Lemon 	bcm_ptp_framesync_restore(phydev, orig_ctrl);
238*39db6be7SJonathan Lemon 
239*39db6be7SJonathan Lemon 	return reg & INTC_FSYNC ? 0 : -ETIMEDOUT;
240*39db6be7SJonathan Lemon }
241*39db6be7SJonathan Lemon 
242*39db6be7SJonathan Lemon static int bcm_ptp_gettimex(struct ptp_clock_info *info,
243*39db6be7SJonathan Lemon 			    struct timespec64 *ts,
244*39db6be7SJonathan Lemon 			    struct ptp_system_timestamp *sts)
245*39db6be7SJonathan Lemon {
246*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
247*39db6be7SJonathan Lemon 	int err;
248*39db6be7SJonathan Lemon 
249*39db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
250*39db6be7SJonathan Lemon 	err = bcm_ptp_framesync_ts(priv->phydev, sts, ts, priv->nse_ctrl);
251*39db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
252*39db6be7SJonathan Lemon 
253*39db6be7SJonathan Lemon 	return err;
254*39db6be7SJonathan Lemon }
255*39db6be7SJonathan Lemon 
256*39db6be7SJonathan Lemon static int bcm_ptp_settime_locked(struct bcm_ptp_private *priv,
257*39db6be7SJonathan Lemon 				  const struct timespec64 *ts)
258*39db6be7SJonathan Lemon {
259*39db6be7SJonathan Lemon 	struct phy_device *phydev = priv->phydev;
260*39db6be7SJonathan Lemon 	u16 ctrl;
261*39db6be7SJonathan Lemon 	u64 ns;
262*39db6be7SJonathan Lemon 
263*39db6be7SJonathan Lemon 	ctrl = bcm_ptp_framesync_disable(phydev, priv->nse_ctrl);
264*39db6be7SJonathan Lemon 
265*39db6be7SJonathan Lemon 	/* set up time code */
266*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_0, ts->tv_nsec);
267*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_1, ts->tv_nsec >> 16);
268*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_2, ts->tv_sec);
269*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_3, ts->tv_sec >> 16);
270*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_4, ts->tv_sec >> 32);
271*39db6be7SJonathan Lemon 
272*39db6be7SJonathan Lemon 	/* set NCO counter to match */
273*39db6be7SJonathan Lemon 	ns = timespec64_to_ns(ts);
274*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NCO_TIME_0, ns >> 4);
275*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NCO_TIME_1, ns >> 20);
276*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NCO_TIME_2_CTRL, (ns >> 36) & 0xfff);
277*39db6be7SJonathan Lemon 
278*39db6be7SJonathan Lemon 	/* set up load on next frame sync (auto-clears due to NSE_INIT) */
279*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, SHADOW_LOAD, TIME_CODE_LOAD | NCO_TIME_LOAD);
280*39db6be7SJonathan Lemon 
281*39db6be7SJonathan Lemon 	/* must have NSE_INIT in order to write time code */
282*39db6be7SJonathan Lemon 	bcm_ptp_framesync(phydev, ctrl | NSE_INIT);
283*39db6be7SJonathan Lemon 
284*39db6be7SJonathan Lemon 	bcm_ptp_framesync_restore(phydev, priv->nse_ctrl);
285*39db6be7SJonathan Lemon 
286*39db6be7SJonathan Lemon 	return 0;
287*39db6be7SJonathan Lemon }
288*39db6be7SJonathan Lemon 
289*39db6be7SJonathan Lemon static int bcm_ptp_settime(struct ptp_clock_info *info,
290*39db6be7SJonathan Lemon 			   const struct timespec64 *ts)
291*39db6be7SJonathan Lemon {
292*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
293*39db6be7SJonathan Lemon 	int err;
294*39db6be7SJonathan Lemon 
295*39db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
296*39db6be7SJonathan Lemon 	err = bcm_ptp_settime_locked(priv, ts);
297*39db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
298*39db6be7SJonathan Lemon 
299*39db6be7SJonathan Lemon 	return err;
300*39db6be7SJonathan Lemon }
301*39db6be7SJonathan Lemon 
302*39db6be7SJonathan Lemon static int bcm_ptp_adjtime_locked(struct bcm_ptp_private *priv,
303*39db6be7SJonathan Lemon 				  s64 delta_ns)
304*39db6be7SJonathan Lemon {
305*39db6be7SJonathan Lemon 	struct timespec64 ts;
306*39db6be7SJonathan Lemon 	int err;
307*39db6be7SJonathan Lemon 	s64 ns;
308*39db6be7SJonathan Lemon 
309*39db6be7SJonathan Lemon 	err = bcm_ptp_framesync_ts(priv->phydev, NULL, &ts, priv->nse_ctrl);
310*39db6be7SJonathan Lemon 	if (!err) {
311*39db6be7SJonathan Lemon 		ns = timespec64_to_ns(&ts) + delta_ns;
312*39db6be7SJonathan Lemon 		ts = ns_to_timespec64(ns);
313*39db6be7SJonathan Lemon 		err = bcm_ptp_settime_locked(priv, &ts);
314*39db6be7SJonathan Lemon 	}
315*39db6be7SJonathan Lemon 	return err;
316*39db6be7SJonathan Lemon }
317*39db6be7SJonathan Lemon 
318*39db6be7SJonathan Lemon static int bcm_ptp_adjtime(struct ptp_clock_info *info, s64 delta_ns)
319*39db6be7SJonathan Lemon {
320*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
321*39db6be7SJonathan Lemon 	int err;
322*39db6be7SJonathan Lemon 
323*39db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
324*39db6be7SJonathan Lemon 	err = bcm_ptp_adjtime_locked(priv, delta_ns);
325*39db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
326*39db6be7SJonathan Lemon 
327*39db6be7SJonathan Lemon 	return err;
328*39db6be7SJonathan Lemon }
329*39db6be7SJonathan Lemon 
330*39db6be7SJonathan Lemon /* A 125Mhz clock should adjust 8ns per pulse.
331*39db6be7SJonathan Lemon  * The frequency adjustment base is 0x8000 0000, or 8*2^28.
332*39db6be7SJonathan Lemon  *
333*39db6be7SJonathan Lemon  * Frequency adjustment is
334*39db6be7SJonathan Lemon  * adj = scaled_ppm * 8*2^28 / (10^6 * 2^16)
335*39db6be7SJonathan Lemon  *   which simplifies to:
336*39db6be7SJonathan Lemon  * adj = scaled_ppm * 2^9 / 5^6
337*39db6be7SJonathan Lemon  */
338*39db6be7SJonathan Lemon static int bcm_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
339*39db6be7SJonathan Lemon {
340*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
341*39db6be7SJonathan Lemon 	int neg_adj = 0;
342*39db6be7SJonathan Lemon 	u32 diff, freq;
343*39db6be7SJonathan Lemon 	u16 ctrl;
344*39db6be7SJonathan Lemon 	u64 adj;
345*39db6be7SJonathan Lemon 
346*39db6be7SJonathan Lemon 	if (scaled_ppm < 0) {
347*39db6be7SJonathan Lemon 		neg_adj = 1;
348*39db6be7SJonathan Lemon 		scaled_ppm = -scaled_ppm;
349*39db6be7SJonathan Lemon 	}
350*39db6be7SJonathan Lemon 
351*39db6be7SJonathan Lemon 	adj = scaled_ppm << 9;
352*39db6be7SJonathan Lemon 	diff = div_u64(adj, 15625);
353*39db6be7SJonathan Lemon 	freq = (8 << 28) + (neg_adj ? -diff : diff);
354*39db6be7SJonathan Lemon 
355*39db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
356*39db6be7SJonathan Lemon 
357*39db6be7SJonathan Lemon 	ctrl = bcm_ptp_framesync_disable(priv->phydev, priv->nse_ctrl);
358*39db6be7SJonathan Lemon 
359*39db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, NCO_FREQ_LSB, freq);
360*39db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, NCO_FREQ_MSB, freq >> 16);
361*39db6be7SJonathan Lemon 
362*39db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, NCO_TIME_2_CTRL, FREQ_MDIO_SEL);
363*39db6be7SJonathan Lemon 
364*39db6be7SJonathan Lemon 	/* load on next framesync */
365*39db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, SHADOW_LOAD, FREQ_LOAD);
366*39db6be7SJonathan Lemon 
367*39db6be7SJonathan Lemon 	bcm_ptp_framesync(priv->phydev, ctrl);
368*39db6be7SJonathan Lemon 
369*39db6be7SJonathan Lemon 	/* clear load */
370*39db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, SHADOW_LOAD, 0);
371*39db6be7SJonathan Lemon 
372*39db6be7SJonathan Lemon 	bcm_ptp_framesync_restore(priv->phydev, priv->nse_ctrl);
373*39db6be7SJonathan Lemon 
374*39db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
375*39db6be7SJonathan Lemon 
376*39db6be7SJonathan Lemon 	return 0;
377*39db6be7SJonathan Lemon }
378*39db6be7SJonathan Lemon 
379*39db6be7SJonathan Lemon static bool bcm_ptp_rxtstamp(struct mii_timestamper *mii_ts,
380*39db6be7SJonathan Lemon 			     struct sk_buff *skb, int type)
381*39db6be7SJonathan Lemon {
382*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = mii2priv(mii_ts);
383*39db6be7SJonathan Lemon 	struct skb_shared_hwtstamps *hwts;
384*39db6be7SJonathan Lemon 	struct ptp_header *header;
385*39db6be7SJonathan Lemon 	u32 sec, nsec;
386*39db6be7SJonathan Lemon 	u8 *data;
387*39db6be7SJonathan Lemon 	int off;
388*39db6be7SJonathan Lemon 
389*39db6be7SJonathan Lemon 	if (!priv->hwts_rx)
390*39db6be7SJonathan Lemon 		return false;
391*39db6be7SJonathan Lemon 
392*39db6be7SJonathan Lemon 	header = ptp_parse_header(skb, type);
393*39db6be7SJonathan Lemon 	if (!header)
394*39db6be7SJonathan Lemon 		return false;
395*39db6be7SJonathan Lemon 
396*39db6be7SJonathan Lemon 	data = (u8 *)(header + 1);
397*39db6be7SJonathan Lemon 	sec = get_unaligned_be32(data);
398*39db6be7SJonathan Lemon 	nsec = get_unaligned_be32(data + 4);
399*39db6be7SJonathan Lemon 
400*39db6be7SJonathan Lemon 	hwts = skb_hwtstamps(skb);
401*39db6be7SJonathan Lemon 	hwts->hwtstamp = ktime_set(sec, nsec);
402*39db6be7SJonathan Lemon 
403*39db6be7SJonathan Lemon 	off = data - skb->data + 8;
404*39db6be7SJonathan Lemon 	if (off < skb->len) {
405*39db6be7SJonathan Lemon 		memmove(data, data + 8, skb->len - off);
406*39db6be7SJonathan Lemon 		__pskb_trim(skb, skb->len - 8);
407*39db6be7SJonathan Lemon 	}
408*39db6be7SJonathan Lemon 
409*39db6be7SJonathan Lemon 	return false;
410*39db6be7SJonathan Lemon }
411*39db6be7SJonathan Lemon 
412*39db6be7SJonathan Lemon static bool bcm_ptp_get_tstamp(struct bcm_ptp_private *priv,
413*39db6be7SJonathan Lemon 			       struct bcm_ptp_capture *capts)
414*39db6be7SJonathan Lemon {
415*39db6be7SJonathan Lemon 	struct phy_device *phydev = priv->phydev;
416*39db6be7SJonathan Lemon 	u16 ts[4], reg;
417*39db6be7SJonathan Lemon 	u32 sec, nsec;
418*39db6be7SJonathan Lemon 
419*39db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
420*39db6be7SJonathan Lemon 
421*39db6be7SJonathan Lemon 	reg = bcm_phy_read_exp(phydev, INTR_STATUS);
422*39db6be7SJonathan Lemon 	if ((reg & INTC_SOP) == 0) {
423*39db6be7SJonathan Lemon 		mutex_unlock(&priv->mutex);
424*39db6be7SJonathan Lemon 		return false;
425*39db6be7SJonathan Lemon 	}
426*39db6be7SJonathan Lemon 
427*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TS_READ_CTRL, TS_READ_START);
428*39db6be7SJonathan Lemon 
429*39db6be7SJonathan Lemon 	ts[0] = bcm_phy_read_exp(phydev, TS_REG_0);
430*39db6be7SJonathan Lemon 	ts[1] = bcm_phy_read_exp(phydev, TS_REG_1);
431*39db6be7SJonathan Lemon 	ts[2] = bcm_phy_read_exp(phydev, TS_REG_2);
432*39db6be7SJonathan Lemon 	ts[3] = bcm_phy_read_exp(phydev, TS_REG_3);
433*39db6be7SJonathan Lemon 
434*39db6be7SJonathan Lemon 	/* not in be32 format for some reason */
435*39db6be7SJonathan Lemon 	capts->seq_id = bcm_phy_read_exp(priv->phydev, TS_INFO_0);
436*39db6be7SJonathan Lemon 
437*39db6be7SJonathan Lemon 	reg = bcm_phy_read_exp(phydev, TS_INFO_1);
438*39db6be7SJonathan Lemon 	capts->msgtype = reg >> 12;
439*39db6be7SJonathan Lemon 	capts->tx_dir = !!(reg & BIT(11));
440*39db6be7SJonathan Lemon 
441*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TS_READ_CTRL, TS_READ_END);
442*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TS_READ_CTRL, 0);
443*39db6be7SJonathan Lemon 
444*39db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
445*39db6be7SJonathan Lemon 
446*39db6be7SJonathan Lemon 	sec = (ts[3] << 16) | ts[2];
447*39db6be7SJonathan Lemon 	nsec = (ts[1] << 16) | ts[0];
448*39db6be7SJonathan Lemon 	capts->hwtstamp = ktime_set(sec, nsec);
449*39db6be7SJonathan Lemon 
450*39db6be7SJonathan Lemon 	return true;
451*39db6be7SJonathan Lemon }
452*39db6be7SJonathan Lemon 
453*39db6be7SJonathan Lemon static void bcm_ptp_match_tstamp(struct bcm_ptp_private *priv,
454*39db6be7SJonathan Lemon 				 struct bcm_ptp_capture *capts)
455*39db6be7SJonathan Lemon {
456*39db6be7SJonathan Lemon 	struct skb_shared_hwtstamps hwts;
457*39db6be7SJonathan Lemon 	struct sk_buff *skb, *ts_skb;
458*39db6be7SJonathan Lemon 	unsigned long flags;
459*39db6be7SJonathan Lemon 	bool first = false;
460*39db6be7SJonathan Lemon 
461*39db6be7SJonathan Lemon 	ts_skb = NULL;
462*39db6be7SJonathan Lemon 	spin_lock_irqsave(&priv->tx_queue.lock, flags);
463*39db6be7SJonathan Lemon 	skb_queue_walk(&priv->tx_queue, skb) {
464*39db6be7SJonathan Lemon 		if (BCM_SKB_CB(skb)->seq_id == capts->seq_id &&
465*39db6be7SJonathan Lemon 		    BCM_SKB_CB(skb)->msgtype == capts->msgtype) {
466*39db6be7SJonathan Lemon 			first = skb_queue_is_first(&priv->tx_queue, skb);
467*39db6be7SJonathan Lemon 			__skb_unlink(skb, &priv->tx_queue);
468*39db6be7SJonathan Lemon 			ts_skb = skb;
469*39db6be7SJonathan Lemon 			break;
470*39db6be7SJonathan Lemon 		}
471*39db6be7SJonathan Lemon 	}
472*39db6be7SJonathan Lemon 	spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
473*39db6be7SJonathan Lemon 
474*39db6be7SJonathan Lemon 	/* TX captures one-step packets, discard them if needed. */
475*39db6be7SJonathan Lemon 	if (ts_skb) {
476*39db6be7SJonathan Lemon 		if (BCM_SKB_CB(ts_skb)->discard) {
477*39db6be7SJonathan Lemon 			kfree_skb(ts_skb);
478*39db6be7SJonathan Lemon 		} else {
479*39db6be7SJonathan Lemon 			memset(&hwts, 0, sizeof(hwts));
480*39db6be7SJonathan Lemon 			hwts.hwtstamp = capts->hwtstamp;
481*39db6be7SJonathan Lemon 			skb_complete_tx_timestamp(ts_skb, &hwts);
482*39db6be7SJonathan Lemon 		}
483*39db6be7SJonathan Lemon 	}
484*39db6be7SJonathan Lemon 
485*39db6be7SJonathan Lemon 	/* not first match, try and expire entries */
486*39db6be7SJonathan Lemon 	if (!first) {
487*39db6be7SJonathan Lemon 		while ((skb = skb_dequeue(&priv->tx_queue))) {
488*39db6be7SJonathan Lemon 			if (!time_after(jiffies, BCM_SKB_CB(skb)->timeout)) {
489*39db6be7SJonathan Lemon 				skb_queue_head(&priv->tx_queue, skb);
490*39db6be7SJonathan Lemon 				break;
491*39db6be7SJonathan Lemon 			}
492*39db6be7SJonathan Lemon 			kfree_skb(skb);
493*39db6be7SJonathan Lemon 		}
494*39db6be7SJonathan Lemon 	}
495*39db6be7SJonathan Lemon }
496*39db6be7SJonathan Lemon 
497*39db6be7SJonathan Lemon static long bcm_ptp_do_aux_work(struct ptp_clock_info *info)
498*39db6be7SJonathan Lemon {
499*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
500*39db6be7SJonathan Lemon 	struct bcm_ptp_capture capts;
501*39db6be7SJonathan Lemon 	bool reschedule = false;
502*39db6be7SJonathan Lemon 
503*39db6be7SJonathan Lemon 	while (!skb_queue_empty_lockless(&priv->tx_queue)) {
504*39db6be7SJonathan Lemon 		if (!bcm_ptp_get_tstamp(priv, &capts)) {
505*39db6be7SJonathan Lemon 			reschedule = true;
506*39db6be7SJonathan Lemon 			break;
507*39db6be7SJonathan Lemon 		}
508*39db6be7SJonathan Lemon 		bcm_ptp_match_tstamp(priv, &capts);
509*39db6be7SJonathan Lemon 	}
510*39db6be7SJonathan Lemon 
511*39db6be7SJonathan Lemon 	return reschedule ? 1 : -1;
512*39db6be7SJonathan Lemon }
513*39db6be7SJonathan Lemon 
514*39db6be7SJonathan Lemon static const struct ptp_clock_info bcm_ptp_clock_info = {
515*39db6be7SJonathan Lemon 	.owner		= THIS_MODULE,
516*39db6be7SJonathan Lemon 	.name		= KBUILD_MODNAME,
517*39db6be7SJonathan Lemon 	.max_adj	= 100000000,
518*39db6be7SJonathan Lemon 	.gettimex64	= bcm_ptp_gettimex,
519*39db6be7SJonathan Lemon 	.settime64	= bcm_ptp_settime,
520*39db6be7SJonathan Lemon 	.adjtime	= bcm_ptp_adjtime,
521*39db6be7SJonathan Lemon 	.adjfine	= bcm_ptp_adjfine,
522*39db6be7SJonathan Lemon 	.do_aux_work	= bcm_ptp_do_aux_work,
523*39db6be7SJonathan Lemon };
524*39db6be7SJonathan Lemon 
525*39db6be7SJonathan Lemon static void bcm_ptp_txtstamp(struct mii_timestamper *mii_ts,
526*39db6be7SJonathan Lemon 			     struct sk_buff *skb, int type)
527*39db6be7SJonathan Lemon {
528*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = mii2priv(mii_ts);
529*39db6be7SJonathan Lemon 	struct ptp_header *hdr;
530*39db6be7SJonathan Lemon 	bool discard = false;
531*39db6be7SJonathan Lemon 	int msgtype;
532*39db6be7SJonathan Lemon 
533*39db6be7SJonathan Lemon 	hdr = ptp_parse_header(skb, type);
534*39db6be7SJonathan Lemon 	if (!hdr)
535*39db6be7SJonathan Lemon 		goto out;
536*39db6be7SJonathan Lemon 	msgtype = ptp_get_msgtype(hdr, type);
537*39db6be7SJonathan Lemon 
538*39db6be7SJonathan Lemon 	switch (priv->tx_type) {
539*39db6be7SJonathan Lemon 	case HWTSTAMP_TX_ONESTEP_P2P:
540*39db6be7SJonathan Lemon 		if (msgtype == PTP_MSGTYPE_PDELAY_RESP)
541*39db6be7SJonathan Lemon 			discard = true;
542*39db6be7SJonathan Lemon 		fallthrough;
543*39db6be7SJonathan Lemon 	case HWTSTAMP_TX_ONESTEP_SYNC:
544*39db6be7SJonathan Lemon 		if (msgtype == PTP_MSGTYPE_SYNC)
545*39db6be7SJonathan Lemon 			discard = true;
546*39db6be7SJonathan Lemon 		fallthrough;
547*39db6be7SJonathan Lemon 	case HWTSTAMP_TX_ON:
548*39db6be7SJonathan Lemon 		BCM_SKB_CB(skb)->timeout = jiffies + SKB_TS_TIMEOUT;
549*39db6be7SJonathan Lemon 		BCM_SKB_CB(skb)->seq_id = be16_to_cpu(hdr->sequence_id);
550*39db6be7SJonathan Lemon 		BCM_SKB_CB(skb)->msgtype = msgtype;
551*39db6be7SJonathan Lemon 		BCM_SKB_CB(skb)->discard = discard;
552*39db6be7SJonathan Lemon 		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
553*39db6be7SJonathan Lemon 		skb_queue_tail(&priv->tx_queue, skb);
554*39db6be7SJonathan Lemon 		ptp_schedule_worker(priv->ptp_clock, 0);
555*39db6be7SJonathan Lemon 		return;
556*39db6be7SJonathan Lemon 	default:
557*39db6be7SJonathan Lemon 		break;
558*39db6be7SJonathan Lemon 	}
559*39db6be7SJonathan Lemon 
560*39db6be7SJonathan Lemon out:
561*39db6be7SJonathan Lemon 	kfree_skb(skb);
562*39db6be7SJonathan Lemon }
563*39db6be7SJonathan Lemon 
564*39db6be7SJonathan Lemon static int bcm_ptp_hwtstamp(struct mii_timestamper *mii_ts,
565*39db6be7SJonathan Lemon 			    struct ifreq *ifr)
566*39db6be7SJonathan Lemon {
567*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = mii2priv(mii_ts);
568*39db6be7SJonathan Lemon 	struct hwtstamp_config cfg;
569*39db6be7SJonathan Lemon 	u16 mode, ctrl;
570*39db6be7SJonathan Lemon 
571*39db6be7SJonathan Lemon 	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
572*39db6be7SJonathan Lemon 		return -EFAULT;
573*39db6be7SJonathan Lemon 
574*39db6be7SJonathan Lemon 	switch (cfg.rx_filter) {
575*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_NONE:
576*39db6be7SJonathan Lemon 		priv->hwts_rx = false;
577*39db6be7SJonathan Lemon 		break;
578*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
579*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
580*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
581*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
582*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
583*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
584*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_EVENT:
585*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_SYNC:
586*39db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
587*39db6be7SJonathan Lemon 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
588*39db6be7SJonathan Lemon 		priv->hwts_rx = true;
589*39db6be7SJonathan Lemon 		break;
590*39db6be7SJonathan Lemon 	default:
591*39db6be7SJonathan Lemon 		return -ERANGE;
592*39db6be7SJonathan Lemon 	}
593*39db6be7SJonathan Lemon 
594*39db6be7SJonathan Lemon 	priv->tx_type = cfg.tx_type;
595*39db6be7SJonathan Lemon 
596*39db6be7SJonathan Lemon 	ctrl  = priv->hwts_rx ? SLICE_RX_EN : 0;
597*39db6be7SJonathan Lemon 	ctrl |= priv->tx_type != HWTSTAMP_TX_OFF ? SLICE_TX_EN : 0;
598*39db6be7SJonathan Lemon 
599*39db6be7SJonathan Lemon 	mode = TX_MODE_SEL(PORT, SYNC, REPLACE_TS) |
600*39db6be7SJonathan Lemon 	       TX_MODE_SEL(PORT, DELAY_REQ, REPLACE_TS) |
601*39db6be7SJonathan Lemon 	       TX_MODE_SEL(PORT, PDELAY_REQ, REPLACE_TS) |
602*39db6be7SJonathan Lemon 	       TX_MODE_SEL(PORT, PDELAY_RESP, REPLACE_TS);
603*39db6be7SJonathan Lemon 
604*39db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, TX_EVENT_MODE, mode);
605*39db6be7SJonathan Lemon 
606*39db6be7SJonathan Lemon 	mode = RX_MODE_SEL(PORT, SYNC, INSERT_TS_64) |
607*39db6be7SJonathan Lemon 	       RX_MODE_SEL(PORT, DELAY_REQ, INSERT_TS_64) |
608*39db6be7SJonathan Lemon 	       RX_MODE_SEL(PORT, PDELAY_REQ, INSERT_TS_64) |
609*39db6be7SJonathan Lemon 	       RX_MODE_SEL(PORT, PDELAY_RESP, INSERT_TS_64);
610*39db6be7SJonathan Lemon 
611*39db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, RX_EVENT_MODE, mode);
612*39db6be7SJonathan Lemon 
613*39db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, SLICE_CTRL, ctrl);
614*39db6be7SJonathan Lemon 
615*39db6be7SJonathan Lemon 	if (ctrl & SLICE_TX_EN)
616*39db6be7SJonathan Lemon 		bcm_phy_write_exp(priv->phydev, TX_TS_CAPTURE, TX_TS_CAP_EN);
617*39db6be7SJonathan Lemon 	else
618*39db6be7SJonathan Lemon 		ptp_cancel_worker_sync(priv->ptp_clock);
619*39db6be7SJonathan Lemon 
620*39db6be7SJonathan Lemon 	/* purge existing data */
621*39db6be7SJonathan Lemon 	skb_queue_purge(&priv->tx_queue);
622*39db6be7SJonathan Lemon 
623*39db6be7SJonathan Lemon 	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
624*39db6be7SJonathan Lemon }
625*39db6be7SJonathan Lemon 
626*39db6be7SJonathan Lemon static int bcm_ptp_ts_info(struct mii_timestamper *mii_ts,
627*39db6be7SJonathan Lemon 			   struct ethtool_ts_info *ts_info)
628*39db6be7SJonathan Lemon {
629*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = mii2priv(mii_ts);
630*39db6be7SJonathan Lemon 
631*39db6be7SJonathan Lemon 	ts_info->phc_index = ptp_clock_index(priv->ptp_clock);
632*39db6be7SJonathan Lemon 	ts_info->so_timestamping =
633*39db6be7SJonathan Lemon 		SOF_TIMESTAMPING_TX_HARDWARE |
634*39db6be7SJonathan Lemon 		SOF_TIMESTAMPING_RX_HARDWARE |
635*39db6be7SJonathan Lemon 		SOF_TIMESTAMPING_RAW_HARDWARE;
636*39db6be7SJonathan Lemon 	ts_info->tx_types =
637*39db6be7SJonathan Lemon 		BIT(HWTSTAMP_TX_ON) |
638*39db6be7SJonathan Lemon 		BIT(HWTSTAMP_TX_OFF) |
639*39db6be7SJonathan Lemon 		BIT(HWTSTAMP_TX_ONESTEP_SYNC) |
640*39db6be7SJonathan Lemon 		BIT(HWTSTAMP_TX_ONESTEP_P2P);
641*39db6be7SJonathan Lemon 	ts_info->rx_filters =
642*39db6be7SJonathan Lemon 		BIT(HWTSTAMP_FILTER_NONE) |
643*39db6be7SJonathan Lemon 		BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
644*39db6be7SJonathan Lemon 
645*39db6be7SJonathan Lemon 	return 0;
646*39db6be7SJonathan Lemon }
647*39db6be7SJonathan Lemon 
648*39db6be7SJonathan Lemon void bcm_ptp_stop(struct bcm_ptp_private *priv)
649*39db6be7SJonathan Lemon {
650*39db6be7SJonathan Lemon 	ptp_cancel_worker_sync(priv->ptp_clock);
651*39db6be7SJonathan Lemon }
652*39db6be7SJonathan Lemon EXPORT_SYMBOL_GPL(bcm_ptp_stop);
653*39db6be7SJonathan Lemon 
654*39db6be7SJonathan Lemon void bcm_ptp_config_init(struct phy_device *phydev)
655*39db6be7SJonathan Lemon {
656*39db6be7SJonathan Lemon 	/* init network sync engine */
657*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NSE_CTRL, NSE_GMODE_EN | NSE_INIT);
658*39db6be7SJonathan Lemon 
659*39db6be7SJonathan Lemon 	/* enable time sync (TX/RX SOP capture) */
660*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_SYNC, TIME_SYNC_EN);
661*39db6be7SJonathan Lemon 
662*39db6be7SJonathan Lemon 	/* use sec.nsec heartbeat capture */
663*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, DPLL_SELECT, DPLL_HB_MODE2);
664*39db6be7SJonathan Lemon 
665*39db6be7SJonathan Lemon 	/* use 64 bit timecode for TX */
666*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIMECODE_CTRL, TX_TIMECODE_SEL);
667*39db6be7SJonathan Lemon 
668*39db6be7SJonathan Lemon 	/* always allow FREQ_LOAD on framesync */
669*39db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, SHADOW_CTRL, FREQ_LOAD);
670*39db6be7SJonathan Lemon }
671*39db6be7SJonathan Lemon EXPORT_SYMBOL_GPL(bcm_ptp_config_init);
672*39db6be7SJonathan Lemon 
673*39db6be7SJonathan Lemon static void bcm_ptp_init(struct bcm_ptp_private *priv)
674*39db6be7SJonathan Lemon {
675*39db6be7SJonathan Lemon 	priv->nse_ctrl = NSE_GMODE_EN;
676*39db6be7SJonathan Lemon 
677*39db6be7SJonathan Lemon 	mutex_init(&priv->mutex);
678*39db6be7SJonathan Lemon 	skb_queue_head_init(&priv->tx_queue);
679*39db6be7SJonathan Lemon 
680*39db6be7SJonathan Lemon 	priv->mii_ts.rxtstamp = bcm_ptp_rxtstamp;
681*39db6be7SJonathan Lemon 	priv->mii_ts.txtstamp = bcm_ptp_txtstamp;
682*39db6be7SJonathan Lemon 	priv->mii_ts.hwtstamp = bcm_ptp_hwtstamp;
683*39db6be7SJonathan Lemon 	priv->mii_ts.ts_info = bcm_ptp_ts_info;
684*39db6be7SJonathan Lemon 
685*39db6be7SJonathan Lemon 	priv->phydev->mii_ts = &priv->mii_ts;
686*39db6be7SJonathan Lemon }
687*39db6be7SJonathan Lemon 
688*39db6be7SJonathan Lemon struct bcm_ptp_private *bcm_ptp_probe(struct phy_device *phydev)
689*39db6be7SJonathan Lemon {
690*39db6be7SJonathan Lemon 	struct bcm_ptp_private *priv;
691*39db6be7SJonathan Lemon 	struct ptp_clock *clock;
692*39db6be7SJonathan Lemon 
693*39db6be7SJonathan Lemon 	switch (BRCM_PHY_MODEL(phydev)) {
694*39db6be7SJonathan Lemon 	case PHY_ID_BCM54210E:
695*39db6be7SJonathan Lemon 		break;
696*39db6be7SJonathan Lemon 	default:
697*39db6be7SJonathan Lemon 		return NULL;
698*39db6be7SJonathan Lemon 	}
699*39db6be7SJonathan Lemon 
700*39db6be7SJonathan Lemon 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
701*39db6be7SJonathan Lemon 	if (!priv)
702*39db6be7SJonathan Lemon 		return ERR_PTR(-ENOMEM);
703*39db6be7SJonathan Lemon 
704*39db6be7SJonathan Lemon 	priv->ptp_info = bcm_ptp_clock_info;
705*39db6be7SJonathan Lemon 
706*39db6be7SJonathan Lemon 	clock = ptp_clock_register(&priv->ptp_info, &phydev->mdio.dev);
707*39db6be7SJonathan Lemon 	if (IS_ERR(clock))
708*39db6be7SJonathan Lemon 		return ERR_CAST(clock);
709*39db6be7SJonathan Lemon 	priv->ptp_clock = clock;
710*39db6be7SJonathan Lemon 
711*39db6be7SJonathan Lemon 	priv->phydev = phydev;
712*39db6be7SJonathan Lemon 	bcm_ptp_init(priv);
713*39db6be7SJonathan Lemon 
714*39db6be7SJonathan Lemon 	return priv;
715*39db6be7SJonathan Lemon }
716*39db6be7SJonathan Lemon EXPORT_SYMBOL_GPL(bcm_ptp_probe);
717*39db6be7SJonathan Lemon 
718*39db6be7SJonathan Lemon MODULE_LICENSE("GPL");
719