Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+199 -16
+17 -1
Documentation/driver-api/dpll.rst
··· 179 179 Device may provide ability to measure a phase difference between signals 180 180 on a pin and its parent dpll device. If pin-dpll phase offset measurement 181 181 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 + ================================== ====================================== 183 199 184 200 Device may also provide ability to adjust a signal phase on a pin. 185 201 If pin phase adjustment is supported, minimal and maximal values that pin
+6
Documentation/netlink/specs/dpll.yaml
··· 315 315 If enabled, dpll device shall monitor and notify all currently 316 316 available inputs for changes of their phase offset against the 317 317 dpll device. 318 + - 319 + name: phase-offset-avg-factor 320 + type: u32 321 + doc: Averaging factor applied to calculation of reported phase offset. 318 322 - 319 323 name: pin 320 324 enum-name: dpll_a_pin ··· 527 523 - clock-id 528 524 - type 529 525 - phase-offset-monitor 526 + - phase-offset-avg-factor 530 527 531 528 dump: 532 529 reply: *dev-attrs ··· 545 540 attributes: 546 541 - id 547 542 - phase-offset-monitor 543 + - phase-offset-avg-factor 548 544 - 549 545 name: device-create-ntf 550 546 doc: Notification about device appearing
+59 -7
drivers/dpll/dpll_netlink.c
··· 165 165 } 166 166 167 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 + 188 + static int 168 189 dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll, 169 190 struct netlink_ext_ack *extack) 170 191 { ··· 698 677 ret = dpll_msg_add_phase_offset_monitor(msg, dpll, extack); 699 678 if (ret) 700 679 return ret; 680 + ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack); 681 + if (ret) 682 + return ret; 701 683 702 684 return 0; 703 685 } ··· 861 837 862 838 return ops->phase_offset_monitor_set(dpll, dpll_priv(dpll), state, 863 839 extack); 840 + } 841 + 842 + static int 843 + dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a, 844 + struct netlink_ext_ack *extack) 845 + { 846 + const struct dpll_device_ops *ops = dpll_device_ops(dpll); 847 + u32 factor = nla_get_u32(a); 848 + 849 + if (!ops->phase_offset_avg_factor_set) { 850 + NL_SET_ERR_MSG_ATTR(extack, a, 851 + "device not capable of changing phase offset average factor"); 852 + return -EOPNOTSUPP; 853 + } 854 + 855 + return ops->phase_offset_avg_factor_set(dpll, dpll_priv(dpll), factor, 856 + extack); 864 857 } 865 858 866 859 static int ··· 1777 1736 static int 1778 1737 dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info) 1779 1738 { 1780 - int ret; 1739 + struct nlattr *a; 1740 + int rem, ret; 1781 1741 1782 - if (info->attrs[DPLL_A_PHASE_OFFSET_MONITOR]) { 1783 - struct nlattr *a = info->attrs[DPLL_A_PHASE_OFFSET_MONITOR]; 1784 - 1785 - ret = dpll_phase_offset_monitor_set(dpll, a, info->extack); 1786 - if (ret) 1787 - return ret; 1742 + nla_for_each_attr(a, genlmsg_data(info->genlhdr), 1743 + genlmsg_len(info->genlhdr), rem) { 1744 + switch (nla_type(a)) { 1745 + case DPLL_A_PHASE_OFFSET_MONITOR: 1746 + ret = dpll_phase_offset_monitor_set(dpll, a, 1747 + info->extack); 1748 + if (ret) 1749 + return ret; 1750 + break; 1751 + case DPLL_A_PHASE_OFFSET_AVG_FACTOR: 1752 + ret = dpll_phase_offset_avg_factor_set(dpll, a, 1753 + info->extack); 1754 + if (ret) 1755 + return ret; 1756 + break; 1757 + } 1788 1758 } 1789 1759 1790 1760 return 0;
+3 -2
drivers/dpll/dpll_nl.c
··· 42 42 }; 43 43 44 44 /* 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] = { 46 46 [DPLL_A_ID] = { .type = NLA_U32, }, 47 47 [DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1), 48 + [DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, }, 48 49 }; 49 50 50 51 /* DPLL_CMD_PIN_ID_GET - do */ ··· 113 112 .doit = dpll_nl_device_set_doit, 114 113 .post_doit = dpll_post_doit, 115 114 .policy = dpll_device_set_nl_policy, 116 - .maxattr = DPLL_A_PHASE_OFFSET_MONITOR, 115 + .maxattr = DPLL_A_PHASE_OFFSET_AVG_FACTOR, 117 116 .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, 118 117 }, 119 118 {
+34 -4
drivers/dpll/zl3073x/core.c
··· 956 956 msecs_to_jiffies(500)); 957 957 } 958 958 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 + 959 985 /** 960 986 * zl3073x_dev_phase_meas_setup - setup phase offset measurement 961 987 * @zldev: pointer to zl3073x_dev structure ··· 998 972 u8 dpll_meas_ctrl, mask = 0; 999 973 int rc; 1000 974 975 + /* Setup phase measurement averaging factor */ 976 + rc = zl3073x_dev_phase_avg_factor_set(zldev, zldev->phase_avg_factor); 977 + if (rc) 978 + return rc; 979 + 1001 980 /* Read DPLL phase measurement control register */ 1002 981 rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, &dpll_meas_ctrl); 1003 982 if (rc) 1004 983 return rc; 1005 - 1006 - /* Setup phase measurement averaging factor */ 1007 - dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR; 1008 - dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, 3); 1009 984 1010 985 /* Enable DPLL measurement block */ 1011 986 dpll_meas_ctrl |= ZL_DPLL_MEAS_CTRL_EN; ··· 1234 1207 * using devlink. 1235 1208 */ 1236 1209 zldev->clock_id = get_random_u64(); 1210 + 1211 + /* Default phase offset averaging factor */ 1212 + zldev->phase_avg_factor = 2; 1237 1213 1238 1214 /* Initialize mutex for operations where multiple reads, writes 1239 1215 * and/or polls are required to be done atomically.
+13 -2
drivers/dpll/zl3073x/core.h
··· 68 68 * @dev: pointer to device 69 69 * @regmap: regmap to access device registers 70 70 * @multiop_lock: to serialize multiple register operations 71 - * @clock_id: clock id of the device 72 71 * @ref: array of input references' invariants 73 72 * @out: array of outs' invariants 74 73 * @synth: array of synths' invariants 75 74 * @dplls: list of DPLLs 76 75 * @kworker: thread for periodic work 77 76 * @work: periodic work 77 + * @clock_id: clock id of the device 78 + * @phase_avg_factor: phase offset measurement averaging factor 78 79 */ 79 80 struct zl3073x_dev { 80 81 struct device *dev; 81 82 struct regmap *regmap; 82 83 struct mutex multiop_lock; 83 - u64 clock_id; 84 84 85 85 /* Invariants */ 86 86 struct zl3073x_ref ref[ZL3073X_NUM_REFS]; ··· 93 93 /* Monitor */ 94 94 struct kthread_worker *kworker; 95 95 struct kthread_delayed_work work; 96 + 97 + /* Devlink parameters */ 98 + u64 clock_id; 99 + u8 phase_avg_factor; 96 100 }; 97 101 98 102 struct zl3073x_chip_info { ··· 118 114 119 115 int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full); 120 116 void zl3073x_dev_stop(struct zl3073x_dev *zldev); 117 + 118 + static inline u8 zl3073x_dev_phase_avg_factor_get(struct zl3073x_dev *zldev) 119 + { 120 + return zldev->phase_avg_factor; 121 + } 122 + 123 + int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor); 121 124 122 125 /********************** 123 126 * Registers operations
+58
drivers/dpll/zl3073x/dpll.c
··· 1577 1577 } 1578 1578 1579 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 + 1632 + static int 1580 1633 zl3073x_dpll_phase_offset_monitor_get(const struct dpll_device *dpll, 1581 1634 void *dpll_priv, 1582 1635 enum dpll_feature_state *state, ··· 1688 1635 static const struct dpll_device_ops zl3073x_dpll_device_ops = { 1689 1636 .lock_status_get = zl3073x_dpll_lock_status_get, 1690 1637 .mode_get = zl3073x_dpll_mode_get, 1638 + .phase_offset_avg_factor_get = zl3073x_dpll_phase_offset_avg_factor_get, 1639 + .phase_offset_avg_factor_set = zl3073x_dpll_phase_offset_avg_factor_set, 1691 1640 .phase_offset_monitor_get = zl3073x_dpll_phase_offset_monitor_get, 1692 1641 .phase_offset_monitor_set = zl3073x_dpll_phase_offset_monitor_set, 1693 1642 }; ··· 2038 1983 { 2039 1984 WARN(!zldpll->dpll_dev, "DPLL device is not registered\n"); 2040 1985 1986 + cancel_work_sync(&zldpll->change_work); 1987 + 2041 1988 dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops, 2042 1989 zldpll); 2043 1990 dpll_device_put(zldpll->dpll_dev); ··· 2315 2258 zldpll->dev = zldev; 2316 2259 zldpll->id = ch; 2317 2260 INIT_LIST_HEAD(&zldpll->pins); 2261 + INIT_WORK(&zldpll->change_work, zl3073x_dpll_change_work); 2318 2262 2319 2263 return zldpll; 2320 2264 }
+2
drivers/dpll/zl3073x/dpll.h
··· 20 20 * @dpll_dev: pointer to registered DPLL device 21 21 * @lock_status: last saved DPLL lock status 22 22 * @pins: list of pins 23 + * @change_work: device change notification work 23 24 */ 24 25 struct zl3073x_dpll { 25 26 struct list_head list; ··· 33 32 struct dpll_device *dpll_dev; 34 33 enum dpll_lock_status lock_status; 35 34 struct list_head pins; 35 + struct work_struct change_work; 36 36 }; 37 37 38 38 struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch);
+6
include/linux/dpll.h
··· 38 38 void *dpll_priv, 39 39 enum dpll_feature_state *state, 40 40 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); 41 47 }; 42 48 43 49 struct dpll_pin_ops {
+1
include/uapi/linux/dpll.h
··· 216 216 DPLL_A_LOCK_STATUS_ERROR, 217 217 DPLL_A_CLOCK_QUALITY_LEVEL, 218 218 DPLL_A_PHASE_OFFSET_MONITOR, 219 + DPLL_A_PHASE_OFFSET_AVG_FACTOR, 219 220 220 221 __DPLL_A_MAX, 221 222 DPLL_A_MAX = (__DPLL_A_MAX - 1)