| .. 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. |
| |
| 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. |
| |
| 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 |
| ========================================= ================================= |
| |
| 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_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``. |