Skip to content

Commit c5cb31c

Browse files
committed
Merge branch 'dpll-add-phase-offset-averaging-factor'
Ivan Vecera says: ==================== dpll: add phase offset averaging factor For some hardware, the phase shift may result from averaging previous values and the newly measured value. In this case, the averaging is controlled by a configurable averaging factor. Add new device level attribute phase-offset-avg-factor, appropriate callbacks and implement them in zl3073x driver. ==================== Link: https://patch.msgid.link/20250927084912.2343597-1-ivecera@redhat.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2 parents 377ea33 + 9363b48 commit c5cb31c

10 files changed

Lines changed: 199 additions & 16 deletions

File tree

Documentation/driver-api/dpll.rst

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,23 @@ Phase offset measurement and adjustment
179179
Device may provide ability to measure a phase difference between signals
180180
on a pin and its parent dpll device. If pin-dpll phase offset measurement
181181
is supported, it shall be provided with ``DPLL_A_PIN_PHASE_OFFSET``
182-
attribute for each parent dpll device.
182+
attribute for each parent dpll device. The reported phase offset may be
183+
computed as the average of prior values and the current measurement, using
184+
the following formula:
185+
186+
.. math::
187+
curr\_avg = prev\_avg * \frac{2^N-1}{2^N} + new\_val * \frac{1}{2^N}
188+
189+
where `curr_avg` is the current reported phase offset, `prev_avg` is the
190+
previously reported value, `new_val` is the current measurement, and `N` is
191+
the averaging factor. Configured averaging factor value is provided with
192+
``DPLL_A_PHASE_OFFSET_AVG_FACTOR`` attribute of a device and value change can
193+
be requested with the same attribute with ``DPLL_CMD_DEVICE_SET`` command.
194+
195+
================================== ======================================
196+
``DPLL_A_PHASE_OFFSET_AVG_FACTOR`` attr configured value of phase offset
197+
averaging factor
198+
================================== ======================================
183199

184200
Device may also provide ability to adjust a signal phase on a pin.
185201
If pin phase adjustment is supported, minimal and maximal values that pin

Documentation/netlink/specs/dpll.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ attribute-sets:
315315
If enabled, dpll device shall monitor and notify all currently
316316
available inputs for changes of their phase offset against the
317317
dpll device.
318+
-
319+
name: phase-offset-avg-factor
320+
type: u32
321+
doc: Averaging factor applied to calculation of reported phase offset.
318322
-
319323
name: pin
320324
enum-name: dpll_a_pin
@@ -523,6 +527,7 @@ operations:
523527
- clock-id
524528
- type
525529
- phase-offset-monitor
530+
- phase-offset-avg-factor
526531

527532
dump:
528533
reply: *dev-attrs
@@ -540,6 +545,7 @@ operations:
540545
attributes:
541546
- id
542547
- phase-offset-monitor
548+
- phase-offset-avg-factor
543549
-
544550
name: device-create-ntf
545551
doc: Notification about device appearing

drivers/dpll/dpll_netlink.c

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,27 @@ dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll,
164164
return 0;
165165
}
166166

167+
static int
168+
dpll_msg_add_phase_offset_avg_factor(struct sk_buff *msg,
169+
struct dpll_device *dpll,
170+
struct netlink_ext_ack *extack)
171+
{
172+
const struct dpll_device_ops *ops = dpll_device_ops(dpll);
173+
u32 factor;
174+
int ret;
175+
176+
if (ops->phase_offset_avg_factor_get) {
177+
ret = ops->phase_offset_avg_factor_get(dpll, dpll_priv(dpll),
178+
&factor, extack);
179+
if (ret)
180+
return ret;
181+
if (nla_put_u32(msg, DPLL_A_PHASE_OFFSET_AVG_FACTOR, factor))
182+
return -EMSGSIZE;
183+
}
184+
185+
return 0;
186+
}
187+
167188
static int
168189
dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
169190
struct netlink_ext_ack *extack)
@@ -675,6 +696,9 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
675696
if (nla_put_u32(msg, DPLL_A_TYPE, dpll->type))
676697
return -EMSGSIZE;
677698
ret = dpll_msg_add_phase_offset_monitor(msg, dpll, extack);
699+
if (ret)
700+
return ret;
701+
ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack);
678702
if (ret)
679703
return ret;
680704

