xref: /openbmc/linux/drivers/net/phy/bcm-phy-ptp.c (revision 7bfe91ef)
139db6be7SJonathan Lemon // SPDX-License-Identifier: GPL-2.0
239db6be7SJonathan Lemon /*
339db6be7SJonathan Lemon  * Copyright (C) 2022 Meta Platforms Inc.
439db6be7SJonathan Lemon  * Copyright (C) 2022 Jonathan Lemon <jonathan.lemon@gmail.com>
539db6be7SJonathan Lemon  */
639db6be7SJonathan Lemon 
739db6be7SJonathan Lemon #include <asm/unaligned.h>
839db6be7SJonathan Lemon #include <linux/mii.h>
939db6be7SJonathan Lemon #include <linux/phy.h>
1039db6be7SJonathan Lemon #include <linux/ptp_classify.h>
1139db6be7SJonathan Lemon #include <linux/ptp_clock_kernel.h>
1239db6be7SJonathan Lemon #include <linux/net_tstamp.h>
1339db6be7SJonathan Lemon #include <linux/netdevice.h>
1439db6be7SJonathan Lemon #include <linux/workqueue.h>
1539db6be7SJonathan Lemon 
1639db6be7SJonathan Lemon #include "bcm-phy-lib.h"
1739db6be7SJonathan Lemon 
1839db6be7SJonathan Lemon /* IEEE 1588 Expansion registers */
1939db6be7SJonathan Lemon #define SLICE_CTRL		0x0810
2039db6be7SJonathan Lemon #define  SLICE_TX_EN			BIT(0)
2139db6be7SJonathan Lemon #define  SLICE_RX_EN			BIT(8)
2239db6be7SJonathan Lemon #define TX_EVENT_MODE		0x0811
2339db6be7SJonathan Lemon #define  MODE_TX_UPDATE_CF		BIT(0)
2439db6be7SJonathan Lemon #define  MODE_TX_REPLACE_TS_CF		BIT(1)
2539db6be7SJonathan Lemon #define  MODE_TX_REPLACE_TS		GENMASK(1, 0)
2639db6be7SJonathan Lemon #define RX_EVENT_MODE		0x0819
2739db6be7SJonathan Lemon #define  MODE_RX_UPDATE_CF		BIT(0)
2839db6be7SJonathan Lemon #define  MODE_RX_INSERT_TS_48		BIT(1)
2939db6be7SJonathan Lemon #define  MODE_RX_INSERT_TS_64		GENMASK(1, 0)
3039db6be7SJonathan Lemon 
3139db6be7SJonathan Lemon #define MODE_EVT_SHIFT_SYNC		0
3239db6be7SJonathan Lemon #define MODE_EVT_SHIFT_DELAY_REQ	2
3339db6be7SJonathan Lemon #define MODE_EVT_SHIFT_PDELAY_REQ	4
3439db6be7SJonathan Lemon #define MODE_EVT_SHIFT_PDELAY_RESP	6
3539db6be7SJonathan Lemon 
3639db6be7SJonathan Lemon #define MODE_SEL_SHIFT_PORT		0
3739db6be7SJonathan Lemon #define MODE_SEL_SHIFT_CPU		8
3839db6be7SJonathan Lemon 
3939db6be7SJonathan Lemon #define RX_MODE_SEL(sel, evt, act) \
4039db6be7SJonathan Lemon 	(((MODE_RX_##act) << (MODE_EVT_SHIFT_##evt)) << (MODE_SEL_SHIFT_##sel))
4139db6be7SJonathan Lemon 
4239db6be7SJonathan Lemon #define TX_MODE_SEL(sel, evt, act) \
4339db6be7SJonathan Lemon 	(((MODE_TX_##act) << (MODE_EVT_SHIFT_##evt)) << (MODE_SEL_SHIFT_##sel))
4439db6be7SJonathan Lemon 
4539db6be7SJonathan Lemon /* needs global TS capture first */
4639db6be7SJonathan Lemon #define TX_TS_CAPTURE		0x0821
4739db6be7SJonathan Lemon #define  TX_TS_CAP_EN			BIT(0)
4839db6be7SJonathan Lemon #define RX_TS_CAPTURE		0x0822
4939db6be7SJonathan Lemon #define  RX_TS_CAP_EN			BIT(0)
5039db6be7SJonathan Lemon 
5139db6be7SJonathan Lemon #define TIME_CODE_0		0x0854
5239db6be7SJonathan Lemon #define TIME_CODE_1		0x0855
5339db6be7SJonathan Lemon #define TIME_CODE_2		0x0856
5439db6be7SJonathan Lemon #define TIME_CODE_3		0x0857
5539db6be7SJonathan Lemon #define TIME_CODE_4		0x0858
5639db6be7SJonathan Lemon 
5739db6be7SJonathan Lemon #define DPLL_SELECT		0x085b
5839db6be7SJonathan Lemon #define  DPLL_HB_MODE2			BIT(6)
5939db6be7SJonathan Lemon 
6039db6be7SJonathan Lemon #define SHADOW_CTRL		0x085c
6139db6be7SJonathan Lemon #define SHADOW_LOAD		0x085d
6239db6be7SJonathan Lemon #define  TIME_CODE_LOAD			BIT(10)
6339db6be7SJonathan Lemon #define  SYNC_OUT_LOAD			BIT(9)
6439db6be7SJonathan Lemon #define  NCO_TIME_LOAD			BIT(7)
6539db6be7SJonathan Lemon #define  FREQ_LOAD			BIT(6)
6639db6be7SJonathan Lemon #define INTR_MASK		0x085e
6739db6be7SJonathan Lemon #define INTR_STATUS		0x085f
6839db6be7SJonathan Lemon #define  INTC_FSYNC			BIT(0)
6939db6be7SJonathan Lemon #define  INTC_SOP			BIT(1)
7039db6be7SJonathan Lemon 
7139db6be7SJonathan Lemon #define NCO_FREQ_LSB		0x0873
7239db6be7SJonathan Lemon #define NCO_FREQ_MSB		0x0874
7339db6be7SJonathan Lemon 
7439db6be7SJonathan Lemon #define NCO_TIME_0		0x0875
7539db6be7SJonathan Lemon #define NCO_TIME_1		0x0876
7639db6be7SJonathan Lemon #define NCO_TIME_2_CTRL		0x0877
7739db6be7SJonathan Lemon #define  FREQ_MDIO_SEL			BIT(14)
7839db6be7SJonathan Lemon 
7939db6be7SJonathan Lemon #define SYNC_OUT_0		0x0878
8039db6be7SJonathan Lemon #define SYNC_OUT_1		0x0879
8139db6be7SJonathan Lemon #define SYNC_OUT_2		0x087a
8239db6be7SJonathan Lemon 
83*7bfe91efSJonathan Lemon #define SYNC_IN_DIVIDER		0x087b
84*7bfe91efSJonathan Lemon 
8539db6be7SJonathan Lemon #define SYNOUT_TS_0		0x087c
8639db6be7SJonathan Lemon #define SYNOUT_TS_1		0x087d
8739db6be7SJonathan Lemon #define SYNOUT_TS_2		0x087e
8839db6be7SJonathan Lemon 
8939db6be7SJonathan Lemon #define NSE_CTRL		0x087f
9039db6be7SJonathan Lemon #define  NSE_GMODE_EN			GENMASK(15, 14)
9139db6be7SJonathan Lemon #define  NSE_CAPTURE_EN			BIT(13)
9239db6be7SJonathan Lemon #define  NSE_INIT			BIT(12)
9339db6be7SJonathan Lemon #define  NSE_CPU_FRAMESYNC		BIT(5)
94*7bfe91efSJonathan Lemon #define  NSE_SYNC1_FRAMESYNC		BIT(3)
9539db6be7SJonathan Lemon #define  NSE_FRAMESYNC_MASK		GENMASK(5, 2)
9639db6be7SJonathan Lemon #define  NSE_PEROUT_EN			BIT(1)
9739db6be7SJonathan Lemon #define  NSE_ONESHOT_EN			BIT(0)
9839db6be7SJonathan Lemon #define  NSE_SYNC_OUT_MASK		GENMASK(1, 0)
9939db6be7SJonathan Lemon 
10039db6be7SJonathan Lemon #define TS_READ_CTRL		0x0885
10139db6be7SJonathan Lemon #define  TS_READ_START			BIT(0)
10239db6be7SJonathan Lemon #define  TS_READ_END			BIT(1)
10339db6be7SJonathan Lemon 
10439db6be7SJonathan Lemon #define HB_REG_0		0x0886
10539db6be7SJonathan Lemon #define HB_REG_1		0x0887
10639db6be7SJonathan Lemon #define HB_REG_2		0x0888
10739db6be7SJonathan Lemon #define HB_REG_3		0x08ec
10839db6be7SJonathan Lemon #define HB_REG_4		0x08ed
10939db6be7SJonathan Lemon #define HB_STAT_CTRL		0x088e
11039db6be7SJonathan Lemon #define  HB_READ_START			BIT(10)
11139db6be7SJonathan Lemon #define  HB_READ_END			BIT(11)
11239db6be7SJonathan Lemon #define  HB_READ_MASK			GENMASK(11, 10)
11339db6be7SJonathan Lemon 
11439db6be7SJonathan Lemon #define TS_REG_0		0x0889
11539db6be7SJonathan Lemon #define TS_REG_1		0x088a
11639db6be7SJonathan Lemon #define TS_REG_2		0x088b
11739db6be7SJonathan Lemon #define TS_REG_3		0x08c4
11839db6be7SJonathan Lemon 
11939db6be7SJonathan Lemon #define TS_INFO_0		0x088c
12039db6be7SJonathan Lemon #define TS_INFO_1		0x088d
12139db6be7SJonathan Lemon 
12239db6be7SJonathan Lemon #define TIMECODE_CTRL		0x08c3
12339db6be7SJonathan Lemon #define  TX_TIMECODE_SEL		GENMASK(7, 0)
12439db6be7SJonathan Lemon #define  RX_TIMECODE_SEL		GENMASK(15, 8)
12539db6be7SJonathan Lemon 
12639db6be7SJonathan Lemon #define TIME_SYNC		0x0ff5
12739db6be7SJonathan Lemon #define  TIME_SYNC_EN			BIT(0)
12839db6be7SJonathan Lemon 
12939db6be7SJonathan Lemon struct bcm_ptp_private {
13039db6be7SJonathan Lemon 	struct phy_device *phydev;
13139db6be7SJonathan Lemon 	struct mii_timestamper mii_ts;
13239db6be7SJonathan Lemon 	struct ptp_clock *ptp_clock;
13339db6be7SJonathan Lemon 	struct ptp_clock_info ptp_info;
134*7bfe91efSJonathan Lemon 	struct ptp_pin_desc pin;
13539db6be7SJonathan Lemon 	struct mutex mutex;
13639db6be7SJonathan Lemon 	struct sk_buff_head tx_queue;
13739db6be7SJonathan Lemon 	int tx_type;
13839db6be7SJonathan Lemon 	bool hwts_rx;
13939db6be7SJonathan Lemon 	u16 nse_ctrl;
140*7bfe91efSJonathan Lemon 	bool pin_active;
141*7bfe91efSJonathan Lemon 	struct delayed_work pin_work;
14239db6be7SJonathan Lemon };
14339db6be7SJonathan Lemon 
14439db6be7SJonathan Lemon struct bcm_ptp_skb_cb {
14539db6be7SJonathan Lemon 	unsigned long timeout;
14639db6be7SJonathan Lemon 	u16 seq_id;
14739db6be7SJonathan Lemon 	u8 msgtype;
14839db6be7SJonathan Lemon 	bool discard;
14939db6be7SJonathan Lemon };
15039db6be7SJonathan Lemon 
15139db6be7SJonathan Lemon struct bcm_ptp_capture {
15239db6be7SJonathan Lemon 	ktime_t	hwtstamp;
15339db6be7SJonathan Lemon 	u16 seq_id;
15439db6be7SJonathan Lemon 	u8 msgtype;
15539db6be7SJonathan Lemon 	bool tx_dir;
15639db6be7SJonathan Lemon };
15739db6be7SJonathan Lemon 
15839db6be7SJonathan Lemon #define BCM_SKB_CB(skb)		((struct bcm_ptp_skb_cb *)(skb)->cb)
15939db6be7SJonathan Lemon #define SKB_TS_TIMEOUT		10			/* jiffies */
16039db6be7SJonathan Lemon 
16139db6be7SJonathan Lemon #define BCM_MAX_PULSE_8NS	((1U << 9) - 1)
16239db6be7SJonathan Lemon #define BCM_MAX_PERIOD_8NS	((1U << 30) - 1)
16339db6be7SJonathan Lemon 
16439db6be7SJonathan Lemon #define BRCM_PHY_MODEL(phydev) \
16539db6be7SJonathan Lemon 	((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
16639db6be7SJonathan Lemon 
mii2priv(struct mii_timestamper * mii_ts)16739db6be7SJonathan Lemon static struct bcm_ptp_private *mii2priv(struct mii_timestamper *mii_ts)
16839db6be7SJonathan Lemon {
16939db6be7SJonathan Lemon 	return container_of(mii_ts, struct bcm_ptp_private, mii_ts);
17039db6be7SJonathan Lemon }
17139db6be7SJonathan Lemon 
ptp2priv(struct ptp_clock_info * info)17239db6be7SJonathan Lemon static struct bcm_ptp_private *ptp2priv(struct ptp_clock_info *info)
17339db6be7SJonathan Lemon {
17439db6be7SJonathan Lemon 	return container_of(info, struct bcm_ptp_private, ptp_info);
17539db6be7SJonathan Lemon }
17639db6be7SJonathan Lemon 
bcm_ptp_get_framesync_ts(struct phy_device * phydev,struct timespec64 * ts)17739db6be7SJonathan Lemon static void bcm_ptp_get_framesync_ts(struct phy_device *phydev,
17839db6be7SJonathan Lemon 				     struct timespec64 *ts)
17939db6be7SJonathan Lemon {
18039db6be7SJonathan Lemon 	u16 hb[4];
18139db6be7SJonathan Lemon 
18239db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, HB_STAT_CTRL, HB_READ_START);
18339db6be7SJonathan Lemon 
18439db6be7SJonathan Lemon 	hb[0] = bcm_phy_read_exp(phydev, HB_REG_0);
18539db6be7SJonathan Lemon 	hb[1] = bcm_phy_read_exp(phydev, HB_REG_1);
18639db6be7SJonathan Lemon 	hb[2] = bcm_phy_read_exp(phydev, HB_REG_2);
18739db6be7SJonathan Lemon 	hb[3] = bcm_phy_read_exp(phydev, HB_REG_3);
18839db6be7SJonathan Lemon 
18939db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, HB_STAT_CTRL, HB_READ_END);
19039db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, HB_STAT_CTRL, 0);
19139db6be7SJonathan Lemon 
19239db6be7SJonathan Lemon 	ts->tv_sec = (hb[3] << 16) | hb[2];
19339db6be7SJonathan Lemon 	ts->tv_nsec = (hb[1] << 16) | hb[0];
19439db6be7SJonathan Lemon }
19539db6be7SJonathan Lemon 
bcm_ptp_framesync_disable(struct phy_device * phydev,u16 orig_ctrl)19639db6be7SJonathan Lemon static u16 bcm_ptp_framesync_disable(struct phy_device *phydev, u16 orig_ctrl)
19739db6be7SJonathan Lemon {
19839db6be7SJonathan Lemon 	u16 ctrl = orig_ctrl & ~(NSE_FRAMESYNC_MASK | NSE_CAPTURE_EN);
19939db6be7SJonathan Lemon 
20039db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NSE_CTRL, ctrl);
20139db6be7SJonathan Lemon 
20239db6be7SJonathan Lemon 	return ctrl;
20339db6be7SJonathan Lemon }
20439db6be7SJonathan Lemon 
bcm_ptp_framesync_restore(struct phy_device * phydev,u16 orig_ctrl)20539db6be7SJonathan Lemon static void bcm_ptp_framesync_restore(struct phy_device *phydev, u16 orig_ctrl)
20639db6be7SJonathan Lemon {
20739db6be7SJonathan Lemon 	if (orig_ctrl & NSE_FRAMESYNC_MASK)
20839db6be7SJonathan Lemon 		bcm_phy_write_exp(phydev, NSE_CTRL, orig_ctrl);
20939db6be7SJonathan Lemon }
21039db6be7SJonathan Lemon 
bcm_ptp_framesync(struct phy_device * phydev,u16 ctrl)21139db6be7SJonathan Lemon static void bcm_ptp_framesync(struct phy_device *phydev, u16 ctrl)
21239db6be7SJonathan Lemon {
21339db6be7SJonathan Lemon 	/* trigger framesync - must have 0->1 transition. */
21439db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NSE_CTRL, ctrl | NSE_CPU_FRAMESYNC);
21539db6be7SJonathan Lemon }
21639db6be7SJonathan Lemon 
bcm_ptp_framesync_ts(struct phy_device * phydev,struct ptp_system_timestamp * sts,struct timespec64 * ts,u16 orig_ctrl)21739db6be7SJonathan Lemon static int bcm_ptp_framesync_ts(struct phy_device *phydev,
21839db6be7SJonathan Lemon 				struct ptp_system_timestamp *sts,
21939db6be7SJonathan Lemon 				struct timespec64 *ts,
22039db6be7SJonathan Lemon 				u16 orig_ctrl)
22139db6be7SJonathan Lemon {
22239db6be7SJonathan Lemon 	u16 ctrl, reg;
22339db6be7SJonathan Lemon 	int i;
22439db6be7SJonathan Lemon 
22539db6be7SJonathan Lemon 	ctrl = bcm_ptp_framesync_disable(phydev, orig_ctrl);
22639db6be7SJonathan Lemon 
22739db6be7SJonathan Lemon 	ptp_read_system_prets(sts);
22839db6be7SJonathan Lemon 
22939db6be7SJonathan Lemon 	/* trigger framesync + capture */
23039db6be7SJonathan Lemon 	bcm_ptp_framesync(phydev, ctrl | NSE_CAPTURE_EN);
23139db6be7SJonathan Lemon 
23239db6be7SJonathan Lemon 	ptp_read_system_postts(sts);
23339db6be7SJonathan Lemon 
23439db6be7SJonathan Lemon 	/* poll for FSYNC interrupt from TS capture */
23539db6be7SJonathan Lemon 	for (i = 0; i < 10; i++) {
23639db6be7SJonathan Lemon 		reg = bcm_phy_read_exp(phydev, INTR_STATUS);
23739db6be7SJonathan Lemon 		if (reg & INTC_FSYNC) {
23839db6be7SJonathan Lemon 			bcm_ptp_get_framesync_ts(phydev, ts);
23939db6be7SJonathan Lemon 			break;
24039db6be7SJonathan Lemon 		}
24139db6be7SJonathan Lemon 	}
24239db6be7SJonathan Lemon 
24339db6be7SJonathan Lemon 	bcm_ptp_framesync_restore(phydev, orig_ctrl);
24439db6be7SJonathan Lemon 
24539db6be7SJonathan Lemon 	return reg & INTC_FSYNC ? 0 : -ETIMEDOUT;
24639db6be7SJonathan Lemon }
24739db6be7SJonathan Lemon 
bcm_ptp_gettimex(struct ptp_clock_info * info,struct timespec64 * ts,struct ptp_system_timestamp * sts)24839db6be7SJonathan Lemon static int bcm_ptp_gettimex(struct ptp_clock_info *info,
24939db6be7SJonathan Lemon 			    struct timespec64 *ts,
25039db6be7SJonathan Lemon 			    struct ptp_system_timestamp *sts)
25139db6be7SJonathan Lemon {
25239db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
25339db6be7SJonathan Lemon 	int err;
25439db6be7SJonathan Lemon 
25539db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
25639db6be7SJonathan Lemon 	err = bcm_ptp_framesync_ts(priv->phydev, sts, ts, priv->nse_ctrl);
25739db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
25839db6be7SJonathan Lemon 
25939db6be7SJonathan Lemon 	return err;
26039db6be7SJonathan Lemon }
26139db6be7SJonathan Lemon 
bcm_ptp_settime_locked(struct bcm_ptp_private * priv,const struct timespec64 * ts)26239db6be7SJonathan Lemon static int bcm_ptp_settime_locked(struct bcm_ptp_private *priv,
26339db6be7SJonathan Lemon 				  const struct timespec64 *ts)
26439db6be7SJonathan Lemon {
26539db6be7SJonathan Lemon 	struct phy_device *phydev = priv->phydev;
26639db6be7SJonathan Lemon 	u16 ctrl;
26739db6be7SJonathan Lemon 	u64 ns;
26839db6be7SJonathan Lemon 
26939db6be7SJonathan Lemon 	ctrl = bcm_ptp_framesync_disable(phydev, priv->nse_ctrl);
27039db6be7SJonathan Lemon 
27139db6be7SJonathan Lemon 	/* set up time code */
27239db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_0, ts->tv_nsec);
27339db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_1, ts->tv_nsec >> 16);
27439db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_2, ts->tv_sec);
27539db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_3, ts->tv_sec >> 16);
27639db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_CODE_4, ts->tv_sec >> 32);
27739db6be7SJonathan Lemon 
27839db6be7SJonathan Lemon 	/* set NCO counter to match */
27939db6be7SJonathan Lemon 	ns = timespec64_to_ns(ts);
28039db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NCO_TIME_0, ns >> 4);
28139db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NCO_TIME_1, ns >> 20);
28239db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NCO_TIME_2_CTRL, (ns >> 36) & 0xfff);
28339db6be7SJonathan Lemon 
28439db6be7SJonathan Lemon 	/* set up load on next frame sync (auto-clears due to NSE_INIT) */
28539db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, SHADOW_LOAD, TIME_CODE_LOAD | NCO_TIME_LOAD);
28639db6be7SJonathan Lemon 
28739db6be7SJonathan Lemon 	/* must have NSE_INIT in order to write time code */
28839db6be7SJonathan Lemon 	bcm_ptp_framesync(phydev, ctrl | NSE_INIT);
28939db6be7SJonathan Lemon 
29039db6be7SJonathan Lemon 	bcm_ptp_framesync_restore(phydev, priv->nse_ctrl);
29139db6be7SJonathan Lemon 
29239db6be7SJonathan Lemon 	return 0;
29339db6be7SJonathan Lemon }
29439db6be7SJonathan Lemon 
bcm_ptp_settime(struct ptp_clock_info * info,const struct timespec64 * ts)29539db6be7SJonathan Lemon static int bcm_ptp_settime(struct ptp_clock_info *info,
29639db6be7SJonathan Lemon 			   const struct timespec64 *ts)
29739db6be7SJonathan Lemon {
29839db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
29939db6be7SJonathan Lemon 	int err;
30039db6be7SJonathan Lemon 
30139db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
30239db6be7SJonathan Lemon 	err = bcm_ptp_settime_locked(priv, ts);
30339db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
30439db6be7SJonathan Lemon 
30539db6be7SJonathan Lemon 	return err;
30639db6be7SJonathan Lemon }
30739db6be7SJonathan Lemon 
bcm_ptp_adjtime_locked(struct bcm_ptp_private * priv,s64 delta_ns)30839db6be7SJonathan Lemon static int bcm_ptp_adjtime_locked(struct bcm_ptp_private *priv,
30939db6be7SJonathan Lemon 				  s64 delta_ns)
31039db6be7SJonathan Lemon {
31139db6be7SJonathan Lemon 	struct timespec64 ts;
31239db6be7SJonathan Lemon 	int err;
31339db6be7SJonathan Lemon 	s64 ns;
31439db6be7SJonathan Lemon 
31539db6be7SJonathan Lemon 	err = bcm_ptp_framesync_ts(priv->phydev, NULL, &ts, priv->nse_ctrl);
31639db6be7SJonathan Lemon 	if (!err) {
31739db6be7SJonathan Lemon 		ns = timespec64_to_ns(&ts) + delta_ns;
31839db6be7SJonathan Lemon 		ts = ns_to_timespec64(ns);
31939db6be7SJonathan Lemon 		err = bcm_ptp_settime_locked(priv, &ts);
32039db6be7SJonathan Lemon 	}
32139db6be7SJonathan Lemon 	return err;
32239db6be7SJonathan Lemon }
32339db6be7SJonathan Lemon 
bcm_ptp_adjtime(struct ptp_clock_info * info,s64 delta_ns)32439db6be7SJonathan Lemon static int bcm_ptp_adjtime(struct ptp_clock_info *info, s64 delta_ns)
32539db6be7SJonathan Lemon {
32639db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
32739db6be7SJonathan Lemon 	int err;
32839db6be7SJonathan Lemon 
32939db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
33039db6be7SJonathan Lemon 	err = bcm_ptp_adjtime_locked(priv, delta_ns);
33139db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
33239db6be7SJonathan Lemon 
33339db6be7SJonathan Lemon 	return err;
33439db6be7SJonathan Lemon }
33539db6be7SJonathan Lemon 
33639db6be7SJonathan Lemon /* A 125Mhz clock should adjust 8ns per pulse.
33739db6be7SJonathan Lemon  * The frequency adjustment base is 0x8000 0000, or 8*2^28.
33839db6be7SJonathan Lemon  *
33939db6be7SJonathan Lemon  * Frequency adjustment is
34039db6be7SJonathan Lemon  * adj = scaled_ppm * 8*2^28 / (10^6 * 2^16)
34139db6be7SJonathan Lemon  *   which simplifies to:
34239db6be7SJonathan Lemon  * adj = scaled_ppm * 2^9 / 5^6
34339db6be7SJonathan Lemon  */
bcm_ptp_adjfine(struct ptp_clock_info * info,long scaled_ppm)34439db6be7SJonathan Lemon static int bcm_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
34539db6be7SJonathan Lemon {
34639db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
34739db6be7SJonathan Lemon 	int neg_adj = 0;
34839db6be7SJonathan Lemon 	u32 diff, freq;
34939db6be7SJonathan Lemon 	u16 ctrl;
35039db6be7SJonathan Lemon 	u64 adj;
35139db6be7SJonathan Lemon 
35239db6be7SJonathan Lemon 	if (scaled_ppm < 0) {
35339db6be7SJonathan Lemon 		neg_adj = 1;
35439db6be7SJonathan Lemon 		scaled_ppm = -scaled_ppm;
35539db6be7SJonathan Lemon 	}
35639db6be7SJonathan Lemon 
35739db6be7SJonathan Lemon 	adj = scaled_ppm << 9;
35839db6be7SJonathan Lemon 	diff = div_u64(adj, 15625);
35939db6be7SJonathan Lemon 	freq = (8 << 28) + (neg_adj ? -diff : diff);
36039db6be7SJonathan Lemon 
36139db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
36239db6be7SJonathan Lemon 
36339db6be7SJonathan Lemon 	ctrl = bcm_ptp_framesync_disable(priv->phydev, priv->nse_ctrl);
36439db6be7SJonathan Lemon 
36539db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, NCO_FREQ_LSB, freq);
36639db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, NCO_FREQ_MSB, freq >> 16);
36739db6be7SJonathan Lemon 
36839db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, NCO_TIME_2_CTRL, FREQ_MDIO_SEL);
36939db6be7SJonathan Lemon 
37039db6be7SJonathan Lemon 	/* load on next framesync */
37139db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, SHADOW_LOAD, FREQ_LOAD);
37239db6be7SJonathan Lemon 
37339db6be7SJonathan Lemon 	bcm_ptp_framesync(priv->phydev, ctrl);
37439db6be7SJonathan Lemon 
37539db6be7SJonathan Lemon 	/* clear load */
37639db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, SHADOW_LOAD, 0);
37739db6be7SJonathan Lemon 
37839db6be7SJonathan Lemon 	bcm_ptp_framesync_restore(priv->phydev, priv->nse_ctrl);
37939db6be7SJonathan Lemon 
38039db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
38139db6be7SJonathan Lemon 
38239db6be7SJonathan Lemon 	return 0;
38339db6be7SJonathan Lemon }
38439db6be7SJonathan Lemon 
bcm_ptp_rxtstamp(struct mii_timestamper * mii_ts,struct sk_buff * skb,int type)38539db6be7SJonathan Lemon static bool bcm_ptp_rxtstamp(struct mii_timestamper *mii_ts,
38639db6be7SJonathan Lemon 			     struct sk_buff *skb, int type)
38739db6be7SJonathan Lemon {
38839db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = mii2priv(mii_ts);
38939db6be7SJonathan Lemon 	struct skb_shared_hwtstamps *hwts;
39039db6be7SJonathan Lemon 	struct ptp_header *header;
39139db6be7SJonathan Lemon 	u32 sec, nsec;
39239db6be7SJonathan Lemon 	u8 *data;
39339db6be7SJonathan Lemon 	int off;
39439db6be7SJonathan Lemon 
39539db6be7SJonathan Lemon 	if (!priv->hwts_rx)
39639db6be7SJonathan Lemon 		return false;
39739db6be7SJonathan Lemon 
39839db6be7SJonathan Lemon 	header = ptp_parse_header(skb, type);
39939db6be7SJonathan Lemon 	if (!header)
40039db6be7SJonathan Lemon 		return false;
40139db6be7SJonathan Lemon 
40239db6be7SJonathan Lemon 	data = (u8 *)(header + 1);
40339db6be7SJonathan Lemon 	sec = get_unaligned_be32(data);
40439db6be7SJonathan Lemon 	nsec = get_unaligned_be32(data + 4);
40539db6be7SJonathan Lemon 
40639db6be7SJonathan Lemon 	hwts = skb_hwtstamps(skb);
40739db6be7SJonathan Lemon 	hwts->hwtstamp = ktime_set(sec, nsec);
40839db6be7SJonathan Lemon 
40939db6be7SJonathan Lemon 	off = data - skb->data + 8;
41039db6be7SJonathan Lemon 	if (off < skb->len) {
41139db6be7SJonathan Lemon 		memmove(data, data + 8, skb->len - off);
41239db6be7SJonathan Lemon 		__pskb_trim(skb, skb->len - 8);
41339db6be7SJonathan Lemon 	}
41439db6be7SJonathan Lemon 
41539db6be7SJonathan Lemon 	return false;
41639db6be7SJonathan Lemon }
41739db6be7SJonathan Lemon 
bcm_ptp_get_tstamp(struct bcm_ptp_private * priv,struct bcm_ptp_capture * capts)41839db6be7SJonathan Lemon static bool bcm_ptp_get_tstamp(struct bcm_ptp_private *priv,
41939db6be7SJonathan Lemon 			       struct bcm_ptp_capture *capts)
42039db6be7SJonathan Lemon {
42139db6be7SJonathan Lemon 	struct phy_device *phydev = priv->phydev;
42239db6be7SJonathan Lemon 	u16 ts[4], reg;
42339db6be7SJonathan Lemon 	u32 sec, nsec;
42439db6be7SJonathan Lemon 
42539db6be7SJonathan Lemon 	mutex_lock(&priv->mutex);
42639db6be7SJonathan Lemon 
42739db6be7SJonathan Lemon 	reg = bcm_phy_read_exp(phydev, INTR_STATUS);
42839db6be7SJonathan Lemon 	if ((reg & INTC_SOP) == 0) {
42939db6be7SJonathan Lemon 		mutex_unlock(&priv->mutex);
43039db6be7SJonathan Lemon 		return false;
43139db6be7SJonathan Lemon 	}
43239db6be7SJonathan Lemon 
43339db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TS_READ_CTRL, TS_READ_START);
43439db6be7SJonathan Lemon 
43539db6be7SJonathan Lemon 	ts[0] = bcm_phy_read_exp(phydev, TS_REG_0);
43639db6be7SJonathan Lemon 	ts[1] = bcm_phy_read_exp(phydev, TS_REG_1);
43739db6be7SJonathan Lemon 	ts[2] = bcm_phy_read_exp(phydev, TS_REG_2);
43839db6be7SJonathan Lemon 	ts[3] = bcm_phy_read_exp(phydev, TS_REG_3);
43939db6be7SJonathan Lemon 
44039db6be7SJonathan Lemon 	/* not in be32 format for some reason */
44139db6be7SJonathan Lemon 	capts->seq_id = bcm_phy_read_exp(priv->phydev, TS_INFO_0);
44239db6be7SJonathan Lemon 
44339db6be7SJonathan Lemon 	reg = bcm_phy_read_exp(phydev, TS_INFO_1);
44439db6be7SJonathan Lemon 	capts->msgtype = reg >> 12;
44539db6be7SJonathan Lemon 	capts->tx_dir = !!(reg & BIT(11));
44639db6be7SJonathan Lemon 
44739db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TS_READ_CTRL, TS_READ_END);
44839db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TS_READ_CTRL, 0);
44939db6be7SJonathan Lemon 
45039db6be7SJonathan Lemon 	mutex_unlock(&priv->mutex);
45139db6be7SJonathan Lemon 
45239db6be7SJonathan Lemon 	sec = (ts[3] << 16) | ts[2];
45339db6be7SJonathan Lemon 	nsec = (ts[1] << 16) | ts[0];
45439db6be7SJonathan Lemon 	capts->hwtstamp = ktime_set(sec, nsec);
45539db6be7SJonathan Lemon 
45639db6be7SJonathan Lemon 	return true;
45739db6be7SJonathan Lemon }
45839db6be7SJonathan Lemon 
bcm_ptp_match_tstamp(struct bcm_ptp_private * priv,struct bcm_ptp_capture * capts)45939db6be7SJonathan Lemon static void bcm_ptp_match_tstamp(struct bcm_ptp_private *priv,
46039db6be7SJonathan Lemon 				 struct bcm_ptp_capture *capts)
46139db6be7SJonathan Lemon {
46239db6be7SJonathan Lemon 	struct skb_shared_hwtstamps hwts;
46339db6be7SJonathan Lemon 	struct sk_buff *skb, *ts_skb;
46439db6be7SJonathan Lemon 	unsigned long flags;
46539db6be7SJonathan Lemon 	bool first = false;
46639db6be7SJonathan Lemon 
46739db6be7SJonathan Lemon 	ts_skb = NULL;
46839db6be7SJonathan Lemon 	spin_lock_irqsave(&priv->tx_queue.lock, flags);
46939db6be7SJonathan Lemon 	skb_queue_walk(&priv->tx_queue, skb) {
47039db6be7SJonathan Lemon 		if (BCM_SKB_CB(skb)->seq_id == capts->seq_id &&
47139db6be7SJonathan Lemon 		    BCM_SKB_CB(skb)->msgtype == capts->msgtype) {
47239db6be7SJonathan Lemon 			first = skb_queue_is_first(&priv->tx_queue, skb);
47339db6be7SJonathan Lemon 			__skb_unlink(skb, &priv->tx_queue);
47439db6be7SJonathan Lemon 			ts_skb = skb;
47539db6be7SJonathan Lemon 			break;
47639db6be7SJonathan Lemon 		}
47739db6be7SJonathan Lemon 	}
47839db6be7SJonathan Lemon 	spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
47939db6be7SJonathan Lemon 
48039db6be7SJonathan Lemon 	/* TX captures one-step packets, discard them if needed. */
48139db6be7SJonathan Lemon 	if (ts_skb) {
48239db6be7SJonathan Lemon 		if (BCM_SKB_CB(ts_skb)->discard) {
48339db6be7SJonathan Lemon 			kfree_skb(ts_skb);
48439db6be7SJonathan Lemon 		} else {
48539db6be7SJonathan Lemon 			memset(&hwts, 0, sizeof(hwts));
48639db6be7SJonathan Lemon 			hwts.hwtstamp = capts->hwtstamp;
48739db6be7SJonathan Lemon 			skb_complete_tx_timestamp(ts_skb, &hwts);
48839db6be7SJonathan Lemon 		}
48939db6be7SJonathan Lemon 	}
49039db6be7SJonathan Lemon 
49139db6be7SJonathan Lemon 	/* not first match, try and expire entries */
49239db6be7SJonathan Lemon 	if (!first) {
49339db6be7SJonathan Lemon 		while ((skb = skb_dequeue(&priv->tx_queue))) {
49439db6be7SJonathan Lemon 			if (!time_after(jiffies, BCM_SKB_CB(skb)->timeout)) {
49539db6be7SJonathan Lemon 				skb_queue_head(&priv->tx_queue, skb);
49639db6be7SJonathan Lemon 				break;
49739db6be7SJonathan Lemon 			}
49839db6be7SJonathan Lemon 			kfree_skb(skb);
49939db6be7SJonathan Lemon 		}
50039db6be7SJonathan Lemon 	}
50139db6be7SJonathan Lemon }
50239db6be7SJonathan Lemon 
bcm_ptp_do_aux_work(struct ptp_clock_info * info)50339db6be7SJonathan Lemon static long bcm_ptp_do_aux_work(struct ptp_clock_info *info)
50439db6be7SJonathan Lemon {
50539db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
50639db6be7SJonathan Lemon 	struct bcm_ptp_capture capts;
50739db6be7SJonathan Lemon 	bool reschedule = false;
50839db6be7SJonathan Lemon 
50939db6be7SJonathan Lemon 	while (!skb_queue_empty_lockless(&priv->tx_queue)) {
51039db6be7SJonathan Lemon 		if (!bcm_ptp_get_tstamp(priv, &capts)) {
51139db6be7SJonathan Lemon 			reschedule = true;
51239db6be7SJonathan Lemon 			break;
51339db6be7SJonathan Lemon 		}
51439db6be7SJonathan Lemon 		bcm_ptp_match_tstamp(priv, &capts);
51539db6be7SJonathan Lemon 	}
51639db6be7SJonathan Lemon 
51739db6be7SJonathan Lemon 	return reschedule ? 1 : -1;
51839db6be7SJonathan Lemon }
51939db6be7SJonathan Lemon 
bcm_ptp_cancel_func(struct bcm_ptp_private * priv)520*7bfe91efSJonathan Lemon static int bcm_ptp_cancel_func(struct bcm_ptp_private *priv)
521*7bfe91efSJonathan Lemon {
522*7bfe91efSJonathan Lemon 	if (!priv->pin_active)
523*7bfe91efSJonathan Lemon 		return 0;
524*7bfe91efSJonathan Lemon 
525*7bfe91efSJonathan Lemon 	priv->pin_active = false;
526*7bfe91efSJonathan Lemon 
527*7bfe91efSJonathan Lemon 	priv->nse_ctrl &= ~(NSE_SYNC_OUT_MASK | NSE_SYNC1_FRAMESYNC |
528*7bfe91efSJonathan Lemon 			    NSE_CAPTURE_EN);
529*7bfe91efSJonathan Lemon 	bcm_phy_write_exp(priv->phydev, NSE_CTRL, priv->nse_ctrl);
530*7bfe91efSJonathan Lemon 
531*7bfe91efSJonathan Lemon 	cancel_delayed_work_sync(&priv->pin_work);
532*7bfe91efSJonathan Lemon 
533*7bfe91efSJonathan Lemon 	return 0;
534*7bfe91efSJonathan Lemon }
535*7bfe91efSJonathan Lemon 
bcm_ptp_perout_work(struct work_struct * pin_work)536*7bfe91efSJonathan Lemon static void bcm_ptp_perout_work(struct work_struct *pin_work)
537*7bfe91efSJonathan Lemon {
538*7bfe91efSJonathan Lemon 	struct bcm_ptp_private *priv =
539*7bfe91efSJonathan Lemon 		container_of(pin_work, struct bcm_ptp_private, pin_work.work);
540*7bfe91efSJonathan Lemon 	struct phy_device *phydev = priv->phydev;
541*7bfe91efSJonathan Lemon 	struct timespec64 ts;
542*7bfe91efSJonathan Lemon 	u64 ns, next;
543*7bfe91efSJonathan Lemon 	u16 ctrl;
544*7bfe91efSJonathan Lemon 
545*7bfe91efSJonathan Lemon 	mutex_lock(&priv->mutex);
546*7bfe91efSJonathan Lemon 
547*7bfe91efSJonathan Lemon 	/* no longer running */
548*7bfe91efSJonathan Lemon 	if (!priv->pin_active) {
549*7bfe91efSJonathan Lemon 		mutex_unlock(&priv->mutex);
550*7bfe91efSJonathan Lemon 		return;
551*7bfe91efSJonathan Lemon 	}
552*7bfe91efSJonathan Lemon 
553*7bfe91efSJonathan Lemon 	bcm_ptp_framesync_ts(phydev, NULL, &ts, priv->nse_ctrl);
554*7bfe91efSJonathan Lemon 
555*7bfe91efSJonathan Lemon 	/* this is 1PPS only */
556*7bfe91efSJonathan Lemon 	next = NSEC_PER_SEC - ts.tv_nsec;
557*7bfe91efSJonathan Lemon 	ts.tv_sec += next < NSEC_PER_MSEC ? 2 : 1;
558*7bfe91efSJonathan Lemon 	ts.tv_nsec = 0;
559*7bfe91efSJonathan Lemon 
560*7bfe91efSJonathan Lemon 	ns = timespec64_to_ns(&ts);
561*7bfe91efSJonathan Lemon 
562*7bfe91efSJonathan Lemon 	/* force 0->1 transition for ONESHOT */
563*7bfe91efSJonathan Lemon 	ctrl = bcm_ptp_framesync_disable(phydev,
564*7bfe91efSJonathan Lemon 					 priv->nse_ctrl & ~NSE_ONESHOT_EN);
565*7bfe91efSJonathan Lemon 
566*7bfe91efSJonathan Lemon 	bcm_phy_write_exp(phydev, SYNOUT_TS_0, ns & 0xfff0);
567*7bfe91efSJonathan Lemon 	bcm_phy_write_exp(phydev, SYNOUT_TS_1, ns >> 16);
568*7bfe91efSJonathan Lemon 	bcm_phy_write_exp(phydev, SYNOUT_TS_2, ns >> 32);
569*7bfe91efSJonathan Lemon 
570*7bfe91efSJonathan Lemon 	/* load values on next framesync */
571*7bfe91efSJonathan Lemon 	bcm_phy_write_exp(phydev, SHADOW_LOAD, SYNC_OUT_LOAD);
572*7bfe91efSJonathan Lemon 
573*7bfe91efSJonathan Lemon 	bcm_ptp_framesync(phydev, ctrl | NSE_ONESHOT_EN | NSE_INIT);
574*7bfe91efSJonathan Lemon 
575*7bfe91efSJonathan Lemon 	priv->nse_ctrl |= NSE_ONESHOT_EN;
576*7bfe91efSJonathan Lemon 	bcm_ptp_framesync_restore(phydev, priv->nse_ctrl);
577*7bfe91efSJonathan Lemon 
578*7bfe91efSJonathan Lemon 	mutex_unlock(&priv->mutex);
579*7bfe91efSJonathan Lemon 
580*7bfe91efSJonathan Lemon 	next = next + NSEC_PER_MSEC;
581*7bfe91efSJonathan Lemon 	schedule_delayed_work(&priv->pin_work, nsecs_to_jiffies(next));
582*7bfe91efSJonathan Lemon }
583*7bfe91efSJonathan Lemon 
bcm_ptp_perout_locked(struct bcm_ptp_private * priv,struct ptp_perout_request * req,int on)584*7bfe91efSJonathan Lemon static int bcm_ptp_perout_locked(struct bcm_ptp_private *priv,
585*7bfe91efSJonathan Lemon 				 struct ptp_perout_request *req, int on)
586*7bfe91efSJonathan Lemon {
587*7bfe91efSJonathan Lemon 	struct phy_device *phydev = priv->phydev;
588*7bfe91efSJonathan Lemon 	u64 period, pulse;
589*7bfe91efSJonathan Lemon 	u16 val;
590*7bfe91efSJonathan Lemon 
591*7bfe91efSJonathan Lemon 	if (!on)
592*7bfe91efSJonathan Lemon 		return bcm_ptp_cancel_func(priv);
593*7bfe91efSJonathan Lemon 
594*7bfe91efSJonathan Lemon 	/* 1PPS */
595*7bfe91efSJonathan Lemon 	if (req->period.sec != 1 || req->period.nsec != 0)
596*7bfe91efSJonathan Lemon 		return -EINVAL;
597*7bfe91efSJonathan Lemon 
598*7bfe91efSJonathan Lemon 	period = BCM_MAX_PERIOD_8NS;	/* write nonzero value */
599*7bfe91efSJonathan Lemon 
600*7bfe91efSJonathan Lemon 	if (req->flags & PTP_PEROUT_PHASE)
601*7bfe91efSJonathan Lemon 		return -EOPNOTSUPP;
602*7bfe91efSJonathan Lemon 
603*7bfe91efSJonathan Lemon 	if (req->flags & PTP_PEROUT_DUTY_CYCLE)
604*7bfe91efSJonathan Lemon 		pulse = ktime_to_ns(ktime_set(req->on.sec, req->on.nsec));
605*7bfe91efSJonathan Lemon 	else
606*7bfe91efSJonathan Lemon 		pulse = (u64)BCM_MAX_PULSE_8NS << 3;
607*7bfe91efSJonathan Lemon 
608*7bfe91efSJonathan Lemon 	/* convert to 8ns units */
609*7bfe91efSJonathan Lemon 	pulse >>= 3;
610*7bfe91efSJonathan Lemon 
611*7bfe91efSJonathan Lemon 	if (!pulse || pulse > period || pulse > BCM_MAX_PULSE_8NS)
612*7bfe91efSJonathan Lemon 		return -EINVAL;
613*7bfe91efSJonathan Lemon 
614*7bfe91efSJonathan Lemon 	bcm_phy_write_exp(phydev, SYNC_OUT_0, period);
615*7bfe91efSJonathan Lemon 
616*7bfe91efSJonathan Lemon 	val = ((pulse & 0x3) << 14) | ((period >> 16) & 0x3fff);
617*7bfe91efSJonathan Lemon 	bcm_phy_write_exp(phydev, SYNC_OUT_1, val);
618*7bfe91efSJonathan Lemon 
619*7bfe91efSJonathan Lemon 	val = ((pulse >> 2) & 0x7f) | (pulse << 7);
620*7bfe91efSJonathan Lemon 	bcm_phy_write_exp(phydev, SYNC_OUT_2, val);
621*7bfe91efSJonathan Lemon 
622*7bfe91efSJonathan Lemon 	if (priv->pin_active)
623*7bfe91efSJonathan Lemon 		cancel_delayed_work_sync(&priv->pin_work);
624*7bfe91efSJonathan Lemon 
625*7bfe91efSJonathan Lemon 	priv->pin_active = true;
626*7bfe91efSJonathan Lemon 	INIT_DELAYED_WORK(&priv->pin_work, bcm_ptp_perout_work);
627*7bfe91efSJonathan Lemon 	schedule_delayed_work(&priv->pin_work, 0);
628*7bfe91efSJonathan Lemon 
629*7bfe91efSJonathan Lemon 	return 0;
630*7bfe91efSJonathan Lemon }
631*7bfe91efSJonathan Lemon 
bcm_ptp_extts_work(struct work_struct * pin_work)632*7bfe91efSJonathan Lemon static void bcm_ptp_extts_work(struct work_struct *pin_work)
633*7bfe91efSJonathan Lemon {
634*7bfe91efSJonathan Lemon 	struct bcm_ptp_private *priv =
635*7bfe91efSJonathan Lemon 		container_of(pin_work, struct bcm_ptp_private, pin_work.work);
636*7bfe91efSJonathan Lemon 	struct phy_device *phydev = priv->phydev;
637*7bfe91efSJonathan Lemon 	struct ptp_clock_event event;
638*7bfe91efSJonathan Lemon 	struct timespec64 ts;
639*7bfe91efSJonathan Lemon 	u16 reg;
640*7bfe91efSJonathan Lemon 
641*7bfe91efSJonathan Lemon 	mutex_lock(&priv->mutex);
642*7bfe91efSJonathan Lemon 
643*7bfe91efSJonathan Lemon 	/* no longer running */
644*7bfe91efSJonathan Lemon 	if (!priv->pin_active) {
645*7bfe91efSJonathan Lemon 		mutex_unlock(&priv->mutex);
646*7bfe91efSJonathan Lemon 		return;
647*7bfe91efSJonathan Lemon 	}
648*7bfe91efSJonathan Lemon 
649*7bfe91efSJonathan Lemon 	reg = bcm_phy_read_exp(phydev, INTR_STATUS);
650*7bfe91efSJonathan Lemon 	if ((reg & INTC_FSYNC) == 0)
651*7bfe91efSJonathan Lemon 		goto out;
652*7bfe91efSJonathan Lemon 
653*7bfe91efSJonathan Lemon 	bcm_ptp_get_framesync_ts(phydev, &ts);
654*7bfe91efSJonathan Lemon 
655*7bfe91efSJonathan Lemon 	event.index = 0;
656*7bfe91efSJonathan Lemon 	event.type = PTP_CLOCK_EXTTS;
657*7bfe91efSJonathan Lemon 	event.timestamp = timespec64_to_ns(&ts);
658*7bfe91efSJonathan Lemon 	ptp_clock_event(priv->ptp_clock, &event);
659*7bfe91efSJonathan Lemon 
660*7bfe91efSJonathan Lemon out:
661*7bfe91efSJonathan Lemon 	mutex_unlock(&priv->mutex);
662*7bfe91efSJonathan Lemon 	schedule_delayed_work(&priv->pin_work, HZ / 4);
663*7bfe91efSJonathan Lemon }
664*7bfe91efSJonathan Lemon 
bcm_ptp_extts_locked(struct bcm_ptp_private * priv,int on)665*7bfe91efSJonathan Lemon static int bcm_ptp_extts_locked(struct bcm_ptp_private *priv, int on)
666*7bfe91efSJonathan Lemon {
667*7bfe91efSJonathan Lemon 	struct phy_device *phydev = priv->phydev;
668*7bfe91efSJonathan Lemon 
669*7bfe91efSJonathan Lemon 	if (!on)
670*7bfe91efSJonathan Lemon 		return bcm_ptp_cancel_func(priv);
671*7bfe91efSJonathan Lemon 
672*7bfe91efSJonathan Lemon 	if (priv->pin_active)
673*7bfe91efSJonathan Lemon 		cancel_delayed_work_sync(&priv->pin_work);
674*7bfe91efSJonathan Lemon 
675*7bfe91efSJonathan Lemon 	bcm_ptp_framesync_disable(phydev, priv->nse_ctrl);
676*7bfe91efSJonathan Lemon 
677*7bfe91efSJonathan Lemon 	priv->nse_ctrl |= NSE_SYNC1_FRAMESYNC | NSE_CAPTURE_EN;
678*7bfe91efSJonathan Lemon 
679*7bfe91efSJonathan Lemon 	bcm_ptp_framesync_restore(phydev, priv->nse_ctrl);
680*7bfe91efSJonathan Lemon 
681*7bfe91efSJonathan Lemon 	priv->pin_active = true;
682*7bfe91efSJonathan Lemon 	INIT_DELAYED_WORK(&priv->pin_work, bcm_ptp_extts_work);
683*7bfe91efSJonathan Lemon 	schedule_delayed_work(&priv->pin_work, 0);
684*7bfe91efSJonathan Lemon 
685*7bfe91efSJonathan Lemon 	return 0;
686*7bfe91efSJonathan Lemon }
687*7bfe91efSJonathan Lemon 
bcm_ptp_enable(struct ptp_clock_info * info,struct ptp_clock_request * rq,int on)688*7bfe91efSJonathan Lemon static int bcm_ptp_enable(struct ptp_clock_info *info,
689*7bfe91efSJonathan Lemon 			  struct ptp_clock_request *rq, int on)
690*7bfe91efSJonathan Lemon {
691*7bfe91efSJonathan Lemon 	struct bcm_ptp_private *priv = ptp2priv(info);
692*7bfe91efSJonathan Lemon 	int err = -EBUSY;
693*7bfe91efSJonathan Lemon 
694*7bfe91efSJonathan Lemon 	mutex_lock(&priv->mutex);
695*7bfe91efSJonathan Lemon 
696*7bfe91efSJonathan Lemon 	switch (rq->type) {
697*7bfe91efSJonathan Lemon 	case PTP_CLK_REQ_PEROUT:
698*7bfe91efSJonathan Lemon 		if (priv->pin.func == PTP_PF_PEROUT)
699*7bfe91efSJonathan Lemon 			err = bcm_ptp_perout_locked(priv, &rq->perout, on);
700*7bfe91efSJonathan Lemon 		break;
701*7bfe91efSJonathan Lemon 	case PTP_CLK_REQ_EXTTS:
702*7bfe91efSJonathan Lemon 		if (priv->pin.func == PTP_PF_EXTTS)
703*7bfe91efSJonathan Lemon 			err = bcm_ptp_extts_locked(priv, on);
704*7bfe91efSJonathan Lemon 		break;
705*7bfe91efSJonathan Lemon 	default:
706*7bfe91efSJonathan Lemon 		err = -EOPNOTSUPP;
707*7bfe91efSJonathan Lemon 		break;
708*7bfe91efSJonathan Lemon 	}
709*7bfe91efSJonathan Lemon 
710*7bfe91efSJonathan Lemon 	mutex_unlock(&priv->mutex);
711*7bfe91efSJonathan Lemon 
712*7bfe91efSJonathan Lemon 	return err;
713*7bfe91efSJonathan Lemon }
714*7bfe91efSJonathan Lemon 
bcm_ptp_verify(struct ptp_clock_info * info,unsigned int pin,enum ptp_pin_function func,unsigned int chan)715*7bfe91efSJonathan Lemon static int bcm_ptp_verify(struct ptp_clock_info *info, unsigned int pin,
716*7bfe91efSJonathan Lemon 			  enum ptp_pin_function func, unsigned int chan)
717*7bfe91efSJonathan Lemon {
718*7bfe91efSJonathan Lemon 	switch (func) {
719*7bfe91efSJonathan Lemon 	case PTP_PF_NONE:
720*7bfe91efSJonathan Lemon 	case PTP_PF_EXTTS:
721*7bfe91efSJonathan Lemon 	case PTP_PF_PEROUT:
722*7bfe91efSJonathan Lemon 		break;
723*7bfe91efSJonathan Lemon 	default:
724*7bfe91efSJonathan Lemon 		return -EOPNOTSUPP;
725*7bfe91efSJonathan Lemon 	}
726*7bfe91efSJonathan Lemon 	return 0;
727*7bfe91efSJonathan Lemon }
728*7bfe91efSJonathan Lemon 
72939db6be7SJonathan Lemon static const struct ptp_clock_info bcm_ptp_clock_info = {
73039db6be7SJonathan Lemon 	.owner		= THIS_MODULE,
73139db6be7SJonathan Lemon 	.name		= KBUILD_MODNAME,
73239db6be7SJonathan Lemon 	.max_adj	= 100000000,
73339db6be7SJonathan Lemon 	.gettimex64	= bcm_ptp_gettimex,
73439db6be7SJonathan Lemon 	.settime64	= bcm_ptp_settime,
73539db6be7SJonathan Lemon 	.adjtime	= bcm_ptp_adjtime,
73639db6be7SJonathan Lemon 	.adjfine	= bcm_ptp_adjfine,
737*7bfe91efSJonathan Lemon 	.enable		= bcm_ptp_enable,
738*7bfe91efSJonathan Lemon 	.verify		= bcm_ptp_verify,
73939db6be7SJonathan Lemon 	.do_aux_work	= bcm_ptp_do_aux_work,
740*7bfe91efSJonathan Lemon 	.n_pins		= 1,
741*7bfe91efSJonathan Lemon 	.n_per_out	= 1,
742*7bfe91efSJonathan Lemon 	.n_ext_ts	= 1,
74339db6be7SJonathan Lemon };
74439db6be7SJonathan Lemon 
bcm_ptp_txtstamp(struct mii_timestamper * mii_ts,struct sk_buff * skb,int type)74539db6be7SJonathan Lemon static void bcm_ptp_txtstamp(struct mii_timestamper *mii_ts,
74639db6be7SJonathan Lemon 			     struct sk_buff *skb, int type)
74739db6be7SJonathan Lemon {
74839db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = mii2priv(mii_ts);
74939db6be7SJonathan Lemon 	struct ptp_header *hdr;
75039db6be7SJonathan Lemon 	bool discard = false;
75139db6be7SJonathan Lemon 	int msgtype;
75239db6be7SJonathan Lemon 
75339db6be7SJonathan Lemon 	hdr = ptp_parse_header(skb, type);
75439db6be7SJonathan Lemon 	if (!hdr)
75539db6be7SJonathan Lemon 		goto out;
75639db6be7SJonathan Lemon 	msgtype = ptp_get_msgtype(hdr, type);
75739db6be7SJonathan Lemon 
75839db6be7SJonathan Lemon 	switch (priv->tx_type) {
75939db6be7SJonathan Lemon 	case HWTSTAMP_TX_ONESTEP_P2P:
76039db6be7SJonathan Lemon 		if (msgtype == PTP_MSGTYPE_PDELAY_RESP)
76139db6be7SJonathan Lemon 			discard = true;
76239db6be7SJonathan Lemon 		fallthrough;
76339db6be7SJonathan Lemon 	case HWTSTAMP_TX_ONESTEP_SYNC:
76439db6be7SJonathan Lemon 		if (msgtype == PTP_MSGTYPE_SYNC)
76539db6be7SJonathan Lemon 			discard = true;
76639db6be7SJonathan Lemon 		fallthrough;
76739db6be7SJonathan Lemon 	case HWTSTAMP_TX_ON:
76839db6be7SJonathan Lemon 		BCM_SKB_CB(skb)->timeout = jiffies + SKB_TS_TIMEOUT;
76939db6be7SJonathan Lemon 		BCM_SKB_CB(skb)->seq_id = be16_to_cpu(hdr->sequence_id);
77039db6be7SJonathan Lemon 		BCM_SKB_CB(skb)->msgtype = msgtype;
77139db6be7SJonathan Lemon 		BCM_SKB_CB(skb)->discard = discard;
77239db6be7SJonathan Lemon 		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
77339db6be7SJonathan Lemon 		skb_queue_tail(&priv->tx_queue, skb);
77439db6be7SJonathan Lemon 		ptp_schedule_worker(priv->ptp_clock, 0);
77539db6be7SJonathan Lemon 		return;
77639db6be7SJonathan Lemon 	default:
77739db6be7SJonathan Lemon 		break;
77839db6be7SJonathan Lemon 	}
77939db6be7SJonathan Lemon 
78039db6be7SJonathan Lemon out:
78139db6be7SJonathan Lemon 	kfree_skb(skb);
78239db6be7SJonathan Lemon }
78339db6be7SJonathan Lemon 
bcm_ptp_hwtstamp(struct mii_timestamper * mii_ts,struct ifreq * ifr)78439db6be7SJonathan Lemon static int bcm_ptp_hwtstamp(struct mii_timestamper *mii_ts,
78539db6be7SJonathan Lemon 			    struct ifreq *ifr)
78639db6be7SJonathan Lemon {
78739db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = mii2priv(mii_ts);
78839db6be7SJonathan Lemon 	struct hwtstamp_config cfg;
78939db6be7SJonathan Lemon 	u16 mode, ctrl;
79039db6be7SJonathan Lemon 
79139db6be7SJonathan Lemon 	if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
79239db6be7SJonathan Lemon 		return -EFAULT;
79339db6be7SJonathan Lemon 
79439db6be7SJonathan Lemon 	switch (cfg.rx_filter) {
79539db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_NONE:
79639db6be7SJonathan Lemon 		priv->hwts_rx = false;
79739db6be7SJonathan Lemon 		break;
79839db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
79939db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
80039db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
80139db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
80239db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
80339db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
80439db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_EVENT:
80539db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_SYNC:
80639db6be7SJonathan Lemon 	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
80739db6be7SJonathan Lemon 		cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
80839db6be7SJonathan Lemon 		priv->hwts_rx = true;
80939db6be7SJonathan Lemon 		break;
81039db6be7SJonathan Lemon 	default:
81139db6be7SJonathan Lemon 		return -ERANGE;
81239db6be7SJonathan Lemon 	}
81339db6be7SJonathan Lemon 
81439db6be7SJonathan Lemon 	priv->tx_type = cfg.tx_type;
81539db6be7SJonathan Lemon 
81639db6be7SJonathan Lemon 	ctrl  = priv->hwts_rx ? SLICE_RX_EN : 0;
81739db6be7SJonathan Lemon 	ctrl |= priv->tx_type != HWTSTAMP_TX_OFF ? SLICE_TX_EN : 0;
81839db6be7SJonathan Lemon 
81939db6be7SJonathan Lemon 	mode = TX_MODE_SEL(PORT, SYNC, REPLACE_TS) |
82039db6be7SJonathan Lemon 	       TX_MODE_SEL(PORT, DELAY_REQ, REPLACE_TS) |
82139db6be7SJonathan Lemon 	       TX_MODE_SEL(PORT, PDELAY_REQ, REPLACE_TS) |
82239db6be7SJonathan Lemon 	       TX_MODE_SEL(PORT, PDELAY_RESP, REPLACE_TS);
82339db6be7SJonathan Lemon 
82439db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, TX_EVENT_MODE, mode);
82539db6be7SJonathan Lemon 
82639db6be7SJonathan Lemon 	mode = RX_MODE_SEL(PORT, SYNC, INSERT_TS_64) |
82739db6be7SJonathan Lemon 	       RX_MODE_SEL(PORT, DELAY_REQ, INSERT_TS_64) |
82839db6be7SJonathan Lemon 	       RX_MODE_SEL(PORT, PDELAY_REQ, INSERT_TS_64) |
82939db6be7SJonathan Lemon 	       RX_MODE_SEL(PORT, PDELAY_RESP, INSERT_TS_64);
83039db6be7SJonathan Lemon 
83139db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, RX_EVENT_MODE, mode);
83239db6be7SJonathan Lemon 
83339db6be7SJonathan Lemon 	bcm_phy_write_exp(priv->phydev, SLICE_CTRL, ctrl);
83439db6be7SJonathan Lemon 
83539db6be7SJonathan Lemon 	if (ctrl & SLICE_TX_EN)
83639db6be7SJonathan Lemon 		bcm_phy_write_exp(priv->phydev, TX_TS_CAPTURE, TX_TS_CAP_EN);
83739db6be7SJonathan Lemon 	else
83839db6be7SJonathan Lemon 		ptp_cancel_worker_sync(priv->ptp_clock);
83939db6be7SJonathan Lemon 
84039db6be7SJonathan Lemon 	/* purge existing data */
84139db6be7SJonathan Lemon 	skb_queue_purge(&priv->tx_queue);
84239db6be7SJonathan Lemon 
84339db6be7SJonathan Lemon 	return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
84439db6be7SJonathan Lemon }
84539db6be7SJonathan Lemon 
bcm_ptp_ts_info(struct mii_timestamper * mii_ts,struct ethtool_ts_info * ts_info)84639db6be7SJonathan Lemon static int bcm_ptp_ts_info(struct mii_timestamper *mii_ts,
84739db6be7SJonathan Lemon 			   struct ethtool_ts_info *ts_info)
84839db6be7SJonathan Lemon {
84939db6be7SJonathan Lemon 	struct bcm_ptp_private *priv = mii2priv(mii_ts);
85039db6be7SJonathan Lemon 
85139db6be7SJonathan Lemon 	ts_info->phc_index = ptp_clock_index(priv->ptp_clock);
85239db6be7SJonathan Lemon 	ts_info->so_timestamping =
85339db6be7SJonathan Lemon 		SOF_TIMESTAMPING_TX_HARDWARE |
85439db6be7SJonathan Lemon 		SOF_TIMESTAMPING_RX_HARDWARE |
85539db6be7SJonathan Lemon 		SOF_TIMESTAMPING_RAW_HARDWARE;
85639db6be7SJonathan Lemon 	ts_info->tx_types =
85739db6be7SJonathan Lemon 		BIT(HWTSTAMP_TX_ON) |
85839db6be7SJonathan Lemon 		BIT(HWTSTAMP_TX_OFF) |
85939db6be7SJonathan Lemon 		BIT(HWTSTAMP_TX_ONESTEP_SYNC) |
86039db6be7SJonathan Lemon 		BIT(HWTSTAMP_TX_ONESTEP_P2P);
86139db6be7SJonathan Lemon 	ts_info->rx_filters =
86239db6be7SJonathan Lemon 		BIT(HWTSTAMP_FILTER_NONE) |
86339db6be7SJonathan Lemon 		BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
86439db6be7SJonathan Lemon 
86539db6be7SJonathan Lemon 	return 0;
86639db6be7SJonathan Lemon }
86739db6be7SJonathan Lemon 
bcm_ptp_stop(struct bcm_ptp_private * priv)86839db6be7SJonathan Lemon void bcm_ptp_stop(struct bcm_ptp_private *priv)
86939db6be7SJonathan Lemon {
87039db6be7SJonathan Lemon 	ptp_cancel_worker_sync(priv->ptp_clock);
871*7bfe91efSJonathan Lemon 	bcm_ptp_cancel_func(priv);
87239db6be7SJonathan Lemon }
87339db6be7SJonathan Lemon EXPORT_SYMBOL_GPL(bcm_ptp_stop);
87439db6be7SJonathan Lemon 
bcm_ptp_config_init(struct phy_device * phydev)87539db6be7SJonathan Lemon void bcm_ptp_config_init(struct phy_device *phydev)
87639db6be7SJonathan Lemon {
87739db6be7SJonathan Lemon 	/* init network sync engine */
87839db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, NSE_CTRL, NSE_GMODE_EN | NSE_INIT);
87939db6be7SJonathan Lemon 
88039db6be7SJonathan Lemon 	/* enable time sync (TX/RX SOP capture) */
88139db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIME_SYNC, TIME_SYNC_EN);
88239db6be7SJonathan Lemon 
88339db6be7SJonathan Lemon 	/* use sec.nsec heartbeat capture */
88439db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, DPLL_SELECT, DPLL_HB_MODE2);
88539db6be7SJonathan Lemon 
88639db6be7SJonathan Lemon 	/* use 64 bit timecode for TX */
88739db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, TIMECODE_CTRL, TX_TIMECODE_SEL);
88839db6be7SJonathan Lemon 
88939db6be7SJonathan Lemon 	/* always allow FREQ_LOAD on framesync */
89039db6be7SJonathan Lemon 	bcm_phy_write_exp(phydev, SHADOW_CTRL, FREQ_LOAD);
891*7bfe91efSJonathan Lemon 
892*7bfe91efSJonathan Lemon 	bcm_phy_write_exp(phydev, SYNC_IN_DIVIDER, 1);
89339db6be7SJonathan Lemon }
89439db6be7SJonathan Lemon EXPORT_SYMBOL_GPL(bcm_ptp_config_init);
89539db6be7SJonathan Lemon 
bcm_ptp_init(struct bcm_ptp_private * priv)89639db6be7SJonathan Lemon static void bcm_ptp_init(struct bcm_ptp_private *priv)
89739db6be7SJonathan Lemon {
89839db6be7SJonathan Lemon 	priv->nse_ctrl = NSE_GMODE_EN;
89939db6be7SJonathan Lemon 
90039db6be7SJonathan Lemon 	mutex_init(&priv->mutex);
90139db6be7SJonathan Lemon 	skb_queue_head_init(&priv->tx_queue);
90239db6be7SJonathan Lemon 
90339db6be7SJonathan Lemon 	priv->mii_ts.rxtstamp = bcm_ptp_rxtstamp;
90439db6be7SJonathan Lemon 	priv->mii_ts.txtstamp = bcm_ptp_txtstamp;
90539db6be7SJonathan Lemon 	priv->mii_ts.hwtstamp = bcm_ptp_hwtstamp;
90639db6be7SJonathan Lemon 	priv->mii_ts.ts_info = bcm_ptp_ts_info;
90739db6be7SJonathan Lemon 
90839db6be7SJonathan Lemon 	priv->phydev->mii_ts = &priv->mii_ts;
90939db6be7SJonathan Lemon }
91039db6be7SJonathan Lemon 
bcm_ptp_probe(struct phy_device * phydev)91139db6be7SJonathan Lemon struct bcm_ptp_private *bcm_ptp_probe(struct phy_device *phydev)
91239db6be7SJonathan Lemon {
91339db6be7SJonathan Lemon 	struct bcm_ptp_private *priv;
91439db6be7SJonathan Lemon 	struct ptp_clock *clock;
91539db6be7SJonathan Lemon 
91639db6be7SJonathan Lemon 	switch (BRCM_PHY_MODEL(phydev)) {
91739db6be7SJonathan Lemon 	case PHY_ID_BCM54210E:
91839db6be7SJonathan Lemon 		break;
91939db6be7SJonathan Lemon 	default:
92039db6be7SJonathan Lemon 		return NULL;
92139db6be7SJonathan Lemon 	}
92239db6be7SJonathan Lemon 
92339db6be7SJonathan Lemon 	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
92439db6be7SJonathan Lemon 	if (!priv)
92539db6be7SJonathan Lemon 		return ERR_PTR(-ENOMEM);
92639db6be7SJonathan Lemon 
92739db6be7SJonathan Lemon 	priv->ptp_info = bcm_ptp_clock_info;
92839db6be7SJonathan Lemon 
929*7bfe91efSJonathan Lemon 	snprintf(priv->pin.name, sizeof(priv->pin.name), "SYNC_OUT");
930*7bfe91efSJonathan Lemon 	priv->ptp_info.pin_config = &priv->pin;
931*7bfe91efSJonathan Lemon 
93239db6be7SJonathan Lemon 	clock = ptp_clock_register(&priv->ptp_info, &phydev->mdio.dev);
93339db6be7SJonathan Lemon 	if (IS_ERR(clock))
93439db6be7SJonathan Lemon 		return ERR_CAST(clock);
93539db6be7SJonathan Lemon 	priv->ptp_clock = clock;
93639db6be7SJonathan Lemon 
93739db6be7SJonathan Lemon 	priv->phydev = phydev;
93839db6be7SJonathan Lemon 	bcm_ptp_init(priv);
93939db6be7SJonathan Lemon 
94039db6be7SJonathan Lemon 	return priv;
94139db6be7SJonathan Lemon }
94239db6be7SJonathan Lemon EXPORT_SYMBOL_GPL(bcm_ptp_probe);
94339db6be7SJonathan Lemon 
94439db6be7SJonathan Lemon MODULE_LICENSE("GPL");
945