Skip to content

Commit

Permalink
Merge branch 'dpll-phase-offset-phase-adjust'
Browse files Browse the repository at this point in the history
Arkadiusz Kubalewski says:

====================
dpll: add phase-offset and phase-adjust

Improve monitoring and control over dpll devices.
Allow user to receive measurement of phase difference between signals
on pin and dpll (phase-offset).
Allow user to receive and control adjustable value of pin's signal
phase (phase-adjust).

v4->v5:
- rebase series on top of net-next/main, fix conflict - remove redundant
  attribute type definition in subset definition

v3->v4:
- do not increase do version of uAPI header as it is not needed (v3 did
  not have this change)
- fix spelling around commit messages, argument descriptions and docs
- add missing extack errors on failure set callbacks for pin phase
  adjust and frequency
- remove ice check if value is already set, now redundant as checked in
  the dpll subsystem

v2->v3:
- do not increase do version of uAPI header as it is not needed

v1->v2:
- improve handling for error case of requesting the phase adjust set
- align handling for error case of frequency set request with the
approach introduced for phase adjust
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 15, 2023
2 parents cc30c63 + 20f6677 commit 99620ea
Show file tree
Hide file tree
Showing 9 changed files with 517 additions and 18 deletions.
53 changes: 52 additions & 1 deletion Documentation/driver-api/dpll.rst
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,47 @@ in order to configure active input of a MUX-type pin, the user needs to
request desired pin state of the child pin on the parent pin,
as described in the ``MUX-type pins`` chapter.

Phase offset measurement and adjustment
========================================

Device may provide ability to measure a phase difference between signals
on a pin and its parent dpll device. If pin-dpll phase offset measurement
is supported, it shall be provided with ``DPLL_A_PIN_PHASE_OFFSET``
attribute for each parent dpll device.

Device may also provide ability to adjust a signal phase on a pin.
If pin phase adjustment is supported, minimal and maximal values that pin
handle shall be provide to the user on ``DPLL_CMD_PIN_GET`` respond
with ``DPLL_A_PIN_PHASE_ADJUST_MIN`` and ``DPLL_A_PIN_PHASE_ADJUST_MAX``
attributes. Configured phase adjust value is provided with
``DPLL_A_PIN_PHASE_ADJUST`` attribute of a pin, and value change can be
requested with the same attribute with ``DPLL_CMD_PIN_SET`` command.

=============================== ======================================
``DPLL_A_PIN_ID`` configured pin id
``DPLL_A_PIN_PHASE_ADJUST_MIN`` attr minimum value of phase adjustment
``DPLL_A_PIN_PHASE_ADJUST_MAX`` attr maximum value of phase adjustment
``DPLL_A_PIN_PHASE_ADJUST`` attr configured value of phase
adjustment on parent dpll device
``DPLL_A_PIN_PARENT_DEVICE`` nested attribute for requesting
configuration on given parent dpll
device
``DPLL_A_PIN_PARENT_ID`` parent dpll device id
``DPLL_A_PIN_PHASE_OFFSET`` attr measured phase difference
between a pin and parent dpll device
=============================== ======================================

All phase related values are provided in pico seconds, which represents
time difference between signals phase. The negative value means that
phase of signal on pin is earlier in time than dpll's signal. Positive
value means that phase of signal on pin is later in time than signal of
a dpll.

Phase adjust (also min and max) values are integers, but measured phase
offset values are fractional with 3-digit decimal places and shell be
divided with ``DPLL_PIN_PHASE_OFFSET_DIVIDER`` to get integer part and
modulo divided to get fractional part.

Configuration commands group
============================