@@ -839,6 +863,23 @@ dpll_phase_offset_monitor_set(struct dpll_device *dpll, struct nlattr *a,
839863
extack);
840864
}
841865

866+
static int
867+
dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a,
868+
struct netlink_ext_ack *extack)
869+
{
870+
const struct dpll_device_ops *ops = dpll_device_ops(dpll);
871+
u32 factor = nla_get_u32(a);
872+
873+
if (!ops->phase_offset_avg_factor_set) {
874+
NL_SET_ERR_MSG_ATTR(extack, a,
875+
"device not capable of changing phase offset average factor");
876+
return -EOPNOTSUPP;
877+
}
878+
879+
return ops->phase_offset_avg_factor_set(dpll, dpll_priv(dpll), factor,
880+
extack);
881+
}
882+
842883
static int
843884
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
844885
struct netlink_ext_ack *extack)
@@ -1736,14 +1777,25 @@ int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)
17361777
static int
17371778
dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
17381779
{
1739-
int ret;
1740-
1741-
if (info->attrs[DPLL_A_PHASE_OFFSET_MONITOR]) {
1742-
struct nlattr *a = info->attrs[DPLL_A_PHASE_OFFSET_MONITOR];
1780+
struct nlattr *a;
1781+
int rem, ret;
17431782

1744-
ret = dpll_phase_offset_monitor_set(dpll, a, info->extack);
1745-
if (ret)
1746-
return ret;
1783+
nla_for_each_attr(a, genlmsg_data(info->genlhdr),
1784+
genlmsg_len(info->genlhdr), rem) {
1785+
switch (nla_type(a)) {
1786+
case DPLL_A_PHASE_OFFSET_MONITOR:
1787+
ret = dpll_phase_offset_monitor_set(dpll, a,
1788+
info->extack);
1789+
if (ret)
1790+
return ret;
1791+
break;
1792+
case DPLL_A_PHASE_OFFSET_AVG_FACTOR:
1793+
ret = dpll_phase_offset_avg_factor_set(dpll, a,
1794+
info->extack);
1795+
if (ret)
1796+
return ret;
1797+
break;
1798+
}
17471799
}
17481800

17491801
return 0;

drivers/dpll/dpll_nl.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = {
4242
};
4343

4444
/* DPLL_CMD_DEVICE_SET - do */
45-
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_MONITOR + 1] = {
45+
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_AVG_FACTOR + 1] = {
4646
[DPLL_A_ID] = { .type = NLA_U32, },
4747
[DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
48+
[DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, },
4849
};
4950

5051
/* DPLL_CMD_PIN_ID_GET - do */
@@ -112,7 +113,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
112113
.doit = dpll_nl_device_set_doit,
113114
.post_doit = dpll_post_doit,
114115
.policy = dpll_device_set_nl_policy,
115-
.maxattr = DPLL_A_PHASE_OFFSET_MONITOR,
116+
.maxattr = DPLL_A_PHASE_OFFSET_AVG_FACTOR,
116117
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
117118
},
118119
{

drivers/dpll/zl3073x/core.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,32 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
956956
msecs_to_jiffies(500));
957957
}
958958

