dpll: zl3073x: Allow to configure phase offset averaging factor
The DPLL phase measurement block uses an exponential moving average with a configurable averaging factor. Measurements are taken at approximately 40 Hz or at the reference frequency, whichever is lower. Currently, factor=2 is used to prioritize fast response for dynamic phase changes. For applications needing a stable, precise average phase offset where rapid changes are unlikely, a higher factor is recommended. Implement the .phase_offset_avg_factor_get/set callbacks to allow a user to adjust this factor. Signed-off-by: Ivan Vecera <ivecera@redhat.com> Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev> Link: https://patch.msgid.link/20250927084912.2343597-4-ivecera@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
e28d5a68b6
commit
9363b48376
|
@ -956,6 +956,32 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
|||
msecs_to_jiffies(500));
|
||||
}
|
||||
|
||||
int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor)
|
||||
{
|
||||
u8 dpll_meas_ctrl, value;
|
||||
int rc;
|
||||
|
||||
/* Read DPLL phase measurement control register */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Convert requested factor to register value */
|
||||
value = (factor + 1) & 0x0f;
|
||||
|
||||
/* Update phase measurement control register */
|
||||
dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
|
||||
dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, value);
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Save the new factor */
|
||||
zldev->phase_avg_factor = factor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_dev_phase_meas_setup - setup phase offset measurement
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
|
@ -972,15 +998,16 @@ zl3073x_dev_phase_meas_setup(struct zl3073x_dev *zldev)
|
|||
u8 dpll_meas_ctrl, mask = 0;
|
||||
int rc;
|
||||
|
||||
/* Setup phase measurement averaging factor */
|
||||
rc = zl3073x_dev_phase_avg_factor_set(zldev, zldev->phase_avg_factor);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Read DPLL phase measurement control register */
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Setup phase measurement averaging factor */
|
||||
dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
|
||||
dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, 3);
|
||||
|
||||
/* Enable DPLL measurement block */
|
||||
dpll_meas_ctrl |= ZL_DPLL_MEAS_CTRL_EN;
|
||||
|
||||
|
@ -1208,6 +1235,9 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
|||
*/
|
||||
zldev->clock_id = get_random_u64();
|
||||
|
||||
/* Default phase offset averaging factor */
|
||||
zldev->phase_avg_factor = 2;
|
||||
|
||||
/* Initialize mutex for operations where multiple reads, writes
|
||||
* and/or polls are required to be done atomically.
|
||||
*/
|
||||
|
|
|
@ -68,19 +68,19 @@ struct zl3073x_synth {
|
|||
* @dev: pointer to device
|
||||
* @regmap: regmap to access device registers
|
||||
* @multiop_lock: to serialize multiple register operations
|
||||
* @clock_id: clock id of the device
|
||||
* @ref: array of input references' invariants
|
||||
* @out: array of outs' invariants
|
||||
* @synth: array of synths' invariants
|
||||
* @dplls: list of DPLLs
|
||||
* @kworker: thread for periodic work
|
||||
* @work: periodic work
|
||||
* @clock_id: clock id of the device
|
||||
* @phase_avg_factor: phase offset measurement averaging factor
|
||||
*/
|
||||
struct zl3073x_dev {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct mutex multiop_lock;
|
||||
u64 clock_id;
|
||||
|
||||
/* Invariants */
|
||||
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
|
||||
|
@ -93,6 +93,10 @@ struct zl3073x_dev {
|
|||
/* Monitor */
|
||||
struct kthread_worker *kworker;
|
||||
struct kthread_delayed_work work;
|
||||
|
||||
/* Devlink parameters */
|
||||
u64 clock_id;
|
||||
u8 phase_avg_factor;
|
||||
};
|
||||
|
||||
struct zl3073x_chip_info {
|
||||
|
@ -115,6 +119,13 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
|||
int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full);
|
||||
void zl3073x_dev_stop(struct zl3073x_dev *zldev);
|
||||
|
||||
static inline u8 zl3073x_dev_phase_avg_factor_get(struct zl3073x_dev *zldev)
|
||||
{
|
||||
return zldev->phase_avg_factor;
|
||||
}
|
||||
|
||||
int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor);
|
||||
|
||||
/**********************
|
||||
* Registers operations
|
||||
**********************/
|
||||
|
|
|
@ -1576,6 +1576,59 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dpll_phase_offset_avg_factor_get(const struct dpll_device *dpll,
|
||||
void *dpll_priv, u32 *factor,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
|
||||
*factor = zl3073x_dev_phase_avg_factor_get(zldpll->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
zl3073x_dpll_change_work(struct work_struct *work)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll;
|
||||
|
||||
zldpll = container_of(work, struct zl3073x_dpll, change_work);
|
||||
dpll_device_change_ntf(zldpll->dpll_dev);
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll,
|
||||
void *dpll_priv, u32 factor,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll *item, *zldpll = dpll_priv;
|
||||
int rc;
|
||||
|
||||
if (factor > 15) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"Phase offset average factor has to be from range <0,15>");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = zl3073x_dev_phase_avg_factor_set(zldpll->dev, factor);
|
||||
if (rc) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"Failed to set phase offset averaging factor");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The averaging factor is common for all DPLL channels so after change
|
||||
* we have to send a notification for other DPLL devices.
|
||||
*/
|
||||
list_for_each_entry(item, &zldpll->dev->dplls, list) {
|
||||
if (item != zldpll)
|
||||
schedule_work(&item->change_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dpll_phase_offset_monitor_get(const struct dpll_device *dpll,
|
||||
void *dpll_priv,
|
||||
|
@ -1635,6 +1688,8 @@ static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = {
|
|||
static const struct dpll_device_ops zl3073x_dpll_device_ops = {
|
||||
.lock_status_get = zl3073x_dpll_lock_status_get,
|
||||
.mode_get = zl3073x_dpll_mode_get,
|
||||
.phase_offset_avg_factor_get = zl3073x_dpll_phase_offset_avg_factor_get,
|
||||
.phase_offset_avg_factor_set = zl3073x_dpll_phase_offset_avg_factor_set,
|
||||
.phase_offset_monitor_get = zl3073x_dpll_phase_offset_monitor_get,
|
||||
.phase_offset_monitor_set = zl3073x_dpll_phase_offset_monitor_set,
|
||||
};
|
||||
|
@ -1983,6 +2038,8 @@ zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
|
|||
{
|
||||
WARN(!zldpll->dpll_dev, "DPLL device is not registered\n");
|
||||
|
||||
cancel_work_sync(&zldpll->change_work);
|
||||
|
||||
dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops,
|
||||
zldpll);
|
||||
dpll_device_put(zldpll->dpll_dev);
|
||||
|
@ -2258,6 +2315,7 @@ zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch)
|
|||
zldpll->dev = zldev;
|
||||
zldpll->id = ch;
|
||||
INIT_LIST_HEAD(&zldpll->pins);
|
||||
INIT_WORK(&zldpll->change_work, zl3073x_dpll_change_work);
|
||||
|
||||
return zldpll;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
* @dpll_dev: pointer to registered DPLL device
|
||||
* @lock_status: last saved DPLL lock status
|
||||
* @pins: list of pins
|
||||
* @change_work: device change notification work
|
||||
*/
|
||||
struct zl3073x_dpll {
|
||||
struct list_head list;
|
||||
|
@ -32,6 +33,7 @@ struct zl3073x_dpll {
|
|||
struct dpll_device *dpll_dev;
|
||||
enum dpll_lock_status lock_status;
|
||||
struct list_head pins;
|
||||
struct work_struct change_work;
|
||||
};
|
||||
|
||||
struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);
|
||||
|
|
Loading…
Reference in New Issue