Expand Down Expand Up @@ -263,15 +304,23 @@ according to attribute purpose.
frequencies
``DPLL_A_PIN_ANY_FREQUENCY_MIN`` attr minimum value of frequency
``DPLL_A_PIN_ANY_FREQUENCY_MAX`` attr maximum value of frequency
``DPLL_A_PIN_PHASE_ADJUST_MIN`` attr minimum value of phase
adjustment
``DPLL_A_PIN_PHASE_ADJUST_MAX`` attr maximum value of phase
adjustment
``DPLL_A_PIN_PHASE_ADJUST`` attr configured value of phase
adjustment on parent device
``DPLL_A_PIN_PARENT_DEVICE`` nested attr for each parent device
the pin is connected with
``DPLL_A_PIN_PARENT_ID`` attr parent dpll device id
``DPLL_A_PIN_PRIO`` attr priority of pin on the
dpll device
``DPLL_A_PIN_STATE`` attr state of pin on the parent
dpll device
``DPLL_A_PIN_DIRECTION`` attr direction of a pin on the
``DPLL_A_PIN_DIRECTION`` attr direction of a pin on the
parent dpll device
``DPLL_A_PIN_PHASE_OFFSET`` attr measured phase difference
between a pin and parent dpll
``DPLL_A_PIN_PARENT_PIN`` nested attr for each parent pin
the pin is connected with
``DPLL_A_PIN_PARENT_ID`` attr parent pin id
Expand All @@ -284,6 +333,8 @@ according to attribute purpose.
``DPLL_CMD_PIN_SET`` command to set pins configuration
``DPLL_A_PIN_ID`` attr unique a pin ID
``DPLL_A_PIN_FREQUENCY`` attr requested frequency of a pin
``DPLL_A_PIN_PHASE_ADJUST`` attr requested value of phase
adjustment on parent device
``DPLL_A_PIN_PARENT_DEVICE`` nested attr for each parent dpll
device configuration request
``DPLL_A_PIN_PARENT_ID`` attr parent dpll device id
Expand Down
30 changes: 30 additions & 0 deletions Documentation/netlink/specs/dpll.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ definitions:
-
name: state-can-change
doc: pin state can be changed
-
type: const
name: phase-offset-divider
value: 1000
doc: |
phase offset divider allows userspace to calculate a value of
measured signal phase difference between a pin and dpll device
as a fractional value with three digit decimal precision.
Value of (DPLL_A_PHASE_OFFSET / DPLL_PHASE_OFFSET_DIVIDER) is an
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.
attribute-sets:
-
Expand Down Expand Up @@ -272,6 +284,18 @@ attribute-sets:
type: nest
multi-attr: true
nested-attributes: pin-parent-pin
-
name: phase-adjust-min
type: s32
-
name: phase-adjust-max
type: s32
-
name: phase-adjust
type: s32
-
name: phase-offset
type: s64
-
name: pin-parent-device
subset-of: pin
Expand All @@ -284,6 +308,8 @@ attribute-sets:
name: prio
-
name: state
-
name: phase-offset
-
name: pin-parent-pin
subset-of: pin
Expand Down Expand Up @@ -431,6 +457,9 @@ operations:
- capabilities
- parent-device
- parent-pin
- phase-adjust-min
- phase-adjust-max
- phase-adjust

dump:
pre: dpll-lock-dumpit
Expand Down Expand Up @@ -458,6 +487,7 @@ operations:
- state
- parent-device
- parent-pin
- phase-adjust
-
name: pin-create-ntf
doc: Notification about pin appearing
Expand Down
188 changes: 179 additions & 9 deletions drivers/dpll/dpll_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,53 @@ dpll_msg_add_pin_direction(struct sk_buff *msg, struct dpll_pin *pin,
return 0;
}

static int
dpll_msg_add_pin_phase_adjust(struct sk_buff *msg, struct dpll_pin *pin,
struct dpll_pin_ref *ref,
struct netlink_ext_ack *extack)
{
const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
struct dpll_device *dpll = ref->dpll;
s32 phase_adjust;
int ret;

if (!ops->phase_adjust_get)
return 0;
ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll),
&phase_adjust, extack);
if (ret)
return ret;
if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST, phase_adjust))
return -EMSGSIZE;

return 0;
}

static int
dpll_msg_add_phase_offset(struct sk_buff *msg, struct dpll_pin *pin,
struct dpll_pin_ref *ref,
struct netlink_ext_ack *extack)
{
const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
struct dpll_device *dpll = ref->dpll;
s64 phase_offset;
int ret;

if (!ops->phase_offset_get)
return 0;
ret = ops->phase_offset_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), &phase_offset,
extack);
if (ret)
return ret;
if (nla_put_64bit(msg, DPLL_A_PIN_PHASE_OFFSET, sizeof(phase_offset),
&phase_offset, 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)
Expand Down Expand Up @@ -330,6 +377,9 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
if (ret)
goto nest_cancel;
ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
if (ret)
goto nest_cancel;
ret = dpll_msg_add_phase_offset(msg, pin, ref, extack);
if (ret)
goto nest_cancel;
nla_nest_end(msg, attr);
Expand Down Expand Up @@ -377,6 +427,15 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
if (nla_put_u32(msg, DPLL_A_PIN_CAPABILITIES, prop->capabilities))
return -EMSGSIZE;
ret = dpll_msg_add_pin_freq(msg, pin, ref, extack);
if (ret)
return ret;
if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MIN,
prop->phase_range.min))
return -EMSGSIZE;
if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MAX,
prop->phase_range.max))
return -EMSGSIZE;
ret = dpll_msg_add_pin_phase_adjust(msg, pin, ref, extack);
if (ret)
return ret;
if (xa_empty(&pin->parent_refs))
Expand Down Expand Up @@ -416,7 +475,7 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
if (nla_put_u32(msg, DPLL_A_TYPE, dpll->type))
return -EMSGSIZE;

