mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-29 20:58:57 -04:00
Replace the per-reference frequency offset measurement (which was redundant with measured-frequency) with a direct read of the DPLL's delta frequency offset vs its tracked input reference. The new implementation uses the dpll_df_offset_x register with ref_ofst=1 via the dpll_df_read_x semaphore mechanism. This provides 2^-48 resolution (~3.5 fE) and reports the actual frequency difference between the DPLL and its active input. Switch supported_ffo from DPLL_FFO_PORT_RXTX_RATE to DPLL_FFO_PIN_DEVICE so FFO is reported only in the per-parent context for the active input pin. Use atomic64_t for freq_offset to prevent torn reads on 32-bit architectures between the periodic worker and netlink callbacks. Rewrite ffo_check to compare the cached df_offset converted to PPT instead of using the old per-reference measurement. Remove the ref_ffo_update periodic measurement and the ref ffo field since they are no longer needed. Changes v3 -> v4: - Switch to DPLL_FFO_PIN_DEVICE, remove dpll=NULL guard - Use atomic64_t for freq_offset (torn read on 32-bit) Reviewed-by: Petr Oros <poros@redhat.com> Signed-off-by: Ivan Vecera <ivecera@redhat.com> Link: https://patch.msgid.link/20260511155816.99936-3-ivecera@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
189 lines
4.9 KiB
C
189 lines
4.9 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/types.h>
|
|
|
|
#include "regs.h"
|
|
|
|
struct zl3073x_dev;
|
|
|
|
/**
|
|
* struct zl3073x_chan - DPLL channel state
|
|
* @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 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);
|
|
|
|
/**
|
|
* 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_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 */
|