959+
int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor)
960+
{
961+
u8 dpll_meas_ctrl, value;
962+
int rc;
963+
964+
/* Read DPLL phase measurement control register */
965+
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl);
966+
if (rc)
967+
return rc;
968+
969+
/* Convert requested factor to register value */
970+
value = (factor + 1) & 0x0f;
971+
972+
/* Update phase measurement control register */
973+
dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
974+
dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, value);
975+
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl);
976+
if (rc)
977+
return rc;
978+
979+
/* Save the new factor */
980+
zldev->phase_avg_factor = factor;
981+
982+
return 0;
983+
}
984+
959985
/**
960986
* zl3073x_dev_phase_meas_setup - setup phase offset measurement
961987
* @zldev: pointer to zl3073x_dev structure
@@ -972,15 +998,16 @@ zl3073x_dev_phase_meas_setup(struct zl3073x_dev *zldev)
972998
u8 dpll_meas_ctrl, mask = 0;
973999
int rc;
9741000

1001+
/* Setup phase measurement averaging factor */
1002+
rc = zl3073x_dev_phase_avg_factor_set(zldev, zldev->phase_avg_factor);
1003+
if (rc)
1004+
return rc;
1005+
9751006
/* Read DPLL phase measurement control register */
9761007
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl);
9771008
if (rc)
9781009
return rc;
9791010

980-
/* Setup phase measurement averaging factor */
981-
dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
982-
dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, 3);
983-
9841011
/* Enable DPLL measurement block */
9851012
dpll_meas_ctrl |= ZL_DPLL_MEAS_CTRL_EN;
9861013

@@ -1208,6 +1235,9 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
12081235
*/
12091236
zldev->clock_id = get_random_u64();
12101237

1238+
/* Default phase offset averaging factor */
1239+
zldev->phase_avg_factor = 2;
1240+
12111241
/* Initialize mutex for operations where multiple reads, writes
12121242
* and/or polls are required to be done atomically.
12131243
*/

drivers/dpll/zl3073x/core.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,19 @@ struct zl3073x_synth {
6868
* @dev: pointer to device
6969
* @regmap: regmap to access device registers
7070
* @multiop_lock: to serialize multiple register operations
71-
* @clock_id: clock id of the device
7271
* @ref: array of input references' invariants
7372
* @out: array of outs' invariants
7473
* @synth: array of synths' invariants
7574
* @dplls: list of DPLLs
7675
* @kworker: thread for periodic work
7776
* @work: periodic work
77+
* @clock_id: clock id of the device
78+
* @phase_avg_factor: phase offset measurement averaging factor
7879
*/
7980
struct zl3073x_dev {
8081
struct device *dev;
8182
struct regmap *regmap;
8283
struct mutex multiop_lock;
83-
u64 clock_id;
8484

8585
/* Invariants */
8686
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
@@ -93,6 +93,10 @@ struct zl3073x_dev {
9393
/* Monitor */
9494
struct kthread_worker *kworker;
9595
struct kthread_delayed_work work;
96+
97+
/* Devlink parameters */
98+
u64 clock_id;
99+
u8 phase_avg_factor;
96100
};
97101

98102
struct zl3073x_chip_info {
@@ -115,6 +119,13 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
115119
int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full);
116120
void zl3073x_dev_stop(struct zl3073x_dev *zldev);
117121

122+
static inline u8 zl3073x_dev_phase_avg_factor_get(struct zl3073x_dev *zldev)
123+
{
124+
return zldev->phase_avg_factor;
125+
}
126+
127+
int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor);
128+
118129
/**********************
119130
* Registers operations
120131
**********************/

drivers/dpll/zl3073x/dpll.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,6 +1576,59 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
15761576
return 0;
15771577
}
15781578