return ret;
return 0;
}

static int
Expand Down Expand Up @@ -556,8 +615,10 @@ static int
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
struct netlink_ext_ack *extack)
{
u64 freq = nla_get_u64(a);
struct dpll_pin_ref *ref;
u64 freq = nla_get_u64(a), old_freq;
struct dpll_pin_ref *ref, *failed;
const struct dpll_pin_ops *ops;
struct dpll_device *dpll;
unsigned long i;
int ret;

Expand All @@ -567,19 +628,51 @@ dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
}

xa_for_each(&pin->dpll_refs, i, ref) {
const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
struct dpll_device *dpll = ref->dpll;

if (!ops->frequency_set)
ops = dpll_pin_ops(ref);
if (!ops->frequency_set || !ops->frequency_get) {
NL_SET_ERR_MSG(extack, "frequency set not supported by the device");
return -EOPNOTSUPP;
}
}
ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
ops = dpll_pin_ops(ref);
dpll = ref->dpll;
ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
dpll_priv(dpll), &old_freq, extack);
if (ret) {
NL_SET_ERR_MSG(extack, "unable to get old frequency value");
return ret;
}
if (freq == old_freq)
return 0;

xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
dpll = ref->dpll;
ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), freq, extack);
if (ret)
return ret;
if (ret) {
failed = ref;
NL_SET_ERR_MSG_FMT(extack, "frequency set failed for dpll_id:%u",
dpll->id);
goto rollback;
}
}
__dpll_pin_change_ntf(pin);

return 0;

rollback:
xa_for_each(&pin->dpll_refs, i, ref) {
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
dpll = ref->dpll;
if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), old_freq, extack))
NL_SET_ERR_MSG(extack, "set frequency rollback failed");
}
return ret;
}

static int
Expand Down Expand Up @@ -705,6 +798,78 @@ dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
return 0;
}

static int
dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
struct netlink_ext_ack *extack)
{
struct dpll_pin_ref *ref, *failed;
const struct dpll_pin_ops *ops;
s32 phase_adj, old_phase_adj;
struct dpll_device *dpll;
unsigned long i;
int ret;

phase_adj = nla_get_s32(phase_adj_attr);
if (phase_adj > pin->prop->phase_range.max ||
phase_adj < pin->prop->phase_range.min) {
NL_SET_ERR_MSG_ATTR(extack, phase_adj_attr,
"phase adjust value not supported");
return -EINVAL;
}

xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
if (!ops->phase_adjust_set || !ops->phase_adjust_get) {
NL_SET_ERR_MSG(extack, "phase adjust not supported");
return -EOPNOTSUPP;
}
}
ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
ops = dpll_pin_ops(ref);
dpll = ref->dpll;
ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), &old_phase_adj,
extack);
if (ret) {
NL_SET_ERR_MSG(extack, "unable to get old phase adjust value");
return ret;
}
if (phase_adj == old_phase_adj)
return 0;

xa_for_each(&pin->dpll_refs, i, ref) {
ops = dpll_pin_ops(ref);
dpll = ref->dpll;
ret = ops->phase_adjust_set(pin,
dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), phase_adj,
extack);
if (ret) {
failed = ref;
NL_SET_ERR_MSG_FMT(extack,
"phase adjust set failed for dpll_id:%u",
dpll->id);
goto rollback;
}
}
__dpll_pin_change_ntf(pin);

return 0;

rollback:
xa_for_each(&pin->dpll_refs, i, ref) {
if (ref == failed)
break;
ops = dpll_pin_ops(ref);
dpll = ref->dpll;
if (ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), old_phase_adj,
extack))
NL_SET_ERR_MSG(extack, "set phase adjust rollback failed");
}
return ret;
}

static int
dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,
struct netlink_ext_ack *extack)
Expand Down Expand Up @@ -793,6 +958,11 @@ dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
if (ret)
return ret;
break;
case DPLL_A_PIN_PHASE_ADJUST:
ret = dpll_pin_phase_adj_set(pin, a, info->extack);
if (ret)
return ret;
break;
case DPLL_A_PIN_PARENT_DEVICE:
ret = dpll_pin_parent_device_set(pin, a, info->extack);
if (ret)
Expand Down
Loading

0 comments on commit 99620ea

Please sign in to comment.