요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
===============================
The Linux kernel dpll subsystem
===============================
DPLL
====
PLL - Phase Locked Loop is an electronic circuit which syntonizes clock
signal of a device with an external clock signal. Effectively enabling
device to run on the same clock signal beat as provided on a PLL input.
DPLL - Digital Phase Locked Loop is an integrated circuit which in
addition to plain PLL behavior incorporates a digital phase detector
and may have digital divider in the loop. As a result, the frequency on
DPLL's input and output may be configurable.
Subsystem
=========
The main purpose of dpll subsystem is to provide general interface
to configure devices that use any kind of Digital PLL and could use
different sources of input signal to synchronize to, as well as
different types of outputs.
The main interface is NETLINK_GENERIC based protocol with an event
monitoring multicast group defined.
Device object
=============
Single dpll device object means single Digital PLL circuit and bunch of
connected pins.
It reports the supported modes of operation and current status to the
user in response to the `do` request of netlink command
``DPLL_CMD_DEVICE_GET`` and list of dplls registered in the subsystem
with `dump` netlink request of the same command.
Changing the configuration of dpll device is done with `do` request of
netlink ``DPLL_CMD_DEVICE_SET`` command.
A device handle is ``DPLL_A_ID``, it shall be provided to get or set
configuration of particular device in the system. It can be obtained
with a ``DPLL_CMD_DEVICE_GET`` `dump` request or
a ``DPLL_CMD_DEVICE_ID_GET`` `do` request, where the one must provide
attributes that result in single device match.
Pin object
==========
A pin is amorphic object which represents either input or output, it
could be internal component of the device, as well as externally
connected.
The number of pins per dpll vary, but usually multiple pins shall be
provided for a single dpll device.
Pin's properties, capabilities and status is provided to the user in
response to `do` request of netlink ``DPLL_CMD_PIN_GET`` command.
It is also possible to list all the pins that were registered in the
system with `dump` request of ``DPLL_CMD_PIN_GET`` command.
Configuration of a pin can be changed by `do` request of netlink
``DPLL_CMD_PIN_SET`` command.
Pin handle is a ``DPLL_A_PIN_ID``, it shall be provided to get or set
configuration of particular pin in the system. It can be obtained with
``DPLL_CMD_PIN_GET`` `dump` request or ``DPLL_CMD_PIN_ID_GET`` `do`
request, where user provides attributes that result in single pin match.
Pin selection
=============
In general, selected pin (the one which signal is driving the dpll
device) can be obtained from ``DPLL_A_PIN_STATE`` attribute, and only
one pin shall be in ``DPLL_PIN_STATE_CONNECTED`` state for any dpll
device.
Pin selection can be done either manually or automatically, depending
on hardware capabilities and active dpll device work mode
(``DPLL_A_MODE`` attribute). The consequence is that there are
differences for each mode in terms of available pin states, as well as
for the states the user can request for a dpll device.
In manual mode (``DPLL_MODE_MANUAL``) the user can request or receive
one of following pin states:
- ``DPLL_PIN_STATE_CONNECTED`` - the pin is used to drive dpll device
- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not used to drive dpll
device
In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request or
receive one of following pin states:
- ``DPLL_PIN_STATE_SELECTABLE`` - the pin shall be considered as valid
input for automatic selection algorithm
- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
a valid input for automatic selection algorithm
In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can only receive
pin state ``DPLL_PIN_STATE_CONNECTED`` once automatic selection
algorithm locks a dpll device with one of the inputs.
Shared pins
===========
A single pin object can be attached to multiple dpll devices.
Then there are two groups of configuration knobs:
1) Set on a pin - the configuration affects all dpll devices pin is
registered to (i.e., ``DPLL_A_PIN_FREQUENCY``),
2) Set on a pin-dpll tuple - the configuration affects only selected
dpll device (i.e., ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE``,
``DPLL_A_PIN_DIRECTION``).
MUX-type pins
=============
A pin can be MUX-type, it aggregates child pins and serves as a pin
multiplexer. One or more pins are registered with MUX-type instead of
being directly registered to a dpll device.
Pins registered with a MUX-type pin provide user with additional nested
attribute ``DPLL_A_PIN_PARENT_PIN`` for each parent they were registered
with.
If a pin was registered with multiple parent pins, they behave like a
multiple output multiplexer. In this case output of a
``DPLL_CMD_PIN_GET`` would contain multiple pin-parent nested
attributes with current state related to each parent, like::
'pin': [{{
'clock-id': 282574471561216,
'module-name': 'ice',
'capabilities': 4,
'id': 13,
'parent-pin': [
{'parent-id': 2, 'state': 'connected'},
{'parent-id': 3, 'state': 'disconnected'}
],
'type': 'synce-eth-port'
}}]
Only one child pin can provide its signal to the parent MUX-type pin at
a time, the selection is done by requesting change of a child pin state
on desired parent, with the use of ``DPLL_A_PIN_PARENT`` nested
attribute. Example of netlink `set state on parent pin` message format:
========================== =============================================
``DPLL_A_PIN_ID`` child pin id
``DPLL_A_PIN_PARENT_PIN`` nested attribute for requesting configuration
related to parent pin
``DPLL_A_PIN_PARENT_ID`` parent pin id
``DPLL_A_PIN_STATE`` requested pin state on parent
========================== =============================================
Pin priority
============
Some devices might offer a capability of automatic pin selection mode
(enum value ``DPLL_MODE_AUTOMATIC`` of ``DPLL_A_MODE`` attribute).
Usually, automatic selection is performed on the hardware level, which
means only pins directly connected to the dpll can be used for automatic
input pin selection.
In automatic selection mode, the user cannot manually select a input
pin for the device, instead the user shall provide all directly
connected pins with a priority ``DPLL_A_PIN_PRIO``, the device would
pick a highest priority valid signal and use it to control the DPLL
device. Example of netlink `set priority on parent pin` message format:
============================ =============================================
``DPLL_A_PIN_ID`` configured pin id
``DPLL_A_PIN_PARENT_DEVICE`` nested attribute for requesting configuration
related to parent dpll device
``DPLL_A_PIN_PARENT_ID`` parent dpll device id
``DPLL_A_PIN_PRIO`` requested pin prio on parent dpll
============================ =============================================
Child pin of MUX-type pin is not capable of automatic input pin selection,
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. The reported phase offset may be
computed as the average of prior values and the current measurement, using
the following formula:
.. math::
curr\_avg = prev\_avg * \frac{2^N-1}{2^N} + new\_val * \frac{1}{2^N}
where `curr_avg` is the current reported phase offset, `prev_avg` is the
previously reported value, `new_val` is the current measurement, and `N` is
the averaging factor. Configured averaging factor value is provided with
``DPLL_A_PHASE_OFFSET_AVG_FACTOR`` attribute of a device and value change can
be requested with the same attribute with ``DPLL_CMD_DEVICE_SET`` command.
================================== ======================================
``DPLL_A_PHASE_OFFSET_AVG_FACTOR`` attr configured value of phase offset
averaging factor
================================== ======================================
Device may also provide ability to adjust a signal phase on a pin.
If pin phase adjustment is supported, minimal and maximal values and
granularity that pin handle shall be provided to the user on
``DPLL_CMD_PIN_GET`` respond with ``DPLL_A_PIN_PHASE_ADJUST_MIN``,
``DPLL_A_PIN_PHASE_ADJUST_MAX`` and ``DPLL_A_PIN_PHASE_ADJUST_GRAN``
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_GRAN`` attr granularity of phase adjustment value
``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.
Phase offset monitor
====================
Phase offset measurement is typically performed against the current active
source. However, some DPLL (Digital Phase-Locked Loop) devices may offer
the capability to monitor phase offsets across all available inputs.
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 phase offset measurements for the input shall be returned
in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute.
=============================== ========================
``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature
=============================== ========================
Embedded SYNC
=============
Device may provide ability to use Embedded SYNC feature. It allows
to embed additional SYNC signal into the base frequency of a pin - a one
special pulse of base frequency signal every time SYNC signal pulse
happens. The user can configure the frequency of Embedded SYNC.
The Embedded SYNC capability is always related to a given base frequency
and HW capabilities. The user is provided a range of Embedded SYNC
frequencies supported, depending on current base frequency configured for
the pin.
========================================= =================================
``DPLL_A_PIN_ESYNC_FREQUENCY`` current Embedded SYNC frequency
``DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED`` nest available Embedded SYNC
frequency ranges
``DPLL_A_PIN_FREQUENCY_MIN`` attr minimum value of frequency
``DPLL_A_PIN_FREQUENCY_MAX`` attr maximum value of frequency
``DPLL_A_PIN_ESYNC_PULSE`` pulse type of Embedded SYNC
========================================= =================================
Reference SYNC
==============
The device may support the Reference SYNC feature, which allows the combination
of two inputs into a input pair. In this configuration, clock signals
from both inputs are used to synchronize the DPLL device. The higher frequency
signal is utilized for the loop bandwidth of the DPLL, while the lower frequency
signal is used to syntonize the output signal of the DPLL device. This feature
enables the provision of a high-quality loop bandwidth signal from an external
source.
A capable input provides a list of inputs that can be bound with to create
Reference SYNC. To control this feature, the user must request a desired
state for a target pin: use ``DPLL_PIN_STATE_CONNECTED`` to enable or
``DPLL_PIN_STATE_DISCONNECTED`` to disable the feature. An input pin can be
bound to only one other pin at any given time.
============================== ==========================================
``DPLL_A_PIN_REFERENCE_SYNC`` nested attribute for providing info or
requesting configuration of the Reference
SYNC feature
``DPLL_A_PIN_ID`` target pin id for Reference SYNC feature
``DPLL_A_PIN_STATE`` state of Reference SYNC connection
============================== ==========================================
Configuration commands group
============================
Configuration commands are used to get information about registered
dpll devices (and pins), as well as set configuration of device or pins.
As dpll devices must be abstracted and reflect real hardware,
there is no way to add new dpll device via netlink from user space and
each device should be registered by its driver.
All netlink commands require ``GENL_ADMIN_PERM``. This is to prevent
any spamming/DoS from unauthorized userspace applications.
List of netlink commands with possible attributes
=================================================
Constants identifying command types for dpll device uses a
``DPLL_CMD_`` prefix and suffix according to command purpose.
The dpll device related attributes use a ``DPLL_A_`` prefix and
suffix according to attribute purpose.
==================================== =================================
``DPLL_CMD_DEVICE_ID_GET`` command to get device ID
``DPLL_A_MODULE_NAME`` attr module name of registerer
``DPLL_A_CLOCK_ID`` attr Unique Clock Identifier
(EUI-64), as defined by the
IEEE 1588 standard
``DPLL_A_TYPE`` attr type of dpll device
==================================== =================================
==================================== =================================
``DPLL_CMD_DEVICE_GET`` command to get device info or
dump list of available devices
``DPLL_A_ID`` attr unique dpll device ID
``DPLL_A_MODULE_NAME`` attr module name of registerer
``DPLL_A_CLOCK_ID`` attr Unique Clock Identifier
(EUI-64), as defined by the
IEEE 1588 standard
``DPLL_A_MODE`` attr selection mode
``DPLL_A_MODE_SUPPORTED`` attr available selection modes
``DPLL_A_LOCK_STATUS`` attr dpll device lock status
``DPLL_A_TEMP`` attr device temperature info
``DPLL_A_TYPE`` attr type of dpll device
==================================== =================================
==================================== =================================
``DPLL_CMD_DEVICE_SET`` command to set dpll device config
``DPLL_A_ID`` attr internal dpll device index
``DPLL_A_MODE`` attr selection mode to configure
==================================== =================================
Constants identifying command types for pins uses a
``DPLL_CMD_PIN_`` prefix and suffix according to command purpose.
The pin related attributes use a ``DPLL_A_PIN_`` prefix and suffix
according to attribute purpose.
==================================== =================================
``DPLL_CMD_PIN_ID_GET`` command to get pin ID
``DPLL_A_PIN_MODULE_NAME`` attr module name of registerer
``DPLL_A_PIN_CLOCK_ID`` attr Unique Clock Identifier
(EUI-64), as defined by the
IEEE 1588 standard
``DPLL_A_PIN_BOARD_LABEL`` attr pin board label provided
by registerer
``DPLL_A_PIN_PANEL_LABEL`` attr pin panel label provided
by registerer
``DPLL_A_PIN_PACKAGE_LABEL`` attr pin package label provided
by registerer
``DPLL_A_PIN_TYPE`` attr type of a pin
==================================== =================================
==================================== ==================================
``DPLL_CMD_PIN_GET`` command to get pin info or dump
list of available pins
``DPLL_A_PIN_ID`` attr unique a pin ID
``DPLL_A_PIN_MODULE_NAME`` attr module name of registerer
``DPLL_A_PIN_CLOCK_ID`` attr Unique Clock Identifier
(EUI-64), as defined by the
IEEE 1588 standard
``DPLL_A_PIN_BOARD_LABEL`` attr pin board label provided
by registerer
``DPLL_A_PIN_PANEL_LABEL`` attr pin panel label provided
by registerer
``DPLL_A_PIN_PACKAGE_LABEL`` attr pin package label provided
by registerer
``DPLL_A_PIN_TYPE`` attr type of a pin
``DPLL_A_PIN_FREQUENCY`` attr current frequency of a pin
``DPLL_A_PIN_FREQUENCY_SUPPORTED`` nested attr provides supported
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_GRAN`` attr granularity of phase
adjustment value
``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
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
``DPLL_A_PIN_STATE`` attr state of pin on the parent
pin
``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities
==================================== ==================================
==================================== =================================
``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
``DPLL_A_PIN_DIRECTION`` attr requested direction of a pin
``DPLL_A_PIN_PRIO`` attr requested priority of pin on
the dpll device
``DPLL_A_PIN_STATE`` attr requested state of pin on
the dpll device
``DPLL_A_PIN_PARENT_PIN`` nested attr for each parent pin
configuration request
``DPLL_A_PIN_PARENT_ID`` attr parent pin id
``DPLL_A_PIN_STATE`` attr requested state of pin on
parent pin
==================================== =================================
Netlink dump requests
=====================
The ``DPLL_CMD_DEVICE_GET`` and ``DPLL_CMD_PIN_GET`` commands are
capable of dump type netlink requests, in which case the response is in
the same format as for their ``do`` request, but every device or pin
registered in the system is returned.
SET commands format
===================
``DPLL_CMD_DEVICE_SET`` - to target a dpll device, the user provides
``DPLL_A_ID``, which is unique identifier of dpll device in the system,
as well as parameter being configured (``DPLL_A_MODE``).
``DPLL_CMD_PIN_SET`` - to target a pin user must provide a
``DPLL_A_PIN_ID``, which is unique identifier of a pin in the system.
Also configured pin parameters must be added.
If ``DPLL_A_PIN_FREQUENCY`` is configured, this affects all the dpll
devices that are connected with the pin, that is why frequency attribute
shall not be enclosed in ``DPLL_A_PIN_PARENT_DEVICE``.
Other attributes: ``DPLL_A_PIN_PRIO``, ``DPLL_A_PIN_STATE`` or
``DPLL_A_PIN_DIRECTION`` must be enclosed in
``DPLL_A_PIN_PARENT_DEVICE`` as their configuration relates to only one
of parent dplls, targeted by ``DPLL_A_PIN_PARENT_ID`` attribute which is
also required inside that nest.
For MUX-type pins the ``DPLL_A_PIN_STATE`` attribute is configured in
similar way, by enclosing required state in ``DPLL_A_PIN_PARENT_PIN``
nested attribute and targeted parent pin id in ``DPLL_A_PIN_PARENT_ID``.
In general, it is possible to configure multiple parameters at once, but
internally each parameter change will be invoked separately, where order
of configuration is not guaranteed by any means.
Configuration pre-defined enums
===============================
.. kernel-doc:: include/uapi/linux/dpll.h
Notifications
=============
dpll device can provide notifications regarding status changes of the
device, i.e. lock status changes, input/output changes or other alarms.
There is one multicast group that is used to notify user-space apps via
netlink socket: ``DPLL_MCGRP_MONITOR``
Notifications messages:
============================== =====================================
``DPLL_CMD_DEVICE_CREATE_NTF`` dpll device was created
``DPLL_CMD_DEVICE_DELETE_NTF`` dpll device was deleted
``DPLL_CMD_DEVICE_CHANGE_NTF`` dpll device has changed
``DPLL_CMD_PIN_CREATE_NTF`` dpll pin was created
``DPLL_CMD_PIN_DELETE_NTF`` dpll pin was deleted
``DPLL_CMD_PIN_CHANGE_NTF`` dpll pin has changed
============================== =====================================
Events format is the same as for the corresponding get command.
Format of ``DPLL_CMD_DEVICE_`` events is the same as response of
``DPLL_CMD_DEVICE_GET``.
Format of ``DPLL_CMD_PIN_`` events is same as response of
``DPLL_CMD_PIN_GET``.
Device driver implementation
============================
Device is allocated by dpll_device_get() call. Second call with the
same arguments will not create new object but provides pointer to
previously created device for given arguments, it also increases
refcount of that object.
Device is deallocated by dpll_device_put() call, which first
decreases the refcount, once refcount is cleared the object is
destroyed.
Device should implement set of operations and register device via
dpll_device_register() at which point it becomes available to the
users. Multiple driver instances can obtain reference to it with
dpll_device_get(), as well as register dpll device with their own
ops and priv.
The pins are allocated separately with dpll_pin_get(), it works
similarly to dpll_device_get(). Function first creates object and then
for each call with the same arguments only the object refcount
increases. Also dpll_pin_put() works similarly to dpll_device_put().
A pin can be registered with parent dpll device or parent pin, depending
on hardware needs. Each registration requires registerer to provide set
of pin callbacks, and private data pointer for calling them:
- dpll_pin_register() - register pin with a dpll device,
- dpll_pin_on_pin_register() - register pin with another MUX type pin.
Notifications of adding or removing dpll devices are created within
subsystem itself.
Notifications about registering/deregistering pins are also invoked by
the subsystem.
Notifications about status changes either of dpll device or a pin are
invoked in two ways:
- after successful change was requested on dpll subsystem, the subsystem
calls corresponding notification,
- requested by device driver with dpll_device_change_ntf() or
dpll_pin_change_ntf() when driver informs about the status change.
The device driver using dpll interface is not required to implement all
the callback operation. Nevertheless, there are few required to be
implemented.
Required dpll device level callback operations:
- ``.mode_get``,
- ``.lock_status_get``.
Required pin level callback operations:
- ``.state_on_dpll_get`` (pins registered with dpll device),
- ``.state_on_pin_get`` (pins registered with parent pin),
- ``.direction_get``.
Every other operation handler is checked for existence and
``-EOPNOTSUPP`` is returned in case of absence of specific handler.
The simplest implementation is in the OCP TimeCard driver. The ops
structures are defined like this:
.. code-block:: c
static const struct dpll_device_ops dpll_ops = {
.lock_status_get = ptp_ocp_dpll_lock_status_get,
.mode_get = ptp_ocp_dpll_mode_get,
.mode_supported = ptp_ocp_dpll_mode_supported,
};
static const struct dpll_pin_ops dpll_pins_ops = {
.frequency_get = ptp_ocp_dpll_frequency_get,
.frequency_set = ptp_ocp_dpll_frequency_set,
.direction_get = ptp_ocp_dpll_direction_get,
.direction_set = ptp_ocp_dpll_direction_set,
.state_on_dpll_get = ptp_ocp_dpll_state_get,
};
The registration part is then looks like this part:
.. code-block:: c
clkid = pci_get_dsn(pdev);
bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
if (IS_ERR(bp->dpll)) {
err = PTR_ERR(bp->dpll);
dev_err(&pdev->dev, "dpll_device_alloc failed\n");
goto out;
}
err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp);
if (err)
goto out;
for (i = 0; i < OCP_SMA_NUM; i++) {
bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
if (IS_ERR(bp->sma[i].dpll_pin)) {
err = PTR_ERR(bp->dpll);
goto out_dpll;
}
err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
&bp->sma[i]);
if (err) {
dpll_pin_put(bp->sma[i].dpll_pin);
goto out_dpll;
}
}
In the error path we have to rewind every allocation in the reverse order:
.. code-block:: c
while (i) {
--i;
dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
dpll_pin_put(bp->sma[i].dpll_pin);
}
dpll_device_put(bp->dpll);
More complex example can be found in Intel's ICE driver or nVidia's mlx5 driver.
SyncE enablement
================
For SyncE enablement it is required to allow control over dpll device
for a software application which monitors and configures the inputs of
dpll device in response to current state of a dpll device and its
inputs.
In such scenario, dpll device input signal shall be also configurable
to drive dpll with signal recovered from the PHY netdevice.
This is done by exposing a pin to the netdevice - attaching pin to the
netdevice itself with
``dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin)``.
Exposed pin id handle ``DPLL_A_PIN_ID`` is then identifiable by the user
as it is attached to rtnetlink respond to get ``RTM_NEWLINK`` command in
nested attribute ``IFLA_DPLL_PIN``.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Linux kernel dpll subsystem과 DPLL 정의
1-18SPDX license identifier는 `GPL-2.0`입니다.
PLL(Phase Locked Loop)은 device clock signal을 외부 clock signal에 syntonize해 PLL input과 같은 clock beat로 device를 동작시키는 electronic circuit입니다.
DPLL(Digital Phase Locked Loop)은 일반 PLL 동작에 digital phase detector를 포함하고 loop 안에 digital divider를 둘 수 있는 integrated circuit입니다. 따라서 DPLL input과 output frequency를 구성할 수 있습니다.
기본 clock 동기화와 digital phase/divider 기능을 비교했습니다.
Subsystem과 device object
19-45dpll subsystem의 주 목적은 여러 input synchronization source와 여러 output type을 사용할 수 있는 Digital PLL device를 구성하는 general interface를 제공하는 것입니다. 주 interface는 event monitoring multicast group을 정의한 `NETLINK_GENERIC` protocol입니다.
dpll device object 하나는 Digital PLL circuit 하나와 연결된 pin 집합을 뜻합니다. `DPLL_CMD_DEVICE_GET` netlink command의 `do` request에는 지원 operation mode와 현재 status를 반환하고, 같은 command의 `dump` request에는 subsystem에 등록된 DPLL 목록을 반환합니다. `DPLL_CMD_DEVICE_SET`의 `do` request로 configuration을 변경합니다.
device handle은 `DPLL_A_ID`이며 특정 device의 configuration을 get/set할 때 제공해야 합니다. `DPLL_CMD_DEVICE_GET` dump 또는 하나의 device만 match하도록 attribute를 제공한 `DPLL_CMD_DEVICE_ID_GET` do request로 얻습니다.
device 검색, 조회와 변경 request를 구분했습니다.
Pin object
46-64pin은 input 또는 output을 나타내는 amorphic object이며 device 내부 component일 수도, 외부 연결일 수도 있습니다. dpll마다 pin 수는 다르지만 보통 device 하나에 여러 pin이 제공됩니다.
`DPLL_CMD_PIN_GET`의 do request는 pin property, capability와 status를 반환하고 dump request는 system에 등록된 모든 pin을 나열합니다. `DPLL_CMD_PIN_SET` do request로 configuration을 바꿉니다.
pin handle은 `DPLL_A_PIN_ID`이며 특정 pin을 get/set할 때 필요합니다. `DPLL_CMD_PIN_GET` dump 또는 하나의 pin만 match하도록 attribute를 제공한 `DPLL_CMD_PIN_ID_GET` do request로 얻습니다.
Pin selection
65-97일반적으로 DPLL을 drive하는 selected pin은 `DPLL_A_PIN_STATE`에서 확인하며, dpll device마다 `DPLL_PIN_STATE_CONNECTED` 상태의 pin은 하나만 있어야 합니다.
pin selection은 hardware capability와 `DPLL_A_MODE`의 active work mode에 따라 manual 또는 automatic으로 수행합니다. mode마다 사용할 수 있는 pin state와 user가 요청할 수 있는 state가 다릅니다.
- `DPLL_MODE_MANUAL`: `DPLL_PIN_STATE_CONNECTED`는 pin이 device를 drive함을, `DPLL_PIN_STATE_DISCONNECTED`는 사용하지 않음을 뜻합니다.
- `DPLL_MODE_AUTOMATIC`: user가 `DPLL_PIN_STATE_SELECTABLE`로 automatic selection 후보에 포함하거나 `DPLL_PIN_STATE_DISCONNECTED`로 제외할 수 있습니다. selection algorithm이 input 하나에 lock한 뒤에만 user가 `DPLL_PIN_STATE_CONNECTED`를 receive할 수 있습니다.
manual과 automatic mode에서 요청·수신할 수 있는 state를 비교했습니다.
MUX-type pin
110-148MUX-type pin은 child pin을 aggregate하는 pin multiplexer입니다. child는 dpll device에 직접 등록하는 대신 하나 이상의 MUX parent에 등록됩니다. 각 parent마다 `DPLL_A_PIN_PARENT_PIN` nested attribute를 제공합니다.
pin 하나가 여러 parent pin에 등록되면 multiple-output multiplexer처럼 동작하며 `DPLL_CMD_PIN_GET` 출력에 parent별 current state가 여러 nested attribute로 나타납니다.
attributes with current state related to each parent, like::
'pin': [{{
'clock-id': 282574471561216,
'module-name': 'ice',
'capabilities': 4,
'id': 13,
'parent-pin': [
{'parent-id': 2, 'state': 'connected'},
{'parent-id': 3, 'state': 'disconnected'}
],
'type': 'synce-eth-port'
}}]
한 번에 child pin 하나만 parent MUX에 signal을 제공할 수 있습니다. 원하는 parent에서 child state를 변경하도록 `DPLL_A_PIN_PARENT` nested attribute를 사용해 선택합니다.
원문 attribute table을 nested request 구조로 보존했습니다.
Automatic mode pin priority
149-175`DPLL_MODE_AUTOMATIC` capability가 있는 device는 보통 hardware에서 automatic selection을 수행하므로 dpll에 직접 연결된 pin만 automatic input 후보가 될 수 있습니다.
user는 input pin을 직접 선택하지 않고 직접 연결된 모든 pin에 `DPLL_A_PIN_PRIO`를 제공합니다. device는 유효 signal 가운데 priority가 가장 높은 것을 선택해 DPLL을 control합니다.
pin priority request의 parent-device nesting을 보존했습니다.
MUX-type의 child pin은 automatic input selection 대상이 아닙니다. active MUX input은 앞 절처럼 parent pin에서 원하는 child state를 요청해 구성합니다.
Phase offset 측정과 조정
176-234device는 pin signal과 parent dpll 사이 phase difference를 측정할 수 있습니다. 지원된다면 parent별 `DPLL_A_PIN_PHASE_OFFSET`을 제공합니다. 보고 offset은 이전 평균과 현재 측정값으로 다음 식을 사용해 계산할 수 있습니다.
.. math::
curr\_avg = prev\_avg * \frac{2^N-1}{2^N} + new\_val * \frac{1}{2^N}
`curr_avg`는 현재 보고값, `prev_avg`는 이전 보고값, `new_val`은 현재 측정값, `N`은 averaging factor입니다. device의 `DPLL_A_PHASE_OFFSET_AVG_FACTOR`가 factor를 제공하며 `DPLL_CMD_DEVICE_SET`에서 같은 attribute로 변경을 요청합니다.
pin phase adjustment를 지원하면 `DPLL_CMD_PIN_GET` 응답에 `DPLL_A_PIN_PHASE_ADJUST_MIN`, `DPLL_A_PIN_PHASE_ADJUST_MAX`, `DPLL_A_PIN_PHASE_ADJUST_GRAN`을 제공합니다. 현재 값은 `DPLL_A_PIN_PHASE_ADJUST`이고 `DPLL_CMD_PIN_SET`에서 같은 attribute로 변경합니다.
평균화, 조정 범위와 parent별 측정값을 정리했습니다.
모든 phase 값은 signal phase의 시간 차이를 나타내는 picosecond 단위입니다. 음수는 pin signal이 dpll signal보다 빠르고 양수는 늦음을 뜻합니다. phase adjust와 min/max는 integer이지만 measured offset은 소수점 3자리의 fractional value입니다. `DPLL_PIN_PHASE_OFFSET_DIVIDER`로 나눈 몫이 integer part, 나머지가 fractional part입니다.
Phase offset monitor
235-252phase offset은 보통 현재 active source를 기준으로 측정하지만 일부 DPLL은 모든 input을 monitor할 수 있습니다. 지원 device는 `DPLL_CMD_DEVICE_GET` 응답에 `DPLL_A_PHASE_OFFSET_MONITOR`와 current feature state를 포함합니다.
user는 `DPLL_CMD_DEVICE_SET`에서 이 attribute에 `enum dpll_feature_state` 값을 설정해 제어합니다. enable하면 input의 측정값을 `DPLL_A_PIN_PHASE_OFFSET`으로 반환합니다.
Embedded SYNC
253-273Embedded SYNC는 pin의 base frequency에 추가 SYNC signal을 넣습니다. SYNC pulse가 발생할 때마다 base-frequency signal의 special pulse 하나를 삽입하며 user가 Embedded SYNC frequency를 구성할 수 있습니다.
capability는 현재 base frequency와 hardware capability에 종속됩니다. pin에 구성된 base frequency에 따라 지원 Embedded SYNC frequency range를 제공합니다.
현재 frequency, 지원 range와 pulse type을 보존했습니다.
Reference SYNC
274-298Reference SYNC는 input 두 개를 pair로 묶고 두 clock signal을 모두 사용해 DPLL을 synchronize합니다. 높은 frequency signal은 DPLL loop bandwidth에, 낮은 frequency signal은 output syntonization에 사용해 외부 source에서 고품질 loop bandwidth signal을 제공합니다.
지원 input은 결합 가능한 input 목록을 제공합니다. target pin state를 `DPLL_PIN_STATE_CONNECTED`로 요청하면 enable, `DISCONNECTED`로 요청하면 disable합니다. input pin 하나는 한 시점에 다른 pin 하나와만 결합할 수 있습니다.
target pin과 connection state nested attribute를 보존했습니다.
Configuration command group와 permission
299-310configuration command는 등록된 dpll device와 pin 정보를 얻고 configuration을 설정합니다. object는 실제 hardware를 반영해야 하므로 userspace가 netlink로 새 dpll device를 추가할 수 없고 각 driver가 등록해야 합니다.
모든 netlink command에는 `GENL_ADMIN_PERM`이 필요합니다. unauthorized userspace application의 spam/DoS를 방지하기 위한 것입니다.
Netlink command와 attribute 목록
311-436device command는 `DPLL_CMD_`, device attribute는 `DPLL_A_`, pin command는 `DPLL_CMD_PIN_`, pin attribute는 `DPLL_A_PIN_` prefix를 사용하고 목적에 맞는 suffix를 붙입니다.
원문 device command 표의 모든 attribute를 command별로 보존했습니다.
PIN_ID_GET과 PIN_GET의 identity, property, phase, parent 정보를 보존했습니다.
global pin과 parent별 configuration request를 구분했습니다.
Netlink dump와 SET format
437-470`DPLL_CMD_DEVICE_GET`과 `DPLL_CMD_PIN_GET`은 dump request를 지원합니다. response format은 do request와 같지만 system에 등록된 모든 device 또는 pin을 반환합니다.
- `DPLL_CMD_DEVICE_SET`: system에서 unique한 `DPLL_A_ID`와 구성할 `DPLL_A_MODE`를 제공합니다.
- `DPLL_CMD_PIN_SET`: unique `DPLL_A_PIN_ID`와 변경할 pin parameter를 제공합니다. `DPLL_A_PIN_FREQUENCY`는 연결된 모든 dpll에 영향을 주므로 `DPLL_A_PIN_PARENT_DEVICE` 안에 넣지 않습니다.
- `DPLL_A_PIN_PRIO`, `STATE`, `DIRECTION`은 parent 하나에만 관련되므로 `DPLL_A_PIN_PARENT_DEVICE` 안에 넣고 같은 nest에 target `DPLL_A_PIN_PARENT_ID`도 제공합니다.
- MUX child state는 `DPLL_A_PIN_PARENT_PIN` 안에 state와 target parent pin ID를 넣습니다.
여러 parameter를 한 번에 구성할 수 있지만 내부에서는 각 변경 handler를 따로 호출하며 실행 순서는 보장되지 않습니다.
attribute가 영향을 주는 scope에 따라 nest 위치를 정리했습니다.
Pre-defined enum과 notification
471-500configuration pre-defined enum은 `include/uapi/linux/dpll.h`의 kernel-doc에서 가져옵니다.
dpll device는 lock status, input/output 변화와 alarm 같은 status change를 `DPLL_MCGRP_MONITOR` multicast group으로 userspace netlink socket에 알립니다.
device와 pin create/delete/change event를 보존했습니다.
event format은 대응 GET command와 같습니다. `DPLL_CMD_DEVICE_` event는 `DPLL_CMD_DEVICE_GET`, `DPLL_CMD_PIN_` event는 `DPLL_CMD_PIN_GET` response format을 사용합니다.
Device driver object lifecycle와 callback
501-557`dpll_device_get()`은 device를 할당합니다. 같은 argument로 다시 호출하면 새 object 대신 기존 pointer를 반환하고 refcount를 증가시킵니다. `dpll_device_put()`은 refcount를 줄이고 0이 되면 object를 destroy합니다.
operation set을 구현하고 `dpll_device_register()`로 등록하면 userspace에 공개됩니다. 여러 driver instance가 `dpll_device_get()`으로 같은 object reference를 얻고 자체 ops와 priv로 등록할 수 있습니다.
pin은 `dpll_pin_get()`으로 별도 할당하고 같은 argument의 후속 호출은 refcount만 늘립니다. `dpll_pin_put()`은 device put과 같은 방식입니다. hardware에 따라 `dpll_pin_register()`로 dpll device에, `dpll_pin_on_pin_register()`로 다른 MUX-type pin에 등록하며 각 registration은 callback set과 private data pointer가 필요합니다.
get/register와 unregister/put의 refcount 순서를 나타냅니다.
device·pin 추가/제거 notification은 subsystem이 생성합니다. status change는 subsystem request가 성공한 뒤 자동 호출하거나 driver가 `dpll_device_change_ntf()` 또는 `dpll_pin_change_ntf()`를 호출하는 두 방식입니다.
모든 callback을 구현할 필요는 없지만 다음 callback은 필수입니다.
- device level: `.mode_get`, `.lock_status_get`.
- pin level: dpll device에 등록된 pin의 `.state_on_dpll_get`, parent pin에 등록된 pin의 `.state_on_pin_get`, `.direction_get`.
나머지 handler는 존재 여부를 검사하고 없으면 `-EOPNOTSUPP`를 반환합니다.
device와 pin registration별 필수 getter를 정리했습니다.
OCP TimeCard 구현과 error unwind
558-621가장 단순한 구현은 OCP TimeCard driver이며 ops 구조체는 다음과 같습니다.
.. code-block:: c
static const struct dpll_device_ops dpll_ops = {
.lock_status_get = ptp_ocp_dpll_lock_status_get,
.mode_get = ptp_ocp_dpll_mode_get,
.mode_supported = ptp_ocp_dpll_mode_supported,
};
static const struct dpll_pin_ops dpll_pins_ops = {
.frequency_get = ptp_ocp_dpll_frequency_get,
.frequency_set = ptp_ocp_dpll_frequency_set,
.direction_get = ptp_ocp_dpll_direction_get,
.direction_set = ptp_ocp_dpll_direction_set,
.state_on_dpll_get = ptp_ocp_dpll_state_get,
};
device와 pin registration 예시는 다음과 같습니다.
.. code-block:: c
clkid = pci_get_dsn(pdev);
bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
if (IS_ERR(bp->dpll)) {
err = PTR_ERR(bp->dpll);
dev_err(&pdev->dev, "dpll_device_alloc failed\n");
goto out;
}
err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp);
if (err)
goto out;
for (i = 0; i < OCP_SMA_NUM; i++) {
bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
if (IS_ERR(bp->sma[i].dpll_pin)) {
err = PTR_ERR(bp->dpll);
goto out_dpll;
}
err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
&bp->sma[i]);
if (err) {
dpll_pin_put(bp->sma[i].dpll_pin);
goto out_dpll;
}
}
error path에서는 모든 allocation을 역순으로 되돌려야 합니다.
.. code-block:: c
while (i) {
--i;
dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
dpll_pin_put(bp->sma[i].dpll_pin);
}
dpll_device_put(bp->dpll);
더 복잡한 예시는 Intel ICE driver와 NVIDIA mlx5 driver에서 찾을 수 있습니다.
SyncE enablement
622-635SyncE를 enable하려면 dpll device와 input current state를 monitor하고 그에 맞춰 input을 구성하는 software application이 dpll device를 control할 수 있어야 합니다.
dpll input signal은 PHY netdevice에서 복구한 signal로 dpll을 drive하도록 구성할 수 있어야 합니다. `dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin)`으로 pin을 netdevice 자체에 attach해 노출합니다.
노출된 `DPLL_A_PIN_ID` handle은 `RTM_NEWLINK` get response의 nested `IFLA_DPLL_PIN`에 붙으므로 user가 식별할 수 있습니다.
PHY 복구 signal을 DPLL input과 rtnetlink handle로 연결하는 흐름입니다.
요약과 해설
dpll.rst:1-635Linux dpll subsystem은 NETLINK_GENERIC으로 Digital PLL device와 shared/MUX pin을 조회·구성하고 multicast notification을 제공합니다. manual/automatic pin selection, parent별 priority·state·direction, picosecond phase offset과 adjustment, Embedded/Reference SYNC를 명확한 nested attribute scope로 관리합니다. driver는 refcounted device/pin을 등록하고 필수 getter를 구현하며, error path에서 pin과 device allocation을 역순으로 해제해야 합니다. SyncE에서는 netdevice의 recovered clock pin을 DPLL에 연결하고 rtnetlink로 handle을 노출합니다.