From: Ivan Vecera <ivecera@redhat.com>
To: netdev@vger.kernel.org
Cc: Chris du Quesnay <Chris.duQuesnay@microchip.com>,
Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>,
"David S. Miller" <davem@davemloft.net>,
Jakub Kicinski <kuba@kernel.org>, Jiri Pirko <jiri@resnulli.us>,
Michal Schmidt <mschmidt@redhat.com>,
Paolo Abeni <pabeni@redhat.com>,
Pasi Vaananen <pvaanane@redhat.com>, Petr Oros <poros@redhat.com>,
Prathosh Satish <Prathosh.Satish@microchip.com>,
Richard Cochran <richardcochran@gmail.com>,
Simon Horman <horms@kernel.org>,
Vadim Fedorenko <vadim.fedorenko@linux.dev>,
linux-kernel@vger.kernel.org
Subject: [PATCH net-next 4/4] dpll: zl3073x: add PTP clock adjphase and TIE support
Date: Wed, 8 Jul 2026 19:05:27 +0200 [thread overview]
Message-ID: <20260708170527.916035-5-ivecera@redhat.com> (raw)
In-Reply-To: <20260708170527.916035-1-ivecera@redhat.com>
Add adjphase and getmaxphase PTP callbacks using TIE write for
sub-picosecond phase adjustment in AUTO and REFLOCK modes.
Extend the adjtime callback to handle non-NCO modes: in AUTO/REFLOCK
modes the sub-second remainder is applied via TIE write instead of
phase step. In modes that support neither, the full adjustment falls
back to ToD read-modify-write.
Remove the NCO-only gate from PTP clock registration since the PTP
clock now provides useful operations in all modes. Add a mode check
to adjfine so it returns -EOPNOTSUPP when not in NCO mode.
Tested-by: Chris du Quesnay <Chris.duQuesnay@microchip.com>
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
drivers/dpll/zl3073x/dpll.c | 102 +++++++++++++++++++++++++++---------
1 file changed, 78 insertions(+), 24 deletions(-)
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index 86770dea3d652..74001e6d4a32f 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -2489,10 +2489,13 @@ static int zl3073x_dpll_ptp_adjtime_phase_step(struct zl3073x_dpll *zldpll,
* @info: PTP clock info
* @delta: time adjustment in nanoseconds
*
- * For large deltas (>= 1 second), the seconds part is adjusted via ToD
- * read-modify-write at 1 Hz boundaries and the sub-second remainder via
- * output phase step. For sub-second deltas, uses phase step directly.
- * Falls back to full ToD read-modify-write if phase step is unavailable.
+ * In NCO mode, large deltas (>= 1 second) are split into a ToD
+ * read-modify-write for the seconds part and an output phase step for
+ * the sub-second remainder. Sub-second deltas use phase step directly,
+ * falling back to ToD read-modify-write if phase step is unavailable.
+ * In AUTO/REFLOCK modes, large deltas are split into ToD
+ * read-modify-write for seconds and TIE write for the sub-second
+ * remainder. Sub-second deltas use TIE write directly.
*
* Return: 0 on success, <0 on error
*/
@@ -2501,6 +2504,7 @@ static int zl3073x_dpll_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
struct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,
ptp_info);
struct zl3073x_dev *zldev = zldpll->dev;
+ const struct zl3073x_chan *chan;
bool sec_adjusted = false;
struct timespec64 ts;
int rc;
@@ -2510,8 +2514,16 @@ static int zl3073x_dpll_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
guard(mutex)(&zldpll->lock);
+ /* Modes without phase step or TIE use plain ToD adjust */
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
+ if (!zl3073x_chan_mode_is_nco(chan) &&
+ !zl3073x_chan_mode_supports_tie(chan))
+ return zl3073x_chan_tod_adjust(zldev, zldpll->id,
+ ns_to_timespec64(delta));
+
/* Split off seconds via ToD read-modify-write so the sub-second
- * remainder can be applied through the output-coherent phase step.
+ * remainder can be applied through the output-coherent mechanism
+ * (phase step or TIE write).
*/
if (abs(delta) >= NSEC_PER_SEC) {
s32 remainder;
@@ -2531,19 +2543,26 @@ static int zl3073x_dpll_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
return 0;
}
- rc = zl3073x_dpll_ptp_adjtime_phase_step(zldpll, delta);
- if (!rc)
- return 0;
+ /* Apply sub-second delta via phase step (NCO) or TIE write */
+ if (zl3073x_chan_mode_is_nco(chan)) {
+ rc = zl3073x_dpll_ptp_adjtime_phase_step(zldpll, delta);
+ if (!rc)
+ return 0;
+ } else {
+ rc = zl3073x_chan_tie_write(zldev, zldpll->id, delta);
+ if (!rc)
+ return 0;
+ }
- /* Phase step failed or unavailable, fall back to ToD adjust */
+ /* Phase step or TIE write failed, fall back to ToD adjust */
rc = zl3073x_chan_tod_adjust(zldev, zldpll->id,
ns_to_timespec64(delta));
- /* In the unlikely event that both phase step and fallback ToD adjust
- * fail after seconds were already committed, return success to prevent
- * the PTP servo from retrying the full delta and applying seconds
- * again. The sub-second residual will self-correct in the next servo
- * cycle.
+ /* In the unlikely event that both phase step/TIE write and fallback
+ * ToD adjust fail after seconds were already committed, return
+ * success to prevent the PTP servo from retrying the full delta and
+ * applying seconds again. The sub-second residual will self-correct
+ * in the next servo cycle.
*/
if (rc && sec_adjusted) {
dev_warn(zldev->dev,
@@ -2559,9 +2578,9 @@ static int zl3073x_dpll_ptp_adjtime(struct ptp_clock_info *info, s64 delta)
* @info: PTP clock info
* @scaled_ppm: frequency adjustment in scaled ppm (ppm * 2^16)
*
- * Writes the delta frequency offset register.
+ * Only supported in NCO mode. Writes the delta frequency offset register.
*
- * Return: 0 on success, <0 on error
+ * Return: 0 on success, -EOPNOTSUPP if not in NCO mode, <0 on error
*/
static int
zl3073x_dpll_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
@@ -2583,12 +2602,53 @@ zl3073x_dpll_ptp_adjfine(struct ptp_clock_info *info, long scaled_ppm)
guard(mutex)(&zldpll->lock);
chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
+ if (!zl3073x_chan_mode_is_nco(chan))
+ return scaled_ppm ? -EOPNOTSUPP : 0;
if (offset == chan->df_offset)
return 0;
return zl3073x_chan_df_offset_set(zldpll->dev, zldpll->id, offset);
}
+/**
+ * zl3073x_dpll_ptp_adjphase - adjust PTP clock phase
+ * @info: PTP clock info
+ * @delta: phase adjustment in nanoseconds
+ *
+ * Only supported in AUTO and REFLOCK modes. Uses TIE write for
+ * nanosecond resolution phase adjustment.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if mode doesn't support TIE, <0 on error
+ */
+static int zl3073x_dpll_ptp_adjphase(struct ptp_clock_info *info, s32 delta)
+{
+ struct zl3073x_dpll *zldpll = container_of(info, struct zl3073x_dpll,
+ ptp_info);
+ struct zl3073x_dev *zldev = zldpll->dev;
+ const struct zl3073x_chan *chan;
+
+ if (!delta)
+ return 0;
+
+ guard(mutex)(&zldpll->lock);
+
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
+
+ if (!zl3073x_chan_mode_supports_tie(chan))
+ return -EOPNOTSUPP;
+
+ return zl3073x_chan_tie_write(zldev, zldpll->id, delta);
+}
+
+static s32
+zl3073x_dpll_ptp_getmaxphase(struct ptp_clock_info *info __always_unused)
+{
+ /* HW limits TIE write to +-1 second. Return the constant HW
+ * limit and let adjphase handle mode-specific checks.
+ */
+ return NSEC_PER_SEC - 1;
+}
+
/**
* zl3073x_dpll_ptp_perout_find_pin - find pin by perout channel index
* @zldpll: DPLL channel
@@ -2678,6 +2738,8 @@ static const struct ptp_clock_info zl3073x_dpll_ptp_clock_info = {
.settime64 = zl3073x_dpll_ptp_settime64,
.adjtime = zl3073x_dpll_ptp_adjtime,
.adjfine = zl3073x_dpll_ptp_adjfine,
+ .adjphase = zl3073x_dpll_ptp_adjphase,
+ .getmaxphase = zl3073x_dpll_ptp_getmaxphase,
.enable = zl3073x_dpll_ptp_enable,
};
@@ -2685,21 +2747,13 @@ static const struct ptp_clock_info zl3073x_dpll_ptp_clock_info = {
* zl3073x_dpll_ptp_register - register PTP clock for a DPLL channel
* @zldpll: DPLL channel to register PTP clock for
*
- * PTP clock is only registered when the channel is in NCO mode.
- * If the channel is not in NCO mode, this is a no-op.
- *
* Return: 0 on success, <0 on error
*/
static int zl3073x_dpll_ptp_register(struct zl3073x_dpll *zldpll)
{
struct zl3073x_dev *zldev = zldpll->dev;
- const struct zl3073x_chan *chan;
struct ptp_clock *ptp_clock;
- chan = zl3073x_chan_state_get(zldev, zldpll->id);
- if (!zl3073x_chan_mode_is_nco(chan))
- return 0;
-
snprintf(zldpll->ptp_info.name, sizeof(zldpll->ptp_info.name),
"zl3073x-dpll%u", zldpll->id);
--
2.53.0
prev parent reply other threads:[~2026-07-08 17:06 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-08 17:05 [PATCH net-next 0/4] dpll: zl3073x: add PTP clock support Ivan Vecera
2026-07-08 17:05 ` [PATCH net-next 1/4] dpll: zl3073x: add channel ToD and phase step operations Ivan Vecera
2026-07-10 12:56 ` Vadim Fedorenko
2026-07-10 15:02 ` Ivan Vecera
2026-07-08 17:05 ` [PATCH net-next 2/4] dpll: zl3073x: add PTP clock support Ivan Vecera
2026-07-10 13:08 ` Vadim Fedorenko
2026-07-10 15:19 ` Ivan Vecera
2026-07-08 17:05 ` [PATCH net-next 3/4] dpll: zl3073x: add channel TIE write operation Ivan Vecera
2026-07-08 17:05 ` Ivan Vecera [this message]
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=20260708170527.916035-5-ivecera@redhat.com \
--to=ivecera@redhat.com \
--cc=Chris.duQuesnay@microchip.com \
--cc=Prathosh.Satish@microchip.com \
--cc=arkadiusz.kubalewski@intel.com \
--cc=davem@davemloft.net \
--cc=horms@kernel.org \
--cc=jiri@resnulli.us \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mschmidt@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=poros@redhat.com \
--cc=pvaanane@redhat.com \
--cc=richardcochran@gmail.com \
--cc=vadim.fedorenko@linux.dev \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox