From: Jonathan Lemon <jonathan.lemon@gmail.com>
To: netdev@vger.kernel.org
Cc: kernel-team@fb.com, Andrew Lunn <andrew@lunn.ch>,
Florian Fainelli <f.fainelli@gmail.com>,
Richard Cochran <richardcochran@gmail.com>,
Lasse Johnsen <l@ssejohnsen.me>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Broadcom internal kernel review list
<bcm-kernel-feedback-list@broadcom.com>
Subject: [PATCH net-next v7 3/3] net: phy: Add support for 1PPS out and external timestamps
Date: Mon, 13 Jun 2022 22:08:10 -0700 [thread overview]
Message-ID: <20220614050810.54425-4-jonathan.lemon@gmail.com> (raw)
In-Reply-To: <20220614050810.54425-1-jonathan.lemon@gmail.com>
The perout function is used to generate a 1PPS signal, synchronized
to the PHC. This is accomplished by a using the hardware oneshot
functionality, which is reset by a timer.
The external timestamp function is set up for a 1PPS input pulse,
and uses a timer to poll for temestamps.
Both functions use the SYNC_OUT/SYNC_IN1 pin, so cannot run
simultaneously.
Co-developed-by: Lasse Johnsen <l@ssejohnsen.me>
Signed-off-by: Lasse Johnsen <l@ssejohnsen.me>
Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
---
drivers/net/phy/bcm-phy-ptp.c | 227 ++++++++++++++++++++++++++++++++++
1 file changed, 227 insertions(+)
diff --git a/drivers/net/phy/bcm-phy-ptp.c b/drivers/net/phy/bcm-phy-ptp.c
index 6f462c232e9e..cd8dab4d4e10 100644
--- a/drivers/net/phy/bcm-phy-ptp.c
+++ b/drivers/net/phy/bcm-phy-ptp.c
@@ -80,6 +80,8 @@
#define SYNC_OUT_1 0x0879
#define SYNC_OUT_2 0x087a
+#define SYNC_IN_DIVIDER 0x087b
+
#define SYNOUT_TS_0 0x087c
#define SYNOUT_TS_1 0x087d
#define SYNOUT_TS_2 0x087e
@@ -89,6 +91,7 @@
#define NSE_CAPTURE_EN BIT(13)
#define NSE_INIT BIT(12)
#define NSE_CPU_FRAMESYNC BIT(5)
+#define NSE_SYNC1_FRAMESYNC BIT(3)
#define NSE_FRAMESYNC_MASK GENMASK(5, 2)
#define NSE_PEROUT_EN BIT(1)
#define NSE_ONESHOT_EN BIT(0)
@@ -128,11 +131,14 @@ struct bcm_ptp_private {
struct mii_timestamper mii_ts;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_info;
+ struct ptp_pin_desc pin;
struct mutex mutex;
struct sk_buff_head tx_queue;
int tx_type;
bool hwts_rx;
u16 nse_ctrl;
+ bool pin_active;
+ struct delayed_work pin_work;
};
struct bcm_ptp_skb_cb {
@@ -511,6 +517,216 @@ static long bcm_ptp_do_aux_work(struct ptp_clock_info *info)
return reschedule ? 1 : -1;
}
+static int bcm_ptp_cancel_func(struct bcm_ptp_private *priv)
+{
+ if (priv->pin_active) {
+ priv->pin_active = false;
+ priv->nse_ctrl &= ~(NSE_SYNC_OUT_MASK | NSE_SYNC1_FRAMESYNC |
+ NSE_CAPTURE_EN);
+ bcm_phy_write_exp(priv->phydev, NSE_CTRL, priv->nse_ctrl);
+ cancel_delayed_work_sync(&priv->pin_work);
+ }
+ return 0;
+}
+
+static void bcm_ptp_perout_work(struct work_struct *pin_work)
+{
+ struct bcm_ptp_private *priv =
+ container_of(pin_work, struct bcm_ptp_private, pin_work.work);
+ struct phy_device *phydev = priv->phydev;
+ struct timespec64 ts;
+ u64 ns, next;
+ u16 ctrl;
+
+ mutex_lock(&priv->mutex);
+
+ /* no longer running */
+ if (!priv->pin_active) {
+ mutex_unlock(&priv->mutex);
+ return;
+ }
+
+ bcm_ptp_framesync_ts(phydev, NULL, &ts, priv->nse_ctrl);
+
+ /* this is 1PPS only */
+ next = NSEC_PER_SEC - ts.tv_nsec;
+ ts.tv_sec += next < NSEC_PER_MSEC ? 2 : 1;
+ ts.tv_nsec = 0;
+
+ ns = timespec64_to_ns(&ts);
+
+ /* force 0->1 transition for ONESHOT */
+ ctrl = bcm_ptp_framesync_disable(phydev,
+ priv->nse_ctrl & ~NSE_ONESHOT_EN);
+
+ bcm_phy_write_exp(phydev, SYNOUT_TS_0, ns & 0xfff0);
+ bcm_phy_write_exp(phydev, SYNOUT_TS_1, ns >> 16);
+ bcm_phy_write_exp(phydev, SYNOUT_TS_2, ns >> 32);
+
+ /* load values on next framesync */
+ bcm_phy_write_exp(phydev, SHADOW_LOAD, SYNC_OUT_LOAD);
+
+ bcm_ptp_framesync(phydev, ctrl | NSE_ONESHOT_EN | NSE_INIT);
+
+ priv->nse_ctrl |= NSE_ONESHOT_EN;
+ bcm_ptp_framesync_restore(phydev, priv->nse_ctrl);
+
+ mutex_unlock(&priv->mutex);
+
+ next = next + NSEC_PER_MSEC;
+ schedule_delayed_work(&priv->pin_work, nsecs_to_jiffies(next));
+}
+
+static int bcm_ptp_perout_locked(struct bcm_ptp_private *priv,
+ struct ptp_perout_request *req, int on)
+{
+ struct phy_device *phydev = priv->phydev;
+ u64 period, pulse;
+ u16 val;
+
+ if (!on)
+ return bcm_ptp_cancel_func(priv);
+
+ /* 1PPS */
+ if (req->period.sec != 1 || req->period.nsec != 0)
+ return -EINVAL;
+ period = BCM_MAX_PERIOD_8NS; /* write nonzero value */
+
+ if (req->flags & PTP_PEROUT_PHASE)
+ return -EOPNOTSUPP;
+
+ if (req->flags & PTP_PEROUT_DUTY_CYCLE)
+ pulse = ktime_to_ns(ktime_set(req->on.sec, req->on.nsec));
+ else
+ pulse = (u64)BCM_MAX_PULSE_8NS << 3;
+
+ /* convert to 8ns units */
+ pulse >>= 3;
+
+ if (!pulse)
+ return -EINVAL;
+
+ if (pulse > period)
+ return -EINVAL;
+
+ if (pulse > BCM_MAX_PULSE_8NS)
+ return -EINVAL;
+
+ bcm_phy_write_exp(phydev, SYNC_OUT_0, period);
+
+ val = ((pulse & 0x3) << 14) | ((period >> 16) & 0x3fff);
+ bcm_phy_write_exp(phydev, SYNC_OUT_1, val);
+
+ val = ((pulse >> 2) & 0x7f) | (pulse << 7);
+ bcm_phy_write_exp(phydev, SYNC_OUT_2, val);
+
+ if (priv->pin_active)
+ cancel_delayed_work_sync(&priv->pin_work);
+
+ priv->pin_active = true;
+ INIT_DELAYED_WORK(&priv->pin_work, bcm_ptp_perout_work);
+ schedule_delayed_work(&priv->pin_work, 0);
+
+ return 0;
+}
+
+static void bcm_ptp_extts_work(struct work_struct *pin_work)
+{
+ struct bcm_ptp_private *priv =
+ container_of(pin_work, struct bcm_ptp_private, pin_work.work);
+ struct phy_device *phydev = priv->phydev;
+ struct ptp_clock_event event;
+ struct timespec64 ts;
+ u16 reg;
+
+ mutex_lock(&priv->mutex);
+
+ /* no longer running */
+ if (!priv->pin_active) {
+ mutex_unlock(&priv->mutex);
+ return;
+ }
+
+ reg = bcm_phy_read_exp(phydev, INTR_STATUS);
+ if ((reg & INTC_FSYNC) == 0)
+ goto out;
+
+ bcm_ptp_get_framesync_ts(phydev, &ts);
+
+ event.index = 0;
+ event.type = PTP_CLOCK_EXTTS;
+ event.timestamp = timespec64_to_ns(&ts);
+ ptp_clock_event(priv->ptp_clock, &event);
+
+out:
+ mutex_unlock(&priv->mutex);
+ schedule_delayed_work(&priv->pin_work, HZ / 4);
+}
+
+static int bcm_ptp_extts_locked(struct bcm_ptp_private *priv, int on)
+{
+ struct phy_device *phydev = priv->phydev;
+
+ if (!on)
+ return bcm_ptp_cancel_func(priv);
+
+ if (priv->pin_active)
+ cancel_delayed_work_sync(&priv->pin_work);
+
+ bcm_ptp_framesync_disable(phydev, priv->nse_ctrl);
+
+ priv->nse_ctrl |= NSE_SYNC1_FRAMESYNC | NSE_CAPTURE_EN;
+
+ bcm_ptp_framesync_restore(phydev, priv->nse_ctrl);
+
+ priv->pin_active = true;
+ INIT_DELAYED_WORK(&priv->pin_work, bcm_ptp_extts_work);
+ schedule_delayed_work(&priv->pin_work, 0);
+
+ return 0;
+}
+
+static int bcm_ptp_enable(struct ptp_clock_info *info,
+ struct ptp_clock_request *rq, int on)
+{
+ struct bcm_ptp_private *priv = ptp2priv(info);
+ int err = -EBUSY;
+
+ mutex_lock(&priv->mutex);
+
+ switch (rq->type) {
+ case PTP_CLK_REQ_PEROUT:
+ if (priv->pin.func == PTP_PF_PEROUT)
+ err = bcm_ptp_perout_locked(priv, &rq->perout, on);
+ break;
+ case PTP_CLK_REQ_EXTTS:
+ if (priv->pin.func == PTP_PF_EXTTS)
+ err = bcm_ptp_extts_locked(priv, on);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ mutex_unlock(&priv->mutex);
+
+ return err;
+}
+
+static int bcm_ptp_verify(struct ptp_clock_info *info, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ switch (func) {
+ case PTP_PF_NONE:
+ case PTP_PF_EXTTS:
+ case PTP_PF_PEROUT:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
static const struct ptp_clock_info bcm_ptp_clock_info = {
.owner = THIS_MODULE,
.name = KBUILD_MODNAME,
@@ -519,7 +735,12 @@ static const struct ptp_clock_info bcm_ptp_clock_info = {
.settime64 = bcm_ptp_settime,
.adjtime = bcm_ptp_adjtime,
.adjfine = bcm_ptp_adjfine,
+ .enable = bcm_ptp_enable,
+ .verify = bcm_ptp_verify,
.do_aux_work = bcm_ptp_do_aux_work,
+ .n_pins = 1,
+ .n_per_out = 1,
+ .n_ext_ts = 1,
};
static void bcm_ptp_txtstamp(struct mii_timestamper *mii_ts,
@@ -648,6 +869,7 @@ static int bcm_ptp_ts_info(struct mii_timestamper *mii_ts,
void bcm_ptp_stop(struct bcm_ptp_private *priv)
{
ptp_cancel_worker_sync(priv->ptp_clock);
+ bcm_ptp_cancel_func(priv);
}
EXPORT_SYMBOL_GPL(bcm_ptp_stop);
@@ -667,6 +889,8 @@ void bcm_ptp_config_init(struct phy_device *phydev)
/* always allow FREQ_LOAD on framesync */
bcm_phy_write_exp(phydev, SHADOW_CTRL, FREQ_LOAD);
+
+ bcm_phy_write_exp(phydev, SYNC_IN_DIVIDER, 1);
}
EXPORT_SYMBOL_GPL(bcm_ptp_config_init);
@@ -703,6 +927,9 @@ struct bcm_ptp_private *bcm_ptp_probe(struct phy_device *phydev)
priv->ptp_info = bcm_ptp_clock_info;
+ snprintf(priv->pin.name, sizeof(priv->pin.name), "SYNC_OUT");
+ priv->ptp_info.pin_config = &priv->pin;
+
clock = ptp_clock_register(&priv->ptp_info, &phydev->mdio.dev);
if (IS_ERR(clock))
return ERR_CAST(clock);
--
2.34.3
next prev parent reply other threads:[~2022-06-14 5:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-14 5:08 [PATCH net-next v7 0/3] Broadcom PTP PHY support Jonathan Lemon
2022-06-14 5:08 ` [PATCH net-next v7 1/3] net: phy: broadcom: Add Broadcom PTP hooks to bcm-phy-lib Jonathan Lemon
2022-06-14 5:08 ` [PATCH net-next v7 2/3] net: phy: broadcom: Add PTP support for some Broadcom PHYs Jonathan Lemon
2022-06-14 16:18 ` Florian Fainelli
2022-06-14 5:08 ` Jonathan Lemon [this message]
2022-06-14 16:25 ` [PATCH net-next v7 3/3] net: phy: Add support for 1PPS out and external timestamps Florian Fainelli
2022-06-15 2:25 ` Jonathan Lemon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220614050810.54425-4-jonathan.lemon@gmail.com \
--to=jonathan.lemon@gmail.com \
--cc=andrew@lunn.ch \
--cc=bcm-kernel-feedback-list@broadcom.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=f.fainelli@gmail.com \
--cc=hkallweit1@gmail.com \
--cc=kernel-team@fb.com \
--cc=kuba@kernel.org \
--cc=l@ssejohnsen.me \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=richardcochran@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.