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