Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 differnece 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
33 changes: 32 additions & 1 deletion Documentation/netlink/specs/dpll.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)

name: dpll

version: 2
doc: DPLL subsystem.

definitions:
Expand Down 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 offest value.
Value of (DPLL_A_PHASE_OFFSET % DPLL_PHASE_OFFSET_DIVIDER) is a
fractional part of a measured phase offest 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 @@ -288,6 +312,9 @@ attribute-sets:
-
name: state
type: u32
-
name: phase-offset
type: s64
-
name: pin-parent-pin
subset-of: pin
Expand Down Expand Up @@ -439,6 +466,9 @@ operations:
- capabilities
- parent-device
- parent-pin
- phase-adjust-min
- phase-adjust-max
- phase-adjust

dump:
pre: dpll-lock-dumpit
Expand Down Expand Up @@ -466,6 +496,7 @@ operations:
- state
- parent-device
- parent-pin
- phase-adjust
-
name: pin-create-ntf
doc: Notification about pin appearing
Expand Down
99 changes: 98 additions & 1 deletion 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 @@ -705,6 +764,39 @@ 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;
unsigned long i;
s32 phase_adj;
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(extack, "phase adjust value not supported");
return -EINVAL;
}
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->phase_adjust_set)
return -EOPNOTSUPP;
ret = ops->phase_adjust_set(pin,
dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), phase_adj,
extack);
if (ret)
return ret;
}
__dpll_pin_change_ntf(pin);

return 0;
}

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 +885,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
8 changes: 5 additions & 3 deletions drivers/dpll/dpll_nl.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
#include <uapi/linux/dpll.h>

/* Common nested types */
const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_STATE + 1] = {
const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_PHASE_OFFSET + 1] = {
[DPLL_A_PIN_PARENT_ID] = { .type = NLA_U32, },
[DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
[DPLL_A_PIN_PHASE_OFFSET] = { .type = NLA_S64, },
};

const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1] = {
Expand Down Expand Up @@ -61,14 +62,15 @@ static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_PIN_ID + 1] =
};

/* DPLL_CMD_PIN_SET - do */
static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT_PIN + 1] = {
static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PHASE_ADJUST + 1] = {
[DPLL_A_PIN_ID] = { .type = NLA_U32, },
[DPLL_A_PIN_FREQUENCY] = { .type = NLA_U64, },
[DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
[DPLL_A_PIN_PARENT_DEVICE] = NLA_POLICY_NESTED(dpll_pin_parent_device_nl_policy),
[DPLL_A_PIN_PARENT_PIN] = NLA_POLICY_NESTED(dpll_pin_parent_pin_nl_policy),
[DPLL_A_PIN_PHASE_ADJUST] = { .type = NLA_S32, },
};

/* Ops table for dpll */
Expand Down Expand Up @@ -140,7 +142,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
.doit = dpll_nl_pin_set_doit,
.post_doit = dpll_pin_post_doit,
.policy = dpll_pin_set_nl_policy,
.maxattr = DPLL_A_PIN_PARENT_PIN,
.maxattr = DPLL_A_PIN_PHASE_ADJUST,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
};
Expand Down
2 changes: 1 addition & 1 deletion drivers/dpll/dpll_nl.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include <uapi/linux/dpll.h>

/* Common nested types */
extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_STATE + 1];
extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_PHASE_OFFSET + 1];
extern const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1];

int dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
Expand Down
Loading