mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
Add low-level DPLL channel operations for ToD read/write/adjust, output phase step, delta frequency offset write and TIE (Time Interval Error) write. These serve as building blocks for the PTP clock callbacks added in the next patch. ToD operations use a wait-before-write pattern to avoid blocking after each operation. The tod_ready_wait helper selects the poll timeout based on the current ToD command - write operations use a longer timeout (1000 ms) than reads (30 ms). The ToD read captures system timestamps (ptp_system_timestamp) around the HW command and completion poll to support cross-timestamping. The TIE write operation provides sub-picosecond resolution phase adjustment for modes where the DPLL is tracking a reference (AUTO and REFLOCK). Add output step-time mask to struct zl3073x_dev and zl3073x_dev_out_is_stepped() helper to check if an output participates in step-time operations. Reviewed-by: Petr Oros <poros@redhat.com> Tested-by: Chris du Quesnay <Chris.duQuesnay@microchip.com> Signed-off-by: Ivan Vecera <ivecera@redhat.com> Link: https://patch.msgid.link/20260814082656.306534-3-ivecera@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
269 lines
7.5 KiB
C
269 lines
7.5 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
#ifndef _ZL3073X_CHAN_H
|
|
#define _ZL3073X_CHAN_H
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/time64.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "regs.h"
|
|
|
|
struct ptp_system_timestamp;
|
|
struct zl3073x_dev;
|
|
|
|
/**
|
|
* struct zl3073x_chan - DPLL channel state
|
|
* @ctrl: DPLL control register value
|
|
* @mode_refsel: mode and reference selection register value
|
|
* @ref_prio: reference priority registers (4 bits per ref, P/N packed)
|
|
* @mon_status: monitor status register value
|
|
* @refsel_status: reference selection status register value
|
|
* @df_offset: frequency offset vs tracked reference in 2^-48 steps
|
|
*/
|
|
struct zl3073x_chan {
|
|
struct_group(cfg,
|
|
u8 ctrl;
|
|
u8 mode_refsel;
|
|
u8 ref_prio[ZL3073X_NUM_REFS / 2];
|
|
);
|
|
struct_group(stat,
|
|
u8 mon_status;
|
|
u8 refsel_status;
|
|
s64 df_offset;
|
|
);
|
|
};
|
|
|
|
int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
|
const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
|
|
u8 index);
|
|
int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
|
const struct zl3073x_chan *chan);
|
|
|
|
int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);
|
|
int zl3073x_chan_nco_mode_set(struct zl3073x_dev *zldev, u8 index);
|
|
|
|
int zl3073x_chan_tod_ready_wait(struct zl3073x_dev *zldev, u8 ch);
|
|
int zl3073x_chan_tod_read(struct zl3073x_dev *zldev, u8 ch,
|
|
bool next_hz, struct timespec64 *ts,
|
|
struct ptp_system_timestamp *sts);
|
|
int zl3073x_chan_tod_write(struct zl3073x_dev *zldev, u8 ch,
|
|
struct timespec64 ts);
|
|
int zl3073x_chan_tod_adjust(struct zl3073x_dev *zldev, u8 ch,
|
|
struct timespec64 delta);
|
|
int zl3073x_chan_phase_step(struct zl3073x_dev *zldev, u8 ch,
|
|
u16 out_mask, s32 step_cycles, bool tod_step);
|
|
|
|
int zl3073x_chan_df_offset_set(struct zl3073x_dev *zldev, u8 ch, s64 offset);
|
|
|
|
int zl3073x_chan_tie_write(struct zl3073x_dev *zldev, u8 ch, s64 delta_ns);
|
|
|
|
/**
|
|
* zl3073x_chan_df_offset_get - get cached df_offset vs tracked reference
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: frequency offset in 2^-48 steps
|
|
*/
|
|
static inline s64
|
|
zl3073x_chan_df_offset_get(const struct zl3073x_chan *chan)
|
|
{
|
|
return chan->df_offset;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_mode_get - get DPLL channel operating mode
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: reference selection mode of the given DPLL channel
|
|
*/
|
|
static inline u8 zl3073x_chan_mode_get(const struct zl3073x_chan *chan)
|
|
{
|
|
return FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE, chan->mode_refsel);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_ref_get - get manually selected reference
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: reference selected in forced reference lock mode
|
|
*/
|
|
static inline u8 zl3073x_chan_ref_get(const struct zl3073x_chan *chan)
|
|
{
|
|
return FIELD_GET(ZL_DPLL_MODE_REFSEL_REF, chan->mode_refsel);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_mode_set - set DPLL channel operating mode
|
|
* @chan: pointer to channel state
|
|
* @mode: mode to set
|
|
*/
|
|
static inline void zl3073x_chan_mode_set(struct zl3073x_chan *chan, u8 mode)
|
|
{
|
|
FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_MODE, &chan->mode_refsel, mode);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_ref_set - set manually selected reference
|
|
* @chan: pointer to channel state
|
|
* @ref: reference to set
|
|
*/
|
|
static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)
|
|
{
|
|
FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_REF, &chan->mode_refsel, ref);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_ref_prio_get - get reference priority
|
|
* @chan: pointer to channel state
|
|
* @ref: input reference index
|
|
*
|
|
* Return: priority of the given reference <0, 15>
|
|
*/
|
|
static inline u8
|
|
zl3073x_chan_ref_prio_get(const struct zl3073x_chan *chan, u8 ref)
|
|
{
|
|
u8 val = chan->ref_prio[ref / 2];
|
|
|
|
if (!(ref & 1))
|
|
return FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, val);
|
|
else
|
|
return FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, val);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_ref_prio_set - set reference priority
|
|
* @chan: pointer to channel state
|
|
* @ref: input reference index
|
|
* @prio: priority to set
|
|
*/
|
|
static inline void
|
|
zl3073x_chan_ref_prio_set(struct zl3073x_chan *chan, u8 ref, u8 prio)
|
|
{
|
|
u8 *val = &chan->ref_prio[ref / 2];
|
|
|
|
if (!(ref & 1))
|
|
FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_P, val, prio);
|
|
else
|
|
FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_N, val, prio);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_ref_is_selectable - check if reference is selectable
|
|
* @chan: pointer to channel state
|
|
* @ref: input reference index
|
|
*
|
|
* Return: true if the reference priority is not NONE, false otherwise
|
|
*/
|
|
static inline bool
|
|
zl3073x_chan_ref_is_selectable(const struct zl3073x_chan *chan, u8 ref)
|
|
{
|
|
return zl3073x_chan_ref_prio_get(chan, ref) != ZL_DPLL_REF_PRIO_NONE;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_lock_state_get - get DPLL channel lock state
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: lock state of the given DPLL channel
|
|
*/
|
|
static inline u8 zl3073x_chan_lock_state_get(const struct zl3073x_chan *chan)
|
|
{
|
|
return FIELD_GET(ZL_DPLL_MON_STATUS_STATE, chan->mon_status);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_is_locked - check if channel is locked to a reference
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: true if channel is locked, false otherwise
|
|
*/
|
|
static inline bool zl3073x_chan_is_locked(const struct zl3073x_chan *chan)
|
|
{
|
|
u8 lock_state = zl3073x_chan_lock_state_get(chan);
|
|
return lock_state == ZL_DPLL_MON_STATUS_STATE_LOCK;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_mode_is_auto - check if channel is in automatic mode
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: true if channel is in automatic mode, false otherwise
|
|
*/
|
|
static inline bool zl3073x_chan_mode_is_auto(const struct zl3073x_chan *chan)
|
|
{
|
|
return zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_AUTO;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_mode_is_nco - check if channel is in NCO mode
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: true if channel is in NCO mode, false otherwise
|
|
*/
|
|
static inline bool zl3073x_chan_mode_is_nco(const struct zl3073x_chan *chan)
|
|
{
|
|
return zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_NCO;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_mode_is_reflock - check if channel is in reflock mode
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: true if channel is in reflock mode, false otherwise
|
|
*/
|
|
static inline bool zl3073x_chan_mode_is_reflock(const struct zl3073x_chan *chan)
|
|
{
|
|
return zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_REFLOCK;
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_mode_supports_tie - check if channel mode supports TIE write
|
|
* @chan: pointer to channel state
|
|
*
|
|
* TIE write is supported in AUTO and REFLOCK modes regardless of lock state.
|
|
*
|
|
* Return: true if TIE write is supported, false otherwise
|
|
*/
|
|
static inline bool
|
|
zl3073x_chan_mode_supports_tie(const struct zl3073x_chan *chan)
|
|
{
|
|
return zl3073x_chan_mode_is_auto(chan) ||
|
|
zl3073x_chan_mode_is_reflock(chan);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_is_ho_ready - check if holdover is ready
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: true if holdover is ready, false otherwise
|
|
*/
|
|
static inline bool zl3073x_chan_is_ho_ready(const struct zl3073x_chan *chan)
|
|
{
|
|
return !!FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, chan->mon_status);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_refsel_state_get - get reference selection state
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: reference selection state of the given DPLL channel
|
|
*/
|
|
static inline u8 zl3073x_chan_refsel_state_get(const struct zl3073x_chan *chan)
|
|
{
|
|
return FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, chan->refsel_status);
|
|
}
|
|
|
|
/**
|
|
* zl3073x_chan_refsel_ref_get - get currently selected reference in auto mode
|
|
* @chan: pointer to channel state
|
|
*
|
|
* Return: reference selected by the DPLL in automatic mode
|
|
*/
|
|
static inline u8 zl3073x_chan_refsel_ref_get(const struct zl3073x_chan *chan)
|
|
{
|
|
return FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, chan->refsel_status);
|
|
}
|
|
|
|
#endif /* _ZL3073X_CHAN_H */
|