mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 04:21:09 -04:00
Merge branch 'dpll-add-frequency-monitoring-feature'
Ivan Vecera says: ==================== dpll: add frequency monitoring feature This series adds support for monitoring the measured input frequency of DPLL input pins via the DPLL netlink interface. Some DPLL devices can measure the actual frequency being received on input pins. The approach mirrors the existing phase-offset-monitor feature: a device-level attribute (DPLL_A_FREQUENCY_MONITOR) enables or disables monitoring, and a per-pin attribute (DPLL_A_PIN_MEASURED_FREQUENCY) exposes the measured frequency in millihertz (mHz) when monitoring is enabled. Patch 1 adds the new attributes to the DPLL netlink spec (dpll.yaml), the DPLL_PIN_MEASURED_FREQUENCY_DIVIDER constant, regenerates the auto-generated UAPI header and netlink policy, and updates Documentation/driver-api/dpll.rst. Patch 2 adds the callback operations (freq_monitor_get/set for devices, measured_freq_get for pins) and the corresponding netlink GET/SET handlers in the DPLL core. The core only invokes measured_freq_get when the frequency monitor is enabled on the parent device. The freq_monitor_get callback is required when measured_freq_get is provided. Patch 3 implements the feature in the ZL3073x driver by extracting a common measurement latch helper from the existing FFO update path, adding a frequency measurement function, and wiring up the new callbacks. ==================== Link: https://patch.msgid.link/20260402184057.1890514-1-ivecera@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -250,6 +250,24 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute.
|
||||
``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature
|
||||
=============================== ========================
|
||||
|
||||
Frequency monitor
|
||||
=================
|
||||
|
||||
Some DPLL devices may offer the capability to measure the actual
|
||||
frequency of all available input pins. The attribute and current feature state
|
||||
shall be included in the response message of the ``DPLL_CMD_DEVICE_GET``
|
||||
command for supported DPLL devices. In such cases, users can also control
|
||||
the feature using the ``DPLL_CMD_DEVICE_SET`` command by setting the
|
||||
``enum dpll_feature_state`` values for the attribute.
|
||||
Once enabled the measured input frequency for each input pin shall be
|
||||
returned in the ``DPLL_A_PIN_MEASURED_FREQUENCY`` attribute. The value
|
||||
is in millihertz (mHz), using ``DPLL_PIN_MEASURED_FREQUENCY_DIVIDER``
|
||||
as the divider.
|
||||
|
||||
=============================== ========================
|
||||
``DPLL_A_FREQUENCY_MONITOR`` attr state of a feature
|
||||
=============================== ========================
|
||||
|
||||
Embedded SYNC
|
||||
=============
|
||||
|
||||
@@ -411,6 +429,8 @@ according to attribute purpose.
|
||||
``DPLL_A_PIN_STATE`` attr state of pin on the parent
|
||||
pin
|
||||
``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities
|
||||
``DPLL_A_PIN_MEASURED_FREQUENCY`` attr measured frequency of
|
||||
an input pin in mHz
|
||||
==================================== ==================================
|
||||
|
||||
==================================== =================================
|
||||
|
||||
@@ -240,6 +240,20 @@ definitions:
|
||||
integer part of a measured phase offset value.
|
||||
Value of (DPLL_A_PHASE_OFFSET % DPLL_PHASE_OFFSET_DIVIDER) is a
|
||||
fractional part of a measured phase offset value.
|
||||
-
|
||||
type: const
|
||||
name: pin-measured-frequency-divider
|
||||
value: 1000
|
||||
doc: |
|
||||
pin measured frequency divider allows userspace to calculate
|
||||
a value of measured input frequency as a fractional value with
|
||||
three digit decimal precision (millihertz).
|
||||
Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
|
||||
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part of
|
||||
a measured frequency value.
|
||||
Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
|
||||
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part of
|
||||
a measured frequency value.
|
||||
-
|
||||
type: enum
|
||||
name: feature-state
|
||||
@@ -319,6 +333,13 @@ attribute-sets:
|
||||
name: phase-offset-avg-factor
|
||||
type: u32
|
||||
doc: Averaging factor applied to calculation of reported phase offset.
|
||||
-
|
||||
name: frequency-monitor
|
||||
type: u32
|
||||
enum: feature-state
|
||||
doc: Current or desired state of the frequency monitor feature.
|
||||
If enabled, dpll device shall measure all currently available
|
||||
inputs for their actual input frequency.
|
||||
-
|
||||
name: pin
|
||||
enum-name: dpll_a_pin
|
||||
@@ -456,6 +477,17 @@ attribute-sets:
|
||||
Value is in PPT (parts per trillion, 10^-12).
|
||||
Note: This attribute provides higher resolution than the standard
|
||||
fractional-frequency-offset (which is in PPM).
|
||||
-
|
||||
name: measured-frequency
|
||||
type: u64
|
||||
doc: |
|
||||
The measured frequency of the input pin in millihertz (mHz).
|
||||
Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
|
||||
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part (Hz)
|
||||
of a measured frequency value.
|
||||
Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
|
||||
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part
|
||||
of a measured frequency value.
|
||||
|
||||
-
|
||||
name: pin-parent-device
|
||||
@@ -544,6 +576,7 @@ operations:
|
||||
- type
|
||||
- phase-offset-monitor
|
||||
- phase-offset-avg-factor
|
||||
- frequency-monitor
|
||||
|
||||
dump:
|
||||
reply: *dev-attrs
|
||||
@@ -563,6 +596,7 @@ operations:
|
||||
- mode
|
||||
- phase-offset-monitor
|
||||
- phase-offset-avg-factor
|
||||
- frequency-monitor
|
||||
-
|
||||
name: device-create-ntf
|
||||
doc: Notification about device appearing
|
||||
@@ -643,6 +677,7 @@ operations:
|
||||
- esync-frequency-supported
|
||||
- esync-pulse
|
||||
- reference-sync
|
||||
- measured-frequency
|
||||
|
||||
dump:
|
||||
request:
|
||||
|
||||
@@ -876,7 +876,10 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
|
||||
if (WARN_ON(!ops) ||
|
||||
WARN_ON(!ops->state_on_dpll_get) ||
|
||||
WARN_ON(!ops->direction_get))
|
||||
WARN_ON(!ops->direction_get) ||
|
||||
WARN_ON(ops->measured_freq_get &&
|
||||
(!dpll_device_ops(dpll)->freq_monitor_get ||
|
||||
!dpll_device_ops(dpll)->freq_monitor_set)))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
|
||||
@@ -175,6 +175,26 @@ dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_msg_add_freq_monitor(struct sk_buff *msg, struct dpll_device *dpll,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct dpll_device_ops *ops = dpll_device_ops(dpll);
|
||||
enum dpll_feature_state state;
|
||||
int ret;
|
||||
|
||||
if (ops->freq_monitor_set && ops->freq_monitor_get) {
|
||||
ret = ops->freq_monitor_get(dpll, dpll_priv(dpll),
|
||||
&state, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (nla_put_u32(msg, DPLL_A_FREQUENCY_MONITOR, state))
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_msg_add_phase_offset_avg_factor(struct sk_buff *msg,
|
||||
struct dpll_device *dpll,
|
||||
@@ -400,6 +420,38 @@ static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
ffo);
|
||||
}
|
||||
|
||||
static int dpll_msg_add_measured_freq(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
struct dpll_pin_ref *ref,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct dpll_device_ops *dev_ops = dpll_device_ops(ref->dpll);
|
||||
const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
|
||||
struct dpll_device *dpll = ref->dpll;
|
||||
enum dpll_feature_state state;
|
||||
u64 measured_freq;
|
||||
int ret;
|
||||
|
||||
if (!ops->measured_freq_get)
|
||||
return 0;
|
||||
ret = dev_ops->freq_monitor_get(dpll, dpll_priv(dpll),
|
||||
&state, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (state == DPLL_FEATURE_STATE_DISABLE)
|
||||
return 0;
|
||||
ret = ops->measured_freq_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
dpll, dpll_priv(dpll), &measured_freq,
|
||||
extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (nla_put_64bit(msg, DPLL_A_PIN_MEASURED_FREQUENCY,
|
||||
sizeof(measured_freq), &measured_freq,
|
||||
DPLL_A_PIN_PAD))
|
||||
return -EMSGSIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
|
||||
@@ -670,6 +722,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_ffo(msg, pin, ref, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_measured_freq(msg, pin, ref, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
|
||||
@@ -722,6 +777,9 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_freq_monitor(msg, dpll, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -948,6 +1006,32 @@ dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a,
|
||||
extack);
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_freq_monitor_set(struct dpll_device *dpll, struct nlattr *a,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct dpll_device_ops *ops = dpll_device_ops(dpll);
|
||||
enum dpll_feature_state state = nla_get_u32(a), old_state;
|
||||
int ret;
|
||||
|
||||
if (!(ops->freq_monitor_set && ops->freq_monitor_get)) {
|
||||
NL_SET_ERR_MSG_ATTR(extack, a,
|
||||
"dpll device not capable of frequency monitor");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
ret = ops->freq_monitor_get(dpll, dpll_priv(dpll), &old_state,
|
||||
extack);
|
||||
if (ret) {
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"unable to get current state of frequency monitor");
|
||||
return ret;
|
||||
}
|
||||
if (state == old_state)
|
||||
return 0;
|
||||
|
||||
return ops->freq_monitor_set(dpll, dpll_priv(dpll), state, extack);
|
||||
}
|
||||
|
||||
static int
|
||||
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
|
||||
struct netlink_ext_ack *extack)
|
||||
@@ -1878,6 +1962,12 @@ dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case DPLL_A_FREQUENCY_MONITOR:
|
||||
ret = dpll_freq_monitor_set(dpll, a,
|
||||
info->extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,11 +43,12 @@ static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = {
|
||||
};
|
||||
|
||||
/* DPLL_CMD_DEVICE_SET - do */
|
||||
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_AVG_FACTOR + 1] = {
|
||||
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_FREQUENCY_MONITOR + 1] = {
|
||||
[DPLL_A_ID] = { .type = NLA_U32, },
|
||||
[DPLL_A_MODE] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
|
||||
[DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
|
||||
[DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, },
|
||||
[DPLL_A_FREQUENCY_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
|
||||
};
|
||||
|
||||
/* DPLL_CMD_PIN_ID_GET - do */
|
||||
@@ -115,7 +116,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
|
||||
.doit = dpll_nl_device_set_doit,
|
||||
.post_doit = dpll_post_doit,
|
||||
.policy = dpll_device_set_nl_policy,
|
||||
.maxattr = DPLL_A_PHASE_OFFSET_AVG_FACTOR,
|
||||
.maxattr = DPLL_A_FREQUENCY_MONITOR,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -632,22 +632,21 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_ffo_update - update reference fractional frequency offsets
|
||||
* zl3073x_ref_freq_meas_latch - latch reference frequency measurements
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @type: measurement type (ZL_REF_FREQ_MEAS_CTRL_*)
|
||||
*
|
||||
* The function asks device to update fractional frequency offsets latch
|
||||
* registers the latest measured values, reads and stores them into
|
||||
* The function waits for the previous measurement to finish, selects all
|
||||
* references and requests a new measurement of the given type.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, u8 type)
|
||||
{
|
||||
int i, rc;
|
||||
int rc;
|
||||
|
||||
/* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero
|
||||
* to ensure that the measured data are coherent.
|
||||
*/
|
||||
/* Wait for previous measurement to finish */
|
||||
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
ZL_REF_FREQ_MEAS_CTRL);
|
||||
if (rc)
|
||||
@@ -663,15 +662,64 @@ zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Request frequency offset measurement */
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
|
||||
/* Request measurement */
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, type);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Wait for finish */
|
||||
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
ZL_REF_FREQ_MEAS_CTRL);
|
||||
return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
ZL_REF_FREQ_MEAS_CTRL);
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_freq_meas_update - update measured input reference frequencies
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
*
|
||||
* The function asks device to latch measured input reference frequencies
|
||||
* and stores the results in the ref state.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
rc = zl3073x_ref_freq_meas_latch(zldev, ZL_REF_FREQ_MEAS_CTRL_REF_FREQ);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Read measured frequencies in Hz (unsigned 32-bit, LSB = 1 Hz) */
|
||||
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
|
||||
u32 value;
|
||||
|
||||
rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
zldev->ref[i].meas_freq = value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_ffo_update - update reference fractional frequency offsets
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
*
|
||||
* The function asks device to latch the latest measured fractional
|
||||
* frequency offset values, reads and stores them into the ref state.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
rc = zl3073x_ref_freq_meas_latch(zldev,
|
||||
ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@@ -714,6 +762,20 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
|
||||
ERR_PTR(rc));
|
||||
|
||||
/* Update measured input reference frequencies if any DPLL has
|
||||
* frequency monitoring enabled.
|
||||
*/
|
||||
list_for_each_entry(zldpll, &zldev->dplls, list) {
|
||||
if (zldpll->freq_monitor) {
|
||||
rc = zl3073x_ref_freq_meas_update(zldev);
|
||||
if (rc)
|
||||
dev_warn(zldev->dev,
|
||||
"Failed to update measured frequencies: %pe\n",
|
||||
ERR_PTR(rc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update references' fractional frequency offsets */
|
||||
rc = zl3073x_ref_ffo_update(zldev);
|
||||
if (rc)
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
* @pin_state: last saved pin state
|
||||
* @phase_offset: last saved pin phase offset
|
||||
* @freq_offset: last saved fractional frequency offset
|
||||
* @measured_freq: last saved measured frequency
|
||||
*/
|
||||
struct zl3073x_dpll_pin {
|
||||
struct list_head list;
|
||||
@@ -54,6 +55,7 @@ struct zl3073x_dpll_pin {
|
||||
enum dpll_pin_state pin_state;
|
||||
s64 phase_offset;
|
||||
s64 freq_offset;
|
||||
u32 measured_freq;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -202,6 +204,21 @@ zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dpll_input_pin_measured_freq_get(const struct dpll_pin *dpll_pin,
|
||||
void *pin_priv,
|
||||
const struct dpll_device *dpll,
|
||||
void *dpll_priv, u64 *measured_freq,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
|
||||
*measured_freq = pin->measured_freq;
|
||||
*measured_freq *= DPLL_PIN_MEASURED_FREQUENCY_DIVIDER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
|
||||
void *pin_priv,
|
||||
@@ -1116,6 +1133,35 @@ zl3073x_dpll_phase_offset_monitor_set(const struct dpll_device *dpll,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dpll_freq_monitor_get(const struct dpll_device *dpll,
|
||||
void *dpll_priv,
|
||||
enum dpll_feature_state *state,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
|
||||
if (zldpll->freq_monitor)
|
||||
*state = DPLL_FEATURE_STATE_ENABLE;
|
||||
else
|
||||
*state = DPLL_FEATURE_STATE_DISABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll,
|
||||
void *dpll_priv,
|
||||
enum dpll_feature_state state,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
|
||||
zldpll->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
||||
.direction_get = zl3073x_dpll_pin_direction_get,
|
||||
.esync_get = zl3073x_dpll_input_pin_esync_get,
|
||||
@@ -1123,6 +1169,7 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
||||
.ffo_get = zl3073x_dpll_input_pin_ffo_get,
|
||||
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
|
||||
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
|
||||
.measured_freq_get = zl3073x_dpll_input_pin_measured_freq_get,
|
||||
.phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get,
|
||||
.phase_adjust_get = zl3073x_dpll_input_pin_phase_adjust_get,
|
||||
.phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set,
|
||||
@@ -1151,6 +1198,8 @@ static const struct dpll_device_ops zl3073x_dpll_device_ops = {
|
||||
.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,
|
||||
.freq_monitor_get = zl3073x_dpll_freq_monitor_get,
|
||||
.freq_monitor_set = zl3073x_dpll_freq_monitor_set,
|
||||
.supported_modes_get = zl3073x_dpll_supported_modes_get,
|
||||
};
|
||||
|
||||
@@ -1572,6 +1621,7 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
const struct zl3073x_ref *ref;
|
||||
u8 ref_id;
|
||||
s64 ffo;
|
||||
|
||||
/* Get reference monitor status */
|
||||
ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
@@ -1582,10 +1632,47 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
|
||||
return false;
|
||||
|
||||
/* Compare with previous value */
|
||||
if (pin->freq_offset != ref->ffo) {
|
||||
ffo = zl3073x_ref_ffo_get(ref);
|
||||
if (pin->freq_offset != ffo) {
|
||||
dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n",
|
||||
pin->label, pin->freq_offset, ref->ffo);
|
||||
pin->freq_offset = ref->ffo;
|
||||
pin->label, pin->freq_offset, ffo);
|
||||
pin->freq_offset = ffo;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_dpll_pin_measured_freq_check - check for pin measured frequency
|
||||
* change
|
||||
* @pin: pin to check
|
||||
*
|
||||
* Check for the given pin's measured frequency change.
|
||||
*
|
||||
* Return: true on measured frequency change, false otherwise
|
||||
*/
|
||||
static bool
|
||||
zl3073x_dpll_pin_measured_freq_check(struct zl3073x_dpll_pin *pin)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
const struct zl3073x_ref *ref;
|
||||
u8 ref_id;
|
||||
u32 freq;
|
||||
|
||||
if (!zldpll->freq_monitor)
|
||||
return false;
|
||||
|
||||
ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
ref = zl3073x_ref_state_get(zldev, ref_id);
|
||||
|
||||
freq = zl3073x_ref_meas_freq_get(ref);
|
||||
if (pin->measured_freq != freq) {
|
||||
dev_dbg(zldev->dev, "%s measured freq changed: %u -> %u\n",
|
||||
pin->label, pin->measured_freq, freq);
|
||||
pin->measured_freq = freq;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1677,13 +1764,18 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
||||
pin_changed = true;
|
||||
}
|
||||
|
||||
/* Check for phase offset and ffo change once per second */
|
||||
/* Check for phase offset, ffo, and measured freq change
|
||||
* once per second.
|
||||
*/
|
||||
if (zldpll->check_count % 2 == 0) {
|
||||
if (zl3073x_dpll_pin_phase_offset_check(pin))
|
||||
pin_changed = true;
|
||||
|
||||
if (zl3073x_dpll_pin_ffo_check(pin))
|
||||
pin_changed = true;
|
||||
|
||||
if (zl3073x_dpll_pin_measured_freq_check(pin))
|
||||
pin_changed = true;
|
||||
}
|
||||
|
||||
if (pin_changed)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* @id: DPLL index
|
||||
* @check_count: periodic check counter
|
||||
* @phase_monitor: is phase offset monitor enabled
|
||||
* @freq_monitor: is frequency monitor enabled
|
||||
* @ops: DPLL device operations for this instance
|
||||
* @dpll_dev: pointer to registered DPLL device
|
||||
* @tracker: tracking object for the acquired reference
|
||||
@@ -28,6 +29,7 @@ struct zl3073x_dpll {
|
||||
u8 id;
|
||||
u8 check_count;
|
||||
bool phase_monitor;
|
||||
bool freq_monitor;
|
||||
struct dpll_device_ops ops;
|
||||
struct dpll_device *dpll_dev;
|
||||
dpll_tracker tracker;
|
||||
|
||||
@@ -23,6 +23,7 @@ struct zl3073x_dev;
|
||||
* @sync_ctrl: reference sync control
|
||||
* @config: reference config
|
||||
* @ffo: current fractional frequency offset
|
||||
* @meas_freq: measured input frequency in Hz
|
||||
* @mon_status: reference monitor status
|
||||
*/
|
||||
struct zl3073x_ref {
|
||||
@@ -40,6 +41,7 @@ struct zl3073x_ref {
|
||||
);
|
||||
struct_group(stat, /* Status */
|
||||
s64 ffo;
|
||||
u32 meas_freq;
|
||||
u8 mon_status;
|
||||
);
|
||||
};
|
||||
@@ -68,6 +70,18 @@ zl3073x_ref_ffo_get(const struct zl3073x_ref *ref)
|
||||
return ref->ffo;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_meas_freq_get - get measured input frequency
|
||||
* @ref: pointer to ref state
|
||||
*
|
||||
* Return: measured input frequency in Hz
|
||||
*/
|
||||
static inline u32
|
||||
zl3073x_ref_meas_freq_get(const struct zl3073x_ref *ref)
|
||||
{
|
||||
return ref->meas_freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* zl3073x_ref_freq_get - get given input reference frequency
|
||||
* @ref: pointer to ref state
|
||||
|
||||
@@ -52,6 +52,12 @@ struct dpll_device_ops {
|
||||
int (*phase_offset_avg_factor_get)(const struct dpll_device *dpll,
|
||||
void *dpll_priv, u32 *factor,
|
||||
struct netlink_ext_ack *extack);
|
||||
int (*freq_monitor_set)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
enum dpll_feature_state state,
|
||||
struct netlink_ext_ack *extack);
|
||||
int (*freq_monitor_get)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
enum dpll_feature_state *state,
|
||||
struct netlink_ext_ack *extack);
|
||||
};
|
||||
|
||||
struct dpll_pin_ops {
|
||||
@@ -110,6 +116,10 @@ struct dpll_pin_ops {
|
||||
int (*ffo_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
s64 *ffo, struct netlink_ext_ack *extack);
|
||||
int (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll,
|
||||
void *dpll_priv, u64 *measured_freq,
|
||||
struct netlink_ext_ack *extack);
|
||||
int (*esync_set)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
u64 freq, struct netlink_ext_ack *extack);
|
||||
|
||||
@@ -191,7 +191,8 @@ enum dpll_pin_capabilities {
|
||||
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE = 4,
|
||||
};
|
||||
|
||||
#define DPLL_PHASE_OFFSET_DIVIDER 1000
|
||||
#define DPLL_PHASE_OFFSET_DIVIDER 1000
|
||||
#define DPLL_PIN_MEASURED_FREQUENCY_DIVIDER 1000
|
||||
|
||||
/**
|
||||
* enum dpll_feature_state - Allow control (enable/disable) and status checking
|
||||
@@ -218,6 +219,7 @@ enum dpll_a {
|
||||
DPLL_A_CLOCK_QUALITY_LEVEL,
|
||||
DPLL_A_PHASE_OFFSET_MONITOR,
|
||||
DPLL_A_PHASE_OFFSET_AVG_FACTOR,
|
||||
DPLL_A_FREQUENCY_MONITOR,
|
||||
|
||||
__DPLL_A_MAX,
|
||||
DPLL_A_MAX = (__DPLL_A_MAX - 1)
|
||||
@@ -254,6 +256,7 @@ enum dpll_a_pin {
|
||||
DPLL_A_PIN_REFERENCE_SYNC,
|
||||
DPLL_A_PIN_PHASE_ADJUST_GRAN,
|
||||
DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
|
||||
DPLL_A_PIN_MEASURED_FREQUENCY,
|
||||
|
||||
__DPLL_A_PIN_MAX,
|
||||
DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1)
|
||||
|
||||
Reference in New Issue
Block a user