1579+
static int
1580+
zl3073x_dpll_phase_offset_avg_factor_get(const struct dpll_device *dpll,
1581+
void *dpll_priv, u32 *factor,
1582+
struct netlink_ext_ack *extack)
1583+
{
1584+
struct zl3073x_dpll *zldpll = dpll_priv;
1585+
1586+
*factor = zl3073x_dev_phase_avg_factor_get(zldpll->dev);
1587+
1588+
return 0;
1589+
}
1590+
1591+
static void
1592+
zl3073x_dpll_change_work(struct work_struct *work)
1593+
{
1594+
struct zl3073x_dpll *zldpll;
1595+
1596+
zldpll = container_of(work, struct zl3073x_dpll, change_work);
1597+
dpll_device_change_ntf(zldpll->dpll_dev);
1598+
}
1599+
1600+
static int
1601+
zl3073x_dpll_phase_offset_avg_factor_set(const struct dpll_device *dpll,
1602+
void *dpll_priv, u32 factor,
1603+
struct netlink_ext_ack *extack)
1604+
{
1605+
struct zl3073x_dpll *item, *zldpll = dpll_priv;
1606+
int rc;
1607+
1608+
if (factor > 15) {
1609+
NL_SET_ERR_MSG_FMT(extack,
1610+
"Phase offset average factor has to be from range <0,15>");
1611+
return -EINVAL;
1612+
}
1613+
1614+
rc = zl3073x_dev_phase_avg_factor_set(zldpll->dev, factor);
1615+
if (rc) {
1616+
NL_SET_ERR_MSG_FMT(extack,
1617+
"Failed to set phase offset averaging factor");
1618+
return rc;
1619+
}
1620+
1621+
/* The averaging factor is common for all DPLL channels so after change
1622+
* we have to send a notification for other DPLL devices.
1623+
*/
1624+
list_for_each_entry(item, &zldpll->dev->dplls, list) {
1625+
if (item != zldpll)
1626+
schedule_work(&item->change_work);
1627+
}
1628+
1629+
return 0;
1630+
}
1631+
15791632
static int
15801633
zl3073x_dpll_phase_offset_monitor_get(const struct dpll_device *dpll,
15811634
void *dpll_priv,
@@ -1635,6 +1688,8 @@ static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops = {
16351688
static const struct dpll_device_ops zl3073x_dpll_device_ops = {
16361689
.lock_status_get = zl3073x_dpll_lock_status_get,
16371690
.mode_get = zl3073x_dpll_mode_get,
1691+
.phase_offset_avg_factor_get = zl3073x_dpll_phase_offset_avg_factor_get,
1692+
.phase_offset_avg_factor_set = zl3073x_dpll_phase_offset_avg_factor_set,
16381693
.phase_offset_monitor_get = zl3073x_dpll_phase_offset_monitor_get,
16391694
.phase_offset_monitor_set = zl3073x_dpll_phase_offset_monitor_set,
16401695
};
@@ -1983,6 +2038,8 @@ zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
19832038
{
19842039
WARN(!zldpll->dpll_dev, "DPLL device is not registered\n");
19852040

2041+
cancel_work_sync(&zldpll->change_work);
2042+
19862043
dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops,
19872044
zldpll);
19882045
dpll_device_put(zldpll->dpll_dev);
@@ -2258,6 +2315,7 @@ zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch)
22582315
zldpll->dev = zldev;
22592316
zldpll->id = ch;
22602317
INIT_LIST_HEAD(&zldpll->pins);
2318+
INIT_WORK(&zldpll->change_work, zl3073x_dpll_change_work);
22612319

22622320
return zldpll;
22632321
}

drivers/dpll/zl3073x/dpll.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* @dpll_dev: pointer to registered DPLL device
2121
* @lock_status: last saved DPLL lock status
2222
* @pins: list of pins
23+
* @change_work: device change notification work
2324
*/
2425
struct zl3073x_dpll {
2526
struct list_head list;
@@ -32,6 +33,7 @@ struct zl3073x_dpll {
3233
struct dpll_device *dpll_dev;
3334
enum dpll_lock_status lock_status;
3435
struct list_head pins;
36+
struct work_struct change_work;
3537
};
3638

3739
struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);

include/linux/dpll.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ struct dpll_device_ops {
3838
void *dpll_priv,
3939
enum dpll_feature_state *state,
4040
struct netlink_ext_ack *extack);
41+
int (*phase_offset_avg_factor_set)(const struct dpll_device *dpll,
42+
void *dpll_priv, u32 factor,
43+
struct netlink_ext_ack *extack);
44+
int (*phase_offset_avg_factor_get)(const struct dpll_device *dpll,
45+
void *dpll_priv, u32 *factor,
46+
struct netlink_ext_ack *extack);
4147
};
4248

4349
struct dpll_pin_ops {

0 commit comments

Comments
 (0)