2023-06-09 12:29:20

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 00/10] Create common DPLL configuration API

Implement common API for clock/DPLL configuration and status reporting.
The API utilises netlink interface as transport for commands and event
notifications. This API aim to extend current pin configuration and
make it flexible and easy to cover special configurations.

Netlink interface is based on ynl spec, it allows use of in-kernel
tools/net/ynl/cli.py application to control the interface with properly
formated command and json attribute strings. Here are few command
examples of how it works with `ice` driver on supported NIC:

- dump dpll devices
$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--dump device-get
[{'clock-id': 282574471561216,
'id': 0,
'lock-status': 'unlocked',
'mode': 'automatic',
'module-name': 'ice',
'type': 'eec'},
{'clock-id': 282574471561216,
'id': 1,
'lock-status': 'unlocked',
'mode': 'automatic',
'module-name': 'ice',
'type': 'pps'}]

- get single pin info:
$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--do pin-get --json '{"pin-id":2}'
{'clock-id': 282574471561216,
'module-name': 'ice',
'pin-board-label': 'C827_0-RCLKA',
'pin-dpll-caps': 6,
'pin-frequency': 1953125,
'pin-id': 2,
'pin-parent': [{'id': 0,
'pin-direction': 'input',
'pin-prio': 11,
'pin-state': 'selectable'},
{'id': 1,
'pin-direction': 'input',
'pin-prio': 9,
'pin-state': 'selectable'}],
'pin-type': 'mux'}

- set pin's state on dpll:
$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--do pin-set --json '{"pin-id":2, "pin-parent":{"id":1, "pin-state":2}}'

- set pin's prio on dpll:
$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--do pin-set --json '{"pin-id":2, "pin-parent":{"id":1, "pin-prio":4}}'

- set pin's state on parent pin:
$# ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml \
--do pin-set --json '{"pin-id":13, \
"pin-parent":{"pin-id":2, "pin-state":1}}'


v7 -> v8:
[0/10] Create common DPLL configuration API
- reorder the patches in patch series
- split patch "[RFC PATCH v7 2/8] dpll: Add DPLL framework base functions"
into 3 smaller patches for easier review:
- [03/10] dpll: core: Add DPLL framework base functions
- [04/10] dpll: netlink: Add DPLL framework base functions
- [05/10] dpll: api header: Add DPLL framework base
- add cli.py usage examples in commit message

[01/10] dpll: documentation on DPLL subsystem interface
- fix DPLL_MODE_MANUAL documentation
- remove DPLL_MODE_NCO
- remove DPLL_LOCK_STATUS_CALIBRATING
- add grepability Use full names of commands, attributes and values of
dpll subsystem in the documentation
- align documentation with changes introduced in v8
- fix typos
- fix phrases to better show the intentions
- move dpll.rst to Documentation/driver-api/

[02/10] dpll: spec: Add Netlink spec in YAML
- remove unspec attribute values
- add 10 KHZ and 77,5 KHZ frequency defines
- fix documentation
- remove assigned values from subset attributes
- reorder dpll attributes
- fix `device` nested attribute usage, device get is not used on pin-get
- temperature with 3 digit float precision
- remove enum from subset definitions
- move pin-direction to pin-dpll tuple/subset
- remove DPLL_MODE_NCO
- remove DPLL_LOCK_STATUS_CALIBRATING
- fix naming scheme od notification interface functions
- separate notifications for pins
- rename attribute enum name: dplla -> dpll_a
- rename pin-idx to pin-id
- remove attributes: pin-parent-idx, device
- replace bus-name and dev-name attributes with module-name
- replace pin-label with 3 new attributes: pin-board-label,
pin-panel-label, pin-package-label
- add device-id-get and pin-id-get commands
- remove rclk-dev-name atribute
- rename DPLL_PIN_DIRECTION_SOURCE -> DPLL_PIN_DIRECTION_INPUT

[03/10] dpll: core: Add DPLL framework base functions
[04/10] dpll: netlink: Add DPLL framework base functions
[05/10] dpll: api header: Add DPLL framework base
- remove unspec attributes after removing from dpll netlink spec
- move pin-direction to pin-dpll tuple
- pass parent_priv on state_on_pin_<get/set>
- align with new notification definitions from netlink spec
- use separated notifications for dpll pins and devices
- format notification messages as corresponding get netlink commands
- rename pin-idx to pin-id
- remove attributes pin-parent-idx, device
- use DPLL_A_PIN_PARENT to hold information on parent pin or dpll device
- refactor lookup for pins and dplls for dpll subsystem
- replace bus-name, dev-name with module-name
- replace pin-label with 3 new attributes: pin-board-label,
pin-panel-label, pin-package-label
- add device-id-get and pin-id-get commands
- rename dpll_xa_lock to dpll_lock
- improve doxygen in dpll_core.c
- remove unused parent and dev fields from dpll_device struct
- use u32 for pin_idx in dpll_pin_alloc
- use driver provided pin properties struct
- verify pin/dpll owner on registering pin
- remove const arg modifier for helper _priv functions
- remove function declaration _get_by_name()
- update SPDX headers
- parse netlink set attributes with nlattr array
- remove rclk-dev-name attribute
- remove device pointer from dpll_pin_register/dpll_device_register
- remove redundant doxygen from dpll header
- use module_name() to get name of module
- add missing/remove outdated kdocs
- fix call frequency_set only if available
- fix call direction_set only for pin-dpll tuple

[06/10] netdev: expose DPLL pin handle for netdevice
- rebased on top of v8 changes
- use dpll_msg_add_pin_handle() in dpll_pin_find_from_nlattr()
and dpll_msg_add_pin_parents()
- fixed handle to use DPLL_A_PIN_ID and removed temporary comments
- added documentation record for dpll_pin pointer
- fixed compilation of net/core/dev.c when CONFIG_DPLL is not enabled
- adjusted patch description a bit

[07/10] ice: add admin commands to access cgu configuration
- Remove unspec attributes after removing from dpll netlink spec.

[08/10] ice: implement dpll interface to control cgu
- remove unspec attributes
- do not store pin flags received in set commands
- use pin state field to provide pin state to the caller
- remove include of uapi header
- remove redundant check against null arguments
- propagate lock function return value to the caller
- use switch case instead of if statements
- fix dev_dbg to dev_err for error cases
- fix dpll/pin lookup on dpll subsytem callbacks
- fix extack of dpll subsystem callbacks
- remove double negation and variable cast
- simplify ice_dpll_pin_state_set function
- pass parent_priv on state_on_pin_<get/set>
- remove parent hw_idx lookup
- fix use const qualifier for dpll/dpll_pin ops
- fix IS_ERR macros usage in ice_dpll
- add notify previous source state change
- fix mutex locking on releasing pins
- use '|=' instead of '+=' when modifing capabilities field
- rename ice_dpll_register_pins function
- clock_id function to return clock ID on the stack instead of using
an output variable
- DPLL_LOCK_STATUS_CALIBRATING was removed, return:
DPLL_LOCK_STATUS_LOCKED - if dpll was locked
DPLL_LOCK_STATUS_LOCKED_HO_ACQ - if dpll was locked and holdover is
acquired
- propagate and use dpll_priv to obtain pf pointer in corresponding
functions.
- remove null check for pf pointer
- adapt to `dpll: core: fix notification scheme`
- expose pf related pin to corresponding netdevice
- fix dpll init error path
- fix dpll pins naming scheme `source` -> `input`
- replace pin-label with pin-board-label
- dpll remove parent and dev fields from dpll_device
- remove device pointer from dpll_pin_register/dpll_device_register
- rename DPLL_PIN_DIRECTION_SOURCE -> DPLL_PIN_DIRECTION_INPUT

[09/10] ptp_ocp: implement DPLL ops
- replace pin-label with pin-board-label
- dpll remove parent and dev fields from dpll_device
- remove device pointer from dpll_pin_register/dpll_device_register
- rename DPLL_PIN_DIRECTION_SOURCE -> DPLL_PIN_DIRECTION_INPUT

[10/10] mlx5: Implement SyncE support using DPLL infrastructure
- rebased on top of v8 changes:
- changed notification scheme
- no need to fill pin label
- implemented locked_ho_acq status
- rename DPLL_PIN_DIRECTION_SOURCE -> DPLL_PIN_DIRECTION_INPUT
- remove device pointer from dpll_pin_register/dpll_device_register
- fixed MSEES register writes
- adjusted pin state and lock state values reported
- fixed a white space issue

v6 -> v7:
* YAML spec:
- remove nested 'pin' attribute
- clean up definitions on top of the latest changes
* pin object:
- pin xarray uses id provided by the driver
- remove usage of PIN_IDX_INVALID in set function
- source_pin_get() returns object instead of idx
- fixes in frequency support API
* device and pin operations are const now
* small fixes in naming in Makefile and in the functions
* single mutex for the subsystem to avoid possible ABBA locks
* no special *_priv() helpers anymore, private data is passed as void*
* no netlink filters by name anymore, only index is supported
* update ptp_ocp and ice drivers to follow new API version
* add mlx5e driver as a new customer of the subsystem
v5 -> v6:
* rework pin part to better fit shared pins use cases
* add YAML spec to easy generate user-space apps
* simple implementation in ptp_ocp is back again
v4 -> v5:
* fix code issues found during last reviews:
- replace cookie with clock id
- follow one naming schema in dpll subsys
- move function comments to dpll_core.c, fix exports
- remove single-use helper functions
- merge device register with alloc
- lock and unlock mutex on dpll device release
- move dpll_type to uapi header
- rename DPLLA_DUMP_FILTER to DPLLA_FILTER
- rename dpll_pin_state to dpll_pin_mode
- rename DPLL_MODE_FORCED to DPLL_MODE_MANUAL
- remove DPLL_CHANGE_PIN_TYPE enum value
* rewrite framework once again (Arkadiusz)
- add clock class:
Provide userspace with clock class value of DPLL with dpll device
dump netlink request. Clock class is assigned by driver allocating
a dpll device. Clock class values are defined as specified in:
ITU-T G.8273.2/Y.1368.2 recommendation.
- dpll device naming schema use new pattern:
"dpll_%s_%d_%d", where:
- %s - dev_name(parent) of parent device,
- %d (1) - enum value of dpll type,
- %d (2) - device index provided by parent device.
- new muxed/shared pin registration:
Let the kernel module to register a shared or muxed pin without
finding it or its parent. Instead use a parent/shared pin
description to find correct pin internally in dpll_core, simplifing
a dpll API
* Implement complex DPLL design in ice driver (Arkadiusz)
* Remove ptp_ocp driver from the series for now
v3 -> v4:
* redesign framework to make pins dynamically allocated (Arkadiusz)
* implement shared pins (Arkadiusz)
v2 -> v3:
* implement source select mode (Arkadiusz)
* add documentation
* implementation improvements (Jakub)
v1 -> v2:
* implement returning supported input/output types
* ptp_ocp: follow suggestions from Jonathan
* add linux-clk mailing list
v0 -> v1:
* fix code style and errors
* add linux-arm mailing list

Arkadiusz Kubalewski (3):
dpll: spec: Add Netlink spec in YAML
ice: add admin commands to access cgu configuration
ice: implement dpll interface to control cgu

Jiri Pirko (2):
netdev: expose DPLL pin handle for netdevice
mlx5: Implement SyncE support using DPLL infrastructure

Vadim Fedorenko (5):
dpll: documentation on DPLL subsystem interface
dpll: core: Add DPLL framework base functions
dpll: netlink: Add DPLL framework base functions
dpll: api header: Add DPLL framework base functions
ptp_ocp: implement DPLL ops

Documentation/driver-api/dpll.rst | 458 ++++
Documentation/driver-api/index.rst | 1 +
Documentation/netlink/specs/dpll.yaml | 466 ++++
MAINTAINERS | 8 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/dpll/Kconfig | 7 +
drivers/dpll/Makefile | 9 +
drivers/dpll/dpll_core.c | 953 ++++++++
drivers/dpll/dpll_core.h | 104 +
drivers/dpll/dpll_netlink.c | 1195 ++++++++++
drivers/dpll/dpll_netlink.h | 44 +
drivers/dpll/dpll_nl.c | 161 ++
drivers/dpll/dpll_nl.h | 50 +
drivers/net/ethernet/intel/Kconfig | 1 +
drivers/net/ethernet/intel/ice/Makefile | 3 +-
drivers/net/ethernet/intel/ice/ice.h | 5 +
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 240 +-
drivers/net/ethernet/intel/ice/ice_common.c | 467 ++++
drivers/net/ethernet/intel/ice/ice_common.h | 43 +
drivers/net/ethernet/intel/ice/ice_dpll.c | 2015 +++++++++++++++++
drivers/net/ethernet/intel/ice/ice_dpll.h | 102 +
drivers/net/ethernet/intel/ice/ice_lib.c | 17 +-
drivers/net/ethernet/intel/ice/ice_main.c | 7 +
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 414 ++++
drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 230 ++
drivers/net/ethernet/intel/ice/ice_type.h | 1 +
.../net/ethernet/mellanox/mlx5/core/Kconfig | 8 +
.../net/ethernet/mellanox/mlx5/core/Makefile | 3 +
drivers/net/ethernet/mellanox/mlx5/core/dev.c | 17 +
.../net/ethernet/mellanox/mlx5/core/dpll.c | 432 ++++
drivers/ptp/Kconfig | 1 +
drivers/ptp/ptp_ocp.c | 329 ++-
include/linux/dpll.h | 164 ++
include/linux/mlx5/driver.h | 2 +
include/linux/mlx5/mlx5_ifc.h | 59 +-
include/linux/netdevice.h | 10 +
include/uapi/linux/dpll.h | 184 ++
include/uapi/linux/if_link.h | 2 +
net/core/dev.c | 22 +
net/core/rtnetlink.c | 38 +
41 files changed, 8216 insertions(+), 59 deletions(-)
create mode 100644 Documentation/driver-api/dpll.rst
create mode 100644 Documentation/netlink/specs/dpll.yaml
create mode 100644 drivers/dpll/Kconfig
create mode 100644 drivers/dpll/Makefile
create mode 100644 drivers/dpll/dpll_core.c
create mode 100644 drivers/dpll/dpll_core.h
create mode 100644 drivers/dpll/dpll_netlink.c
create mode 100644 drivers/dpll/dpll_netlink.h
create mode 100644 drivers/dpll/dpll_nl.c
create mode 100644 drivers/dpll/dpll_nl.h
create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/dpll.c
create mode 100644 include/linux/dpll.h
create mode 100644 include/uapi/linux/dpll.h

--
2.37.3



2023-06-09 12:29:31

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

From: Vadim Fedorenko <[email protected]>

DPLL framework is used to represent and configure DPLL devices
in systems. Each device that has DPLL and can configure inputs
and outputs can use this framework.

Implement core framework functions for further interactions
with device drivers implementing dpll subsystem, as well as for
interactions of DPLL netlink framework part with the subsystem
itself.

Co-developed-by: Milena Olech <[email protected]>
Signed-off-by: Milena Olech <[email protected]>
Co-developed-by: Michal Michalik <[email protected]>
Signed-off-by: Michal Michalik <[email protected]>
Signed-off-by: Vadim Fedorenko <[email protected]>
Co-developed-by: Arkadiusz Kubalewski <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
drivers/dpll/dpll_core.c | 953 +++++++++++++++++++++++++++++++++++++++
drivers/dpll/dpll_core.h | 104 +++++
2 files changed, 1057 insertions(+)
create mode 100644 drivers/dpll/dpll_core.c
create mode 100644 drivers/dpll/dpll_core.h

diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
new file mode 100644
index 000000000000..ee515b7c18be
--- /dev/null
+++ b/drivers/dpll/dpll_core.c
@@ -0,0 +1,953 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dpll_core.c - Generic DPLL Management class support.
+ *
+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ * Copyright (c) 2023 Intel Corporation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dpll_core.h"
+
+DEFINE_MUTEX(dpll_lock);
+
+DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
+DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
+
+#define ASSERT_DPLL_REGISTERED(d) \
+ WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+#define ASSERT_DPLL_NOT_REGISTERED(d) \
+ WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
+
+/**
+ * dpll_device_get_by_id - find dpll device by it's id
+ * @id: id of searched dpll
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return:
+ * * dpll_device struct if found
+ * * NULL otherwise
+ */
+struct dpll_device *dpll_device_get_by_id(int id)
+{
+ if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
+ return xa_load(&dpll_device_xa, id);
+
+ return NULL;
+}
+
+/**
+ * dpll_pin_registration_find - find a pin registration record
+ * @ref: reference between dpll and pin
+ * @ops: searched pin ops pointer
+ * @priv: searched pin priv pointer
+ *
+ * Search dpll's registered pins for given ops and priv data.
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return:
+ * * NULL - if pin was not found
+ * * pointer to `struct dpll_pin_registration` if found
+ */
+static struct dpll_pin_registration *
+dpll_pin_registration_find(struct dpll_pin_ref *ref,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ struct dpll_pin_registration *reg;
+
+ list_for_each_entry(reg, &ref->registration_list, list) {
+ if (reg->ops == ops && reg->priv == priv)
+ return reg;
+ }
+ return NULL;
+}
+
+/**
+ * dpll_xa_ref_pin_add - add pin reference to a given xarray
+ * @xa_pins: dpll_pin_ref xarray holding pins
+ * @pin: pin being added
+ * @ops: ops for a pin
+ * @priv: pointer to private data of owner
+ *
+ * Allocate and create reference of a pin and enlist a registration
+ * structure storing ops and priv pointers of a caller registant.
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * -ENOMEM on failed allocation
+ */
+static int
+dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ struct dpll_pin_registration *reg;
+ struct dpll_pin_ref *ref;
+ bool ref_exists = false;
+ unsigned long i;
+ int ret;
+
+ xa_for_each(xa_pins, i, ref) {
+ if (ref->pin != pin)
+ continue;
+ reg = dpll_pin_registration_find(ref, ops, priv);
+ if (reg) {
+ refcount_inc(&ref->refcount);
+ return 0;
+ }
+ ref_exists = true;
+ break;
+ }
+
+ if (!ref_exists) {
+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!ref)
+ return -ENOMEM;
+ ref->pin = pin;
+ INIT_LIST_HEAD(&ref->registration_list);
+ ret = xa_insert(xa_pins, pin->pin_idx, ref, GFP_KERNEL);
+ if (ret) {
+ kfree(ref);
+ return ret;
+ }
+ refcount_set(&ref->refcount, 1);
+ }
+
+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+ if (!reg) {
+ if (!ref_exists)
+ kfree(ref);
+ return -ENOMEM;
+ }
+ reg->ops = ops;
+ reg->priv = priv;
+ if (ref_exists)
+ refcount_inc(&ref->refcount);
+ list_add_tail(&reg->list, &ref->registration_list);
+
+ return 0;
+}
+
+/**
+ * dpll_xa_ref_pin_del - remove reference of a pin from xarray
+ * @xa_pins: dpll_pin_ref xarray holding pins
+ * @pin: pointer to a pin being removed
+ * @ops: pointer to ops of pin being removed
+ * @priv: pointer to private data of registerer who invoked pin removal
+ *
+ * Decrement refcount of existing pin reference on given xarray.
+ * If all registrations are lifted delete the reference and free its memory.
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * -EINVAL if reference to a pin was not found
+ */
+static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ struct dpll_pin_registration *reg;
+ struct dpll_pin_ref *ref;
+ unsigned long i;
+
+ xa_for_each(xa_pins, i, ref) {
+ if (ref->pin != pin)
+ continue;
+ reg = dpll_pin_registration_find(ref, ops, priv);
+ if (WARN_ON(!reg))
+ return -EINVAL;
+ if (refcount_dec_and_test(&ref->refcount)) {
+ list_del(&reg->list);
+ kfree(reg);
+ xa_erase(xa_pins, i);
+ WARN_ON(!list_empty(&ref->registration_list));
+ kfree(ref);
+ }
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * dpll_xa_ref_dpll_add - add dpll reference to a given xarray
+ * @xa_dplls: dpll_pin_ref xarray holding dplls
+ * @dpll: dpll being added
+ * @ops: pin-reference ops for a dpll
+ * @priv: pointer to private data of owner
+ *
+ * Allocate and create reference of a dpll-pin ops or increase refcount
+ * on existing dpll reference on given xarray.
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * -ENOMEM on failed allocation
+ */
+static int
+dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ struct dpll_pin_registration *reg;
+ struct dpll_pin_ref *ref;
+ bool ref_exists = false;
+ unsigned long i;
+ int ret;
+
+ xa_for_each(xa_dplls, i, ref) {
+ if (ref->dpll != dpll)
+ continue;
+ reg = dpll_pin_registration_find(ref, ops, priv);
+ if (reg) {
+ refcount_inc(&ref->refcount);
+ return 0;
+ }
+ ref_exists = true;
+ break;
+ }
+
+ if (!ref_exists) {
+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!ref)
+ return -ENOMEM;
+ ref->dpll = dpll;
+ INIT_LIST_HEAD(&ref->registration_list);
+ ret = xa_insert(xa_dplls, dpll->device_idx, ref, GFP_KERNEL);
+ if (ret) {
+ kfree(ref);
+ return ret;
+ }
+ refcount_set(&ref->refcount, 1);
+ }
+
+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+ if (!reg) {
+ if (!ref_exists)
+ kfree(ref);
+ return -ENOMEM;
+ }
+ reg->ops = ops;
+ reg->priv = priv;
+ if (ref_exists)
+ refcount_inc(&ref->refcount);
+ list_add_tail(&reg->list, &ref->registration_list);
+
+ return 0;
+}
+
+/**
+ * dpll_xa_ref_dpll_del - remove reference of a dpll from xarray
+ * @xa_dplls: dpll_pin_ref xarray holding dplls
+ * @dpll: pointer to a dpll to remove
+ * @ops: pointer to ops of dpll being removed
+ * @priv: pointer to private data of registerer who invoked dpll removal
+ *
+ * Decrement refcount of existing dpll reference on given xarray.
+ * If all references are dropped, delete the reference and free its memory.
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ */
+static void
+dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ struct dpll_pin_registration *reg;
+ struct dpll_pin_ref *ref;
+ unsigned long i;
+
+ xa_for_each(xa_dplls, i, ref) {
+ if (ref->dpll != dpll)
+ continue;
+ reg = dpll_pin_registration_find(ref, ops, priv);
+ if (WARN_ON(!reg))
+ return;
+ if (refcount_dec_and_test(&ref->refcount)) {
+ list_del(&reg->list);
+ kfree(reg);
+ xa_erase(xa_dplls, i);
+ WARN_ON(!list_empty(&ref->registration_list));
+ kfree(ref);
+ }
+ return;
+ }
+}
+
+/**
+ * dpll_xa_ref_dpll_find - find dpll reference on xarray
+ * @xa_refs: dpll_pin_ref xarray holding dpll references
+ * @dpll: pointer to a dpll being searched
+ *
+ * Search for dpll-pin ops reference struct of a given dpll on given xarray.
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return:
+ * * pin reference struct pointer on success
+ * * NULL - reference to a pin was not found
+ */
+struct dpll_pin_ref *
+dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll)
+{
+ struct dpll_pin_ref *ref;
+ unsigned long i;
+
+ xa_for_each(xa_refs, i, ref) {
+ if (ref->dpll == dpll)
+ return ref;
+ }
+
+ return NULL;
+}
+
+/**
+ * dpll_xa_ref_dpll_first - find first record of given xarray
+ * @xa_refs: xarray
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return: first element on given xaaray
+ */
+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs)
+{
+ struct dpll_pin_ref *ref;
+ unsigned long i = 0;
+
+ ref = xa_find(xa_refs, &i, ULONG_MAX, XA_PRESENT);
+ WARN_ON(!ref);
+ return ref;
+}
+
+/**
+ * dpll_device_alloc - allocate the memory for dpll device
+ * @clock_id: clock_id of creator
+ * @device_idx: id given by dev driver
+ * @module: reference to registering module
+ *
+ * Allocates memory and initialize dpll device, hold its reference on global
+ * xarray.
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return:
+ * * valid dpll_device struct pointer if succeeded
+ * * ERR_PTR(-ENOMEM) - failed memory allocation
+ * * ERR_PTR(X) - failed allocation on dpll's xa
+ */
+static struct dpll_device *
+dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
+{
+ struct dpll_device *dpll;
+ int ret;
+
+ dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
+ if (!dpll)
+ return ERR_PTR(-ENOMEM);
+ refcount_set(&dpll->refcount, 1);
+ INIT_LIST_HEAD(&dpll->registration_list);
+ dpll->device_idx = device_idx;
+ dpll->clock_id = clock_id;
+ dpll->module = module;
+ ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll, xa_limit_16b,
+ GFP_KERNEL);
+ if (ret) {
+ kfree(dpll);
+ return ERR_PTR(ret);
+ }
+ xa_init_flags(&dpll->pin_refs, XA_FLAGS_ALLOC);
+
+ return dpll;
+}
+
+/**
+ * dpll_device_get - find existing or create new dpll device
+ * @clock_id: clock_id of creator
+ * @device_idx: idx given by device driver
+ * @module: reference to registering module
+ *
+ * Get existing object of a dpll device, unique for given arguments.
+ * Create new if doesn't exist yet.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * valid dpll_device struct pointer if succeeded
+ * * ERR_PTR(-ENOMEM) - failed memory allocation
+ * * ERR_PTR(X) - failed allocation on dpll's xa
+ */
+struct dpll_device *
+dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
+{
+ struct dpll_device *dpll, *ret = NULL;
+ unsigned long index;
+
+ mutex_lock(&dpll_lock);
+ xa_for_each(&dpll_device_xa, index, dpll) {
+ if (dpll->clock_id == clock_id &&
+ dpll->device_idx == device_idx &&
+ dpll->module == module) {
+ ret = dpll;
+ refcount_inc(&ret->refcount);
+ break;
+ }
+ }
+ if (!ret)
+ ret = dpll_device_alloc(clock_id, device_idx, module);
+ mutex_unlock(&dpll_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_device_get);
+
+/**
+ * dpll_device_put - decrease the refcount and free memory if possible
+ * @dpll: dpll_device struct pointer
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Drop reference for a dpll device, if all references are gone, delete
+ * dpll device object.
+ */
+void dpll_device_put(struct dpll_device *dpll)
+{
+ if (!dpll)
+ return;
+ mutex_lock(&dpll_lock);
+ if (refcount_dec_and_test(&dpll->refcount)) {
+ ASSERT_DPLL_NOT_REGISTERED(dpll);
+ WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
+ xa_destroy(&dpll->pin_refs);
+ xa_erase(&dpll_device_xa, dpll->id);
+ WARN_ON(!list_empty(&dpll->registration_list));
+ kfree(dpll);
+ }
+ mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_put);
+
+static struct dpll_device_registration *
+dpll_device_registration_find(struct dpll_device *dpll,
+ const struct dpll_device_ops *ops, void *priv)
+{
+ struct dpll_device_registration *reg;
+
+ list_for_each_entry(reg, &dpll->registration_list, list) {
+ if (reg->ops == ops && reg->priv == priv)
+ return reg;
+ }
+ return NULL;
+}
+
+/**
+ * dpll_device_register - register the dpll device in the subsystem
+ * @dpll: pointer to a dpll
+ * @type: type of a dpll
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Make dpll device available for user space.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * -EINVAL on failure due to wrong arguments provided
+ * * -EEXIST if device was already registered
+ */
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+ const struct dpll_device_ops *ops, void *priv)
+{
+ struct dpll_device_registration *reg;
+ bool first_registration = false;
+
+ if (WARN_ON(!ops))
+ return -EINVAL;
+ if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX))
+ return -EINVAL;
+
+ mutex_lock(&dpll_lock);
+ reg = dpll_device_registration_find(dpll, ops, priv);
+ if (reg) {
+ mutex_unlock(&dpll_lock);
+ return -EEXIST;
+ }
+
+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
+ if (!reg) {
+ mutex_unlock(&dpll_lock);
+ return -EEXIST;
+ }
+ reg->ops = ops;
+ reg->priv = priv;
+ dpll->type = type;
+ first_registration = list_empty(&dpll->registration_list);
+ list_add_tail(&reg->list, &dpll->registration_list);
+ if (!first_registration) {
+ mutex_unlock(&dpll_lock);
+ return 0;
+ }
+
+ xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+ mutex_unlock(&dpll_lock);
+ dpll_device_create_ntf(dpll);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_device_register);
+
+/**
+ * dpll_device_unregister - unregister dpll device
+ * @dpll: registered dpll pointer
+ * @ops: ops for a dpll device
+ * @priv: pointer to private information of owner
+ *
+ * Unregister device, make it unavailable for userspace.
+ * Note: It does not free the memory
+ * Context: Acquires a lock (dpll_lock)
+ */
+void dpll_device_unregister(struct dpll_device *dpll,
+ const struct dpll_device_ops *ops, void *priv)
+{
+ struct dpll_device_registration *reg;
+
+ mutex_lock(&dpll_lock);
+ ASSERT_DPLL_REGISTERED(dpll);
+ dpll_device_delete_ntf(dpll);
+ reg = dpll_device_registration_find(dpll, ops, priv);
+ if (WARN_ON(!reg)) {
+ mutex_unlock(&dpll_lock);
+ return;
+ }
+ list_del(&reg->list);
+ kfree(reg);
+
+ if (!list_empty(&dpll->registration_list)) {
+ mutex_unlock(&dpll_lock);
+ return;
+ }
+ xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
+ mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_device_unregister);
+
+/**
+ * dpll_pin_alloc - allocate the memory for dpll pin
+ * @clock_id: clock_id of creator
+ * @pin_idx: idx given by dev driver
+ * @module: reference to registering module
+ * @prop: dpll pin properties
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return:
+ * * valid allocated dpll_pin struct pointer if succeeded
+ * * ERR_PTR(-ENOMEM) - failed memory allocation
+ * * ERR_PTR(-EINVAL) - wrong pin type property value
+ */
+static struct dpll_pin *
+dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
+ const struct dpll_pin_properties *prop)
+{
+ struct dpll_pin *pin;
+ int ret;
+
+ pin = kzalloc(sizeof(*pin), GFP_KERNEL);
+ if (!pin)
+ return ERR_PTR(-ENOMEM);
+ pin->pin_idx = pin_idx;
+ pin->clock_id = clock_id;
+ pin->module = module;
+ if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX ||
+ prop->type > DPLL_PIN_TYPE_MAX)) {
+ ret = -EINVAL;
+ goto err;
+ }
+ pin->prop = prop;
+ refcount_set(&pin->refcount, 1);
+ xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC);
+ xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC);
+ ret = xa_alloc(&dpll_pin_xa, &pin->id, pin, xa_limit_16b, GFP_KERNEL);
+ if (ret)
+ goto err;
+ return pin;
+err:
+ xa_destroy(&pin->dpll_refs);
+ xa_destroy(&pin->parent_refs);
+ kfree(pin);
+ return ERR_PTR(ret);
+}
+
+/**
+ * dpll_pin_get - find existing or create new dpll pin
+ * @clock_id: clock_id of creator
+ * @pin_idx: idx given by dev driver
+ * @module: reference to registering module
+ * @prop: dpll pin properties
+ *
+ * Get existing object of a pin (unique for given arguments) or create new
+ * if doesn't exist yet.
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return:
+ * * valid allocated dpll_pin struct pointer if succeeded
+ * * ERR_PTR of an error
+ */
+struct dpll_pin *
+dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
+ const struct dpll_pin_properties *prop)
+{
+ struct dpll_pin *pos, *ret = NULL;
+ unsigned long i;
+
+ xa_for_each(&dpll_pin_xa, i, pos) {
+ if (pos->clock_id == clock_id &&
+ pos->pin_idx == pin_idx &&
+ pos->module == module) {
+ ret = pos;
+ refcount_inc(&ret->refcount);
+ break;
+ }
+ }
+ if (!ret)
+ ret = dpll_pin_alloc(clock_id, pin_idx, module, prop);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_get);
+
+/**
+ * dpll_pin_put - decrease the refcount and free memory if possible
+ * @pin: pointer to a pin to be put
+ *
+ * Drop reference for a pin, if all references are gone, delete pin object.
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ */
+void dpll_pin_put(struct dpll_pin *pin)
+{
+ if (!pin)
+ return;
+ if (refcount_dec_and_test(&pin->refcount)) {
+ xa_destroy(&pin->dpll_refs);
+ xa_destroy(&pin->parent_refs);
+ xa_erase(&dpll_pin_xa, pin->id);
+ kfree(pin);
+ }
+}
+EXPORT_SYMBOL_GPL(dpll_pin_put);
+
+static int
+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ int ret;
+
+ if (WARN_ON(!ops))
+ return -EINVAL;
+
+ ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
+ if (ret)
+ return ret;
+ ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
+ if (ret)
+ goto ref_pin_del;
+ else
+ dpll_pin_create_ntf(pin);
+
+ return ret;
+
+ref_pin_del:
+ dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
+ return ret;
+}
+
+/**
+ * dpll_pin_register - register the dpll pin in the subsystem
+ * @dpll: pointer to a dpll
+ * @pin: pointer to a dpll pin
+ * @ops: ops for a dpll pin ops
+ * @priv: pointer to private information of owner
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * -EINVAL - missing pin ops
+ * * -ENOMEM - failed to allocate memory
+ */
+int
+dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ int ret;
+
+ mutex_lock(&dpll_lock);
+ if (WARN_ON(!(dpll->module == pin->module &&
+ dpll->clock_id == pin->clock_id)))
+ ret = -EFAULT;
+ else
+ ret = __dpll_pin_register(dpll, pin, ops, priv);
+ mutex_unlock(&dpll_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_register);
+
+static void
+__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
+ dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv);
+}
+
+/**
+ * dpll_pin_unregister - unregister dpll pin from dpll device
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @ops: ops for a dpll pin
+ * @priv: pointer to private information of owner
+ *
+ * Note: It does not free the memory
+ * Context: Acquires a lock (dpll_lock)
+ */
+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ if (WARN_ON(xa_empty(&dpll->pin_refs)))
+ return;
+
+ mutex_lock(&dpll_lock);
+ __dpll_pin_unregister(dpll, pin, ops, priv);
+ mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_unregister);
+
+/**
+ * dpll_pin_on_pin_register - register a pin with a parent pin
+ * @parent: pointer to a parent pin
+ * @pin: pointer to a pin
+ * @ops: ops for a dpll pin
+ * @priv: pointer to private information of owner
+ *
+ * Register a pin with a parent pin, create references between them and
+ * between newly registered pin and dplls connected with a parent pin.
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Return:
+ * * 0 on success
+ * * -EINVAL missing pin or parent
+ * * -ENOMEM failed allocation
+ * * -EPERM if parent is not allowed
+ */
+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ struct dpll_pin_ref *ref;
+ unsigned long i, stop;
+ int ret;
+
+ if (WARN_ON(parent->prop->type != DPLL_PIN_TYPE_MUX))
+ return -EINVAL;
+ ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv);
+ if (ret)
+ goto unlock;
+ refcount_inc(&pin->refcount);
+ xa_for_each(&parent->dpll_refs, i, ref) {
+ mutex_lock(&dpll_lock);
+ ret = __dpll_pin_register(ref->dpll, pin, ops, priv);
+ mutex_unlock(&dpll_lock);
+ if (ret) {
+ stop = i;
+ goto dpll_unregister;
+ }
+ dpll_pin_create_ntf(pin);
+ }
+
+ return ret;
+
+dpll_unregister:
+ xa_for_each(&parent->dpll_refs, i, ref) {
+ if (i < stop) {
+ mutex_lock(&dpll_lock);
+ __dpll_pin_unregister(ref->dpll, pin, ops, priv);
+ mutex_unlock(&dpll_lock);
+ }
+ }
+ refcount_dec(&pin->refcount);
+ dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
+unlock:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register);
+
+/**
+ * dpll_pin_on_pin_unregister - unregister dpll pin from a parent pin
+ * @parent: pointer to a parent pin
+ * @pin: pointer to a pin
+ * @ops: ops for a dpll pin
+ * @priv: pointer to private information of owner
+ *
+ * Context: Acquires a lock (dpll_lock)
+ * Note: It does not free the memory
+ */
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv)
+{
+ struct dpll_pin_ref *ref;
+ unsigned long i;
+
+ mutex_lock(&dpll_lock);
+ dpll_pin_delete_ntf(pin);
+ dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
+ refcount_dec(&pin->refcount);
+ xa_for_each(&pin->dpll_refs, i, ref) {
+ __dpll_pin_unregister(ref->dpll, pin, ops, priv);
+ }
+ mutex_unlock(&dpll_lock);
+}
+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
+
+
+/**
+ * dpll_device_registration_first - get first registration of dpll device
+ * @dpll: pointer to a dpll
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return: pointer to the first registration of a dpll
+ */
+static struct dpll_device_registration *
+dpll_device_registration_first(struct dpll_device *dpll)
+{
+ struct dpll_device_registration *reg;
+
+ reg = list_first_entry_or_null((struct list_head *) &dpll->registration_list,
+ struct dpll_device_registration, list);
+ WARN_ON(!reg);
+ return reg;
+}
+
+/**
+ * dpll_priv - get the dpll device private owner data
+ * @dpll: registered dpll pointer
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return: pointer to the first registration priv data
+ */
+void *dpll_priv(struct dpll_device *dpll)
+{
+ struct dpll_device_registration *reg;
+
+ reg = dpll_device_registration_first(dpll);
+ return reg->priv;
+}
+
+/**
+ * dpll_device_ops - get the dpll device ops pointer
+ * @dpll: registered dpll pointer
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return: pointer to the first registration ops of the dpll
+ */
+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
+{
+ struct dpll_device_registration *reg;
+
+ reg = dpll_device_registration_first(dpll);
+ return reg->ops;
+}
+
+/**
+ * dpll_pin_registration_first - get first registration of dpll pin ref
+ * @ref: pointer to a pin ref struct
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return: pointer to the first registration of a dpll_pin_ref
+ */
+static struct dpll_pin_registration *
+dpll_pin_registration_first(struct dpll_pin_ref *ref)
+{
+ struct dpll_pin_registration *reg;
+
+ reg = list_first_entry_or_null(&ref->registration_list,
+ struct dpll_pin_registration, list);
+ WARN_ON(!reg);
+ return reg;
+}
+
+/**
+ * dpll_pin_on_dpll_priv - get the dpll device private owner data
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return: pointer to the data
+ */
+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll,
+ struct dpll_pin *pin)
+{
+ struct dpll_pin_registration *reg;
+ struct dpll_pin_ref *ref;
+
+ ref = xa_load(&dpll->pin_refs, pin->pin_idx);
+ if (!ref)
+ return NULL;
+ reg = dpll_pin_registration_first(ref);
+ return reg->priv;
+}
+
+/**
+ * dpll_pin_on_pin_priv - get the dpll pin private owner data
+ * @parent: pointer to a parent pin
+ * @pin: pointer to a pin
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return: pointer to the data
+ */
+void *dpll_pin_on_pin_priv(struct dpll_pin *parent,
+ struct dpll_pin *pin)
+{
+ struct dpll_pin_registration *reg;
+ struct dpll_pin_ref *ref;
+
+ ref = xa_load(&pin->parent_refs, parent->pin_idx);
+ if (!ref)
+ return NULL;
+ reg = dpll_pin_registration_first(ref);
+ return reg->priv;
+}
+
+/**
+ * dpll_pin_ops - get the pin ops pointer
+ * @ref: dpll pin ref
+ *
+ * Context: shall be called under a lock (dpll_lock)
+ * Return: pointer to the first ops registered with the pin
+ */
+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref)
+{
+ struct dpll_pin_registration *reg;
+
+ reg = dpll_pin_registration_first(ref);
+ return reg->ops;
+}
+
+/**
+ * dpll_init - initialize dpll subsystem
+ *
+ * Return:
+ * 0 - success
+ * negative - netlink init error
+ */
+static int __init dpll_init(void)
+{
+ int ret;
+
+ ret = dpll_netlink_init();
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ mutex_destroy(&dpll_lock);
+ return ret;
+}
+subsys_initcall(dpll_init);
diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
new file mode 100644
index 000000000000..ef95e272937c
--- /dev/null
+++ b/drivers/dpll/dpll_core.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ * Copyright (c) 2023 Intel and affiliates
+ */
+
+#ifndef __DPLL_CORE_H__
+#define __DPLL_CORE_H__
+
+#include <linux/dpll.h>
+#include <linux/list.h>
+#include <linux/refcount.h>
+#include "dpll_netlink.h"
+
+#define DPLL_REGISTERED XA_MARK_1
+
+struct dpll_device_registration {
+ struct list_head list;
+ const struct dpll_device_ops *ops;
+ void *priv;
+};
+
+/**
+ * struct dpll_device - stores DPLL device internal data
+ * @id: unique id number for each device given by kernel
+ * @device_idx: id given by dev driver
+ * @clock_id: unique identifier (clock_id) of a dpll
+ * @module: module of creator
+ * @type: type of a dpll
+ * @pin_refs: stores pins registered within a dpll
+ * @mode_supported_mask: mask of supported modes
+ * @refcount: refcount
+ * @registration_list: list of registered ops and priv data of dpll owners
+ **/
+struct dpll_device {
+ u32 id;
+ u32 device_idx;
+ u64 clock_id;
+ struct module *module;
+ enum dpll_type type;
+ struct xarray pin_refs;
+ unsigned long mode_supported_mask;
+ refcount_t refcount;
+ struct list_head registration_list;
+};
+
+/**
+ * struct dpll_pin - structure for a dpll pin
+ * @id: unique id number for pin given by kernel
+ * @pin_idx: index of a pin given by dev driver
+ * @clock_id: clock_id of creator
+ * @module: module of creator
+ * @dpll_refs: hold referencees to dplls pin was registered with
+ * @parent_refs: hold references to parent pins pin was registered with
+ * @prop: pointer to pin properties given by registerer
+ * @rclk_dev_name: holds name of device when pin can recover clock from it
+ * @refcount: refcount
+ **/
+struct dpll_pin {
+ u32 id;
+ u32 pin_idx;
+ u64 clock_id;
+ struct module *module;
+ struct xarray dpll_refs;
+ struct xarray parent_refs;
+ const struct dpll_pin_properties *prop;
+ char *rclk_dev_name;
+ refcount_t refcount;
+};
+
+struct dpll_pin_registration {
+ struct list_head list;
+ const struct dpll_pin_ops *ops;
+ void *priv;
+};
+
+/**
+ * struct dpll_pin_ref - structure for referencing either dpll or pins
+ * @dpll: pointer to a dpll
+ * @pin: pointer to a pin
+ * @registration_list: list of ops and priv data registered with the ref
+ * @refcount: refcount
+ **/
+struct dpll_pin_ref {
+ union {
+ struct dpll_device *dpll;
+ struct dpll_pin *pin;
+ };
+ struct list_head registration_list;
+ refcount_t refcount;
+};
+
+void *dpll_priv(struct dpll_device *dpll);
+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll, struct dpll_pin *pin);
+void *dpll_pin_on_pin_priv(struct dpll_pin *parent, struct dpll_pin *pin);
+
+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll);
+struct dpll_device *dpll_device_get_by_id(int id);
+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref);
+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
+extern struct xarray dpll_device_xa;
+extern struct xarray dpll_pin_xa;
+extern struct mutex dpll_lock;
+#endif
--
2.37.3


2023-06-09 12:30:49

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 06/10] netdev: expose DPLL pin handle for netdevice

From: Jiri Pirko <[email protected]>

In case netdevice represents a SyncE port, the user needs to understand
the connection between netdevice and associated DPLL pin. There might me
multiple netdevices pointing to the same pin, in case of VF/SF
implementation.

Add a IFLA Netlink attribute to nest the DPLL pin handle, similar to
how it is implemented for devlink port. Add a struct dpll_pin pointer
to netdev and protect access to it by RTNL. Expose netdev_dpll_pin_set()
and netdev_dpll_pin_clear() helpers to the drivers so they can set/clear
the DPLL pin relationship to netdev.

Note that during the lifetime of struct dpll_pin the pin handle does not
change. Therefore it is save to access it lockless. It is drivers
responsibility to call netdev_dpll_pin_clear() before dpll_pin_put().

Signed-off-by: Jiri Pirko <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
drivers/dpll/dpll_netlink.c | 28 ++++++++++++++++++--------
include/linux/dpll.h | 20 +++++++++++++++++++
include/linux/netdevice.h | 10 ++++++++++
include/uapi/linux/if_link.h | 2 ++
net/core/dev.c | 22 +++++++++++++++++++++
net/core/rtnetlink.c | 38 ++++++++++++++++++++++++++++++++++++
6 files changed, 112 insertions(+), 8 deletions(-)

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 44d9699c9e6c..e6efc17aaf26 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -214,10 +214,9 @@ dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
if (!nest)
return -EMSGSIZE;
- if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
- ret = -EMSGSIZE;
+ ret = dpll_msg_add_pin_handle(msg, ppin);
+ if (ret)
goto nest_cancel;
- }
if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
ret = -EMSGSIZE;
goto nest_cancel;
@@ -274,8 +273,9 @@ dpll_cmd_pin_fill_details(struct sk_buff *msg, struct dpll_pin *pin,
const struct dpll_pin_properties *prop = pin->prop;
int ret;

- if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
- return -EMSGSIZE;
+ ret = dpll_msg_add_pin_handle(msg, pin);
+ if (ret)
+ return ret;
if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(pin->module)))
return -EMSGSIZE;
if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(pin->clock_id),
@@ -301,6 +301,20 @@ dpll_cmd_pin_fill_details(struct sk_buff *msg, struct dpll_pin *pin,
return 0;
}

+size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
+{
+ return nla_total_size(4); /* DPLL_A_PIN_ID */
+}
+EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
+
+int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
+{
+ if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
+ return -EMSGSIZE;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);
+
static int
__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
struct netlink_ext_ack *extack)
@@ -690,9 +704,7 @@ dpll_pin_find_from_nlattr(struct genl_info *info, struct sk_buff *skb)
panel_label_attr, package_label_attr);
if (!pin)
return -EINVAL;
- if (nla_put_u32(skb, DPLL_A_PIN_ID, pin->id))
- return -EMSGSIZE;
- return 0;
+ return dpll_msg_add_pin_handle(skb, pin);
}

int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
index a18bcaa13553..8d085dc92cdd 100644
--- a/include/linux/dpll.h
+++ b/include/linux/dpll.h
@@ -108,6 +108,26 @@ struct dpll_pin_properties {
struct dpll_pin_frequency *freq_supported;
};

+#if IS_ENABLED(CONFIG_DPLL)
+
+size_t dpll_msg_pin_handle_size(struct dpll_pin *pin);
+
+int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin);
+
+#else
+
+static inline size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
+{
+ return 0;
+}
+
+static inline int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
+{
+ return 0;
+}
+
+#endif
+
struct dpll_device
*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 08fbd4622ccf..c57723b12f75 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -34,6 +34,7 @@
#include <linux/rculist.h>
#include <linux/workqueue.h>
#include <linux/dynamic_queue_limits.h>
+#include <linux/dpll.h>

#include <net/net_namespace.h>
#ifdef CONFIG_DCB
@@ -2055,6 +2056,9 @@ enum netdev_ml_priv_type {
* SET_NETDEV_DEVLINK_PORT macro. This pointer is static
* during the time netdevice is registered.
*
+ * @dpll_pin: Pointer to the SyncE source pin of a DPLL subsystem,
+ * where the clock is recovered.
+ *
* FIXME: cleanup struct net_device such that network protocol info
* moves out.
*/
@@ -2411,6 +2415,10 @@ struct net_device {
struct rtnl_hw_stats64 *offload_xstats_l3;

struct devlink_port *devlink_port;
+
+#if IS_ENABLED(CONFIG_DPLL)
+ struct dpll_pin *dpll_pin;
+#endif
};
#define to_net_dev(d) container_of(d, struct net_device, dev)

@@ -3954,6 +3962,8 @@ int dev_get_mac_address(struct sockaddr *sa, struct net *net, char *dev_name);
int dev_get_port_parent_id(struct net_device *dev,
struct netdev_phys_item_id *ppid, bool recurse);
bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b);
+void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin);
+void netdev_dpll_pin_clear(struct net_device *dev);
struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again);
struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, int *ret);
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 0f6a0fe09bdb..be03c8292cd7 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -377,6 +377,8 @@ enum {
IFLA_GSO_IPV4_MAX_SIZE,
IFLA_GRO_IPV4_MAX_SIZE,

+ IFLA_DPLL_PIN,
+
__IFLA_MAX
};

diff --git a/net/core/dev.c b/net/core/dev.c
index 99d99b247bc9..7ae0ce75a5c7 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -8993,6 +8993,28 @@ bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b)
}
EXPORT_SYMBOL(netdev_port_same_parent_id);

+static void netdev_dpll_pin_assign(struct net_device *dev, struct dpll_pin *dpll_pin)
+{
+#if IS_ENABLED(CONFIG_DPLL)
+ rtnl_lock();
+ dev->dpll_pin = dpll_pin;
+ rtnl_unlock();
+#endif
+}
+
+void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin)
+{
+ WARN_ON(!dpll_pin);
+ netdev_dpll_pin_assign(dev, dpll_pin);
+}
+EXPORT_SYMBOL(netdev_dpll_pin_set);
+
+void netdev_dpll_pin_clear(struct net_device *dev)
+{
+ netdev_dpll_pin_assign(dev, NULL);
+}
+EXPORT_SYMBOL(netdev_dpll_pin_clear);
+
/**
* dev_change_proto_down - set carrier according to proto_down.
*
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 41de3a2f29e1..ebe9ae8608fc 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1052,6 +1052,16 @@ static size_t rtnl_devlink_port_size(const struct net_device *dev)
return size;
}

+static size_t rtnl_dpll_pin_size(const struct net_device *dev)
+{
+ size_t size = nla_total_size(0); /* nest IFLA_DPLL_PIN */
+
+ if (dev->dpll_pin)
+ size += dpll_msg_pin_handle_size(dev->dpll_pin);
+
+ return size;
+}
+
static noinline size_t if_nlmsg_size(const struct net_device *dev,
u32 ext_filter_mask)
{
@@ -1108,6 +1118,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ rtnl_prop_list_size(dev)
+ nla_total_size(MAX_ADDR_LEN) /* IFLA_PERM_ADDRESS */
+ rtnl_devlink_port_size(dev)
+ + rtnl_dpll_pin_size(dev)
+ 0;
}

@@ -1769,6 +1780,30 @@ static int rtnl_fill_devlink_port(struct sk_buff *skb,
return ret;
}

+static int rtnl_fill_dpll_pin(struct sk_buff *skb,
+ const struct net_device *dev)
+{
+ struct nlattr *dpll_pin_nest;
+ int ret;
+
+ dpll_pin_nest = nla_nest_start(skb, IFLA_DPLL_PIN);
+ if (!dpll_pin_nest)
+ return -EMSGSIZE;
+
+ if (dev->dpll_pin) {
+ ret = dpll_msg_add_pin_handle(skb, dev->dpll_pin);
+ if (ret < 0)
+ goto nest_cancel;
+ }
+
+ nla_nest_end(skb, dpll_pin_nest);
+ return 0;
+
+nest_cancel:
+ nla_nest_cancel(skb, dpll_pin_nest);
+ return ret;
+}
+
static int rtnl_fill_ifinfo(struct sk_buff *skb,
struct net_device *dev, struct net *src_net,
int type, u32 pid, u32 seq, u32 change,
@@ -1911,6 +1946,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb,
if (rtnl_fill_devlink_port(skb, dev))
goto nla_put_failure;

+ if (rtnl_fill_dpll_pin(skb, dev))
+ goto nla_put_failure;
+
nlmsg_end(skb, nlh);
return 0;

--
2.37.3


2023-06-09 12:30:54

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 05/10] dpll: api header: Add DPLL framework base functions

From: Vadim Fedorenko <[email protected]>

DPLL framework is used to represent and configure DPLL devices
in systems. Each device that has DPLL and can configure sources
and outputs can use this framework. Netlink interface is used to
provide configuration data and to receive notification messages
about changes in the configuration or status of DPLL device.
Inputs and outputs of the DPLL device are represented as special
objects which could be dynamically added to and removed from DPLL
device.

Add kernel api header, make dpll subsystem available to device drivers.

Add/update makefiles/Kconfig to allow compilation of dpll subsystem.

Co-developed-by: Milena Olech <[email protected]>
Signed-off-by: Milena Olech <[email protected]>
Co-developed-by: Michal Michalik <[email protected]>
Signed-off-by: Michal Michalik <[email protected]>
Signed-off-by: Vadim Fedorenko <[email protected]>
Co-developed-by: Arkadiusz Kubalewski <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
MAINTAINERS | 8 +++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/dpll/Kconfig | 7 ++
drivers/dpll/Makefile | 9 +++
include/linux/dpll.h | 144 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 171 insertions(+)
create mode 100644 drivers/dpll/Kconfig
create mode 100644 drivers/dpll/Makefile
create mode 100644 include/linux/dpll.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 288d9a5edb9d..0e69429ecc55 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6306,6 +6306,14 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive
F: drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
F: drivers/net/ethernet/freescale/dpaa2/dpsw*

+DPLL CLOCK SUBSYSTEM
+M: Vadim Fedorenko <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/dpll/*
+F: include/net/dpll.h
+F: include/uapi/linux/dpll.h
+
DRBD DRIVER
M: Philipp Reisner <[email protected]>
M: Lars Ellenberg <[email protected]>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 514ae6b24cb2..ce5f63918eba 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -243,4 +243,6 @@ source "drivers/hte/Kconfig"

source "drivers/cdx/Kconfig"

+source "drivers/dpll/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 7241d80a7b29..6fea42a6dd05 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -195,3 +195,4 @@ obj-$(CONFIG_PECI) += peci/
obj-$(CONFIG_HTE) += hte/
obj-$(CONFIG_DRM_ACCEL) += accel/
obj-$(CONFIG_CDX_BUS) += cdx/
+obj-$(CONFIG_DPLL) += dpll/
diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
new file mode 100644
index 000000000000..a4cae73f20d3
--- /dev/null
+++ b/drivers/dpll/Kconfig
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Generic DPLL drivers configuration
+#
+
+config DPLL
+ bool
diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
new file mode 100644
index 000000000000..2e5b27850110
--- /dev/null
+++ b/drivers/dpll/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DPLL drivers.
+#
+
+obj-$(CONFIG_DPLL) += dpll.o
+dpll-y += dpll_core.o
+dpll-y += dpll_netlink.o
+dpll-y += dpll_nl.o
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
new file mode 100644
index 000000000000..a18bcaa13553
--- /dev/null
+++ b/include/linux/dpll.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ * Copyright (c) 2023 Intel and affiliates
+ */
+
+#ifndef __DPLL_H__
+#define __DPLL_H__
+
+#include <uapi/linux/dpll.h>
+#include <linux/device.h>
+#include <linux/netlink.h>
+
+struct dpll_device;
+struct dpll_pin;
+
+struct dpll_device_ops {
+ int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_mode *mode, struct netlink_ext_ack *extack);
+ int (*mode_set)(const struct dpll_device *dpll, void *dpll_priv,
+ const enum dpll_mode mode,
+ struct netlink_ext_ack *extack);
+ bool (*mode_supported)(const struct dpll_device *dpll, void *dpll_priv,
+ const enum dpll_mode mode,
+ struct netlink_ext_ack *extack);
+ int (*source_pin_idx_get)(const struct dpll_device *dpll,
+ void *dpll_priv,
+ u32 *pin_idx,
+ struct netlink_ext_ack *extack);
+ int (*lock_status_get)(const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_lock_status *status,
+ struct netlink_ext_ack *extack);
+ int (*temp_get)(const struct dpll_device *dpll, void *dpll_priv,
+ s32 *temp, struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_ops {
+ int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ const u64 frequency,
+ struct netlink_ext_ack *extack);
+ int (*frequency_get)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u64 *frequency, struct netlink_ext_ack *extack);
+ int (*direction_set)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ const enum dpll_pin_direction direction,
+ struct netlink_ext_ack *extack);
+ int (*direction_get)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_pin_direction *direction,
+ struct netlink_ext_ack *extack);
+ int (*state_on_pin_get)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_pin *parent_pin,
+ void *parent_pin_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack);
+ int (*state_on_dpll_get)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack);
+ int (*state_on_pin_set)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_pin *parent_pin,
+ void *parent_pin_priv,
+ const enum dpll_pin_state state,
+ struct netlink_ext_ack *extack);
+ int (*state_on_dpll_set)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ const enum dpll_pin_state state,
+ struct netlink_ext_ack *extack);
+ int (*prio_get)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u32 *prio, struct netlink_ext_ack *extack);
+ int (*prio_set)(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ const u32 prio, struct netlink_ext_ack *extack);
+};
+
+struct dpll_pin_frequency {
+ u64 min;
+ u64 max;
+};
+
+#define DPLL_PIN_FREQUENCY_RANGE(_min, _max) \
+ { \
+ .min = _min, \
+ .max = _max, \
+ }
+
+#define DPLL_PIN_FREQUENCY(_val) DPLL_PIN_FREQUENCY_RANGE(_val, _val)
+#define DPLL_PIN_FREQUENCY_1PPS \
+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_1_HZ)
+#define DPLL_PIN_FREQUENCY_10MHZ \
+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_MHZ)
+#define DPLL_PIN_FREQUENCY_IRIG_B \
+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_KHZ)
+#define DPLL_PIN_FREQUENCY_DCF77 \
+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_77_5_KHZ)
+
+struct dpll_pin_properties {
+ const char *board_label;
+ const char *panel_label;
+ const char *package_label;
+ enum dpll_pin_type type;
+ unsigned long capabilities;
+ u32 freq_supported_num;
+ struct dpll_pin_frequency *freq_supported;
+};
+
+struct dpll_device
+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
+
+void dpll_device_put(struct dpll_device *dpll);
+
+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
+ const struct dpll_device_ops *ops, void *priv);
+
+void dpll_device_unregister(struct dpll_device *dpll,
+ const struct dpll_device_ops *ops, void *priv);
+
+struct dpll_pin
+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
+ const struct dpll_pin_properties *prop);
+
+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv);
+
+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv);
+
+void dpll_pin_put(struct dpll_pin *pin);
+
+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv);
+
+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
+ const struct dpll_pin_ops *ops, void *priv);
+
+int dpll_device_change_ntf(struct dpll_device *dpll);
+
+int dpll_pin_change_ntf(struct dpll_pin *pin);
+
+#endif
--
2.37.3


2023-06-09 12:30:57

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 02/10] dpll: spec: Add Netlink spec in YAML

Add a protocol spec for DPLL.
Add code generated from the spec.

Signed-off-by: Jakub Kicinski <[email protected]>
Signed-off-by: Michal Michalik <[email protected]>
Signed-off-by: Vadim Fedorenko <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
Documentation/netlink/specs/dpll.yaml | 466 ++++++++++++++++++++++++++
drivers/dpll/dpll_nl.c | 161 +++++++++
drivers/dpll/dpll_nl.h | 50 +++
include/uapi/linux/dpll.h | 184 ++++++++++
4 files changed, 861 insertions(+)
create mode 100644 Documentation/netlink/specs/dpll.yaml
create mode 100644 drivers/dpll/dpll_nl.c
create mode 100644 drivers/dpll/dpll_nl.h
create mode 100644 include/uapi/linux/dpll.h

diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml
new file mode 100644
index 000000000000..f7317003d312
--- /dev/null
+++ b/Documentation/netlink/specs/dpll.yaml
@@ -0,0 +1,466 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+
+name: dpll
+
+doc: DPLL subsystem.
+
+definitions:
+ -
+ type: enum
+ name: mode
+ doc: |
+ working-modes a dpll can support, differentiate if and how dpll selects
+ one of its inputs to syntonize with it, valid values for DPLL_A_MODE
+ attribute
+ entries:
+ -
+ name: manual
+ doc: input can be only selected by sending a request to dpll
+ value: 1
+ -
+ name: automatic
+ doc: highest prio, valid input, auto selected by dpll
+ -
+ name: holdover
+ doc: dpll forced into holdover mode
+ -
+ name: freerun
+ doc: dpll driven on system clk
+ render-max: true
+ -
+ type: enum
+ name: lock-status
+ doc: |
+ provides information of dpll device lock status, valid values for
+ DPLL_A_LOCK_STATUS attribute
+ entries:
+ -
+ name: unlocked
+ doc: |
+ dpll was not yet locked to any valid input (or is in mode:
+ DPLL_MODE_FREERUN)
+ value: 1
+ -
+ name: locked
+ doc: |
+ dpll is locked to a valid signal, but no holdover available
+ -
+ name: locked-ho-acq
+ doc: |
+ dpll is locked and holdover acquired
+ -
+ name: holdover
+ doc: |
+ dpll is in holdover state - lost a valid lock or was forced
+ by selecting DPLL_MODE_HOLDOVER mode (latter possible only
+ when dpll lock-state was already DPLL_LOCK_STATUS_LOCKED,
+ if dpll lock-state was not DPLL_LOCK_STATUS_LOCKED, the
+ dpll's lock-state shall remain DPLL_LOCK_STATUS_UNLOCKED
+ even if DPLL_MODE_HOLDOVER was requested)
+ render-max: true
+ -
+ type: const
+ name: temp-divider
+ value: 1000
+ doc: |
+ temperature divider allowing userspace to calculate the
+ temperature as float with three digit decimal precision.
+ Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
+ temperature value.
+ Value of (DPLL_A_TEMP % DPLL_TEMP_DIVIDER) is fractional part of
+ temperature value.
+ -
+ type: enum
+ name: type
+ doc: type of dpll, valid values for DPLL_A_TYPE attribute
+ entries:
+ -
+ name: pps
+ doc: dpll produces Pulse-Per-Second signal
+ value: 1
+ -
+ name: eec
+ doc: dpll drives the Ethernet Equipment Clock
+ render-max: true
+ -
+ type: enum
+ name: pin-type
+ doc: |
+ defines possible types of a pin, valid values for DPLL_A_PIN_TYPE
+ attribute
+ entries:
+ -
+ name: mux
+ doc: aggregates another layer of selectable pins
+ value: 1
+ -
+ name: ext
+ doc: external input
+ -
+ name: synce-eth-port
+ doc: ethernet port PHY's recovered clock
+ -
+ name: int-oscillator
+ doc: device internal oscillator
+ -
+ name: gnss
+ doc: GNSS recovered clock
+ render-max: true
+ -
+ type: enum
+ name: pin-direction
+ doc: |
+ defines possible direction of a pin, valid values for
+ DPLL_A_PIN_DIRECTION attribute
+ entries:
+ -
+ name: input
+ doc: pin used as a input of a signal
+ value: 1
+ -
+ name: output
+ doc: pin used to output the signal
+ render-max: true
+ -
+ type: const
+ name: pin-frequency-1-hz
+ value: 1
+ -
+ type: const
+ name: pin-frequency-10-khz
+ value: 10000
+ -
+ type: const
+ name: pin-frequency-77_5-khz
+ value: 77500
+ -
+ type: const
+ name: pin-frequency-10-mhz
+ value: 10000000
+ -
+ type: enum
+ name: pin-state
+ doc: |
+ defines possible states of a pin, valid values for
+ DPLL_A_PIN_STATE attribute
+ entries:
+ -
+ name: connected
+ doc: pin connected, active input of phase locked loop
+ value: 1
+ -
+ name: disconnected
+ doc: pin disconnected, not considered as a valid input
+ -
+ name: selectable
+ doc: pin enabled for automatic input selection
+ render-max: true
+ -
+ type: flags
+ name: pin-caps
+ doc: |
+ defines possible capabilities of a pin, valid flags on
+ DPLL_A_PIN_CAPS attribute
+ entries:
+ -
+ name: direction-can-change
+ -
+ name: priority-can-change
+ -
+ name: state-can-change
+
+attribute-sets:
+ -
+ name: dpll
+ enum-name: dpll_a
+ attributes:
+ -
+ name: id
+ type: u32
+ value: 1
+ -
+ name: module-name
+ type: string
+ -
+ name: clock-id
+ type: u64
+ -
+ name: mode
+ type: u8
+ enum: mode
+ -
+ name: mode-supported
+ type: u8
+ enum: mode
+ multi-attr: true
+ -
+ name: lock-status
+ type: u8
+ enum: lock-status
+ -
+ name: temp
+ type: s32
+ -
+ name: type
+ type: u8
+ enum: type
+ -
+ name: pin-id
+ type: u32
+ -
+ name: pin-board-label
+ type: string
+ -
+ name: pin-panel-label
+ type: string
+ -
+ name: pin-package-label
+ type: string
+ -
+ name: pin-type
+ type: u8
+ enum: pin-type
+ -
+ name: pin-direction
+ type: u8
+ enum: pin-direction
+ -
+ name: pin-frequency
+ type: u64
+ -
+ name: pin-frequency-supported
+ type: nest
+ multi-attr: true
+ nested-attributes: pin-frequency-range
+ -
+ name: pin-frequency-min
+ type: u64
+ -
+ name: pin-frequency-max
+ type: u64
+ -
+ name: pin-prio
+ type: u32
+ -
+ name: pin-state
+ type: u8
+ enum: pin-state
+ -
+ name: pin-dpll-caps
+ type: u32
+ -
+ name: pin-parent
+ type: nest
+ multi-attr: true
+ nested-attributes: pin-parent
+ -
+ name: pin-parent
+ subset-of: dpll
+ attributes:
+ -
+ name: id
+ type: u32
+ -
+ name: pin-direction
+ type: u8
+ -
+ name: pin-prio
+ type: u32
+ -
+ name: pin-state
+ type: u8
+ -
+ name: pin-id
+ type: u32
+
+ -
+ name: pin-frequency-range
+ subset-of: dpll
+ attributes:
+ -
+ name: pin-frequency-min
+ type: u64
+ -
+ name: pin-frequency-max
+ type: u64
+
+operations:
+ enum-name: dpll_cmd
+ list:
+ -
+ name: device-id-get
+ doc: |
+ Get id of dpll device that matches given attributes
+ value: 1
+ attribute-set: dpll
+ flags: [ admin-perm ]
+
+ do:
+ pre: dpll-lock-doit
+ post: dpll-unlock-doit
+ request:
+ attributes:
+ - module-name
+ - clock-id
+ - type
+ reply:
+ attributes:
+ - id
+
+ -
+ name: device-get
+ doc: |
+ Get list of DPLL devices (dump) or attributes of a single dpll device
+ attribute-set: dpll
+ flags: [ admin-perm ]
+
+ do:
+ pre: dpll-pre-doit
+ post: dpll-post-doit
+ request:
+ attributes:
+ - id
+ - module-name
+ reply: &dev-attrs
+ attributes:
+ - id
+ - module-name
+ - mode
+ - mode-supported
+ - lock-status
+ - temp
+ - clock-id
+ - type
+
+ dump:
+ pre: dpll-lock-dumpit
+ post: dpll-unlock-dumpit
+ reply: *dev-attrs
+
+ -
+ name: device-set
+ doc: Set attributes for a DPLL device
+ attribute-set: dpll
+ flags: [ admin-perm ]
+
+ do:
+ pre: dpll-pre-doit
+ post: dpll-post-doit
+ request:
+ attributes:
+ - id
+ - mode
+ -
+ name: device-create-ntf
+ doc: Notification about device appearing
+ notify: device-get
+ mcgrp: monitor
+ -
+ name: device-delete-ntf
+ doc: Notification about device disappearing
+ notify: device-get
+ mcgrp: monitor
+ -
+ name: device-change-ntf
+ doc: Notification about device configuration being changed
+ notify: device-get
+ mcgrp: monitor
+ -
+ name: pin-id-get
+ doc: |
+ Get id of a pin that matches given attributes
+ attribute-set: dpll
+ flags: [ admin-perm ]
+
+ do:
+ pre: dpll-lock-doit
+ post: dpll-unlock-doit
+ request:
+ attributes:
+ - module-name
+ - clock-id
+ - pin-board-label
+ - pin-panel-label
+ - pin-package-label
+ - pin-type
+ reply:
+ attributes:
+ - id
+
+ -
+ name: pin-get
+ doc: |
+ Get list of pins and its attributes.
+ - dump request without any attributes given - list all the pins in the
+ system
+ - dump request with target dpll - list all the pins registered with
+ a given dpll device
+ - do request with target dpll and target pin - single pin attributes
+ attribute-set: dpll
+ flags: [ admin-perm ]
+
+ do:
+ pre: dpll-pin-pre-doit
+ post: dpll-pin-post-doit
+ request:
+ attributes:
+ - id
+ - pin-id
+ reply: &pin-attrs
+ attributes:
+ - pin-id
+ - pin-board-label
+ - pin-panel-label
+ - pin-package-label
+ - pin-type
+ - pin-frequency
+ - pin-frequency-supported
+ - pin-parent
+ - pin-dpll-caps
+
+ dump:
+ pre: dpll-lock-dumpit
+ post: dpll-unlock-dumpit
+ request:
+ attributes:
+ - id
+ reply: *pin-attrs
+
+ -
+ name: pin-set
+ doc: Set attributes of a target pin
+ attribute-set: dpll
+ flags: [ admin-perm ]
+
+ do:
+ pre: dpll-pin-pre-doit
+ post: dpll-pin-post-doit
+ request:
+ attributes:
+ - id
+ - pin-id
+ - pin-frequency
+ - pin-direction
+ - pin-prio
+ - pin-state
+ - pin-parent
+ -
+ name: pin-create-ntf
+ doc: Notification about pin appearing
+ notify: pin-get
+ mcgrp: monitor
+ -
+ name: pin-delete-ntf
+ doc: Notification about pin disappearing
+ notify: pin-get
+ mcgrp: monitor
+ -
+ name: pin-change-ntf
+ doc: Notification about pin configuration being changed
+ notify: pin-get
+ mcgrp: monitor
+
+mcast-groups:
+ list:
+ -
+ name: monitor
diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c
new file mode 100644
index 000000000000..f5d1245deed9
--- /dev/null
+++ b/drivers/dpll/dpll_nl.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/dpll.yaml */
+/* YNL-GEN kernel source */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "dpll_nl.h"
+
+#include <uapi/linux/dpll.h>
+
+/* Common nested types */
+const struct nla_policy dpll_pin_parent_nl_policy[DPLL_A_PIN_STATE + 1] = {
+ [DPLL_A_ID] = { .type = NLA_U32, },
+ [DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
+ [DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
+ [DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 3),
+ [DPLL_A_PIN_ID] = { .type = NLA_U32, },
+};
+
+/* DPLL_CMD_DEVICE_ID_GET - do */
+static const struct nla_policy dpll_device_id_get_nl_policy[DPLL_A_TYPE + 1] = {
+ [DPLL_A_MODULE_NAME] = { .type = NLA_NUL_STRING, },
+ [DPLL_A_CLOCK_ID] = { .type = NLA_U64, },
+ [DPLL_A_TYPE] = NLA_POLICY_MAX(NLA_U8, 2),
+};
+
+/* DPLL_CMD_DEVICE_GET - do */
+static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_MODULE_NAME + 1] = {
+ [DPLL_A_ID] = { .type = NLA_U32, },
+ [DPLL_A_MODULE_NAME] = { .type = NLA_NUL_STRING, },
+};
+
+/* DPLL_CMD_DEVICE_SET - do */
+static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_MODE + 1] = {
+ [DPLL_A_ID] = { .type = NLA_U32, },
+ [DPLL_A_MODE] = NLA_POLICY_MAX(NLA_U8, 4),
+};
+
+/* DPLL_CMD_PIN_ID_GET - do */
+static const struct nla_policy dpll_pin_id_get_nl_policy[DPLL_A_PIN_TYPE + 1] = {
+ [DPLL_A_MODULE_NAME] = { .type = NLA_NUL_STRING, },
+ [DPLL_A_CLOCK_ID] = { .type = NLA_U64, },
+ [DPLL_A_PIN_BOARD_LABEL] = { .type = NLA_NUL_STRING, },
+ [DPLL_A_PIN_PANEL_LABEL] = { .type = NLA_NUL_STRING, },
+ [DPLL_A_PIN_PACKAGE_LABEL] = { .type = NLA_NUL_STRING, },
+ [DPLL_A_PIN_TYPE] = NLA_POLICY_MAX(NLA_U8, 5),
+};
+
+/* DPLL_CMD_PIN_GET - do */
+static const struct nla_policy dpll_pin_get_do_nl_policy[DPLL_A_PIN_ID + 1] = {
+ [DPLL_A_ID] = { .type = NLA_U32, },
+ [DPLL_A_PIN_ID] = { .type = NLA_U32, },
+};
+
+/* DPLL_CMD_PIN_GET - dump */
+static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_ID + 1] = {
+ [DPLL_A_ID] = { .type = NLA_U32, },
+};
+
+/* DPLL_CMD_PIN_SET - do */
+static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PARENT + 1] = {
+ [DPLL_A_ID] = { .type = NLA_U32, },
+ [DPLL_A_PIN_ID] = { .type = NLA_U32, },
+ [DPLL_A_PIN_FREQUENCY] = { .type = NLA_U64, },
+ [DPLL_A_PIN_DIRECTION] = NLA_POLICY_MAX(NLA_U8, 2),
+ [DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
+ [DPLL_A_PIN_STATE] = NLA_POLICY_MAX(NLA_U8, 3),
+ [DPLL_A_PIN_PARENT] = { .type = NLA_NESTED, },
+};
+
+/* Ops table for dpll */
+static const struct genl_split_ops dpll_nl_ops[] = {
+ {
+ .cmd = DPLL_CMD_DEVICE_ID_GET,
+ .pre_doit = dpll_lock_doit,
+ .doit = dpll_nl_device_id_get_doit,
+ .post_doit = dpll_unlock_doit,
+ .policy = dpll_device_id_get_nl_policy,
+ .maxattr = DPLL_A_TYPE,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = DPLL_CMD_DEVICE_GET,
+ .pre_doit = dpll_pre_doit,
+ .doit = dpll_nl_device_get_doit,
+ .post_doit = dpll_post_doit,
+ .policy = dpll_device_get_nl_policy,
+ .maxattr = DPLL_A_MODULE_NAME,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = DPLL_CMD_DEVICE_GET,
+ .start = dpll_lock_dumpit,
+ .dumpit = dpll_nl_device_get_dumpit,
+ .done = dpll_unlock_dumpit,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = DPLL_CMD_DEVICE_SET,
+ .pre_doit = dpll_pre_doit,
+ .doit = dpll_nl_device_set_doit,
+ .post_doit = dpll_post_doit,
+ .policy = dpll_device_set_nl_policy,
+ .maxattr = DPLL_A_MODE,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = DPLL_CMD_PIN_ID_GET,
+ .pre_doit = dpll_lock_doit,
+ .doit = dpll_nl_pin_id_get_doit,
+ .post_doit = dpll_unlock_doit,
+ .policy = dpll_pin_id_get_nl_policy,
+ .maxattr = DPLL_A_PIN_TYPE,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = DPLL_CMD_PIN_GET,
+ .pre_doit = dpll_pin_pre_doit,
+ .doit = dpll_nl_pin_get_doit,
+ .post_doit = dpll_pin_post_doit,
+ .policy = dpll_pin_get_do_nl_policy,
+ .maxattr = DPLL_A_PIN_ID,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+ {
+ .cmd = DPLL_CMD_PIN_GET,
+ .start = dpll_lock_dumpit,
+ .dumpit = dpll_nl_pin_get_dumpit,
+ .done = dpll_unlock_dumpit,
+ .policy = dpll_pin_get_dump_nl_policy,
+ .maxattr = DPLL_A_ID,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = DPLL_CMD_PIN_SET,
+ .pre_doit = dpll_pin_pre_doit,
+ .doit = dpll_nl_pin_set_doit,
+ .post_doit = dpll_pin_post_doit,
+ .policy = dpll_pin_set_nl_policy,
+ .maxattr = DPLL_A_PIN_PARENT,
+ .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+};
+
+static const struct genl_multicast_group dpll_nl_mcgrps[] = {
+ [DPLL_NLGRP_MONITOR] = { "monitor", },
+};
+
+struct genl_family dpll_nl_family __ro_after_init = {
+ .name = DPLL_FAMILY_NAME,
+ .version = DPLL_FAMILY_VERSION,
+ .netnsok = true,
+ .parallel_ops = true,
+ .module = THIS_MODULE,
+ .split_ops = dpll_nl_ops,
+ .n_split_ops = ARRAY_SIZE(dpll_nl_ops),
+ .mcgrps = dpll_nl_mcgrps,
+ .n_mcgrps = ARRAY_SIZE(dpll_nl_mcgrps),
+};
diff --git a/drivers/dpll/dpll_nl.h b/drivers/dpll/dpll_nl.h
new file mode 100644
index 000000000000..54d493778888
--- /dev/null
+++ b/drivers/dpll/dpll_nl.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/dpll.yaml */
+/* YNL-GEN kernel header */
+
+#ifndef _LINUX_DPLL_GEN_H
+#define _LINUX_DPLL_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/dpll.h>
+
+/* Common nested types */
+extern const struct nla_policy dpll_pin_parent_nl_policy[DPLL_A_PIN_STATE + 1];
+
+int dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+void
+dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+void
+dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+void
+dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+int dpll_lock_dumpit(struct netlink_callback *cb);
+int dpll_unlock_dumpit(struct netlink_callback *cb);
+
+int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info);
+int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info);
+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info);
+int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info);
+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info);
+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info);
+
+enum {
+ DPLL_NLGRP_MONITOR,
+};
+
+extern struct genl_family dpll_nl_family;
+
+#endif /* _LINUX_DPLL_GEN_H */
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
new file mode 100644
index 000000000000..d99d7dc20670
--- /dev/null
+++ b/include/uapi/linux/dpll.h
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/dpll.yaml */
+/* YNL-GEN uapi header */
+
+#ifndef _UAPI_LINUX_DPLL_H
+#define _UAPI_LINUX_DPLL_H
+
+#define DPLL_FAMILY_NAME "dpll"
+#define DPLL_FAMILY_VERSION 1
+
+/**
+ * enum dpll_mode - working-modes a dpll can support, differentiate if and how
+ * dpll selects one of its inputs to syntonize with it, valid values for
+ * DPLL_A_MODE attribute
+ * @DPLL_MODE_MANUAL: input can be only selected by sending a request to dpll
+ * @DPLL_MODE_AUTOMATIC: highest prio, valid input, auto selected by dpll
+ * @DPLL_MODE_HOLDOVER: dpll forced into holdover mode
+ * @DPLL_MODE_FREERUN: dpll driven on system clk
+ */
+enum dpll_mode {
+ DPLL_MODE_MANUAL = 1,
+ DPLL_MODE_AUTOMATIC,
+ DPLL_MODE_HOLDOVER,
+ DPLL_MODE_FREERUN,
+
+ __DPLL_MODE_MAX,
+ DPLL_MODE_MAX = (__DPLL_MODE_MAX - 1)
+};
+
+/**
+ * enum dpll_lock_status - provides information of dpll device lock status,
+ * valid values for DPLL_A_LOCK_STATUS attribute
+ * @DPLL_LOCK_STATUS_UNLOCKED: dpll was not yet locked to any valid input (or
+ * is in mode: DPLL_MODE_FREERUN)
+ * @DPLL_LOCK_STATUS_LOCKED: dpll is locked to a valid signal, but no holdover
+ * available
+ * @DPLL_LOCK_STATUS_LOCKED_HO_ACQ: dpll is locked and holdover acquired
+ * @DPLL_LOCK_STATUS_HOLDOVER: dpll is in holdover state - lost a valid lock or
+ * was forced by selecting DPLL_MODE_HOLDOVER mode (latter possible only when
+ * dpll lock-state was already DPLL_LOCK_STATUS_LOCKED, if dpll lock-state
+ * was not DPLL_LOCK_STATUS_LOCKED, the dpll's lock-state shall remain
+ * DPLL_LOCK_STATUS_UNLOCKED even if DPLL_MODE_HOLDOVER was requested)
+ */
+enum dpll_lock_status {
+ DPLL_LOCK_STATUS_UNLOCKED = 1,
+ DPLL_LOCK_STATUS_LOCKED,
+ DPLL_LOCK_STATUS_LOCKED_HO_ACQ,
+ DPLL_LOCK_STATUS_HOLDOVER,
+
+ __DPLL_LOCK_STATUS_MAX,
+ DPLL_LOCK_STATUS_MAX = (__DPLL_LOCK_STATUS_MAX - 1)
+};
+
+#define DPLL_TEMP_DIVIDER 1000
+
+/**
+ * enum dpll_type - type of dpll, valid values for DPLL_A_TYPE attribute
+ * @DPLL_TYPE_PPS: dpll produces Pulse-Per-Second signal
+ * @DPLL_TYPE_EEC: dpll drives the Ethernet Equipment Clock
+ */
+enum dpll_type {
+ DPLL_TYPE_PPS = 1,
+ DPLL_TYPE_EEC,
+
+ __DPLL_TYPE_MAX,
+ DPLL_TYPE_MAX = (__DPLL_TYPE_MAX - 1)
+};
+
+/**
+ * enum dpll_pin_type - defines possible types of a pin, valid values for
+ * DPLL_A_PIN_TYPE attribute
+ * @DPLL_PIN_TYPE_MUX: aggregates another layer of selectable pins
+ * @DPLL_PIN_TYPE_EXT: external input
+ * @DPLL_PIN_TYPE_SYNCE_ETH_PORT: ethernet port PHY's recovered clock
+ * @DPLL_PIN_TYPE_INT_OSCILLATOR: device internal oscillator
+ * @DPLL_PIN_TYPE_GNSS: GNSS recovered clock
+ */
+enum dpll_pin_type {
+ DPLL_PIN_TYPE_MUX = 1,
+ DPLL_PIN_TYPE_EXT,
+ DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+ DPLL_PIN_TYPE_INT_OSCILLATOR,
+ DPLL_PIN_TYPE_GNSS,
+
+ __DPLL_PIN_TYPE_MAX,
+ DPLL_PIN_TYPE_MAX = (__DPLL_PIN_TYPE_MAX - 1)
+};
+
+/**
+ * enum dpll_pin_direction - defines possible direction of a pin, valid values
+ * for DPLL_A_PIN_DIRECTION attribute
+ * @DPLL_PIN_DIRECTION_INPUT: pin used as a input of a signal
+ * @DPLL_PIN_DIRECTION_OUTPUT: pin used to output the signal
+ */
+enum dpll_pin_direction {
+ DPLL_PIN_DIRECTION_INPUT = 1,
+ DPLL_PIN_DIRECTION_OUTPUT,
+
+ __DPLL_PIN_DIRECTION_MAX,
+ DPLL_PIN_DIRECTION_MAX = (__DPLL_PIN_DIRECTION_MAX - 1)
+};
+
+#define DPLL_PIN_FREQUENCY_1_HZ 1
+#define DPLL_PIN_FREQUENCY_10_KHZ 10000
+#define DPLL_PIN_FREQUENCY_77_5_KHZ 77500
+#define DPLL_PIN_FREQUENCY_10_MHZ 10000000
+
+/**
+ * enum dpll_pin_state - defines possible states of a pin, valid values for
+ * DPLL_A_PIN_STATE attribute
+ * @DPLL_PIN_STATE_CONNECTED: pin connected, active input of phase locked loop
+ * @DPLL_PIN_STATE_DISCONNECTED: pin disconnected, not considered as a valid
+ * input
+ * @DPLL_PIN_STATE_SELECTABLE: pin enabled for automatic input selection
+ */
+enum dpll_pin_state {
+ DPLL_PIN_STATE_CONNECTED = 1,
+ DPLL_PIN_STATE_DISCONNECTED,
+ DPLL_PIN_STATE_SELECTABLE,
+
+ __DPLL_PIN_STATE_MAX,
+ DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
+};
+
+/**
+ * enum dpll_pin_caps - defines possible capabilities of a pin, valid flags on
+ * DPLL_A_PIN_CAPS attribute
+ */
+enum dpll_pin_caps {
+ DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE = 1,
+ DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE = 2,
+ DPLL_PIN_CAPS_STATE_CAN_CHANGE = 4,
+};
+
+enum dpll_a {
+ DPLL_A_ID = 1,
+ DPLL_A_MODULE_NAME,
+ DPLL_A_CLOCK_ID,
+ DPLL_A_MODE,
+ DPLL_A_MODE_SUPPORTED,
+ DPLL_A_LOCK_STATUS,
+ DPLL_A_TEMP,
+ DPLL_A_TYPE,
+ DPLL_A_PIN_ID,
+ DPLL_A_PIN_BOARD_LABEL,
+ DPLL_A_PIN_PANEL_LABEL,
+ DPLL_A_PIN_PACKAGE_LABEL,
+ DPLL_A_PIN_TYPE,
+ DPLL_A_PIN_DIRECTION,
+ DPLL_A_PIN_FREQUENCY,
+ DPLL_A_PIN_FREQUENCY_SUPPORTED,
+ DPLL_A_PIN_FREQUENCY_MIN,
+ DPLL_A_PIN_FREQUENCY_MAX,
+ DPLL_A_PIN_PRIO,
+ DPLL_A_PIN_STATE,
+ DPLL_A_PIN_DPLL_CAPS,
+ DPLL_A_PIN_PARENT,
+
+ __DPLL_A_MAX,
+ DPLL_A_MAX = (__DPLL_A_MAX - 1)
+};
+
+enum dpll_cmd {
+ DPLL_CMD_DEVICE_ID_GET = 1,
+ DPLL_CMD_DEVICE_GET,
+ DPLL_CMD_DEVICE_SET,
+ DPLL_CMD_DEVICE_CREATE_NTF,
+ DPLL_CMD_DEVICE_DELETE_NTF,
+ DPLL_CMD_DEVICE_CHANGE_NTF,
+ DPLL_CMD_PIN_ID_GET,
+ DPLL_CMD_PIN_GET,
+ DPLL_CMD_PIN_SET,
+ DPLL_CMD_PIN_CREATE_NTF,
+ DPLL_CMD_PIN_DELETE_NTF,
+ DPLL_CMD_PIN_CHANGE_NTF,
+
+ __DPLL_CMD_MAX,
+ DPLL_CMD_MAX = (__DPLL_CMD_MAX - 1)
+};
+
+#define DPLL_MCGRP_MONITOR "monitor"
+
+#endif /* _UAPI_LINUX_DPLL_H */
--
2.37.3


2023-06-09 12:31:13

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 04/10] dpll: netlink: Add DPLL framework base functions

From: Vadim Fedorenko <[email protected]>

DPLL framework is used to represent and configure DPLL devices
in systems. Each device that has DPLL and can configure inputs
and outputs can use this framework.

Implement dpll netlink framework functions for enablement of dpll
subsytem netlink family.

Co-developed-by: Milena Olech <[email protected]>
Signed-off-by: Milena Olech <[email protected]>
Co-developed-by: Michal Michalik <[email protected]>
Signed-off-by: Michal Michalik <[email protected]>
Signed-off-by: Vadim Fedorenko <[email protected]>
Co-developed-by: Arkadiusz Kubalewski <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
drivers/dpll/dpll_netlink.c | 1183 +++++++++++++++++++++++++++++++++++
drivers/dpll/dpll_netlink.h | 44 ++
2 files changed, 1227 insertions(+)
create mode 100644 drivers/dpll/dpll_netlink.c
create mode 100644 drivers/dpll/dpll_netlink.h

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
new file mode 100644
index 000000000000..44d9699c9e6c
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.c
@@ -0,0 +1,1183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Generic netlink for DPLL management framework
+ *
+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ * Copyright (c) 2023 Intel and affiliates
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include "dpll_core.h"
+#include "dpll_nl.h"
+#include <uapi/linux/dpll.h>
+
+static int __dpll_pin_change_ntf(struct dpll_pin *pin);
+
+struct dpll_dump_ctx {
+ unsigned long idx;
+};
+
+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
+{
+ return (struct dpll_dump_ctx *)cb->ctx;
+}
+
+static int
+dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
+{
+ if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int
+dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
+ struct netlink_ext_ack *extack)
+{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+ enum dpll_mode mode;
+
+ if (WARN_ON(!ops->mode_get))
+ return -EOPNOTSUPP;
+ if (ops->mode_get(dpll, dpll_priv(dpll), &mode, extack))
+ return -EFAULT;
+ if (nla_put_u8(msg, DPLL_A_MODE, mode))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int
+dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
+ struct netlink_ext_ack *extack)
+{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+ enum dpll_lock_status status;
+
+ if (WARN_ON(!ops->lock_status_get))
+ return -EOPNOTSUPP;
+ if (ops->lock_status_get(dpll, dpll_priv(dpll), &status, extack))
+ return -EFAULT;
+ if (nla_put_u8(msg, DPLL_A_LOCK_STATUS, status))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int
+dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,
+ struct netlink_ext_ack *extack)
+{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+ s32 temp;
+
+ if (!ops->temp_get)
+ return -EOPNOTSUPP;
+ if (ops->temp_get(dpll, dpll_priv(dpll), &temp, extack))
+ return -EFAULT;
+ if (nla_put_s32(msg, DPLL_A_TEMP, temp))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int
+dpll_msg_add_pin_prio(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;
+ u32 prio;
+
+ if (!ops->prio_get)
+ return -EOPNOTSUPP;
+ if (ops->prio_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+ dpll_priv(dpll), &prio, extack))
+ return -EFAULT;
+ if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int
+dpll_msg_add_pin_on_dpll_state(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;
+ enum dpll_pin_state state;
+
+ if (!ops->state_on_dpll_get)
+ return -EOPNOTSUPP;
+ if (ops->state_on_dpll_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+ dpll_priv(dpll), &state, extack))
+ return -EFAULT;
+ if (nla_put_u8(msg, DPLL_A_PIN_STATE, state))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int
+dpll_msg_add_pin_direction(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;
+ enum dpll_pin_direction direction;
+
+ if (!ops->direction_get)
+ return -EOPNOTSUPP;
+ if (ops->direction_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+ dpll_priv(dpll), &direction, extack))
+ return -EFAULT;
+ if (nla_put_u8(msg, DPLL_A_PIN_DIRECTION, direction))
+ 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,
+ bool dump_freq_supported)
+{
+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+ struct dpll_device *dpll = ref->dpll;
+ struct nlattr *nest;
+ u64 freq;
+ int fs;
+
+ if (!ops->frequency_get)
+ return -EOPNOTSUPP;
+ if (ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+ dpll_priv(dpll), &freq, extack))
+ return -EFAULT;
+ if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq, 0))
+ return -EMSGSIZE;
+ if (!dump_freq_supported)
+ return 0;
+ for (fs = 0; fs < pin->prop->freq_supported_num; fs++) {
+ nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);
+ if (!nest)
+ return -EMSGSIZE;
+ freq = pin->prop->freq_supported[fs].min;
+ if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),
+ &freq, 0)) {
+ nla_nest_cancel(msg, nest);
+ return -EMSGSIZE;
+ }
+ freq = pin->prop->freq_supported[fs].max;
+ if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),
+ &freq, 0)) {
+ nla_nest_cancel(msg, nest);
+ return -EMSGSIZE;
+ }
+ nla_nest_end(msg, nest);
+ }
+
+ return 0;
+}
+
+static int
+dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
+ struct dpll_pin_ref *dpll_ref,
+ struct netlink_ext_ack *extack)
+{
+ enum dpll_pin_state state;
+ struct dpll_pin_ref *ref;
+ struct dpll_pin *ppin;
+ struct nlattr *nest;
+ unsigned long index;
+ int ret;
+
+ xa_for_each(&pin->parent_refs, index, ref) {
+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+ void *parent_priv;
+
+ ppin = ref->pin;
+ parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, ppin);
+ if (WARN_ON(!ops->state_on_pin_get))
+ return -EFAULT;
+ ret = ops->state_on_pin_get(pin,
+ dpll_pin_on_pin_priv(ppin, pin),
+ ppin, parent_priv, &state, extack);
+ if (ret)
+ return -EFAULT;
+ nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
+ if (!nest)
+ return -EMSGSIZE;
+ if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
+ ret = -EMSGSIZE;
+ goto nest_cancel;
+ }
+ if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
+ ret = -EMSGSIZE;
+ goto nest_cancel;
+ }
+ nla_nest_end(msg, nest);
+ }
+
+ return 0;
+
+nest_cancel:
+ nla_nest_cancel(msg, nest);
+ return ret;
+}
+
+static int
+dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
+ struct netlink_ext_ack *extack)
+{
+ struct dpll_pin_ref *ref;
+ struct nlattr *attr;
+ unsigned long index;
+ int ret;
+
+ xa_for_each(&pin->dpll_refs, index, ref) {
+ attr = nla_nest_start(msg, DPLL_A_PIN_PARENT);
+ if (!attr)
+ return -EMSGSIZE;
+ ret = dpll_msg_add_dev_handle(msg, ref->dpll);
+ if (ret)
+ goto nest_cancel;
+ ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
+ if (ret && ret != -EOPNOTSUPP)
+ goto nest_cancel;
+ ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
+ if (ret && ret != -EOPNOTSUPP)
+ goto nest_cancel;
+ ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
+ if (ret)
+ goto nest_cancel;
+ nla_nest_end(msg, attr);
+ }
+
+ return 0;
+
+nest_cancel:
+ nla_nest_end(msg, attr);
+ return ret;
+}
+
+static int
+dpll_cmd_pin_fill_details(struct sk_buff *msg, struct dpll_pin *pin,
+ struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
+{
+ const struct dpll_pin_properties *prop = pin->prop;
+ int ret;
+
+ if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
+ return -EMSGSIZE;
+ if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(pin->module)))
+ return -EMSGSIZE;
+ if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(pin->clock_id),
+ &pin->clock_id, 0))
+ return -EMSGSIZE;
+ if (prop->board_label &&
+ nla_put_string(msg, DPLL_A_PIN_BOARD_LABEL, prop->board_label))
+ return -EMSGSIZE;
+ if (prop->panel_label &&
+ nla_put_string(msg, DPLL_A_PIN_PANEL_LABEL, prop->panel_label))
+ return -EMSGSIZE;
+ if (prop->package_label &&
+ nla_put_string(msg, DPLL_A_PIN_PACKAGE_LABEL,
+ prop->package_label))
+ return -EMSGSIZE;
+ if (nla_put_u8(msg, DPLL_A_PIN_TYPE, prop->type))
+ return -EMSGSIZE;
+ if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, prop->capabilities))
+ return -EMSGSIZE;
+ ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
+ if (ret && ret != -EOPNOTSUPP)
+ return ret;
+ return 0;
+}
+
+static int
+__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
+ struct netlink_ext_ack *extack)
+{
+ struct dpll_pin_ref *ref;
+ int ret;
+
+ ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+ if (!ref)
+ return -EFAULT;
+ ret = dpll_cmd_pin_fill_details(msg, pin, ref, extack);
+ if (ret)
+ return ret;
+ ret = dpll_msg_add_pin_parents(msg, pin, ref, extack);
+ if (ret)
+ return ret;
+ if (!xa_empty(&pin->dpll_refs)) {
+ ret = dpll_msg_add_pin_dplls(msg, pin, extack);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
+ struct netlink_ext_ack *extack)
+{
+ enum dpll_mode mode;
+ int ret;
+
+ ret = dpll_msg_add_dev_handle(msg, dpll);
+ if (ret)
+ return ret;
+ if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(dpll->module)))
+ return -EMSGSIZE;
+ if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
+ &dpll->clock_id, 0))
+ return -EMSGSIZE;
+ ret = dpll_msg_add_temp(msg, dpll, extack);
+ if (ret && ret != -EOPNOTSUPP)
+ return ret;
+ ret = dpll_msg_add_lock_status(msg, dpll, extack);
+ if (ret)
+ return ret;
+ ret = dpll_msg_add_mode(msg, dpll, extack);
+ if (ret)
+ return ret;
+ for (mode = DPLL_MODE_MANUAL; mode <= DPLL_MODE_MAX; mode++)
+ if (test_bit(mode, &dpll->mode_supported_mask))
+ if (nla_put_s32(msg, DPLL_A_MODE_SUPPORTED, mode))
+ return -EMSGSIZE;
+ if (nla_put_u8(msg, DPLL_A_TYPE, dpll->type))
+ return -EMSGSIZE;
+
+ return ret;
+}
+
+static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
+{
+ int fs;
+
+ for (fs = 0; fs < pin->prop->freq_supported_num; fs++)
+ if (freq >= pin->prop->freq_supported[fs].min &&
+ freq <= pin->prop->freq_supported[fs].max)
+ return true;
+ return false;
+}
+
+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;
+ unsigned long i;
+ int ret;
+
+ if (!dpll_pin_is_freq_supported(pin, freq))
+ 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->frequency_set)
+ return -EOPNOTSUPP;
+ ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+ dpll, dpll_priv(dpll), freq, extack);
+ if (ret)
+ return -EFAULT;
+ __dpll_pin_change_ntf(pin);
+ }
+
+ return 0;
+}
+
+static int
+dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ struct dpll_pin_ref *parent_ref;
+ const struct dpll_pin_ops *ops;
+ struct dpll_pin_ref *dpll_ref;
+ struct dpll_pin *parent;
+ unsigned long i;
+
+ if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities))
+ return -EOPNOTSUPP;
+ parent = xa_load(&dpll_pin_xa, parent_idx);
+ if (!parent)
+ return -EINVAL;
+ parent_ref = xa_load(&pin->parent_refs, parent->pin_idx);
+ if (!parent_ref)
+ return -EINVAL;
+ xa_for_each(&parent->dpll_refs, i, dpll_ref) {
+ ops = dpll_pin_ops(parent_ref);
+ if (!ops->state_on_pin_set)
+ return -EOPNOTSUPP;
+ if (ops->state_on_pin_set(pin,
+ dpll_pin_on_pin_priv(parent, pin),
+ parent,
+ dpll_pin_on_dpll_priv(dpll_ref->dpll,
+ parent),
+ state, extack))
+ return -EFAULT;
+ }
+ __dpll_pin_change_ntf(pin);
+
+ return 0;
+}
+
+static int
+dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ const struct dpll_pin_ops *ops;
+ struct dpll_pin_ref *ref;
+
+ if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities))
+ return -EOPNOTSUPP;
+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+ if (!ref)
+ return -EFAULT;
+ ops = dpll_pin_ops(ref);
+ if (!ops->state_on_dpll_set)
+ return -EOPNOTSUPP;
+ if (ops->state_on_dpll_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+ dpll_priv(dpll), state, extack))
+ return -EINVAL;
+ __dpll_pin_change_ntf(pin);
+
+ return 0;
+}
+
+static int
+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
+ u32 prio, struct netlink_ext_ack *extack)
+{
+ const struct dpll_pin_ops *ops;
+ struct dpll_pin_ref *ref;
+
+ if (!(DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE & pin->prop->capabilities))
+ return -EOPNOTSUPP;
+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+ if (!ref)
+ return -EFAULT;
+ ops = dpll_pin_ops(ref);
+ if (!ops->prio_set)
+ return -EOPNOTSUPP;
+ if (ops->prio_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+ dpll_priv(dpll), prio, extack))
+ return -EINVAL;
+ __dpll_pin_change_ntf(pin);
+
+ return 0;
+}
+
+static int
+dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
+ enum dpll_pin_direction direction,
+ struct netlink_ext_ack *extack)
+{
+ const struct dpll_pin_ops *ops;
+ struct dpll_pin_ref *ref;
+
+ if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop->capabilities))
+ return -EOPNOTSUPP;
+
+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+ if (!ref)
+ return -EFAULT;
+ ops = dpll_pin_ops(ref);
+ if (!ops->direction_set)
+ return -EOPNOTSUPP;
+ if (ops->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
+ dpll, dpll_priv(dpll), direction,
+ extack))
+ return -EFAULT;
+ __dpll_pin_change_ntf(pin);
+
+ return 0;
+}
+
+static int
+dpll_pin_parent_set(struct dpll_pin *pin, struct nlattr *parent_nest,
+ struct netlink_ext_ack *extack)
+{
+ struct nlattr *tb[DPLL_A_MAX + 1];
+ enum dpll_pin_direction direction;
+ u32 ppin_idx, pdpll_idx, prio;
+ enum dpll_pin_state state;
+ struct dpll_pin_ref *ref;
+ struct dpll_device *dpll;
+ int ret;
+
+ nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
+ NULL, extack);
+ if ((tb[DPLL_A_ID] && tb[DPLL_A_PIN_ID]) ||
+ !(tb[DPLL_A_ID] || tb[DPLL_A_PIN_ID])) {
+ NL_SET_ERR_MSG(extack, "one parent id expected");
+ return -EINVAL;
+ }
+ if (tb[DPLL_A_ID]) {
+ pdpll_idx = nla_get_u32(tb[DPLL_A_ID]);
+ dpll = xa_load(&dpll_device_xa, pdpll_idx);
+ if (!dpll)
+ return -EINVAL;
+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
+ if (!ref)
+ return -EINVAL;
+ if (tb[DPLL_A_PIN_STATE]) {
+ state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
+ ret = dpll_pin_state_set(dpll, pin, state, extack);
+ if (ret)
+ return ret;
+ }
+ if (tb[DPLL_A_PIN_PRIO]) {
+ prio = nla_get_u8(tb[DPLL_A_PIN_PRIO]);
+ ret = dpll_pin_prio_set(dpll, pin, prio, extack);
+ if (ret)
+ return ret;
+ }
+ if (tb[DPLL_A_PIN_DIRECTION]) {
+ direction = nla_get_u8(tb[DPLL_A_PIN_DIRECTION]);
+ ret = dpll_pin_direction_set(pin, dpll, direction,
+ extack);
+ if (ret)
+ return ret;
+ }
+ } else if (tb[DPLL_A_PIN_ID]) {
+ ppin_idx = nla_get_u32(tb[DPLL_A_PIN_ID]);
+ state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
+ ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
+{
+ int rem, ret = -EINVAL;
+ struct nlattr *a;
+
+ nla_for_each_attr(a, genlmsg_data(info->genlhdr),
+ genlmsg_len(info->genlhdr), rem) {
+ switch (nla_type(a)) {
+ case DPLL_A_PIN_FREQUENCY:
+ ret = dpll_pin_freq_set(pin, a, info->extack);
+ if (ret)
+ return ret;
+ break;
+ case DPLL_A_PIN_PARENT:
+ ret = dpll_pin_parent_set(pin, a, info->extack);
+ if (ret)
+ return ret;
+ break;
+ case DPLL_A_PIN_ID:
+ case DPLL_A_ID:
+ break;
+ default:
+ NL_SET_ERR_MSG_FMT(info->extack,
+ "unsupported attribute (%d)",
+ nla_type(a));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static struct dpll_pin *
+dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,
+ enum dpll_pin_type type, struct nlattr *board_label,
+ struct nlattr *panel_label, struct nlattr *package_label)
+{
+ bool board_match, panel_match, package_match;
+ struct dpll_pin *pin_match = NULL, *pin;
+ const struct dpll_pin_properties *prop;
+ bool cid_match, mod_match, type_match;
+ unsigned long i;
+
+ xa_for_each(&dpll_pin_xa, i, pin) {
+ if (xa_empty(&pin->dpll_refs))
+ continue;
+ prop = pin->prop;
+ cid_match = clock_id ? pin->clock_id == clock_id : true;
+ mod_match = mod_name_attr && module_name(pin->module) ?
+ !nla_strcmp(mod_name_attr,
+ module_name(pin->module)) : true;
+ type_match = type ? prop->type == type : true;
+ board_match = board_label && prop->board_label ?
+ !nla_strcmp(board_label, prop->board_label) : true;
+ panel_match = panel_label && prop->panel_label ?
+ !nla_strcmp(panel_label, prop->panel_label) : true;
+ package_match = package_label && prop->package_label ?
+ !nla_strcmp(package_label,
+ prop->package_label) : true;
+ if (cid_match && mod_match && type_match && board_match &&
+ panel_match && package_match) {
+ if (pin_match)
+ return NULL;
+ pin_match = pin;
+ };
+ }
+
+ return pin_match;
+}
+
+static int
+dpll_pin_find_from_nlattr(struct genl_info *info, struct sk_buff *skb)
+{
+ struct nlattr *attr, *mod_name_attr = NULL, *board_label_attr = NULL,
+ *panel_label_attr = NULL, *package_label_attr = NULL;
+ struct dpll_pin *pin = NULL;
+ enum dpll_pin_type type = 0;
+ u64 clock_id = 0;
+ int rem = 0;
+
+ nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
+ genlmsg_len(info->genlhdr), rem) {
+ switch (nla_type(attr)) {
+ case DPLL_A_CLOCK_ID:
+ if (clock_id)
+ return -EINVAL;
+ clock_id = nla_get_u64(attr);
+ break;
+ case DPLL_A_MODULE_NAME:
+ if (mod_name_attr)
+ return -EINVAL;
+ mod_name_attr = attr;
+ break;
+ case DPLL_A_PIN_TYPE:
+ if (type)
+ return -EINVAL;
+ type = nla_get_u8(attr);
+ break;
+ case DPLL_A_PIN_BOARD_LABEL:
+ if (board_label_attr)
+ return -EINVAL;
+ board_label_attr = attr;
+ break;
+ case DPLL_A_PIN_PANEL_LABEL:
+ if (panel_label_attr)
+ return -EINVAL;
+ panel_label_attr = attr;
+ break;
+ case DPLL_A_PIN_PACKAGE_LABEL:
+ if (package_label_attr)
+ return -EINVAL;
+ package_label_attr = attr;
+ break;
+ default:
+ break;
+ }
+ }
+ if (!(clock_id || mod_name_attr || board_label_attr ||
+ panel_label_attr || package_label_attr))
+ return -EINVAL;
+ pin = dpll_pin_find(clock_id, mod_name_attr, type, board_label_attr,
+ panel_label_attr, package_label_attr);
+ if (!pin)
+ return -EINVAL;
+ if (nla_put_u32(skb, DPLL_A_PIN_ID, pin->id))
+ return -EMSGSIZE;
+ return 0;
+}
+
+int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct nlattr *hdr;
+ int ret;
+
+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+ DPLL_CMD_PIN_ID_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ ret = dpll_pin_find_from_nlattr(info, msg);
+ if (ret) {
+ nlmsg_free(msg);
+ return ret;
+ }
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct dpll_pin *pin = info->user_ptr[0];
+ struct sk_buff *msg;
+ struct nlattr *hdr;
+ int ret;
+
+ if (!pin)
+ return -ENODEV;
+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+ DPLL_CMD_PIN_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+ ret = __dpll_cmd_pin_dump_one(msg, pin, info->extack);
+ if (ret) {
+ nlmsg_free(msg);
+ return ret;
+ }
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+ struct dpll_pin *pin;
+ struct nlattr *hdr;
+ unsigned long i;
+ int ret = 0;
+
+ xa_for_each_start(&dpll_pin_xa, i, pin, ctx->idx) {
+ if (xa_empty(&pin->dpll_refs))
+ continue;
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ &dpll_nl_family, NLM_F_MULTI,
+ DPLL_CMD_PIN_GET);
+ if (!hdr) {
+ ret = -EMSGSIZE;
+ break;
+ }
+ ret = __dpll_cmd_pin_dump_one(skb, pin, cb->extack);
+ if (ret) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+ genlmsg_end(skb, hdr);
+ }
+ if (ret == -EMSGSIZE) {
+ ctx->idx = i;
+ return skb->len;
+ }
+ return ret;
+}
+
+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct dpll_pin *pin = info->user_ptr[0];
+
+ return dpll_pin_set_from_nlattr(pin, info);
+}
+
+static struct dpll_device *
+dpll_device_find(u64 clock_id, struct nlattr *mod_name_attr,
+ enum dpll_type type)
+{
+ struct dpll_device *dpll_match = NULL, *dpll;
+ bool cid_match, mod_match, type_match;
+ unsigned long i;
+
+ xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
+ cid_match = clock_id ? dpll->clock_id == clock_id : true;
+ mod_match = mod_name_attr && module_name(dpll->module) ?
+ !nla_strcmp(mod_name_attr,
+ module_name(dpll->module)) : true;
+ type_match = type ? dpll->type == type : true;
+ if (cid_match && mod_match && type_match) {
+ if (dpll_match)
+ return NULL;
+ dpll_match = dpll;
+ }
+ }
+
+ return dpll_match;
+}
+
+static int
+dpll_device_find_from_nlattr(struct genl_info *info, struct sk_buff *skb)
+{
+ struct nlattr *attr, *mod_name_attr = NULL;
+ struct dpll_device *dpll = NULL;
+ enum dpll_type type = 0;
+ u64 clock_id = 0;
+ int rem = 0;
+
+ nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
+ genlmsg_len(info->genlhdr), rem) {
+ switch (nla_type(attr)) {
+ case DPLL_A_CLOCK_ID:
+ if (clock_id)
+ return -EINVAL;
+ clock_id = nla_get_u64(attr);
+ break;
+ case DPLL_A_MODULE_NAME:
+ if (mod_name_attr)
+ return -EINVAL;
+ mod_name_attr = attr;
+ break;
+ case DPLL_A_TYPE:
+ if (type)
+ return -EINVAL;
+ type = nla_get_u8(attr);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!clock_id && !mod_name_attr && !type)
+ return -EINVAL;
+ dpll = dpll_device_find(clock_id, mod_name_attr, type);
+ if (!dpll)
+ return -EINVAL;
+
+ return dpll_msg_add_dev_handle(skb, dpll);
+}
+
+static int
+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
+{
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
+ struct nlattr *tb[DPLL_A_MAX + 1];
+ int ret = 0;
+
+ nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
+ genlmsg_len(info->genlhdr), NULL, info->extack);
+ if (tb[DPLL_A_MODE]) {
+ ret = ops->mode_set(dpll, dpll_priv(dpll),
+ nla_get_u8(tb[DPLL_A_MODE]), info->extack);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct nlattr *hdr;
+ int ret;
+
+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+ DPLL_CMD_DEVICE_ID_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ ret = dpll_device_find_from_nlattr(info, msg);
+ if (ret) {
+ nlmsg_free(msg);
+ return ret;
+ }
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct dpll_device *dpll = info->user_ptr[0];
+ struct sk_buff *msg;
+ struct nlattr *hdr;
+ int ret;
+
+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
+ DPLL_CMD_DEVICE_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ ret = dpll_device_get_one(dpll, msg, info->extack);
+ if (ret) {
+ nlmsg_free(msg);
+ return ret;
+ }
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_reply(msg, info);
+}
+
+int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ struct dpll_device *dpll = info->user_ptr[0];
+
+ return dpll_set_from_nlattr(dpll, info);
+}
+
+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
+ struct dpll_device *dpll;
+ struct nlattr *hdr;
+ unsigned long i;
+ int ret = 0;
+
+ xa_for_each_start(&dpll_device_xa, i, dpll, ctx->idx) {
+ if (!xa_get_mark(&dpll_device_xa, i, DPLL_REGISTERED))
+ continue;
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, &dpll_nl_family,
+ NLM_F_MULTI, DPLL_CMD_DEVICE_GET);
+ if (!hdr) {
+ ret = -EMSGSIZE;
+ break;
+ }
+ ret = dpll_device_get_one(dpll, skb, cb->extack);
+ if (ret) {
+ genlmsg_cancel(skb, hdr);
+ break;
+ }
+ genlmsg_end(skb, hdr);
+ }
+ if (ret == -EMSGSIZE) {
+ ctx->idx = i;
+ return skb->len;
+ }
+ return ret;
+}
+
+int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct dpll_device *dpll_id = NULL;
+ u32 id;
+
+ if (!info->attrs[DPLL_A_ID])
+ return -EINVAL;
+
+ mutex_lock(&dpll_lock);
+ id = nla_get_u32(info->attrs[DPLL_A_ID]);
+
+ dpll_id = dpll_device_get_by_id(id);
+ if (!dpll_id)
+ goto unlock;
+ info->user_ptr[0] = dpll_id;
+ return 0;
+unlock:
+ mutex_unlock(&dpll_lock);
+ return -ENODEV;
+}
+
+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ mutex_unlock(&dpll_lock);
+}
+
+int
+dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ mutex_lock(&dpll_lock);
+
+ return 0;
+}
+
+void
+dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ mutex_unlock(&dpll_lock);
+}
+
+int dpll_lock_dumpit(struct netlink_callback *cb)
+{
+ mutex_lock(&dpll_lock);
+
+ return 0;
+}
+
+int dpll_unlock_dumpit(struct netlink_callback *cb)
+{
+ mutex_unlock(&dpll_lock);
+
+ return 0;
+}
+
+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ int ret;
+
+ mutex_lock(&dpll_lock);
+ if (!info->attrs[DPLL_A_PIN_ID]) {
+ ret = -EINVAL;
+ goto unlock_dev;
+ }
+ info->user_ptr[0] = xa_load(&dpll_pin_xa,
+ nla_get_u32(info->attrs[DPLL_A_PIN_ID]));
+ if (!info->user_ptr[0]) {
+ ret = -ENODEV;
+ goto unlock_dev;
+ }
+
+ return 0;
+
+unlock_dev:
+ mutex_unlock(&dpll_lock);
+ return ret;
+}
+
+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ mutex_unlock(&dpll_lock);
+}
+
+static int
+dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)
+{
+ struct sk_buff *msg;
+ int ret = -EMSGSIZE;
+ void *hdr;
+
+ if (!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED))
+ return -ENODEV;
+
+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+ hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
+ if (!hdr)
+ goto out_free_msg;
+ ret = dpll_device_get_one(dpll, msg, NULL);
+ if (ret)
+ goto out_cancel_msg;
+ genlmsg_end(msg, hdr);
+ genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
+
+ return 0;
+
+out_cancel_msg:
+ genlmsg_cancel(msg, hdr);
+out_free_msg:
+ nlmsg_free(msg);
+
+ return ret;
+}
+
+int dpll_device_create_ntf(struct dpll_device *dpll)
+{
+ return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
+}
+
+int dpll_device_delete_ntf(struct dpll_device *dpll)
+{
+ return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
+}
+
+int dpll_device_change_ntf(struct dpll_device *dpll)
+{
+ int ret = -EINVAL;
+
+ if (WARN_ON(!dpll))
+ return ret;
+
+ mutex_lock(&dpll_lock);
+ ret = dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
+ mutex_unlock(&dpll_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_device_change_ntf);
+
+static int
+dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
+{
+ struct dpll_pin *pin_verify;
+ struct sk_buff *msg;
+ int ret = -EMSGSIZE;
+ void *hdr;
+
+ pin_verify = xa_load(&dpll_pin_xa, pin->id);
+ if (pin != pin_verify)
+ return -ENODEV;
+
+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
+ if (!hdr)
+ goto out_free_msg;
+ ret = __dpll_cmd_pin_dump_one(msg, pin, NULL);
+ if (ret)
+ goto out_cancel_msg;
+ genlmsg_end(msg, hdr);
+ genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
+
+ return 0;
+
+out_cancel_msg:
+ genlmsg_cancel(msg, hdr);
+out_free_msg:
+ nlmsg_free(msg);
+
+ return ret;
+}
+
+int dpll_pin_create_ntf(struct dpll_pin *pin)
+{
+ return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
+}
+
+int dpll_pin_delete_ntf(struct dpll_pin *pin)
+{
+ return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
+}
+
+static int __dpll_pin_change_ntf(struct dpll_pin *pin)
+{
+ return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
+}
+
+int dpll_pin_change_ntf(struct dpll_pin *pin)
+{
+ int ret = -EINVAL;
+
+ if (WARN_ON(!pin))
+ return ret;
+
+ mutex_lock(&dpll_lock);
+ ret = __dpll_pin_change_ntf(pin);
+ mutex_unlock(&dpll_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dpll_pin_change_ntf);
+
+int __init dpll_netlink_init(void)
+{
+ return genl_register_family(&dpll_nl_family);
+}
+
+void dpll_netlink_finish(void)
+{
+ genl_unregister_family(&dpll_nl_family);
+}
+
+void __exit dpll_netlink_fini(void)
+{
+ dpll_netlink_finish();
+}
diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
new file mode 100644
index 000000000000..b5f9bfc88c9e
--- /dev/null
+++ b/drivers/dpll/dpll_netlink.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
+ * Copyright (c) 2023 Intel and affiliates
+ */
+
+/**
+ * dpll_device_create_ntf - notify that the device has been created
+ * @dpll: registered dpll pointer
+ *
+ * Context: caller shall hold dpll_xa_lock.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_device_create_ntf(struct dpll_device *dpll);
+
+/**
+ * dpll_device_delete_ntf - notify that the device has been deleted
+ * @dpll: registered dpll pointer
+ *
+ * Context: caller shall hold dpll_xa_lock.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_device_delete_ntf(struct dpll_device *dpll);
+
+/**
+ * dpll_pin_create_ntf - notify that the pin has been created
+ * @pin: registered pin pointer
+ *
+ * Context: caller shall hold dpll_xa_lock.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_create_ntf(struct dpll_pin *pin);
+
+/**
+ * dpll_pin_delete_ntf - notify that the pin has been deleted
+ * @pin: registered pin pointer
+ *
+ * Context: caller shall hold dpll_xa_lock.
+ * Return: 0 if succeeds, error code otherwise.
+ */
+int dpll_pin_delete_ntf(struct dpll_pin *pin);
+
+int __init dpll_netlink_init(void);
+void dpll_netlink_finish(void);
--
2.37.3


2023-06-09 12:32:13

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 07/10] ice: add admin commands to access cgu configuration

Add firmware admin command to access clock generation unit
configuration, it is required to enable Extended PTP and SyncE features
in the driver.
Add definitions of possible hardware variations of input and output pins
related to clock generation unit and functions to access the data.

Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
drivers/net/ethernet/intel/ice/ice.h | 1 +
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 240 ++++++++-
drivers/net/ethernet/intel/ice/ice_common.c | 467 ++++++++++++++++++
drivers/net/ethernet/intel/ice/ice_common.h | 43 ++
drivers/net/ethernet/intel/ice/ice_lib.c | 17 +-
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 414 ++++++++++++++++
drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 230 +++++++++
drivers/net/ethernet/intel/ice/ice_type.h | 1 +
8 files changed, 1408 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index b4bca1d964a9..ae58d7499955 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -199,6 +199,7 @@ enum ice_feature {
ICE_F_DSCP,
ICE_F_PTP_EXTTS,
ICE_F_SMA_CTRL,
+ ICE_F_CGU,
ICE_F_GNSS,
ICE_F_MAX
};
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 63d3e1dcbba5..ffa17bb9a8fb 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -1339,6 +1339,32 @@ struct ice_aqc_set_mac_lb {
u8 reserved[15];
};

+/* Set PHY recovered clock output (direct 0x0630) */
+struct ice_aqc_set_phy_rec_clk_out {
+ u8 phy_output;
+ u8 port_num;
+#define ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT 0xFF
+ u8 flags;
+#define ICE_AQC_SET_PHY_REC_CLK_OUT_OUT_EN BIT(0)
+ u8 rsvd;
+ __le32 freq;
+ u8 rsvd2[6];
+ __le16 node_handle;
+};
+
+/* Get PHY recovered clock output (direct 0x0631) */
+struct ice_aqc_get_phy_rec_clk_out {
+ u8 phy_output;
+ u8 port_num;
+#define ICE_AQC_GET_PHY_REC_CLK_OUT_CURR_PORT 0xFF
+ u8 flags;
+#define ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN BIT(0)
+ u8 rsvd;
+ __le32 freq;
+ u8 rsvd2[6];
+ __le16 node_handle;
+};
+
struct ice_aqc_link_topo_params {
u8 lport_num;
u8 lport_num_valid;
@@ -1355,6 +1381,8 @@ struct ice_aqc_link_topo_params {
#define ICE_AQC_LINK_TOPO_NODE_TYPE_CAGE 6
#define ICE_AQC_LINK_TOPO_NODE_TYPE_MEZZ 7
#define ICE_AQC_LINK_TOPO_NODE_TYPE_ID_EEPROM 8
+#define ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL 9
+#define ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_MUX 10
#define ICE_AQC_LINK_TOPO_NODE_CTX_S 4
#define ICE_AQC_LINK_TOPO_NODE_CTX_M \
(0xF << ICE_AQC_LINK_TOPO_NODE_CTX_S)
@@ -1391,7 +1419,12 @@ struct ice_aqc_link_topo_addr {
struct ice_aqc_get_link_topo {
struct ice_aqc_link_topo_addr addr;
u8 node_part_num;
-#define ICE_AQC_GET_LINK_TOPO_NODE_NR_PCA9575 0x21
+#define ICE_AQC_GET_LINK_TOPO_NODE_NR_PCA9575 0x21
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032 0x24
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384 0x25
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_E822_PHY 0x30
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827 0x31
+#define ICE_ACQ_GET_LINK_TOPO_NODE_NR_GEN_CLK_MUX 0x47
u8 rsvd[9];
};

@@ -2079,6 +2112,186 @@ struct ice_aqc_get_pkg_info_resp {
struct ice_aqc_get_pkg_info pkg_info[];
};

+/* Get CGU abilities command response data structure (indirect 0x0C61) */
+struct ice_aqc_get_cgu_abilities {
+ u8 num_inputs;
+ u8 num_outputs;
+ u8 pps_dpll_idx;
+ u8 eec_dpll_idx;
+ __le32 max_in_freq;
+ __le32 max_in_phase_adj;
+ __le32 max_out_freq;
+ __le32 max_out_phase_adj;
+ u8 cgu_part_num;
+ u8 rsvd[3];
+};
+
+/* Set CGU input config (direct 0x0C62) */
+struct ice_aqc_set_cgu_input_config {
+ u8 input_idx;
+ u8 flags1;
+#define ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ BIT(6)
+#define ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_DELAY BIT(7)
+ u8 flags2;
+#define ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN BIT(5)
+#define ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN BIT(6)
+ u8 rsvd;
+ __le32 freq;
+ __le32 phase_delay;
+ u8 rsvd2[2];
+ __le16 node_handle;
+};
+
+/* Get CGU input config response descriptor structure (direct 0x0C63) */
+struct ice_aqc_get_cgu_input_config {
+ u8 input_idx;
+ u8 status;
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_LOS BIT(0)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_SCM_FAIL BIT(1)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_CFM_FAIL BIT(2)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_GST_FAIL BIT(3)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_PFM_FAIL BIT(4)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_ESYNC_FAIL BIT(6)
+#define ICE_AQC_GET_CGU_IN_CFG_STATUS_ESYNC_CAP BIT(7)
+ u8 type;
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_READ_ONLY BIT(0)
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_GPS BIT(4)
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_EXTERNAL BIT(5)
+#define ICE_AQC_GET_CGU_IN_CFG_TYPE_PHY BIT(6)
+ u8 flags1;
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_PHASE_DELAY_SUPP BIT(0)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_1PPS_SUPP BIT(2)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_10MHZ_SUPP BIT(3)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG1_ANYFREQ BIT(7)
+ __le32 freq;
+ __le32 phase_delay;
+ u8 flags2;
+#define ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN BIT(5)
+#define ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN BIT(6)
+ u8 rsvd[1];
+ __le16 node_handle;
+};
+
+/* Set CGU output config (direct 0x0C64) */
+struct ice_aqc_set_cgu_output_config {
+ u8 output_idx;
+ u8 flags;
+#define ICE_AQC_SET_CGU_OUT_CFG_OUT_EN BIT(0)
+#define ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN BIT(1)
+#define ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ BIT(2)
+#define ICE_AQC_SET_CGU_OUT_CFG_UPDATE_PHASE BIT(3)
+#define ICE_AQC_SET_CGU_OUT_CFG_UPDATE_SRC_SEL BIT(4)
+ u8 src_sel;
+#define ICE_AQC_SET_CGU_OUT_CFG_DPLL_SRC_SEL ICE_M(0x1F, 0)
+ u8 rsvd;
+ __le32 freq;
+ __le32 phase_delay;
+ u8 rsvd2[2];
+ __le16 node_handle;
+};
+
+/* Get CGU output config (direct 0x0C65) */
+struct ice_aqc_get_cgu_output_config {
+ u8 output_idx;
+ u8 flags;
+#define ICE_AQC_GET_CGU_OUT_CFG_OUT_EN BIT(0)
+#define ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN BIT(1)
+#define ICE_AQC_GET_CGU_OUT_CFG_ESYNC_ABILITY BIT(2)
+ u8 src_sel;
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL_SHIFT 0
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL \
+ ICE_M(0x1F, ICE_AQC_GET_CGU_OUT_CFG_DPLL_SRC_SEL_SHIFT)
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_MODE_SHIFT 5
+#define ICE_AQC_GET_CGU_OUT_CFG_DPLL_MODE \
+ ICE_M(0x7, ICE_AQC_GET_CGU_OUT_CFG_DPLL_MODE_SHIFT)
+ u8 rsvd;
+ __le32 freq;
+ __le32 src_freq;
+ u8 rsvd2[2];
+ __le16 node_handle;
+};
+
+/* Get CGU DPLL status (direct 0x0C66) */
+struct ice_aqc_get_cgu_dpll_status {
+ u8 dpll_num;
+ u8 ref_state;
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_LOS BIT(0)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_SCM BIT(1)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_CFM BIT(2)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_GST BIT(3)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_PFM BIT(4)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_FAST_LOCK_EN BIT(5)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_REF_SW_ESYNC BIT(6)
+ __le16 dpll_state;
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_LOCK BIT(0)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_HO BIT(1)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_HO_READY BIT(2)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_FLHIT BIT(5)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_PSLHIT BIT(7)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SHIFT 8
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SEL \
+ ICE_M(0x1F, ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SHIFT)
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_MODE_SHIFT 13
+#define ICE_AQC_GET_CGU_DPLL_STATUS_STATE_MODE \
+ ICE_M(0x7, ICE_AQC_GET_CGU_DPLL_STATUS_STATE_MODE_SHIFT)
+ __le32 phase_offset_h;
+ __le32 phase_offset_l;
+ u8 eec_mode;
+#define ICE_AQC_GET_CGU_DPLL_STATUS_EEC_MODE_1 0xA
+#define ICE_AQC_GET_CGU_DPLL_STATUS_EEC_MODE_2 0xB
+#define ICE_AQC_GET_CGU_DPLL_STATUS_EEC_MODE_UNKNOWN 0xF
+ u8 rsvd[1];
+ __le16 node_handle;
+};
+
+/* Set CGU DPLL config (direct 0x0C67) */
+struct ice_aqc_set_cgu_dpll_config {
+ u8 dpll_num;
+ u8 ref_state;
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_LOS BIT(0)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_SCM BIT(1)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_CFM BIT(2)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_GST BIT(3)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_PFM BIT(4)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_FLOCK_EN BIT(5)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_REF_SW_ESYNC BIT(6)
+ u8 rsvd;
+ u8 config;
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_CLK_REF_SEL ICE_M(0x1F, 0)
+#define ICE_AQC_SET_CGU_DPLL_CONFIG_MODE ICE_M(0x7, 5)
+ u8 rsvd2[8];
+ u8 eec_mode;
+ u8 rsvd3[1];
+ __le16 node_handle;
+};
+
+/* Set CGU reference priority (direct 0x0C68) */
+struct ice_aqc_set_cgu_ref_prio {
+ u8 dpll_num;
+ u8 ref_idx;
+ u8 ref_priority;
+ u8 rsvd[11];
+ __le16 node_handle;
+};
+
+/* Get CGU reference priority (direct 0x0C69) */
+struct ice_aqc_get_cgu_ref_prio {
+ u8 dpll_num;
+ u8 ref_idx;
+ u8 ref_priority; /* Valid only in response */
+ u8 rsvd[13];
+};
+
+/* Get CGU info (direct 0x0C6A) */
+struct ice_aqc_get_cgu_info {
+ __le32 cgu_id;
+ __le32 cgu_cfg_ver;
+ __le32 cgu_fw_ver;
+ u8 node_part_num;
+ u8 dev_rev;
+ __le16 node_handle;
+};
+
/* Driver Shared Parameters (direct, 0x0C90) */
struct ice_aqc_driver_shared_params {
u8 set_or_get_op;
@@ -2148,6 +2361,8 @@ struct ice_aq_desc {
struct ice_aqc_get_phy_caps get_phy;
struct ice_aqc_set_phy_cfg set_phy;
struct ice_aqc_restart_an restart_an;
+ struct ice_aqc_set_phy_rec_clk_out set_phy_rec_clk_out;
+ struct ice_aqc_get_phy_rec_clk_out get_phy_rec_clk_out;
struct ice_aqc_gpio read_write_gpio;
struct ice_aqc_sff_eeprom read_write_sff_param;
struct ice_aqc_set_port_id_led set_port_id_led;
@@ -2187,6 +2402,15 @@ struct ice_aq_desc {
struct ice_aqc_fw_logging fw_logging;
struct ice_aqc_get_clear_fw_log get_clear_fw_log;
struct ice_aqc_download_pkg download_pkg;
+ struct ice_aqc_set_cgu_input_config set_cgu_input_config;
+ struct ice_aqc_get_cgu_input_config get_cgu_input_config;
+ struct ice_aqc_set_cgu_output_config set_cgu_output_config;
+ struct ice_aqc_get_cgu_output_config get_cgu_output_config;
+ struct ice_aqc_get_cgu_dpll_status get_cgu_dpll_status;
+ struct ice_aqc_set_cgu_dpll_config set_cgu_dpll_config;
+ struct ice_aqc_set_cgu_ref_prio set_cgu_ref_prio;
+ struct ice_aqc_get_cgu_ref_prio get_cgu_ref_prio;
+ struct ice_aqc_get_cgu_info get_cgu_info;
struct ice_aqc_driver_shared_params drv_shared_params;
struct ice_aqc_set_mac_lb set_mac_lb;
struct ice_aqc_alloc_free_res_cmd sw_res_ctrl;
@@ -2310,6 +2534,8 @@ enum ice_adminq_opc {
ice_aqc_opc_get_link_status = 0x0607,
ice_aqc_opc_set_event_mask = 0x0613,
ice_aqc_opc_set_mac_lb = 0x0620,
+ ice_aqc_opc_set_phy_rec_clk_out = 0x0630,
+ ice_aqc_opc_get_phy_rec_clk_out = 0x0631,
ice_aqc_opc_get_link_topo = 0x06E0,
ice_aqc_opc_read_i2c = 0x06E2,
ice_aqc_opc_write_i2c = 0x06E3,
@@ -2364,6 +2590,18 @@ enum ice_adminq_opc {
ice_aqc_opc_update_pkg = 0x0C42,
ice_aqc_opc_get_pkg_info_list = 0x0C43,

+ /* 1588/SyncE commands/events */
+ ice_aqc_opc_get_cgu_abilities = 0x0C61,
+ ice_aqc_opc_set_cgu_input_config = 0x0C62,
+ ice_aqc_opc_get_cgu_input_config = 0x0C63,
+ ice_aqc_opc_set_cgu_output_config = 0x0C64,
+ ice_aqc_opc_get_cgu_output_config = 0x0C65,
+ ice_aqc_opc_get_cgu_dpll_status = 0x0C66,
+ ice_aqc_opc_set_cgu_dpll_config = 0x0C67,
+ ice_aqc_opc_set_cgu_ref_prio = 0x0C68,
+ ice_aqc_opc_get_cgu_ref_prio = 0x0C69,
+ ice_aqc_opc_get_cgu_info = 0x0C6A,
+
ice_aqc_opc_driver_shared_params = 0x0C90,

/* Standalone Commands/Events */
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index 0157f6e98d3e..35e00133575c 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -434,6 +434,83 @@ ice_aq_get_link_topo_handle(struct ice_port_info *pi, u8 node_type,
return ice_aq_send_cmd(pi->hw, &desc, NULL, 0, cd);
}

+/**
+ * ice_aq_get_netlist_node
+ * @hw: pointer to the hw struct
+ * @cmd: get_link_topo AQ structure
+ * @node_part_number: output node part number if node found
+ * @node_handle: output node handle parameter if node found
+ *
+ * Get netlist node handle.
+ */
+int
+ice_aq_get_netlist_node(struct ice_hw *hw, struct ice_aqc_get_link_topo *cmd,
+ u8 *node_part_number, u16 *node_handle)
+{
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_link_topo);
+ desc.params.get_link_topo = *cmd;
+
+ if (ice_aq_send_cmd(hw, &desc, NULL, 0, NULL))
+ return -EINTR;
+
+ if (node_handle)
+ *node_handle =
+ le16_to_cpu(desc.params.get_link_topo.addr.handle);
+ if (node_part_number)
+ *node_part_number = desc.params.get_link_topo.node_part_num;
+
+ return 0;
+}
+
+#define MAX_NETLIST_SIZE 10
+
+/**
+ * ice_find_netlist_node
+ * @hw: pointer to the hw struct
+ * @node_type_ctx: type of netlist node to look for
+ * @node_part_number: node part number to look for
+ * @node_handle: output parameter if node found - optional
+ *
+ * Find and return the node handle for a given node type and part number in the
+ * netlist. When found ICE_SUCCESS is returned, ICE_ERR_DOES_NOT_EXIST
+ * otherwise. If node_handle provided, it would be set to found node handle.
+ */
+int
+ice_find_netlist_node(struct ice_hw *hw, u8 node_type_ctx, u8 node_part_number,
+ u16 *node_handle)
+{
+ struct ice_aqc_get_link_topo cmd;
+ u8 rec_node_part_number;
+ u16 rec_node_handle;
+ u8 idx;
+
+ for (idx = 0; idx < MAX_NETLIST_SIZE; idx++) {
+ int status;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.addr.topo_params.node_type_ctx =
+ (node_type_ctx << ICE_AQC_LINK_TOPO_NODE_TYPE_S);
+ cmd.addr.topo_params.index = idx;
+
+ status = ice_aq_get_netlist_node(hw, &cmd,
+ &rec_node_part_number,
+ &rec_node_handle);
+ if (status)
+ return status;
+
+ if (rec_node_part_number == node_part_number) {
+ if (node_handle)
+ *node_handle = rec_node_handle;
+ return 0;
+ }
+ }
+
+ return -ENOTBLK;
+}
+
/**
* ice_is_media_cage_present
* @pi: port information structure
@@ -4917,6 +4994,396 @@ ice_dis_vsi_rdma_qset(struct ice_port_info *pi, u16 count, u32 *qset_teid,
return status;
}

+/**
+ * ice_aq_get_cgu_abilities
+ * @hw: pointer to the HW struct
+ * @abilities: CGU abilities
+ *
+ * Get CGU abilities (0x0C61)
+ */
+int
+ice_aq_get_cgu_abilities(struct ice_hw *hw,
+ struct ice_aqc_get_cgu_abilities *abilities)
+{
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_abilities);
+ return ice_aq_send_cmd(hw, &desc, abilities, sizeof(*abilities), NULL);
+}
+
+/**
+ * ice_aq_set_input_pin_cfg
+ * @hw: pointer to the HW struct
+ * @input_idx: Input index
+ * @flags1: Input flags
+ * @flags2: Input flags
+ * @freq: Frequency in Hz
+ * @phase_delay: Delay in ps
+ *
+ * Set CGU input config (0x0C62)
+ */
+int
+ice_aq_set_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 flags1, u8 flags2,
+ u32 freq, s32 phase_delay)
+{
+ struct ice_aqc_set_cgu_input_config *cmd;
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_input_config);
+ cmd = &desc.params.set_cgu_input_config;
+ cmd->input_idx = input_idx;
+ cmd->flags1 = flags1;
+ cmd->flags2 = flags2;
+ cmd->freq = cpu_to_le32(freq);
+ cmd->phase_delay = cpu_to_le32(phase_delay);
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_get_input_pin_cfg
+ * @hw: pointer to the HW struct
+ * @input_idx: Input index
+ * @status: Pin status
+ * @type: Pin type
+ * @flags1: Input flags
+ * @flags2: Input flags
+ * @freq: Frequency in Hz
+ * @phase_delay: Delay in ps
+ *
+ * Get CGU input config (0x0C63)
+ */
+int
+ice_aq_get_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 *status, u8 *type,
+ u8 *flags1, u8 *flags2, u32 *freq, s32 *phase_delay)
+{
+ struct ice_aqc_get_cgu_input_config *cmd;
+ struct ice_aq_desc desc;
+ int ret;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_input_config);
+ cmd = &desc.params.get_cgu_input_config;
+ cmd->input_idx = input_idx;
+
+ ret = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+ if (!ret) {
+ if (status)
+ *status = cmd->status;
+ if (type)
+ *type = cmd->type;
+ if (flags1)
+ *flags1 = cmd->flags1;
+ if (flags2)
+ *flags2 = cmd->flags2;
+ if (freq)
+ *freq = le32_to_cpu(cmd->freq);
+ if (phase_delay)
+ *phase_delay = le32_to_cpu(cmd->phase_delay);
+ }
+
+ return ret;
+}
+
+/**
+ * ice_aq_set_output_pin_cfg
+ * @hw: pointer to the HW struct
+ * @output_idx: Output index
+ * @flags: Output flags
+ * @src_sel: Index of DPLL block
+ * @freq: Output frequency
+ * @phase_delay: Output phase compensation
+ *
+ * Set CGU output config (0x0C64)
+ */
+int
+ice_aq_set_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 flags,
+ u8 src_sel, u32 freq, s32 phase_delay)
+{
+ struct ice_aqc_set_cgu_output_config *cmd;
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_output_config);
+ cmd = &desc.params.set_cgu_output_config;
+ cmd->output_idx = output_idx;
+ cmd->flags = flags;
+ cmd->src_sel = src_sel;
+ cmd->freq = cpu_to_le32(freq);
+ cmd->phase_delay = cpu_to_le32(phase_delay);
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_get_output_pin_cfg
+ * @hw: pointer to the HW struct
+ * @output_idx: Output index
+ * @flags: Output flags
+ * @src_sel: Internal DPLL source
+ * @freq: Output frequency
+ * @src_freq: Source frequency
+ *
+ * Get CGU output config (0x0C65)
+ */
+int
+ice_aq_get_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 *flags,
+ u8 *src_sel, u32 *freq, u32 *src_freq)
+{
+ struct ice_aqc_get_cgu_output_config *cmd;
+ struct ice_aq_desc desc;
+ int ret;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_output_config);
+ cmd = &desc.params.get_cgu_output_config;
+ cmd->output_idx = output_idx;
+
+ ret = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+ if (!ret) {
+ if (flags)
+ *flags = cmd->flags;
+ if (src_sel)
+ *src_sel = cmd->src_sel;
+ if (freq)
+ *freq = le32_to_cpu(cmd->freq);
+ if (src_freq)
+ *src_freq = le32_to_cpu(cmd->src_freq);
+ }
+
+ return ret;
+}
+
+/**
+ * convert_s48_to_s64 - convert 48 bit value to 64 bit value
+ * @signed_48: signed 64 bit variable storing signed 48 bit value
+ *
+ * Convert signed 48 bit value to its 64 bit representation.
+ *
+ * Return: signed 64 bit representation of signed 48 bit value.
+ */
+static inline
+s64 convert_s48_to_s64(s64 signed_48)
+{
+ const s64 MASK_SIGN_BITS = GENMASK_ULL(63, 48);
+ const s64 SIGN_BIT_47 = BIT_ULL(47);
+
+ return ((signed_48 & SIGN_BIT_47) ? (s64)(MASK_SIGN_BITS | signed_48)
+ : signed_48);
+}
+
+/**
+ * ice_aq_get_cgu_dpll_status
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_state: Reference clock state
+ * @dpll_state: DPLL state
+ * @phase_offset: Phase offset in ns
+ * @eec_mode: EEC_mode
+ *
+ * Get CGU DPLL status (0x0C66)
+ */
+int
+ice_aq_get_cgu_dpll_status(struct ice_hw *hw, u8 dpll_num, u8 *ref_state,
+ u16 *dpll_state, s64 *phase_offset, u8 *eec_mode)
+{
+ struct ice_aqc_get_cgu_dpll_status *cmd;
+ const s64 NSEC_PER_PSEC = 1000LL;
+ struct ice_aq_desc desc;
+ int status;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_dpll_status);
+ cmd = &desc.params.get_cgu_dpll_status;
+ cmd->dpll_num = dpll_num;
+
+ status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+ if (!status) {
+ *ref_state = cmd->ref_state;
+ *dpll_state = le16_to_cpu(cmd->dpll_state);
+ *phase_offset = le32_to_cpu(cmd->phase_offset_h);
+ *phase_offset <<= 32;
+ *phase_offset += le32_to_cpu(cmd->phase_offset_l);
+ *phase_offset = convert_s48_to_s64(*phase_offset)
+ / NSEC_PER_PSEC;
+ *eec_mode = cmd->eec_mode;
+ }
+
+ return status;
+}
+
+/**
+ * ice_aq_set_cgu_dpll_config
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_state: Reference clock state
+ * @config: DPLL config
+ * @eec_mode: EEC mode
+ *
+ * Set CGU DPLL config (0x0C67)
+ */
+int
+ice_aq_set_cgu_dpll_config(struct ice_hw *hw, u8 dpll_num, u8 ref_state,
+ u8 config, u8 eec_mode)
+{
+ struct ice_aqc_set_cgu_dpll_config *cmd;
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_dpll_config);
+ cmd = &desc.params.set_cgu_dpll_config;
+ cmd->dpll_num = dpll_num;
+ cmd->ref_state = ref_state;
+ cmd->config = config;
+ cmd->eec_mode = eec_mode;
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_set_cgu_ref_prio
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_idx: Reference pin index
+ * @ref_priority: Reference input priority
+ *
+ * Set CGU reference priority (0x0C68)
+ */
+int
+ice_aq_set_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+ u8 ref_priority)
+{
+ struct ice_aqc_set_cgu_ref_prio *cmd;
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_cgu_ref_prio);
+ cmd = &desc.params.set_cgu_ref_prio;
+ cmd->dpll_num = dpll_num;
+ cmd->ref_idx = ref_idx;
+ cmd->ref_priority = ref_priority;
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_aq_get_cgu_ref_prio
+ * @hw: pointer to the HW struct
+ * @dpll_num: DPLL index
+ * @ref_idx: Reference pin index
+ * @ref_prio: Reference input priority
+ *
+ * Get CGU reference priority (0x0C69)
+ */
+int
+ice_aq_get_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+ u8 *ref_prio)
+{
+ struct ice_aqc_get_cgu_ref_prio *cmd;
+ struct ice_aq_desc desc;
+ int status;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_ref_prio);
+ cmd = &desc.params.get_cgu_ref_prio;
+ cmd->dpll_num = dpll_num;
+ cmd->ref_idx = ref_idx;
+
+ status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+ if (!status)
+ *ref_prio = cmd->ref_priority;
+
+ return status;
+}
+
+/**
+ * ice_aq_get_cgu_info
+ * @hw: pointer to the HW struct
+ * @cgu_id: CGU ID
+ * @cgu_cfg_ver: CGU config version
+ * @cgu_fw_ver: CGU firmware version
+ *
+ * Get CGU info (0x0C6A)
+ */
+int
+ice_aq_get_cgu_info(struct ice_hw *hw, u32 *cgu_id, u32 *cgu_cfg_ver,
+ u32 *cgu_fw_ver)
+{
+ struct ice_aqc_get_cgu_info *cmd;
+ struct ice_aq_desc desc;
+ int status;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_cgu_info);
+ cmd = &desc.params.get_cgu_info;
+
+ status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+ if (!status) {
+ *cgu_id = le32_to_cpu(cmd->cgu_id);
+ *cgu_cfg_ver = le32_to_cpu(cmd->cgu_cfg_ver);
+ *cgu_fw_ver = le32_to_cpu(cmd->cgu_fw_ver);
+ }
+
+ return status;
+}
+
+/**
+ * ice_aq_set_phy_rec_clk_out - set RCLK phy out
+ * @hw: pointer to the HW struct
+ * @phy_output: PHY reference clock output pin
+ * @enable: GPIO state to be applied
+ * @freq: PHY output frequency
+ *
+ * Set CGU reference priority (0x0630)
+ * Return 0 on success or negative value on failure.
+ */
+int
+ice_aq_set_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, bool enable,
+ u32 *freq)
+{
+ struct ice_aqc_set_phy_rec_clk_out *cmd;
+ struct ice_aq_desc desc;
+ int status;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_phy_rec_clk_out);
+ cmd = &desc.params.set_phy_rec_clk_out;
+ cmd->phy_output = phy_output;
+ cmd->port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
+ cmd->flags = enable & ICE_AQC_SET_PHY_REC_CLK_OUT_OUT_EN;
+ cmd->freq = cpu_to_le32(*freq);
+
+ status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+ if (!status)
+ *freq = le32_to_cpu(cmd->freq);
+
+ return status;
+}
+
+/**
+ * ice_aq_get_phy_rec_clk_out
+ * @hw: pointer to the HW struct
+ * @phy_output: PHY reference clock output pin
+ * @port_num: Port number
+ * @flags: PHY flags
+ * @freq: PHY output frequency
+ *
+ * Get PHY recovered clock output (0x0631)
+ */
+int
+ice_aq_get_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, u8 *port_num,
+ u8 *flags, u32 *freq)
+{
+ struct ice_aqc_get_phy_rec_clk_out *cmd;
+ struct ice_aq_desc desc;
+ int status;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_phy_rec_clk_out);
+ cmd = &desc.params.get_phy_rec_clk_out;
+ cmd->phy_output = phy_output;
+ cmd->port_num = *port_num;
+
+ status = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+ if (!status) {
+ *port_num = cmd->port_num;
+ *flags = cmd->flags;
+ *freq = le32_to_cpu(cmd->freq);
+ }
+
+ return status;
+}
+
/**
* ice_replay_pre_init - replay pre initialization
* @hw: pointer to the HW struct
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
index 8ba5f935a092..99c933552cc2 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.h
+++ b/drivers/net/ethernet/intel/ice/ice_common.h
@@ -94,6 +94,12 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
struct ice_aqc_get_phy_caps_data *caps,
struct ice_sq_cd *cd);
int
+ice_find_netlist_node(struct ice_hw *hw, u8 node_type_ctx, u8 node_part_number,
+ u16 *node_handle);
+int
+ice_aq_get_netlist_node(struct ice_hw *hw, struct ice_aqc_get_link_topo *cmd,
+ u8 *node_part_number, u16 *node_handle);
+int
ice_aq_list_caps(struct ice_hw *hw, void *buf, u16 buf_size, u32 *cap_count,
enum ice_adminq_opc opc, struct ice_sq_cd *cd);
int
@@ -192,6 +198,43 @@ void ice_output_fw_log(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf);
struct ice_q_ctx *
ice_get_lan_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 q_handle);
int ice_sbq_rw_reg(struct ice_hw *hw, struct ice_sbq_msg_input *in);
+int
+ice_aq_get_cgu_abilities(struct ice_hw *hw,
+ struct ice_aqc_get_cgu_abilities *abilities);
+int
+ice_aq_set_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 flags1, u8 flags2,
+ u32 freq, s32 phase_delay);
+int
+ice_aq_get_input_pin_cfg(struct ice_hw *hw, u8 input_idx, u8 *status, u8 *type,
+ u8 *flags1, u8 *flags2, u32 *freq, s32 *phase_delay);
+int
+ice_aq_set_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 flags,
+ u8 src_sel, u32 freq, s32 phase_delay);
+int
+ice_aq_get_output_pin_cfg(struct ice_hw *hw, u8 output_idx, u8 *flags,
+ u8 *src_sel, u32 *freq, u32 *src_freq);
+int
+ice_aq_get_cgu_dpll_status(struct ice_hw *hw, u8 dpll_num, u8 *ref_state,
+ u16 *dpll_state, s64 *phase_offset, u8 *eec_mode);
+int
+ice_aq_set_cgu_dpll_config(struct ice_hw *hw, u8 dpll_num, u8 ref_state,
+ u8 config, u8 eec_mode);
+int
+ice_aq_set_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+ u8 ref_priority);
+int
+ice_aq_get_cgu_ref_prio(struct ice_hw *hw, u8 dpll_num, u8 ref_idx,
+ u8 *ref_prio);
+int
+ice_aq_get_cgu_info(struct ice_hw *hw, u32 *cgu_id, u32 *cgu_cfg_ver,
+ u32 *cgu_fw_ver);
+
+int
+ice_aq_set_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, bool enable,
+ u32 *freq);
+int
+ice_aq_get_phy_rec_clk_out(struct ice_hw *hw, u8 phy_output, u8 *port_num,
+ u8 *flags, u32 *freq);
void
ice_stat_update40(struct ice_hw *hw, u32 reg, bool prev_stat_loaded,
u64 *prev_stat, u64 *cur_stat);
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 5ddb95d1073a..3ff9907b1e80 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -4044,13 +4044,22 @@ void ice_init_feature_support(struct ice_pf *pf)
case ICE_DEV_ID_E810C_BACKPLANE:
case ICE_DEV_ID_E810C_QSFP:
case ICE_DEV_ID_E810C_SFP:
+ case ICE_DEV_ID_E810_XXV_BACKPLANE:
+ case ICE_DEV_ID_E810_XXV_QSFP:
+ case ICE_DEV_ID_E810_XXV_SFP:
ice_set_feature_support(pf, ICE_F_DSCP);
ice_set_feature_support(pf, ICE_F_PTP_EXTTS);
- if (ice_is_e810t(&pf->hw)) {
+ if (ice_is_phy_rclk_present(&pf->hw))
+ ice_set_feature_support(pf, ICE_F_PHY_RCLK);
+ /* If we don't own the timer - don't enable other caps */
+ if (!pf->hw.func_caps.ts_func_info.src_tmr_owned)
+ break;
+ if (ice_is_cgu_present(&pf->hw))
+ ice_set_feature_support(pf, ICE_F_CGU);
+ if (ice_is_clock_mux_present_e810t(&pf->hw))
ice_set_feature_support(pf, ICE_F_SMA_CTRL);
- if (ice_gnss_is_gps_present(&pf->hw))
- ice_set_feature_support(pf, ICE_F_GNSS);
- }
+ if (ice_gnss_is_gps_present(&pf->hw))
+ ice_set_feature_support(pf, ICE_F_GNSS);
break;
default:
break;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index a38614d21ea8..39b692945f73 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -3213,6 +3213,91 @@ ice_get_pca9575_handle(struct ice_hw *hw, u16 *pca9575_handle)
return 0;
}

+/**
+ * ice_is_phy_rclk_present
+ * @hw: pointer to the hw struct
+ *
+ * Check if the PHY Recovered Clock device is present in the netlist
+ * Return:
+ * * true - device found in netlist
+ * * false - device not found
+ */
+bool ice_is_phy_rclk_present(struct ice_hw *hw)
+{
+ if (ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+ ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827, NULL) &&
+ ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+ ICE_ACQ_GET_LINK_TOPO_NODE_NR_E822_PHY, NULL))
+ return false;
+
+ return true;
+}
+
+/**
+ * ice_is_clock_mux_present_e810t
+ * @hw: pointer to the hw struct
+ *
+ * Check if the Clock Multiplexer device is present in the netlist
+ * Return:
+ * * true - device found in netlist
+ * * false - device not found
+ */
+bool ice_is_clock_mux_present_e810t(struct ice_hw *hw)
+{
+ if (ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_MUX,
+ ICE_ACQ_GET_LINK_TOPO_NODE_NR_GEN_CLK_MUX,
+ NULL))
+ return false;
+
+ return true;
+}
+
+/**
+ * ice_get_pf_c827_idx - find and return the C827 index for the current pf
+ * @hw: pointer to the hw struct
+ * @idx: index of the found C827 PHY
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx)
+{
+ struct ice_aqc_get_link_topo cmd;
+ u8 node_part_number;
+ u16 node_handle;
+ int status;
+ u8 ctx;
+
+ if (hw->mac_type != ICE_MAC_E810)
+ return -ENODEV;
+
+ if (hw->device_id != ICE_DEV_ID_E810C_QSFP) {
+ *idx = C827_0;
+ return 0;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ ctx = ICE_AQC_LINK_TOPO_NODE_TYPE_PHY << ICE_AQC_LINK_TOPO_NODE_TYPE_S;
+ ctx |= ICE_AQC_LINK_TOPO_NODE_CTX_PORT << ICE_AQC_LINK_TOPO_NODE_CTX_S;
+ cmd.addr.topo_params.node_type_ctx = ctx;
+ cmd.addr.topo_params.index = 0;
+
+ status = ice_aq_get_netlist_node(hw, &cmd, &node_part_number,
+ &node_handle);
+ if (status || node_part_number != ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827)
+ return -ENOENT;
+
+ if (node_handle == E810C_QSFP_C827_0_HANDLE)
+ *idx = C827_0;
+ else if (node_handle == E810C_QSFP_C827_1_HANDLE)
+ *idx = C827_1;
+ else
+ return -EIO;
+
+ return 0;
+}
+
/**
* ice_read_sma_ctrl_e810t
* @hw: pointer to the hw struct
@@ -3381,3 +3466,332 @@ int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready)
return ice_get_phy_tx_tstamp_ready_e822(hw, block,
tstamp_ready);
}
+
+/**
+ * ice_is_cgu_present
+ * @hw: pointer to the hw struct
+ *
+ * Check if the Clock Generation Unit (CGU) device is present in the netlist
+ * Return:
+ * * true - cgu is present
+ * * false - cgu is not present
+ */
+bool ice_is_cgu_present(struct ice_hw *hw)
+{
+ if (!ice_find_netlist_node(hw, ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+ ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032,
+ NULL)) {
+ hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032;
+ return true;
+ } else if (!ice_find_netlist_node(hw,
+ ICE_AQC_LINK_TOPO_NODE_TYPE_CLK_CTRL,
+ ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384,
+ NULL)) {
+ hw->cgu_part_number = ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * ice_cgu_get_pin_desc_e823
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pin
+ * @size: number of inputs/outputs
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc_e823(struct ice_hw *hw, bool input, int *size)
+{
+ static const struct ice_cgu_pin_desc *t;
+
+ if (hw->cgu_part_number ==
+ ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032) {
+ if (input) {
+ t = ice_e823_zl_cgu_inputs;
+ *size = ARRAY_SIZE(ice_e823_zl_cgu_inputs);
+ } else {
+ t = ice_e823_zl_cgu_outputs;
+ *size = ARRAY_SIZE(ice_e823_zl_cgu_outputs);
+ }
+ } else if (hw->cgu_part_number ==
+ ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384) {
+ if (input) {
+ t = ice_e823_si_cgu_inputs;
+ *size = ARRAY_SIZE(ice_e823_si_cgu_inputs);
+ } else {
+ t = ice_e823_si_cgu_outputs;
+ *size = ARRAY_SIZE(ice_e823_si_cgu_outputs);
+ }
+ } else {
+ t = NULL;
+ *size = 0;
+ }
+
+ return t;
+}
+
+/**
+ * ice_cgu_get_pin_desc
+ * @hw: pointer to the hw struct
+ * @input: if request is done against input or output pins
+ * @size: size of array returned by function
+ *
+ * Return: pointer to pin description array associated to given hw.
+ */
+static const struct ice_cgu_pin_desc *
+ice_cgu_get_pin_desc(struct ice_hw *hw, bool input, int *size)
+{
+ const struct ice_cgu_pin_desc *t = NULL;
+
+ switch (hw->device_id) {
+ case ICE_DEV_ID_E810C_SFP:
+ if (input) {
+ t = ice_e810t_sfp_cgu_inputs;
+ *size = ARRAY_SIZE(ice_e810t_sfp_cgu_inputs);
+ } else {
+ t = ice_e810t_sfp_cgu_outputs;
+ *size = ARRAY_SIZE(ice_e810t_sfp_cgu_outputs);
+ }
+ break;
+ case ICE_DEV_ID_E810C_QSFP:
+ if (input) {
+ t = ice_e810t_qsfp_cgu_inputs;
+ *size = ARRAY_SIZE(ice_e810t_qsfp_cgu_inputs);
+ } else {
+ t = ice_e810t_qsfp_cgu_outputs;
+ *size = ARRAY_SIZE(ice_e810t_qsfp_cgu_outputs);
+ }
+ break;
+ case ICE_DEV_ID_E823L_10G_BASE_T:
+ case ICE_DEV_ID_E823L_1GBE:
+ case ICE_DEV_ID_E823L_BACKPLANE:
+ case ICE_DEV_ID_E823L_QSFP:
+ case ICE_DEV_ID_E823L_SFP:
+ case ICE_DEV_ID_E823C_10G_BASE_T:
+ case ICE_DEV_ID_E823C_BACKPLANE:
+ case ICE_DEV_ID_E823C_QSFP:
+ case ICE_DEV_ID_E823C_SFP:
+ case ICE_DEV_ID_E823C_SGMII:
+ t = ice_cgu_get_pin_desc_e823(hw, input, size);
+ break;
+ default:
+ break;
+ }
+
+ return t;
+}
+
+/**
+ * ice_cgu_get_pin_type
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return: type of a pin.
+ */
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input)
+{
+ const struct ice_cgu_pin_desc *t;
+ int t_size;
+
+ t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+ if (!t)
+ return 0;
+
+ if (pin >= t_size)
+ return 0;
+
+ return t[pin].type;
+}
+
+/**
+ * ice_cgu_get_pin_freq_supp
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ * @num: output number of supported frequencies
+ *
+ * Get frequency supported number and array of supported frequencies.
+ *
+ * Return: array of supported frequencies for given pin.
+ */
+struct dpll_pin_frequency *
+ice_cgu_get_pin_freq_supp(struct ice_hw *hw, u8 pin, bool input, u8 *num)
+{
+ const struct ice_cgu_pin_desc *t;
+ int t_size;
+
+ *num = 0;
+ t = ice_cgu_get_pin_desc(hw, input, &t_size);
+ if (!t)
+ return NULL;
+ if (pin >= t_size)
+ return NULL;
+ *num = t[pin].freq_supp_num;
+
+ return t[pin].freq_supp;
+}
+
+/**
+ * ice_cgu_get_pin_name
+ * @hw: pointer to the hw struct
+ * @pin: pin index
+ * @input: if request is done against input or output pin
+ *
+ * Return:
+ * * null terminated char array with name
+ * * NULL in case of failure
+ */
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input)
+{
+ const struct ice_cgu_pin_desc *t;
+ int t_size;
+
+ t = ice_cgu_get_pin_desc(hw, input, &t_size);
+
+ if (!t)
+ return NULL;
+
+ if (pin >= t_size)
+ return NULL;
+
+ return t[pin].name;
+}
+
+/**
+ * ice_get_cgu_state - get the state of the DPLL
+ * @hw: pointer to the hw struct
+ * @dpll_idx: Index of internal DPLL unit
+ * @last_dpll_state: last known state of DPLL
+ * @pin: pointer to a buffer for returning currently active pin
+ * @ref_state: reference clock state
+ * @phase_offset: pointer to a buffer for returning phase offset
+ * @dpll_state: state of the DPLL (output)
+ *
+ * This function will read the state of the DPLL(dpll_idx). Non-null
+ * 'pin', 'ref_state', 'eec_mode' and 'phase_offset' parameters are used to
+ * retrieve currently active pin, state, mode and phase_offset respectively.
+ *
+ * Return: state of the DPLL
+ */
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+ enum ice_cgu_state last_dpll_state, u8 *pin,
+ u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+ enum ice_cgu_state *dpll_state)
+{
+ u8 hw_ref_state, hw_eec_mode;
+ s64 hw_phase_offset;
+ u16 hw_dpll_state;
+ int status;
+
+ status = ice_aq_get_cgu_dpll_status(hw, dpll_idx, &hw_ref_state,
+ &hw_dpll_state, &hw_phase_offset,
+ &hw_eec_mode);
+ if (status) {
+ *dpll_state = ICE_CGU_STATE_INVALID;
+ return status;
+ }
+
+ if (pin) {
+ /* current ref pin in dpll_state_refsel_status_X register */
+ *pin = (hw_dpll_state &
+ ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SEL) >>
+ ICE_AQC_GET_CGU_DPLL_STATUS_STATE_CLK_REF_SHIFT;
+ }
+
+ if (phase_offset)
+ *phase_offset = hw_phase_offset;
+
+ if (ref_state)
+ *ref_state = hw_ref_state;
+
+ if (eec_mode)
+ *eec_mode = hw_eec_mode;
+
+ if (!dpll_state)
+ return status;
+
+ /* According to ZL DPLL documentation, once state reach LOCKED_HO_ACQ
+ * it would never return to FREERUN. This aligns to ITU-T G.781
+ * Recommendation. We cannot report HOLDOVER as HO memory is cleared
+ * while switching to another reference.
+ * Only for situations where previous state was either: "LOCKED without
+ * HO_ACQ" or "HOLDOVER" we actually back to FREERUN.
+ */
+ if (hw_dpll_state & ICE_AQC_GET_CGU_DPLL_STATUS_STATE_LOCK) {
+ if (hw_dpll_state & ICE_AQC_GET_CGU_DPLL_STATUS_STATE_HO_READY)
+ *dpll_state = ICE_CGU_STATE_LOCKED_HO_ACQ;
+ else
+ *dpll_state = ICE_CGU_STATE_LOCKED;
+ } else if (last_dpll_state == ICE_CGU_STATE_LOCKED_HO_ACQ ||
+ last_dpll_state == ICE_CGU_STATE_HOLDOVER) {
+ *dpll_state = ICE_CGU_STATE_HOLDOVER;
+ } else {
+ *dpll_state = ICE_CGU_STATE_FREERUN;
+ }
+
+ return status;
+}
+
+/**
+ * ice_get_cgu_rclk_pin_info - get info on available recovered clock pins
+ * @hw: pointer to the hw struct
+ * @base_idx: returns index of first recovered clock pin on device
+ * @pin_num: returns number of recovered clock pins available on device
+ *
+ * Based on hw provide caller info about recovery clock pins available on the
+ * board.
+ *
+ * Return:
+ * * 0 - success, information is valid
+ * * negative - failure, information is not valid
+ */
+int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num)
+{
+ u8 phy_idx;
+ int ret;
+
+ switch (hw->device_id) {
+ case ICE_DEV_ID_E810C_SFP:
+ case ICE_DEV_ID_E810C_QSFP:
+
+ ret = ice_get_pf_c827_idx(hw, &phy_idx);
+ if (ret)
+ return ret;
+ *base_idx = E810T_CGU_INPUT_C827(phy_idx, ICE_RCLKA_PIN);
+ *pin_num = ICE_E810_RCLK_PINS_NUM;
+ ret = 0;
+ break;
+ case ICE_DEV_ID_E823L_10G_BASE_T:
+ case ICE_DEV_ID_E823L_1GBE:
+ case ICE_DEV_ID_E823L_BACKPLANE:
+ case ICE_DEV_ID_E823L_QSFP:
+ case ICE_DEV_ID_E823L_SFP:
+ case ICE_DEV_ID_E823C_10G_BASE_T:
+ case ICE_DEV_ID_E823C_BACKPLANE:
+ case ICE_DEV_ID_E823C_QSFP:
+ case ICE_DEV_ID_E823C_SFP:
+ case ICE_DEV_ID_E823C_SGMII:
+ *pin_num = ICE_E822_RCLK_PINS_NUM;
+ ret = 0;
+ if (hw->cgu_part_number ==
+ ICE_ACQ_GET_LINK_TOPO_NODE_NR_ZL30632_80032)
+ *base_idx = ZL_REF1P;
+ else if (hw->cgu_part_number ==
+ ICE_ACQ_GET_LINK_TOPO_NODE_NR_SI5383_5384)
+ *base_idx = SI_REF1P;
+ else
+ ret = -ENODEV;
+
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
+ return ret;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index 3b68cb91bd81..129501467c71 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -3,6 +3,7 @@

#ifndef _ICE_PTP_HW_H_
#define _ICE_PTP_HW_H_
+#include <linux/dpll.h>

enum ice_ptp_tmr_cmd {
INIT_TIME,
@@ -109,6 +110,222 @@ struct ice_cgu_pll_params_e822 {
u32 post_pll_div;
};

+#define E810C_QSFP_C827_0_HANDLE 2
+#define E810C_QSFP_C827_1_HANDLE 3
+enum ice_e810_c827_idx {
+ C827_0,
+ C827_1
+};
+
+enum ice_phy_rclk_pins {
+ ICE_RCLKA_PIN = 0, /* SCL pin */
+ ICE_RCLKB_PIN, /* SDA pin */
+};
+
+#define ICE_E810_RCLK_PINS_NUM (ICE_RCLKB_PIN + 1)
+#define ICE_E822_RCLK_PINS_NUM (ICE_RCLKA_PIN + 1)
+#define E810T_CGU_INPUT_C827(_phy, _pin) ((_phy) * ICE_E810_RCLK_PINS_NUM + \
+ (_pin) + ZL_REF1P)
+enum ice_cgu_state {
+ ICE_CGU_STATE_UNKNOWN = -1,
+ ICE_CGU_STATE_INVALID, /* state is not valid */
+ ICE_CGU_STATE_FREERUN, /* clock is free-running */
+ ICE_CGU_STATE_LOCKED, /* clock is locked to the reference,
+ * but the holdover memory is not valid
+ */
+ ICE_CGU_STATE_LOCKED_HO_ACQ, /* clock is locked to the reference
+ * and holdover memory is valid
+ */
+ ICE_CGU_STATE_HOLDOVER, /* clock is in holdover mode */
+ ICE_CGU_STATE_MAX
+};
+
+#define MAX_CGU_STATE_NAME_LEN 14
+struct ice_cgu_state_desc {
+ char name[MAX_CGU_STATE_NAME_LEN];
+ enum ice_cgu_state state;
+};
+
+enum ice_zl_cgu_in_pins {
+ ZL_REF0P = 0,
+ ZL_REF0N,
+ ZL_REF1P,
+ ZL_REF1N,
+ ZL_REF2P,
+ ZL_REF2N,
+ ZL_REF3P,
+ ZL_REF3N,
+ ZL_REF4P,
+ ZL_REF4N,
+ NUM_ZL_CGU_INPUT_PINS
+};
+
+enum ice_zl_cgu_out_pins {
+ ZL_OUT0 = 0,
+ ZL_OUT1,
+ ZL_OUT2,
+ ZL_OUT3,
+ ZL_OUT4,
+ ZL_OUT5,
+ ZL_OUT6,
+ NUM_ZL_CGU_OUTPUT_PINS
+};
+
+enum ice_si_cgu_in_pins {
+ SI_REF0P = 0,
+ SI_REF0N,
+ SI_REF1P,
+ SI_REF1N,
+ SI_REF2P,
+ SI_REF2N,
+ SI_REF3,
+ SI_REF4,
+ NUM_SI_CGU_INPUT_PINS
+};
+
+enum ice_si_cgu_out_pins {
+ SI_OUT0 = 0,
+ SI_OUT1,
+ SI_OUT2,
+ SI_OUT3,
+ SI_OUT4,
+ NUM_SI_CGU_OUTPUT_PINS
+};
+
+static struct dpll_pin_frequency ice_cgu_pin_freq_common[] = {
+ DPLL_PIN_FREQUENCY_1PPS,
+ DPLL_PIN_FREQUENCY_10MHZ,
+};
+
+static struct dpll_pin_frequency ice_cgu_pin_freq_1_hz[] = {
+ DPLL_PIN_FREQUENCY_1PPS,
+};
+
+static struct dpll_pin_frequency ice_cgu_pin_freq_10_mhz[] = {
+ DPLL_PIN_FREQUENCY_10MHZ,
+};
+
+struct ice_cgu_pin_desc {
+ char *name;
+ u8 index;
+ enum dpll_pin_type type;
+ u32 freq_supp_num;
+ struct dpll_pin_frequency *freq_supp;
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_inputs[] = {
+ { "CVL-SDP22", ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "CVL-SDP20", ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, 0, },
+ { "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, 0, },
+ { "SMA1", ZL_REF3P, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "SMA2/U.FL2", ZL_REF3N, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "GNSS-1PPS", ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+ ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+ { "OCXO", ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0, },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_inputs[] = {
+ { "CVL-SDP22", ZL_REF0P, DPLL_PIN_TYPE_INT_OSCILLATOR,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "CVL-SDP20", ZL_REF0N, DPLL_PIN_TYPE_INT_OSCILLATOR,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "C827_0-RCLKA", ZL_REF1P, DPLL_PIN_TYPE_MUX, },
+ { "C827_0-RCLKB", ZL_REF1N, DPLL_PIN_TYPE_MUX, },
+ { "C827_1-RCLKA", ZL_REF2P, DPLL_PIN_TYPE_MUX, },
+ { "C827_1-RCLKB", ZL_REF2N, DPLL_PIN_TYPE_MUX, },
+ { "SMA1", ZL_REF3P, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "SMA2/U.FL2", ZL_REF3N, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "GNSS-1PPS", ZL_REF4P, DPLL_PIN_TYPE_GNSS,
+ ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+ { "OCXO", ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_sfp_cgu_outputs[] = {
+ { "REF-SMA1", ZL_OUT0, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "PHY-CLK", ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
+ { "MAC-CLK", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, },
+ { "CVL-SDP21", ZL_OUT4, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+ { "CVL-SDP23", ZL_OUT5, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+};
+
+static const struct ice_cgu_pin_desc ice_e810t_qsfp_cgu_outputs[] = {
+ { "REF-SMA1", ZL_OUT0, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "REF-SMA2/U.FL2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "PHY-CLK", ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+ { "PHY2-CLK", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+ { "MAC-CLK", ZL_OUT4, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+ { "CVL-SDP21", ZL_OUT5, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+ { "CVL-SDP23", ZL_OUT6, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_inputs[] = {
+ { "NONE", SI_REF0P, 0, 0 },
+ { "NONE", SI_REF0N, 0, 0 },
+ { "SYNCE0_DP", SI_REF1P, DPLL_PIN_TYPE_MUX, 0 },
+ { "SYNCE0_DN", SI_REF1N, DPLL_PIN_TYPE_MUX, 0 },
+ { "EXT_CLK_SYNC", SI_REF2P, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "NONE", SI_REF2N, 0, 0 },
+ { "EXT_PPS_OUT", SI_REF3, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "INT_PPS_OUT", SI_REF4, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_si_cgu_outputs[] = {
+ { "1588-TIME_SYNC", SI_OUT0, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "PHY-CLK", SI_OUT1, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+ { "10MHZ-SMA2", SI_OUT2, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
+ { "PPS-SMA1", SI_OUT3, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_inputs[] = {
+ { "NONE", ZL_REF0P, 0, 0 },
+ { "INT_PPS_OUT", ZL_REF0N, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+ { "SYNCE0_DP", ZL_REF1P, DPLL_PIN_TYPE_MUX, 0 },
+ { "SYNCE0_DN", ZL_REF1N, DPLL_PIN_TYPE_MUX, 0 },
+ { "NONE", ZL_REF2P, 0, 0 },
+ { "NONE", ZL_REF2N, 0, 0 },
+ { "EXT_CLK_SYNC", ZL_REF3P, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "NONE", ZL_REF3N, 0, 0 },
+ { "EXT_PPS_OUT", ZL_REF4P, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+ { "OCXO", ZL_REF4N, DPLL_PIN_TYPE_INT_OSCILLATOR, 0 },
+};
+
+static const struct ice_cgu_pin_desc ice_e823_zl_cgu_outputs[] = {
+ { "PPS-SMA1", ZL_OUT0, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_1_hz), ice_cgu_pin_freq_1_hz },
+ { "10MHZ-SMA2", ZL_OUT1, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_10_mhz), ice_cgu_pin_freq_10_mhz },
+ { "PHY-CLK", ZL_OUT2, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+ { "1588-TIME_REF", ZL_OUT3, DPLL_PIN_TYPE_SYNCE_ETH_PORT, 0 },
+ { "CPK-TIME_SYNC", ZL_OUT4, DPLL_PIN_TYPE_EXT,
+ ARRAY_SIZE(ice_cgu_pin_freq_common), ice_cgu_pin_freq_common },
+ { "NONE", ZL_OUT5, 0, 0 },
+};
+
extern const struct
ice_cgu_pll_params_e822 e822_cgu_params[NUM_ICE_TIME_REF_FREQ];

@@ -197,6 +414,19 @@ int ice_read_sma_ctrl_e810t(struct ice_hw *hw, u8 *data);
int ice_write_sma_ctrl_e810t(struct ice_hw *hw, u8 data);
int ice_read_pca9575_reg_e810t(struct ice_hw *hw, u8 offset, u8 *data);
bool ice_is_pca9575_present(struct ice_hw *hw);
+bool ice_is_phy_rclk_present(struct ice_hw *hw);
+bool ice_is_clock_mux_present_e810t(struct ice_hw *hw);
+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx);
+bool ice_is_cgu_present(struct ice_hw *hw);
+enum dpll_pin_type ice_cgu_get_pin_type(struct ice_hw *hw, u8 pin, bool input);
+struct dpll_pin_frequency *
+ice_cgu_get_pin_freq_supp(struct ice_hw *hw, u8 pin, bool input, u8 *num);
+const char *ice_cgu_get_pin_name(struct ice_hw *hw, u8 pin, bool input);
+int ice_get_cgu_state(struct ice_hw *hw, u8 dpll_idx,
+ enum ice_cgu_state last_dpll_state, u8 *pin,
+ u8 *ref_state, u8 *eec_mode, s64 *phase_offset,
+ enum ice_cgu_state *dpll_state);
+int ice_get_cgu_rclk_pin_info(struct ice_hw *hw, u8 *base_idx, u8 *pin_num);

#define PFTSYN_SEM_BYTES 4

diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index a09556e57803..128bc4d326f9 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -963,6 +963,7 @@ struct ice_hw {
DECLARE_BITMAP(hw_ptype, ICE_FLOW_PTYPE_MAX);
u8 dvm_ena;
u16 io_expander_handle;
+ u8 cgu_part_number;
};

/* Statistics collected by each port, VSI, VEB, and S-channel */
--
2.37.3


2023-06-09 12:32:34

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

Control over clock generation unit is required for further development
of Synchronous Ethernet feature. Interface provides ability to obtain
current state of a dpll, its sources and outputs which are pins, and
allows their configuration.

Co-developed-by: Milena Olech <[email protected]>
Signed-off-by: Milena Olech <[email protected]>
Co-developed-by: Michal Michalik <[email protected]>
Signed-off-by: Michal Michalik <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
drivers/net/ethernet/intel/Kconfig | 1 +
drivers/net/ethernet/intel/ice/Makefile | 3 +-
drivers/net/ethernet/intel/ice/ice.h | 4 +
drivers/net/ethernet/intel/ice/ice_dpll.c | 2015 +++++++++++++++++++++
drivers/net/ethernet/intel/ice/ice_dpll.h | 102 ++
drivers/net/ethernet/intel/ice/ice_main.c | 7 +
6 files changed, 2131 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h

diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 9bc0a9519899..913dcf928d15 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -284,6 +284,7 @@ config ICE
select DIMLIB
select NET_DEVLINK
select PLDMFW
+ select DPLL
help
This driver supports Intel(R) Ethernet Connection E800 Series of
devices. For more information on how to identify your adapter, go
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index 817977e3039d..85d6366d1f5b 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -34,7 +34,8 @@ ice-y := ice_main.o \
ice_lag.o \
ice_ethtool.o \
ice_repr.o \
- ice_tc_lib.o
+ ice_tc_lib.o \
+ ice_dpll.o
ice-$(CONFIG_PCI_IOV) += \
ice_sriov.o \
ice_virtchnl.o \
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index ae58d7499955..8a110272a799 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -76,6 +76,7 @@
#include "ice_vsi_vlan_ops.h"
#include "ice_gnss.h"
#include "ice_irq.h"
+#include "ice_dpll.h"

#define ICE_BAR0 0
#define ICE_REQ_DESC_MULTIPLE 32
@@ -198,6 +199,7 @@
enum ice_feature {
ICE_F_DSCP,
ICE_F_PTP_EXTTS,
+ ICE_F_PHY_RCLK,
ICE_F_SMA_CTRL,
ICE_F_CGU,
ICE_F_GNSS,
@@ -506,6 +508,7 @@ enum ice_pf_flags {
ICE_FLAG_UNPLUG_AUX_DEV,
ICE_FLAG_MTU_CHANGED,
ICE_FLAG_GNSS, /* GNSS successfully initialized */
+ ICE_FLAG_DPLL, /* SyncE/PTP dplls initialized */
ICE_PF_FLAGS_NBITS /* must be last */
};

@@ -628,6 +631,7 @@ struct ice_pf {
#define ICE_VF_AGG_NODE_ID_START 65
#define ICE_MAX_VF_AGG_NODES 32
struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
+ struct ice_dplls dplls;
};

struct ice_netdev_priv {
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
new file mode 100644
index 000000000000..22a69197188a
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -0,0 +1,2015 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2022, Intel Corporation. */
+
+#include "ice.h"
+#include "ice_lib.h"
+#include "ice_trace.h"
+#include <linux/dpll.h>
+
+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD 50
+#define ICE_DPLL_LOCK_TRIES 1000
+#define ICE_DPLL_PIN_IDX_INVALID 0xff
+#define ICE_DPLL_RCLK_NUM_PER_PF 1
+
+/**
+ * dpll_lock_status - map ice cgu states into dpll's subsystem lock status
+ */
+static const enum dpll_lock_status
+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
+ [ICE_CGU_STATE_INVALID] = 0,
+ [ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
+ [ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_LOCKED,
+ [ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED_HO_ACQ,
+ [ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
+};
+
+/**
+ * ice_dpll_pin_type - enumerate ice pin types
+ */
+enum ice_dpll_pin_type {
+ ICE_DPLL_PIN_INVALID = 0,
+ ICE_DPLL_PIN_TYPE_INPUT,
+ ICE_DPLL_PIN_TYPE_OUTPUT,
+ ICE_DPLL_PIN_TYPE_RCLK_INPUT,
+};
+
+/**
+ * pin_type_name - string names of ice pin types
+ */
+static const char * const pin_type_name[] = {
+ [ICE_DPLL_PIN_TYPE_INPUT] = "input",
+ [ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
+ [ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
+};
+
+/**
+ * ice_dpll_cb_lock - lock dplls mutex in callback context
+ * @pf: private board structure
+ *
+ * Lock the mutex from the callback operations invoked by dpll subsystem.
+ * Prevent dead lock caused by `rmmod ice` when dpll callbacks are under stress
+ * tests.
+ *
+ * Return:
+ * 0 - if lock acquired
+ * negative - lock not acquired or dpll was deinitialized
+ */
+static int ice_dpll_cb_lock(struct ice_pf *pf)
+{
+ int i;
+
+ for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
+ if (mutex_trylock(&pf->dplls.lock))
+ return 0;
+ usleep_range(100, 150);
+ if (!test_bit(ICE_FLAG_DPLL, pf->flags))
+ return -EFAULT;
+ }
+
+ return -EBUSY;
+}
+
+/**
+ * ice_dpll_cb_unlock - unlock dplls mutex in callback context
+ * @pf: private board structure
+ *
+ * Unlock the mutex from the callback operations invoked by dpll subsystem.
+ */
+static void ice_dpll_cb_unlock(struct ice_pf *pf)
+{
+ mutex_unlock(&pf->dplls.lock);
+}
+
+/**
+ * ice_dpll_pin_freq_set - set pin's frequency
+ * @pf: private board structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being configured
+ * @freq: frequency to be set
+ *
+ * Set requested frequency on a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error on AQ or wrong pin type given
+ */
+static int
+ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
+ enum ice_dpll_pin_type pin_type, const u32 freq)
+{
+ int ret = -EINVAL;
+ u8 flags;
+
+ switch (pin_type) {
+ case ICE_DPLL_PIN_TYPE_INPUT:
+ flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
+ ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
+ pin->flags[0], freq, 0);
+ break;
+ case ICE_DPLL_PIN_TYPE_OUTPUT:
+ flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
+ ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
+ 0, freq, 0);
+ break;
+ default:
+ break;
+ }
+ if (ret) {
+ dev_err(ice_pf_to_dev(pf),
+ "err:%d %s failed to set pin freq:%u on pin:%u\n",
+ ret, ice_aq_str(pf->hw.adminq.sq_last_status),
+ freq, pin->idx);
+ return ret;
+ }
+ pin->freq = freq;
+
+ return 0;
+}
+
+/**
+ * ice_dpll_frequency_set - wrapper for pin callback for set frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ * @pin_type: type of pin being configured
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ const u32 frequency,
+ struct netlink_ext_ack *extack,
+ enum ice_dpll_pin_type pin_type)
+{
+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
+ struct ice_dpll_pin *p = pin_priv;
+ int ret = -EINVAL;
+
+ ret = ice_dpll_cb_lock(pf);
+ if (ret)
+ return ret;
+ ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency);
+ ice_dpll_cb_unlock(pf);
+ if (ret)
+ NL_SET_ERR_MSG(extack, "frequency was not set");
+
+ return ret;
+}
+
+/**
+ * ice_dpll_input_frequency_set - input pin callback for set frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u64 frequency, struct netlink_ext_ack *extack)
+{
+ return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
+ extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_output_frequency_set - output pin callback for set frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: frequency to be set
+ * @extack: error reporting
+ *
+ * Wraps internal set frequency command on a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't set in hw
+ */
+static int
+ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u64 frequency, struct netlink_ext_ack *extack)
+{
+ return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_frequency_get - wrapper for pin callback for get frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ * @pin_type: type of pin being configured
+ *
+ * Wraps internal get frequency command of a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u64 *frequency, struct netlink_ext_ack *extack,
+ enum ice_dpll_pin_type pin_type)
+{
+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
+ struct ice_dpll_pin *p = pin_priv;
+ int ret = -EINVAL;
+
+ ret = ice_dpll_cb_lock(pf);
+ if (ret)
+ return ret;
+ *frequency = p->freq;
+ ice_dpll_cb_unlock(pf);
+
+ return 0;
+}
+
+/**
+ * ice_dpll_input_frequency_get - input pin callback for get frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ *
+ * Wraps internal get frequency command of a input pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u64 *frequency, struct netlink_ext_ack *extack)
+{
+ return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
+ extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_output_frequency_get - output pin callback for get frequency
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: pointer to dpll
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @frequency: on success holds pin's frequency
+ * @extack: error reporting
+ *
+ * Wraps internal get frequency command of a pin.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - error pin not found or couldn't get from hw
+ */
+static int
+ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u64 *frequency, struct netlink_ext_ack *extack)
+{
+ return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_pin_enable - enable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being enabled
+ *
+ * Enable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+ enum ice_dpll_pin_type pin_type)
+{
+ int ret = -EINVAL;
+ u8 flags = 0;
+
+ switch (pin_type) {
+ case ICE_DPLL_PIN_TYPE_INPUT:
+ if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
+ flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
+ flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
+ ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+ break;
+ case ICE_DPLL_PIN_TYPE_OUTPUT:
+ if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
+ flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
+ flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
+ ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+ break;
+ default:
+ break;
+ }
+ if (ret)
+ dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+ "err:%d %s failed to enable %s pin:%u\n",
+ ret, ice_aq_str(hw->adminq.sq_last_status),
+ pin_type_name[pin_type], pin->idx);
+
+ return ret;
+}
+
+/**
+ * ice_dpll_pin_disable - disable a pin on dplls
+ * @hw: board private hw structure
+ * @pin: pointer to a pin
+ * @pin_type: type of pin being disabled
+ *
+ * Disable a pin on both dplls. Store current state in pin->flags.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+static int
+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
+ enum ice_dpll_pin_type pin_type)
+{
+ int ret = -EINVAL;
+ u8 flags = 0;
+
+ switch (pin_type) {
+ case ICE_DPLL_PIN_TYPE_INPUT:
+ if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
+ flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
+ ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
+ break;
+ case ICE_DPLL_PIN_TYPE_OUTPUT:
+ if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
+ flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
+ ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
+ break;
+ default:
+ break;
+ }
+ if (ret)
+ dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
+ "err:%d %s failed to disable %s pin:%u\n",
+ ret, ice_aq_str(hw->adminq.sq_last_status),
+ pin_type_name[pin_type], pin->idx);
+
+ return ret;
+}
+
+/**
+ * ice_dpll_pin_state_update - update pin's state
+ * @hw: private board struct
+ * @pin: structure with pin attributes to be updated
+ * @pin_type: type of pin being updated
+ *
+ * Determine pin current state and frequency, then update struct
+ * holding the pin info. For input pin states are separated for each
+ * dpll, for rclk pins states are separated for each parent.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - OK
+ * * negative - error
+ */
+int
+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
+ enum ice_dpll_pin_type pin_type)
+{
+ int ret = -EINVAL;
+
+ switch (pin_type) {
+ case ICE_DPLL_PIN_TYPE_INPUT:
+ ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
+ NULL, &pin->flags[0],
+ &pin->freq, NULL);
+ if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
+ if (pin->pin) {
+ pin->state[pf->dplls.eec.dpll_idx] =
+ pin->pin == pf->dplls.eec.active_input ?
+ DPLL_PIN_STATE_CONNECTED :
+ DPLL_PIN_STATE_SELECTABLE;
+ pin->state[pf->dplls.pps.dpll_idx] =
+ pin->pin == pf->dplls.pps.active_input ?
+ DPLL_PIN_STATE_CONNECTED :
+ DPLL_PIN_STATE_SELECTABLE;
+ } else {
+ pin->state[pf->dplls.eec.dpll_idx] =
+ DPLL_PIN_STATE_SELECTABLE;
+ pin->state[pf->dplls.pps.dpll_idx] =
+ DPLL_PIN_STATE_SELECTABLE;
+ }
+ } else {
+ pin->state[pf->dplls.eec.dpll_idx] =
+ DPLL_PIN_STATE_DISCONNECTED;
+ pin->state[pf->dplls.pps.dpll_idx] =
+ DPLL_PIN_STATE_DISCONNECTED;
+ }
+ break;
+ case ICE_DPLL_PIN_TYPE_OUTPUT:
+ ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
+ &pin->flags[0], NULL,
+ &pin->freq, NULL);
+ if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
+ pin->state[0] = DPLL_PIN_STATE_CONNECTED;
+ else
+ pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
+ break;
+ case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
+ u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
+
+ for (parent = 0; parent < pf->dplls.rclk.num_parents;
+ parent++) {
+ ret = ice_aq_get_phy_rec_clk_out(&pf->hw, parent,
+ &port_num,
+ &pin->flags[parent],
+ &pin->freq);
+ if (ret)
+ return ret;
+ if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
+ pin->flags[parent])
+ pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
+ else
+ pin->state[parent] =
+ DPLL_PIN_STATE_DISCONNECTED;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * ice_find_dpll - find ice_dpll on a pf
+ * @pf: private board structure
+ * @dpll: kernel's dpll_device pointer to be searched
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * pointer if ice_dpll with given device dpll pointer is found
+ * * NULL if not found
+ */
+static struct ice_dpll
+*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
+{
+ if (!pf || !dpll)
+ return NULL;
+
+ return dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
+ dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL;
+}
+
+/**
+ * ice_dpll_hw_input_prio_set - set input priority value in hardware
+ * @pf: board private structure
+ * @dpll: ice dpll pointer
+ * @pin: ice pin pointer
+ * @prio: priority value being set on a dpll
+ *
+ * Internal wrapper for setting the priority in the hardware.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
+ struct ice_dpll_pin *pin, const u32 prio)
+{
+ int ret;
+
+ ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
+ (u8)prio);
+ if (ret)
+ dev_err(ice_pf_to_dev(pf),
+ "err:%d %s failed to set pin prio:%u on pin:%u\n",
+ ret, ice_aq_str(pf->hw.adminq.sq_last_status),
+ prio, pin->idx);
+ else
+ dpll->input_prio[pin->idx] = prio;
+
+ return ret;
+}
+
+/**
+ * ice_dpll_lock_status_get - get dpll lock status callback
+ * @dpll: registered dpll pointer
+ * @status: on success holds dpll's lock status
+ *
+ * Dpll subsystem callback, provides dpll's lock status.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_lock_status_get(const struct dpll_device *dpll, void *priv,
+ enum dpll_lock_status *status,
+ struct netlink_ext_ack *extack)
+{
+ struct ice_dpll *d = priv;
+ struct ice_pf *pf = d->pf;
+ int ret = -EINVAL;
+
+ ret = ice_dpll_cb_lock(pf);
+ if (ret)
+ return ret;
+ *status = ice_dpll_status[d->dpll_state];
+ ice_dpll_cb_unlock(pf);
+ dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p, ret:%d\n", __func__,
+ dpll, pf, ret);
+
+ return ret;
+}
+
+/**
+ * ice_dpll_mode_get - get dpll's working mode
+ * @dpll: registered dpll pointer
+ * @priv: private data pointer passed on dpll registration
+ * @mode: on success holds current working mode of dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Provides working mode of dpll.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
+ enum dpll_mode *mode,
+ struct netlink_ext_ack *extack)
+{
+ *mode = DPLL_MODE_AUTOMATIC;
+
+ return 0;
+}
+
+/**
+ * ice_dpll_mode_get - check if dpll's working mode is supported
+ * @dpll: registered dpll pointer
+ * @priv: private data pointer passed on dpll registration
+ * @mode: mode to be checked for support
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Provides information if working mode is supported
+ * by dpll.
+ *
+ * Return:
+ * * true - mode is supported
+ * * false - mode is not supported
+ */
+static bool ice_dpll_mode_supported(const struct dpll_device *dpll, void *priv,
+ enum dpll_mode mode,
+ struct netlink_ext_ack *extack)
+{
+ if (mode == DPLL_MODE_AUTOMATIC)
+ return true;
+
+ return false;
+}
+
+/**
+ * ice_dpll_pin_state_set - set pin's state on dpll
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: state of pin to be set
+ * @extack: error reporting
+ * @pin_type: type of a pin
+ *
+ * Set pin state on a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - OK or no change required
+ * * negative - error
+ */
+static int
+ice_dpll_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ bool enable, struct netlink_ext_ack *extack,
+ enum ice_dpll_pin_type pin_type)
+{
+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
+ struct ice_dpll_pin *p = pin_priv;
+ int ret = -EINVAL;
+
+ ret = ice_dpll_cb_lock(pf);
+ if (ret)
+ return ret;
+ if (enable)
+ ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
+ else
+ ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
+ if (!ret)
+ ret = ice_dpll_pin_state_update(pf, p, pin_type);
+ ice_dpll_cb_unlock(pf);
+ if (ret)
+ dev_err(ice_pf_to_dev(pf),
+ "%s: dpll:%p, pin:%p, p:%p pf:%p enable:%d ret:%d\n",
+ __func__, dpll, pin, p, pf, enable, ret);
+
+ return ret;
+}
+
+/**
+ * ice_dpll_output_state_set - enable/disable output pin on dpll device
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: dpll being configured
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: state of pin to be set
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Set given state on output type pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int
+ice_dpll_output_state_set(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
+
+ return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_input_state_set - enable/disable input pin on dpll levice
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: dpll being configured
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: state of pin to be set
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Enables given mode on input type pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - successfully enabled mode
+ * * negative - failed to enable mode
+ */
+static int
+ice_dpll_input_state_set(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ bool enable = state == DPLL_PIN_STATE_SELECTABLE ? true : false;
+
+ return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
+ extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_pin_state_get - set pin's state on dpll
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ * @pin_type: type of questioned pin
+ *
+ * Determine pin state set it on a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_pin_state_get(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack,
+ enum ice_dpll_pin_type pin_type)
+{
+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
+ struct ice_dpll_pin *p = pin_priv;
+ struct ice_dpll *d;
+ int ret = -EINVAL;
+
+ ret = ice_dpll_cb_lock(pf);
+ if (ret)
+ return ret;
+ d = ice_find_dpll(pf, dpll);
+ if (!d)
+ goto unlock;
+ ret = ice_dpll_pin_state_update(pf, p, pin_type);
+ if (ret)
+ goto unlock;
+ if (pin_type == ICE_DPLL_PIN_TYPE_INPUT)
+ *state = p->state[d->dpll_idx];
+ else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
+ *state = p->state[0];
+ ret = 0;
+unlock:
+ ice_dpll_cb_unlock(pf);
+ if (ret)
+ dev_err(ice_pf_to_dev(pf),
+ "%s: dpll:%p, pin:%p, pf:%p state: %d ret:%d\n",
+ __func__, dpll, pin, pf, *state, ret);
+
+ return ret;
+}
+
+/**
+ * ice_dpll_output_state_get - get output pin state on dpll device
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Check state of a pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_output_state_get(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack)
+{
+ return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
+}
+
+/**
+ * ice_dpll_input_state_get - get input pin state on dpll device
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @state: on success holds state of the pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Check state of a input pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failed to get state
+ */
+static int
+ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack)
+{
+ return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
+ extack, ICE_DPLL_PIN_TYPE_INPUT);
+}
+
+/**
+ * ice_dpll_input_prio_get - get dpll's input prio
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @prio: on success - returns input priority on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting priority of a input pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_input_prio_get(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u32 *prio, struct netlink_ext_ack *extack)
+{
+ struct ice_dpll_pin *p = pin_priv;
+ struct ice_dpll *d = dpll_priv;
+ struct ice_pf *pf = d->pf;
+ int ret = -EINVAL;
+
+ ret = ice_dpll_cb_lock(pf);
+ if (ret)
+ return ret;
+ *prio = d->input_prio[p->idx];
+ ice_dpll_cb_unlock(pf);
+ dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
+ __func__, dpll, pin, pf, ret);
+
+ return 0;
+}
+
+/**
+ * ice_dpll_input_prio_set - set dpll input prio
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @prio: input priority to be set on dpll
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for setting priority of a input pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_input_prio_set(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ u32 prio, struct netlink_ext_ack *extack)
+{
+ struct ice_dpll_pin *p = pin_priv;
+ struct ice_dpll *d = dpll_priv;
+ struct ice_pf *pf = d->pf;
+ int ret = -EINVAL;
+
+ if (prio > ICE_DPLL_PRIO_MAX) {
+ NL_SET_ERR_MSG_FMT(extack, "prio out of supported range 0-%d",
+ ICE_DPLL_PRIO_MAX);
+ return ret;
+ }
+
+ ret = ice_dpll_cb_lock(pf);
+ if (ret)
+ return ret;
+ ret = ice_dpll_hw_input_prio_set(pf, d, p, prio);
+ if (ret)
+ NL_SET_ERR_MSG_FMT(extack, "unable to set prio: %u", prio);
+ ice_dpll_cb_unlock(pf);
+ dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
+ __func__, dpll, pin, pf, ret);
+
+ return ret;
+}
+
+/**
+ * ice_dpll_input_direction - callback for get input pin direction
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @direction: holds input pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting direction of a input pin.
+ *
+ * Return:
+ * * 0 - success
+ */
+static int
+ice_dpll_input_direction(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_pin_direction *direction,
+ struct netlink_ext_ack *extack)
+{
+ *direction = DPLL_PIN_DIRECTION_INPUT;
+
+ return 0;
+}
+
+/**
+ * ice_dpll_output_direction - callback for get output pin direction
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @dpll: registered dpll pointer
+ * @dpll_priv: private data pointer passed on dpll registration
+ * @direction: holds output pin direction
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback. Handler for getting direction of an output pin.
+ *
+ * Return:
+ * * 0 - success
+ */
+static int
+ice_dpll_output_direction(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_device *dpll, void *dpll_priv,
+ enum dpll_pin_direction *direction,
+ struct netlink_ext_ack *extack)
+{
+ *direction = DPLL_PIN_DIRECTION_OUTPUT;
+
+ return 0;
+}
+
+/**
+ * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
+ * @dpll: registered dpll pointer
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @parent_pin: pin parent pointer
+ * @state: state to be set on pin
+ * @extack: error reporting
+ *
+ * Dpll subsystem callback, set a state of a rclk pin on a parent pin
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_pin *parent_pin,
+ void *parent_pin_priv,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
+ struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
+ struct ice_pf *pf = p->pf;
+ int ret = -EINVAL;
+ u32 hw_idx;
+
+ ret = ice_dpll_cb_lock(pf);
+ if (ret)
+ return ret;
+ hw_idx = parent->idx - pf->dplls.base_rclk_idx;
+ if (hw_idx >= pf->dplls.num_inputs)
+ goto unlock;
+
+ if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
+ (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ ret = ice_aq_set_phy_rec_clk_out(&pf->hw, hw_idx, enable,
+ &p->freq);
+unlock:
+ ice_dpll_cb_unlock(pf);
+ dev_dbg(ice_pf_to_dev(pf),
+ "%s: parent:%p, pin:%p, pf:%p hw_idx:%u enable:%d ret:%d\n",
+ __func__, parent_pin, pin, pf, hw_idx, enable, ret);
+
+ return ret;
+}
+
+/**
+ * ice_dpll_rclk_state_on_pin_get - get a state of rclk pin
+ * @pin: pointer to a pin
+ * @pin_priv: private data pointer passed on pin registration
+ * @parent_pin: pin parent pointer
+ * @state: on success holds pin state on parent pin
+ * @extack: error reporting
+ *
+ * dpll subsystem callback, get a state of a recovered clock pin.
+ *
+ * Context: Acquires pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - failure
+ */
+static int
+ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
+ const struct dpll_pin *parent_pin,
+ void *parent_pin_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack)
+{
+ struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
+ struct ice_pf *pf = p->pf;
+ int ret = -EFAULT;
+ u32 hw_idx;
+
+ ret = ice_dpll_cb_lock(pf);
+ if (ret)
+ return ret;
+ hw_idx = parent->idx - pf->dplls.base_rclk_idx;
+ if (hw_idx >= pf->dplls.num_inputs)
+ goto unlock;
+
+ ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
+ if (ret)
+ goto unlock;
+
+ *state = p->state[hw_idx];
+ ret = 0;
+unlock:
+ ice_dpll_cb_unlock(pf);
+ dev_dbg(ice_pf_to_dev(pf),
+ "%s: parent:%p, pin:%p, pf:%p hw_idx:%u state:%u ret:%d\n",
+ __func__, parent_pin, pin, pf, hw_idx, *state, ret);
+
+ return ret;
+}
+
+static const struct dpll_pin_ops ice_dpll_rclk_ops = {
+ .state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
+ .state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
+ .direction_get = ice_dpll_input_direction,
+};
+
+static const struct dpll_pin_ops ice_dpll_input_ops = {
+ .frequency_get = ice_dpll_input_frequency_get,
+ .frequency_set = ice_dpll_input_frequency_set,
+ .state_on_dpll_get = ice_dpll_input_state_get,
+ .state_on_dpll_set = ice_dpll_input_state_set,
+ .prio_get = ice_dpll_input_prio_get,
+ .prio_set = ice_dpll_input_prio_set,
+ .direction_get = ice_dpll_input_direction,
+};
+
+static const struct dpll_pin_ops ice_dpll_output_ops = {
+ .frequency_get = ice_dpll_output_frequency_get,
+ .frequency_set = ice_dpll_output_frequency_set,
+ .state_on_dpll_get = ice_dpll_output_state_get,
+ .state_on_dpll_set = ice_dpll_output_state_set,
+ .direction_get = ice_dpll_output_direction,
+};
+
+static const struct dpll_device_ops ice_dpll_ops = {
+ .lock_status_get = ice_dpll_lock_status_get,
+ .mode_get = ice_dpll_mode_get,
+ .mode_supported = ice_dpll_mode_supported,
+};
+
+/**
+ * ice_dpll_deinit_info - release memory allocated for pins info
+ * @pf: board private structure
+ *
+ * Release memory allocated for pins by ice_dpll_init_info function.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_info(struct ice_pf *pf)
+{
+ kfree(pf->dplls.inputs);
+ pf->dplls.inputs = NULL;
+ kfree(pf->dplls.outputs);
+ pf->dplls.outputs = NULL;
+ kfree(pf->dplls.eec.input_prio);
+ pf->dplls.eec.input_prio = NULL;
+ kfree(pf->dplls.pps.input_prio);
+ pf->dplls.pps.input_prio = NULL;
+}
+
+/**
+ * ice_dpll_deinit_rclk_pin - release rclk pin resources
+ * @pf: board private structure
+ *
+ * Deregister rclk pin from parent pins and release resources in dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
+{
+ struct ice_dpll_pin *rclk = &pf->dplls.rclk;
+ struct ice_vsi *vsi = ice_get_main_vsi(pf);
+ struct dpll_pin *parent;
+ int i;
+
+ for (i = 0; i < rclk->num_parents; i++) {
+ parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
+ if (!parent)
+ continue;
+ if (!IS_ERR_OR_NULL(rclk->pin))
+ dpll_pin_on_pin_unregister(parent, rclk->pin,
+ &ice_dpll_rclk_ops, rclk);
+ }
+ if (WARN_ON_ONCE(!vsi || !vsi->netdev))
+ return;
+ netdev_dpll_pin_clear(vsi->netdev);
+ dpll_pin_put(rclk->pin);
+ rclk->pin = NULL;
+}
+
+/**
+ * ice_dpll_unregister_pins - unregister pins from a dpll
+ * @dpll: dpll device pointer
+ * @pins: pointer to pins array
+ * @ops: callback ops registered with the pins
+ * @count: number of pins
+ *
+ * Unregister pins of a given array of pins from given dpll device registered in
+ * dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void
+ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
+ const struct dpll_pin_ops *ops, int count)
+{
+ struct ice_dpll_pin *p;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ p = &pins[i];
+ if (p && !IS_ERR_OR_NULL(p->pin))
+ dpll_pin_unregister(dpll, p->pin, ops, p);
+ }
+}
+
+/**
+ * ice_dpll_release_pins - release pins resources from dpll subsystem
+ * @pf: board private structure
+ * @pins: pointer to pins array
+ * @count: number of pins
+ *
+ * Release resources of given pins array in the dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
+{
+ struct ice_dpll_pin *p;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ p = &pins[i];
+ if (p && !IS_ERR_OR_NULL(p->pin)) {
+ dpll_pin_put(p->pin);
+ p->pin = NULL;
+ }
+ }
+}
+
+/**
+ * ice_dpll_get_pins - get pins from dpll subsystem
+ * @pf: board private structure
+ * @pins: pointer to pins array
+ * @start_idx: get starts from this pin idx value
+ * @count: number of pins
+ * @clock_id: clock_id of dpll device
+ *
+ * Get pins - allocate - in dpll subsystem, store them in pin field of given
+ * pins array.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - allocation failure reason
+ */
+static int
+ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
+ int start_idx, int count, u64 clock_id)
+{
+ int i, ret;
+
+ for (i = 0; i < count; i++) {
+ pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
+ &pins[i].prop);
+ if (IS_ERR(pins[i].pin)) {
+ ret = PTR_ERR(pins[i].pin);
+ goto release_pins;
+ }
+ }
+
+ return 0;
+
+release_pins:
+ ice_dpll_release_pins(pins, i);
+ return ret;
+}
+
+/**
+ * ice_dpll_register_pins - register pins with a dpll
+ * @dpll: dpll pointer to register pins with
+ * @pins: pointer to pins array
+ * @ops: callback ops registered with the pins
+ * @count: number of pins
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Register pins of a given array with given dpll in dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
+ const struct dpll_pin_ops *ops, int count)
+{
+ int ret, i;
+
+ for (i = 0; i < count; i++) {
+ ret = dpll_pin_register(dpll, pins[i].pin, ops, &pins[i]);
+ if (ret)
+ goto unregister_pins;
+ }
+
+ return 0;
+
+unregister_pins:
+ ice_dpll_unregister_pins(dpll, pins, ops, i);
+ return ret;
+}
+
+/**
+ * ice_dpll_init_direct_pins - initialize direct pins
+ * @dpll: dpll pointer to register pins with
+ * @cgu: if cgu is present and controlled by this NIC
+ * @pins: pointer to pins array
+ * @start_idx: on which index shall allocation start in dpll subsystem
+ * @count: number of pins
+ * @ops: callback ops registered with the pins
+ * @first: dpll device pointer
+ * @second: dpll device pointer
+ *
+ * Allocate directly connected pins of a given array in dpll subsystem.
+ * If cgu is owned register allocated pins with given dplls.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
+ struct ice_dpll_pin *pins, int start_idx, int count,
+ const struct dpll_pin_ops *ops,
+ struct dpll_device *first, struct dpll_device *second)
+{
+ int ret;
+
+ ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf->dplls.clock_id);
+ if (ret)
+ return ret;
+ if (cgu) {
+ ret = ice_dpll_register_pins(first, pins, ops, count);
+ if (ret)
+ goto release_pins;
+ ret = ice_dpll_register_pins(second, pins, ops, count);
+ if (ret)
+ goto unregister_first;
+ }
+
+ return 0;
+
+unregister_first:
+ ice_dpll_unregister_pins(first, pins, ops, count);
+release_pins:
+ ice_dpll_release_pins(pins, count);
+ return ret;
+}
+
+/**
+ * ice_dpll_deinit_direct_pins - deinitialize direct pins
+ * @cgu: if cgu is present and controlled by this NIC
+ * @pins: pointer to pins array
+ * @count: number of pins
+ * @ops: callback ops registered with the pins
+ * @first: dpll device pointer
+ * @second: dpll device pointer
+ *
+ * Context: Called under pf->dplls.lock
+ * If cgu is owned unregister pins from given dplls.
+ * Release pins resources to the dpll subsystem.
+ */
+static void
+ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int count,
+ const struct dpll_pin_ops *ops,
+ struct dpll_device *first,
+ struct dpll_device *second)
+{
+ if (cgu) {
+ ice_dpll_unregister_pins(first, pins, ops, count);
+ ice_dpll_unregister_pins(second, pins, ops, count);
+ }
+ ice_dpll_release_pins(pins, count);
+}
+
+/**
+ * ice_dpll_init_rclk_pins - initialize recovered clock pin
+ * @dpll: dpll pointer to register pins with
+ * @cgu: if cgu is present and controlled by this NIC
+ * @pins: pointer to pins array
+ * @start_idx: on which index shall allocation start in dpll subsystem
+ * @count: number of pins
+ * @ops: callback ops registered with the pins
+ *
+ * Allocate resource for recovered clock pin in dpll subsystem. Register the
+ * pin with the parents it has in the info. Register pin with the pf's main vsi
+ * netdev.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - registration failure reason
+ */
+static int
+ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
+ int start_idx, const struct dpll_pin_ops *ops)
+{
+ struct ice_vsi *vsi = ice_get_main_vsi(pf);
+ struct dpll_pin *parent;
+ int ret, i;
+
+ ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
+ pf->dplls.clock_id);
+ if (ret)
+ return ret;
+ for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
+ parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
+ if (!parent) {
+ ret = -ENODEV;
+ goto unregister_pins;
+ }
+ ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
+ ops, &pf->dplls.rclk);
+ if (ret)
+ goto unregister_pins;
+ }
+ if (WARN_ON((!vsi || !vsi->netdev)))
+ return -EINVAL;
+ netdev_dpll_pin_set(vsi->netdev, pf->dplls.rclk.pin);
+
+ return 0;
+
+unregister_pins:
+ while (i) {
+ parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
+ dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
+ &ice_dpll_rclk_ops, &pf->dplls.rclk);
+ }
+ ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
+ return ret;
+}
+
+/**
+ * ice_dpll_init_pins - init pins and register pins with a dplls
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Initialize directly connected pf's pins within pf's dplls in a Linux dpll
+ * subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - initialization failure reason
+ */
+static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
+{
+ u32 rclk_idx;
+ int ret;
+
+ ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
+ pf->dplls.num_inputs,
+ &ice_dpll_input_ops,
+ pf->dplls.eec.dpll, pf->dplls.pps.dpll);
+ if (ret)
+ return ret;
+ if (cgu) {
+ ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
+ pf->dplls.num_inputs,
+ pf->dplls.num_outputs,
+ &ice_dpll_output_ops,
+ pf->dplls.eec.dpll,
+ pf->dplls.pps.dpll);
+ if (ret)
+ goto deinit_inputs;
+ }
+ rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
+ ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
+ &ice_dpll_rclk_ops);
+ if (ret)
+ goto deinit_outputs;
+
+ return 0;
+deinit_outputs:
+ ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
+ pf->dplls.num_outputs,
+ &ice_dpll_output_ops, pf->dplls.pps.dpll,
+ pf->dplls.eec.dpll);
+deinit_inputs:
+ ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf->dplls.num_inputs,
+ &ice_dpll_input_ops, pf->dplls.pps.dpll,
+ pf->dplls.eec.dpll);
+ return ret;
+}
+
+/**
+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
+ * @pf: board private structure
+ *
+ * Generates unique (per board) clock_id for allocation and search of dpll
+ * devices in Linux dpll subsystem.
+ *
+ * Return: generated clock id for the board
+ */
+static u64 ice_generate_clock_id(struct ice_pf *pf)
+{
+ return pci_get_dsn(pf->pdev);
+}
+
+/**
+ * ice_dpll_init_dpll - initialize dpll device in dpll subsystem
+ * @pf: board private structure
+ * @d: dpll to be initialized
+ * @cgu: if cgu is present and controlled by this NIC
+ * @type: type of dpll being initialized
+ *
+ * Allocate dpll instance for this board in dpll subsystem, if cgu is controlled
+ * by this NIC, register dpll with the callback ops.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - initialization failure reason
+ */
+static int
+ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
+ enum dpll_type type)
+{
+ u64 clock_id = pf->dplls.clock_id;
+ int ret;
+
+ d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
+ if (IS_ERR(d->dpll)) {
+ ret = PTR_ERR(d->dpll);
+ dev_err(ice_pf_to_dev(pf),
+ "dpll_device_get failed (%p) err=%d\n", d, ret);
+ return ret;
+ }
+ d->pf = pf;
+ if (cgu) {
+ ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d);
+ if (ret) {
+ dpll_device_put(d->dpll);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_dpll_update_state - update dpll state
+ * @pf: pf private structure
+ * @d: pointer to queried dpll device
+ *
+ * Poll current state of dpll from hw and update ice_dpll struct.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - AQ failure
+ */
+static int
+ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
+{
+ struct ice_dpll_pin *p;
+ int ret;
+
+ ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
+ &d->input_idx, &d->ref_state, &d->eec_mode,
+ &d->phase_offset, &d->dpll_state);
+
+ dev_dbg(ice_pf_to_dev(pf),
+ "update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d\n",
+ d->dpll_idx, d->prev_input_idx, d->input_idx,
+ d->dpll_state, d->prev_dpll_state);
+ if (ret) {
+ dev_err(ice_pf_to_dev(pf),
+ "update dpll=%d state failed, ret=%d %s\n",
+ d->dpll_idx, ret,
+ ice_aq_str(pf->hw.adminq.sq_last_status));
+ return ret;
+ }
+ if (init) {
+ if (d->dpll_state == ICE_CGU_STATE_LOCKED &&
+ d->dpll_state == ICE_CGU_STATE_LOCKED_HO_ACQ)
+ d->active_input = pf->dplls.inputs[d->input_idx].pin;
+ p = &pf->dplls.inputs[d->input_idx];
+ return ice_dpll_pin_state_update(pf, p,
+ ICE_DPLL_PIN_TYPE_INPUT);
+ }
+ if (d->dpll_state == ICE_CGU_STATE_HOLDOVER ||
+ d->dpll_state == ICE_CGU_STATE_FREERUN) {
+ d->active_input = NULL;
+ p = &pf->dplls.inputs[d->input_idx];
+ d->prev_input_idx = ICE_DPLL_PIN_IDX_INVALID;
+ d->input_idx = ICE_DPLL_PIN_IDX_INVALID;
+ ret = ice_dpll_pin_state_update(pf, p,
+ ICE_DPLL_PIN_TYPE_INPUT);
+ } else if (d->input_idx != d->prev_input_idx) {
+ p = &pf->dplls.inputs[d->prev_input_idx];
+ ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_INPUT);
+ p = &pf->dplls.inputs[d->input_idx];
+ d->active_input = p->pin;
+ ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_INPUT);
+ d->prev_input_idx = d->input_idx;
+ }
+
+ return ret;
+}
+
+/**
+ * ice_dpll_notify_changes - notify dpll subsystem about changes
+ * @d: pointer do dpll
+ *
+ * Once change detected appropriate event is submitted to the dpll subsystem.
+ */
+static void ice_dpll_notify_changes(struct ice_dpll *d)
+{
+ if (d->prev_dpll_state != d->dpll_state) {
+ d->prev_dpll_state = d->dpll_state;
+ dpll_device_change_ntf(d->dpll);
+ }
+ if (d->prev_input != d->active_input) {
+ if (d->prev_input)
+ dpll_pin_change_ntf(d->prev_input);
+ d->prev_input = d->active_input;
+ if (d->active_input)
+ dpll_pin_change_ntf(d->active_input);
+ }
+}
+
+/**
+ * ice_dpll_periodic_work - DPLLs periodic worker
+ * @work: pointer to kthread_work structure
+ *
+ * DPLLs periodic worker is responsible for polling state of dpll.
+ * Context: Holds pf->dplls.lock
+ */
+static void ice_dpll_periodic_work(struct kthread_work *work)
+{
+ struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
+ struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
+ struct ice_dpll *de = &pf->dplls.eec;
+ struct ice_dpll *dp = &pf->dplls.pps;
+ int ret = 0;
+
+ if (!test_bit(ICE_FLAG_DPLL, pf->flags))
+ return;
+ ret = ice_dpll_cb_lock(pf);
+ if (ret) {
+ d->lock_err_num++;
+ goto resched;
+ }
+ ret = ice_dpll_update_state(pf, de, false);
+ if (!ret)
+ ret = ice_dpll_update_state(pf, dp, false);
+ if (ret) {
+ d->cgu_state_acq_err_num++;
+ /* stop rescheduling this worker */
+ if (d->cgu_state_acq_err_num >
+ ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
+ dev_err(ice_pf_to_dev(pf),
+ "EEC/PPS DPLLs periodic work disabled\n");
+ return;
+ }
+ }
+ ice_dpll_cb_unlock(pf);
+ ice_dpll_notify_changes(de);
+ ice_dpll_notify_changes(dp);
+resched:
+ /* Run twice a second or reschedule if update failed */
+ kthread_queue_delayed_work(d->kworker, &d->work,
+ ret ? msecs_to_jiffies(10) :
+ msecs_to_jiffies(500));
+}
+
+/**
+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
+ * @pf: board private structure
+ *
+ * Create and start DPLLs periodic worker.
+ *
+ * Context: Called under pf->dplls.lock
+ * Return:
+ * * 0 - success
+ * * negative - create worker failure
+ */
+static int ice_dpll_init_worker(struct ice_pf *pf)
+{
+ struct ice_dplls *d = &pf->dplls;
+ struct kthread_worker *kworker;
+
+ ice_dpll_update_state(pf, &d->eec, true);
+ ice_dpll_update_state(pf, &d->pps, true);
+ kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
+ kworker = kthread_create_worker(0, "ice-dplls-%s",
+ dev_name(ice_pf_to_dev(pf)));
+ if (IS_ERR(kworker))
+ return PTR_ERR(kworker);
+ d->kworker = kworker;
+ d->cgu_state_acq_err_num = 0;
+ kthread_queue_delayed_work(d->kworker, &d->work, 0);
+
+ return 0;
+}
+
+/**
+ * ice_dpll_deinit_pins - deinitialize direct pins
+ * @pf: board private structure
+ * @cgu: if cgu is controlled by this pf
+ *
+ * If cgu is owned unregister directly connected pins from the dplls.
+ * Release resources of directly connected pins from the dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
+{
+ struct ice_dpll_pin *outputs = pf->dplls.outputs;
+ struct ice_dpll_pin *inputs = pf->dplls.inputs;
+ int num_outputs = pf->dplls.num_outputs;
+ int num_inputs = pf->dplls.num_inputs;
+ struct ice_dplls *d = &pf->dplls;
+ struct ice_dpll *de = &d->eec;
+ struct ice_dpll *dp = &d->pps;
+
+ ice_dpll_deinit_rclk_pin(pf);
+ if (cgu) {
+ ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
+ num_inputs);
+ ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
+ num_inputs);
+ }
+ ice_dpll_release_pins(inputs, num_inputs);
+ if (cgu) {
+ ice_dpll_unregister_pins(dp->dpll, outputs,
+ &ice_dpll_output_ops, num_outputs);
+ ice_dpll_unregister_pins(de->dpll, outputs,
+ &ice_dpll_output_ops, num_outputs);
+ ice_dpll_release_pins(outputs, num_outputs);
+ }
+}
+
+/**
+ * ice_dpll_deinit_dpll - deinitialize dpll device
+ * @pf: board private structure
+ *
+ * If cgu is owned unregister the dpll from dpll subsystem.
+ * Release resources of dpll device from dpll subsystem.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void
+ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
+{
+ if (!IS_ERR(d->dpll)) {
+ if (cgu)
+ dpll_device_unregister(d->dpll, &ice_dpll_ops, d);
+ dpll_device_put(d->dpll);
+ dev_dbg(ice_pf_to_dev(pf), "(%p) dpll removed\n", d);
+ }
+}
+
+/**
+ * ice_dpll_deinit_worker - deinitialize dpll kworker
+ * @pf: board private structure
+ *
+ * Stop dpll's kworker, release it's resources.
+ *
+ * Context: Called under pf->dplls.lock
+ */
+static void ice_dpll_deinit_worker(struct ice_pf *pf)
+{
+ struct ice_dplls *d = &pf->dplls;
+
+ kthread_cancel_delayed_work_sync(&d->work);
+ if (!IS_ERR_OR_NULL(d->kworker)) {
+ kthread_destroy_worker(d->kworker);
+ d->kworker = NULL;
+ dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
+ }
+}
+
+/**
+ * ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
+ * the dpll device.
+ * @pf: board private structure
+ *
+ * Handles the cleanup work required after dpll initialization,freeing resources
+ * and unregistering the dpll, pin and all resources used for handling them.
+ *
+ * Context: Function holds pf->dplls.lock mutex.
+ */
+void ice_dpll_deinit(struct ice_pf *pf)
+{
+ bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
+
+ if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
+ mutex_lock(&pf->dplls.lock);
+ ice_dpll_deinit_pins(pf, cgu);
+ ice_dpll_deinit_info(pf);
+ ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
+ ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
+ if (cgu)
+ ice_dpll_deinit_worker(pf);
+ clear_bit(ICE_FLAG_DPLL, pf->flags);
+ mutex_unlock(&pf->dplls.lock);
+ mutex_destroy(&pf->dplls.lock);
+ }
+}
+
+/**
+ * ice_dpll_init_info_direct_pins - initializes direct pins info
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Init information for directly connected pins, cache them in pf's pins
+ * structures.
+ *
+ * Context: Function initializes and holds pf->dplls.lock mutex.
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int
+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
+ enum ice_dpll_pin_type pin_type)
+{
+ struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
+ int num_pins, i, ret = -EINVAL;
+ struct ice_hw *hw = &pf->hw;
+ struct ice_dpll_pin *pins;
+ u8 freq_supp_num;
+ bool input;
+
+ switch (pin_type) {
+ case ICE_DPLL_PIN_TYPE_INPUT:
+ pins = pf->dplls.inputs;
+ num_pins = pf->dplls.num_inputs;
+ input = true;
+ break;
+ case ICE_DPLL_PIN_TYPE_OUTPUT:
+ pins = pf->dplls.outputs;
+ num_pins = pf->dplls.num_outputs;
+ input = false;
+ break;
+ default:
+ return ret;
+ }
+
+ for (i = 0; i < num_pins; i++) {
+ pins[i].idx = i;
+ pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
+ pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
+ if (input) {
+ ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
+ &de->input_prio[i]);
+ if (ret)
+ return ret;
+ ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
+ &dp->input_prio[i]);
+ if (ret)
+ return ret;
+ pins[i].prop.capabilities |=
+ DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
+ }
+ pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
+ ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type);
+ if (ret)
+ return ret;
+ pins[i].prop.freq_supported =
+ ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
+ pins[i].prop.freq_supported_num = freq_supp_num;
+ pins[i].pf = pf;
+ }
+
+ return ret;
+}
+
+/**
+ * ice_dpll_init_rclk_pin - initializes rclk pin information
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Init information for rclk pin, cache them in pf->dplls.rclk.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int ice_dpll_init_rclk_pin(struct ice_pf *pf)
+{
+ struct ice_dpll_pin *pin = &pf->dplls.rclk;
+ struct device *dev = ice_pf_to_dev(pf);
+
+ pin->prop.board_label = dev_name(dev);
+ pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
+ pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
+ pin->pf = pf;
+
+ return ice_dpll_pin_state_update(pf, pin,
+ ICE_DPLL_PIN_TYPE_RCLK_INPUT);
+}
+
+/**
+ * ice_dpll_init_pins_info - init pins info wrapper
+ * @pf: board private structure
+ * @pin_type: type of pins being initialized
+ *
+ * Wraps functions for pin initialization.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int
+ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type)
+{
+ switch (pin_type) {
+ case ICE_DPLL_PIN_TYPE_INPUT:
+ case ICE_DPLL_PIN_TYPE_OUTPUT:
+ return ice_dpll_init_info_direct_pins(pf, pin_type);
+ case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
+ return ice_dpll_init_rclk_pin(pf);
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * ice_dpll_init_info - prepare pf's dpll information structure
+ * @pf: board private structure
+ * @cgu: if cgu is present and controlled by this NIC
+ *
+ * Acquire (from HW) and set basic dpll information (on pf->dplls struct).
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
+{
+ struct ice_aqc_get_cgu_abilities abilities;
+ struct ice_dpll *de = &pf->dplls.eec;
+ struct ice_dpll *dp = &pf->dplls.pps;
+ struct ice_dplls *d = &pf->dplls;
+ struct ice_hw *hw = &pf->hw;
+ int ret, alloc_size, i;
+
+ d->clock_id = ice_generate_clock_id(pf);
+ ret = ice_aq_get_cgu_abilities(hw, &abilities);
+ if (ret) {
+ dev_err(ice_pf_to_dev(pf),
+ "err:%d %s failed to read cgu abilities\n",
+ ret, ice_aq_str(hw->adminq.sq_last_status));
+ return ret;
+ }
+
+ de->dpll_idx = abilities.eec_dpll_idx;
+ dp->dpll_idx = abilities.pps_dpll_idx;
+ d->num_inputs = abilities.num_inputs;
+ d->num_outputs = abilities.num_outputs;
+
+ alloc_size = sizeof(*d->inputs) * d->num_inputs;
+ d->inputs = kzalloc(alloc_size, GFP_KERNEL);
+ if (!d->inputs)
+ return -ENOMEM;
+
+ alloc_size = sizeof(*de->input_prio) * d->num_inputs;
+ de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+ if (!de->input_prio)
+ return -ENOMEM;
+
+ dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
+ if (!dp->input_prio)
+ return -ENOMEM;
+
+ ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
+ if (ret)
+ goto deinit_info;
+
+ if (cgu) {
+ alloc_size = sizeof(*d->outputs) * d->num_outputs;
+ d->outputs = kzalloc(alloc_size, GFP_KERNEL);
+ if (!d->outputs)
+ goto deinit_info;
+
+ ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
+ if (ret)
+ goto deinit_info;
+ }
+
+ ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
+ &pf->dplls.rclk.num_parents);
+ if (ret)
+ return ret;
+ for (i = 0; i < pf->dplls.rclk.num_parents; i++)
+ pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
+ ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
+ if (ret)
+ return ret;
+
+ dev_dbg(ice_pf_to_dev(pf),
+ "%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
+ __func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
+
+ return 0;
+
+deinit_info:
+ dev_err(ice_pf_to_dev(pf),
+ "%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
+ __func__, d->inputs, de->input_prio,
+ dp->input_prio, d->outputs);
+ ice_dpll_deinit_info(pf);
+ return ret;
+}
+
+/**
+ * ice_dpll_init - initialize support for dpll subsystem
+ * @pf: board private structure
+ *
+ * Set up the device dplls, register them and pins connected within Linux dpll
+ * subsystem. Allow userpsace to obtain state of DPLL and handling of DPLL
+ * configuration requests.
+ *
+ * Return:
+ * * 0 - success
+ * * negative - init failure reason
+ */
+int ice_dpll_init(struct ice_pf *pf)
+{
+ bool cgu_present = ice_is_feature_supported(pf, ICE_F_CGU);
+ struct ice_dplls *d = &pf->dplls;
+ int err = 0;
+
+ mutex_init(&d->lock);
+ mutex_lock(&d->lock);
+ err = ice_dpll_init_info(pf, cgu_present);
+ if (err)
+ goto err_exit;
+ err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu_present,
+ DPLL_TYPE_EEC);
+ if (err)
+ goto deinit_info;
+ err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu_present,
+ DPLL_TYPE_PPS);
+ if (err)
+ goto deinit_eec;
+ err = ice_dpll_init_pins(pf, cgu_present);
+ if (err)
+ goto deinit_pps;
+ set_bit(ICE_FLAG_DPLL, pf->flags);
+ if (cgu_present) {
+ err = ice_dpll_init_worker(pf);
+ if (err)
+ goto deinit_pins;
+ }
+ mutex_unlock(&d->lock);
+ dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");
+
+ return err;
+
+deinit_pins:
+ ice_dpll_deinit_pins(pf, cgu_present);
+deinit_pps:
+ ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu_present);
+deinit_eec:
+ ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu_present);
+deinit_info:
+ ice_dpll_deinit_info(pf);
+err_exit:
+ clear_bit(ICE_FLAG_DPLL, pf->flags);
+ mutex_unlock(&d->lock);
+ mutex_destroy(&d->lock);
+ dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:\n");
+
+ return err;
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h
new file mode 100644
index 000000000000..287892825deb
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2022, Intel Corporation. */
+
+#ifndef _ICE_DPLL_H_
+#define _ICE_DPLL_H_
+
+#include "ice.h"
+
+#define ICE_DPLL_PRIO_MAX 0xF
+#define ICE_DPLL_RCLK_NUM_MAX 4
+/** ice_dpll_pin - store info about pins
+ * @pin: dpll pin structure
+ * @pf: pointer to pf, which has registered the dpll_pin
+ * @flags: pin flags returned from HW
+ * @idx: ice pin private idx
+ * @state: state of a pin
+ * @type: type of a pin
+ * @freq_mask: mask of supported frequencies
+ * @freq: current frequency of a pin
+ * @caps: capabilities of a pin
+ * @name: pin name
+ */
+struct ice_dpll_pin {
+ struct dpll_pin *pin;
+ struct ice_pf *pf;
+ u8 idx;
+ u8 num_parents;
+ u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
+ u8 flags[ICE_DPLL_RCLK_NUM_MAX];
+ u8 state[ICE_DPLL_RCLK_NUM_MAX];
+ struct dpll_pin_properties prop;
+ u32 freq;
+};
+
+/** ice_dpll - store info required for DPLL control
+ * @dpll: pointer to dpll dev
+ * @pf: pointer to pf, which has registered the dpll_device
+ * @dpll_idx: index of dpll on the NIC
+ * @input_idx: currently selected input index
+ * @prev_input_idx: previously selected input index
+ * @ref_state: state of dpll reference signals
+ * @eec_mode: eec_mode dpll is configured for
+ * @phase_offset: phase delay of a dpll
+ * @input_prio: priorities of each input
+ * @dpll_state: current dpll sync state
+ * @prev_dpll_state: last dpll sync state
+ * @active_input: pointer to active input pin
+ * @prev_input: pointer to previous active input pin
+ */
+struct ice_dpll {
+ struct dpll_device *dpll;
+ struct ice_pf *pf;
+ int dpll_idx;
+ u8 input_idx;
+ u8 prev_input_idx;
+ u8 ref_state;
+ u8 eec_mode;
+ s64 phase_offset;
+ u8 *input_prio;
+ enum ice_cgu_state dpll_state;
+ enum ice_cgu_state prev_dpll_state;
+ struct dpll_pin *active_input;
+ struct dpll_pin *prev_input;
+};
+
+/** ice_dplls - store info required for CCU (clock controlling unit)
+ * @kworker: periodic worker
+ * @work: periodic work
+ * @lock: locks access to configuration of a dpll
+ * @eec: pointer to EEC dpll dev
+ * @pps: pointer to PPS dpll dev
+ * @inputs: input pins pointer
+ * @outputs: output pins pointer
+ * @rclk: recovered pins pointer
+ * @num_inputs: number of input pins available on dpll
+ * @num_outputs: number of output pins available on dpll
+ * @cgu_state_acq_err_num: number of errors returned during periodic work
+ * @base_rclk_idx: idx of first pin used for clock revocery pins
+ * @clock_id: clock_id of dplls
+ */
+struct ice_dplls {
+ struct kthread_worker *kworker;
+ struct kthread_delayed_work work;
+ struct mutex lock;
+ struct ice_dpll eec;
+ struct ice_dpll pps;
+ struct ice_dpll_pin *inputs;
+ struct ice_dpll_pin *outputs;
+ struct ice_dpll_pin rclk;
+ u32 num_inputs;
+ u32 num_outputs;
+ int cgu_state_acq_err_num;
+ int lock_err_num;
+ u8 base_rclk_idx;
+ u64 clock_id;
+};
+
+int ice_dpll_init(struct ice_pf *pf);
+
+void ice_dpll_deinit(struct ice_pf *pf);
+
+#endif
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 62e91512aeab..ba5f3bc9075a 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4595,6 +4595,10 @@ static void ice_init_features(struct ice_pf *pf)
if (ice_is_feature_supported(pf, ICE_F_GNSS))
ice_gnss_init(pf);

+ if (ice_is_feature_supported(pf, ICE_F_CGU) ||
+ ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
+ ice_dpll_init(pf);
+
/* Note: Flow director init failure is non-fatal to load */
if (ice_init_fdir(pf))
dev_err(dev, "could not initialize flow director\n");
@@ -4621,6 +4625,9 @@ static void ice_deinit_features(struct ice_pf *pf)
ice_gnss_exit(pf);
if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
ice_ptp_release(pf);
+ if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK) ||
+ ice_is_feature_supported(pf, ICE_F_CGU))
+ ice_dpll_deinit(pf);
}

static void ice_init_wakeup(struct ice_pf *pf)
--
2.37.3


2023-06-09 12:33:20

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 09/10] ptp_ocp: implement DPLL ops

From: Vadim Fedorenko <[email protected]>

Implement basic DPLL operations in ptp_ocp driver as the
simplest example of using new subsystem.

Signed-off-by: Vadim Fedorenko <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
drivers/ptp/Kconfig | 1 +
drivers/ptp/ptp_ocp.c | 329 +++++++++++++++++++++++++++++++++++-------
2 files changed, 278 insertions(+), 52 deletions(-)

diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index b00201d81313..e3575c2e34dc 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -177,6 +177,7 @@ config PTP_1588_CLOCK_OCP
depends on COMMON_CLK
select NET_DEVLINK
select CRC16
+ select DPLL
help
This driver adds support for an OpenCompute time card.

diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index ab8cab4d1560..40a1ab7053d4 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -23,6 +23,7 @@
#include <linux/mtd/mtd.h>
#include <linux/nvmem-consumer.h>
#include <linux/crc16.h>
+#include <linux/dpll.h>

#define PCI_VENDOR_ID_FACEBOOK 0x1d9b
#define PCI_DEVICE_ID_FACEBOOK_TIMECARD 0x0400
@@ -260,12 +261,21 @@ enum ptp_ocp_sma_mode {
SMA_MODE_OUT,
};

+static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
+ DPLL_PIN_FREQUENCY_1PPS,
+ DPLL_PIN_FREQUENCY_10MHZ,
+ DPLL_PIN_FREQUENCY_IRIG_B,
+ DPLL_PIN_FREQUENCY_DCF77,
+};
+
struct ptp_ocp_sma_connector {
enum ptp_ocp_sma_mode mode;
bool fixed_fcn;
bool fixed_dir;
bool disabled;
u8 default_fcn;
+ struct dpll_pin *dpll_pin;
+ struct dpll_pin_properties dpll_prop;
};

struct ocp_attr_group {
@@ -294,6 +304,7 @@ struct ptp_ocp_serial_port {

#define OCP_BOARD_ID_LEN 13
#define OCP_SERIAL_LEN 6
+#define OCP_SMA_NUM 4

struct ptp_ocp {
struct pci_dev *pdev;
@@ -350,8 +361,9 @@ struct ptp_ocp {
u32 ts_window_adjust;
u64 fw_cap;
struct ptp_ocp_signal signal[4];
- struct ptp_ocp_sma_connector sma[4];
+ struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];
const struct ocp_sma_op *sma_op;
+ struct dpll_device *dpll;
};

#define OCP_REQ_TIMESTAMP BIT(0)
@@ -835,6 +847,7 @@ static DEFINE_IDR(ptp_ocp_idr);
struct ocp_selector {
const char *name;
int value;
+ u64 frequency;
};

static const struct ocp_selector ptp_ocp_clock[] = {
@@ -855,31 +868,31 @@ static const struct ocp_selector ptp_ocp_clock[] = {
#define SMA_SELECT_MASK GENMASK(14, 0)

static const struct ocp_selector ptp_ocp_sma_in[] = {
- { .name = "10Mhz", .value = 0x0000 },
- { .name = "PPS1", .value = 0x0001 },
- { .name = "PPS2", .value = 0x0002 },
- { .name = "TS1", .value = 0x0004 },
- { .name = "TS2", .value = 0x0008 },
- { .name = "IRIG", .value = 0x0010 },
- { .name = "DCF", .value = 0x0020 },
- { .name = "TS3", .value = 0x0040 },
- { .name = "TS4", .value = 0x0080 },
- { .name = "FREQ1", .value = 0x0100 },
- { .name = "FREQ2", .value = 0x0200 },
- { .name = "FREQ3", .value = 0x0400 },
- { .name = "FREQ4", .value = 0x0800 },
- { .name = "None", .value = SMA_DISABLE },
+ { .name = "10Mhz", .value = 0x0000, .frequency = 10000000 },
+ { .name = "PPS1", .value = 0x0001, .frequency = 1 },
+ { .name = "PPS2", .value = 0x0002, .frequency = 1 },
+ { .name = "TS1", .value = 0x0004, .frequency = 0 },
+ { .name = "TS2", .value = 0x0008, .frequency = 0 },
+ { .name = "IRIG", .value = 0x0010, .frequency = 10000 },
+ { .name = "DCF", .value = 0x0020, .frequency = 77500 },
+ { .name = "TS3", .value = 0x0040, .frequency = 0 },
+ { .name = "TS4", .value = 0x0080, .frequency = 0 },
+ { .name = "FREQ1", .value = 0x0100, .frequency = 0 },
+ { .name = "FREQ2", .value = 0x0200, .frequency = 0 },
+ { .name = "FREQ3", .value = 0x0400, .frequency = 0 },
+ { .name = "FREQ4", .value = 0x0800, .frequency = 0 },
+ { .name = "None", .value = SMA_DISABLE, .frequency = 0 },
{ }
};

static const struct ocp_selector ptp_ocp_sma_out[] = {
- { .name = "10Mhz", .value = 0x0000 },
- { .name = "PHC", .value = 0x0001 },
- { .name = "MAC", .value = 0x0002 },
- { .name = "GNSS1", .value = 0x0004 },
- { .name = "GNSS2", .value = 0x0008 },
- { .name = "IRIG", .value = 0x0010 },
- { .name = "DCF", .value = 0x0020 },
+ { .name = "10Mhz", .value = 0x0000, .frequency = 10000000 },
+ { .name = "PHC", .value = 0x0001, .frequency = 1 },
+ { .name = "MAC", .value = 0x0002, .frequency = 1 },
+ { .name = "GNSS1", .value = 0x0004, .frequency = 1 },
+ { .name = "GNSS2", .value = 0x0008, .frequency = 1 },
+ { .name = "IRIG", .value = 0x0010, .frequency = 10000 },
+ { .name = "DCF", .value = 0x0020, .frequency = 77000 },
{ .name = "GEN1", .value = 0x0040 },
{ .name = "GEN2", .value = 0x0080 },
{ .name = "GEN3", .value = 0x0100 },
@@ -890,15 +903,15 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
};

static const struct ocp_selector ptp_ocp_art_sma_in[] = {
- { .name = "PPS1", .value = 0x0001 },
- { .name = "10Mhz", .value = 0x0008 },
+ { .name = "PPS1", .value = 0x0001, .frequency = 1 },
+ { .name = "10Mhz", .value = 0x0008, .frequency = 1000000 },
{ }
};

static const struct ocp_selector ptp_ocp_art_sma_out[] = {
- { .name = "PHC", .value = 0x0002 },
- { .name = "GNSS", .value = 0x0004 },
- { .name = "10Mhz", .value = 0x0010 },
+ { .name = "PHC", .value = 0x0002, .frequency = 1 },
+ { .name = "GNSS", .value = 0x0004, .frequency = 1 },
+ { .name = "10Mhz", .value = 0x0010, .frequency = 10000000 },
{ }
};

@@ -2282,22 +2295,35 @@ ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
static void
ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
{
+ struct dpll_pin_properties prop = {
+ .board_label = NULL,
+ .type = DPLL_PIN_TYPE_EXT,
+ .capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
+ .freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
+ .freq_supported = ptp_ocp_sma_freq,
+
+ };
u32 reg;
int i;

/* defaults */
+ for (i = 0; i < OCP_SMA_NUM; i++) {
+ bp->sma[i].default_fcn = i & 1;
+ bp->sma[i].dpll_prop = prop;
+ bp->sma[i].dpll_prop.board_label =
+ bp->ptp_info.pin_config[i].name;
+ }
bp->sma[0].mode = SMA_MODE_IN;
bp->sma[1].mode = SMA_MODE_IN;
bp->sma[2].mode = SMA_MODE_OUT;
bp->sma[3].mode = SMA_MODE_OUT;
- for (i = 0; i < 4; i++)
- bp->sma[i].default_fcn = i & 1;
-
/* If no SMA1 map, the pin functions and directions are fixed. */
if (!bp->sma_map1) {
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < OCP_SMA_NUM; i++) {
bp->sma[i].fixed_fcn = true;
bp->sma[i].fixed_dir = true;
+ bp->sma[1].dpll_prop.capabilities &=
+ ~DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
}
return;
}
@@ -2307,7 +2333,7 @@ ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
*/
reg = ioread32(&bp->sma_map2->gpio2);
if (reg == 0xffffffff) {
- for (i = 0; i < 4; i++)
+ for (i = 0; i < OCP_SMA_NUM; i++)
bp->sma[i].fixed_dir = true;
} else {
reg = ioread32(&bp->sma_map1->gpio1);
@@ -2329,7 +2355,7 @@ static const struct ocp_sma_op ocp_fb_sma_op = {
};

static int
-ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
+ptp_ocp_set_pins(struct ptp_ocp *bp)
{
struct ptp_pin_desc *config;
int i;
@@ -2396,16 +2422,16 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)

ptp_ocp_tod_init(bp);
ptp_ocp_nmea_out_init(bp);
- ptp_ocp_sma_init(bp);
ptp_ocp_signal_init(bp);

err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
if (err)
return err;

- err = ptp_ocp_fb_set_pins(bp);
+ err = ptp_ocp_set_pins(bp);
if (err)
return err;
+ ptp_ocp_sma_init(bp);

return ptp_ocp_init_clock(bp);
}
@@ -2445,6 +2471,14 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
static void
ptp_ocp_art_sma_init(struct ptp_ocp *bp)
{
+ struct dpll_pin_properties prop = {
+ .board_label = NULL,
+ .type = DPLL_PIN_TYPE_EXT,
+ .capabilities = 0,
+ .freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
+ .freq_supported = ptp_ocp_sma_freq,
+
+ };
u32 reg;
int i;

@@ -2459,16 +2493,17 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
bp->sma[2].default_fcn = 0x10; /* OUT: 10Mhz */
bp->sma[3].default_fcn = 0x02; /* OUT: PHC */

- /* If no SMA map, the pin functions and directions are fixed. */
- if (!bp->art_sma) {
- for (i = 0; i < 4; i++) {
+
+ for (i = 0; i < OCP_SMA_NUM; i++) {
+ /* If no SMA map, the pin functions and directions are fixed. */
+ bp->sma[i].dpll_prop = prop;
+ bp->sma[i].dpll_prop.board_label =
+ bp->ptp_info.pin_config[i].name;
+ if (!bp->art_sma) {
bp->sma[i].fixed_fcn = true;
bp->sma[i].fixed_dir = true;
+ continue;
}
- return;
- }
-
- for (i = 0; i < 4; i++) {
reg = ioread32(&bp->art_sma->map[i].gpio);

switch (reg & 0xff) {
@@ -2479,9 +2514,13 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
case 1:
case 8:
bp->sma[i].mode = SMA_MODE_IN;
+ bp->sma[i].dpll_prop.capabilities =
+ DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
break;
default:
bp->sma[i].mode = SMA_MODE_OUT;
+ bp->sma[i].dpll_prop.capabilities =
+ DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
break;
}
}
@@ -2548,6 +2587,9 @@ ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
/* Enable MAC serial port during initialisation */
iowrite32(1, &bp->board_config->mro50_serial_activate);

+ err = ptp_ocp_set_pins(bp);
+ if (err)
+ return err;
ptp_ocp_sma_init(bp);

err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
@@ -2689,16 +2731,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
}

static int
-ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
{
struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
- enum ptp_ocp_sma_mode mode;
- int val;
-
- mode = sma->mode;
- val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
- if (val < 0)
- return val;

if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
return -EOPNOTSUPP;
@@ -2733,6 +2768,20 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
return val;
}

+static int
+ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
+{
+ struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
+ enum ptp_ocp_sma_mode mode;
+ int val;
+
+ mode = sma->mode;
+ val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
+ if (val < 0)
+ return val;
+ return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
+}
+
static ssize_t
sma1_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
@@ -4171,12 +4220,148 @@ ptp_ocp_detach(struct ptp_ocp *bp)
device_unregister(&bp->dev);
}

+static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
+ void *priv,
+ enum dpll_lock_status *status,
+ struct netlink_ext_ack *extack)
+{
+ struct ptp_ocp *bp = priv;
+ int sync;
+
+ sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
+ *status = sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED;
+
+ return 0;
+}
+
+static int ptp_ocp_dpll_source_idx_get(const struct dpll_device *dpll,
+ void *priv, u32 *idx,
+ struct netlink_ext_ack *extack)
+{
+ struct ptp_ocp *bp = priv;
+
+ if (bp->pps_select) {
+ *idx = ioread32(&bp->pps_select->gpio1);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int ptp_ocp_dpll_mode_get(const struct dpll_device *dpll, void *priv,
+ u32 *mode, struct netlink_ext_ack *extack)
+{
+ *mode = DPLL_MODE_AUTOMATIC;
+ return 0;
+}
+
+static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
+ void *priv, const enum dpll_mode mode,
+ struct netlink_ext_ack *extack)
+{
+ return mode == DPLL_MODE_AUTOMATIC;
+}
+
+static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *priv,
+ enum dpll_pin_direction *direction,
+ struct netlink_ext_ack *extack)
+{
+ struct ptp_ocp_sma_connector *sma = pin_priv;
+
+ *direction = sma->mode == SMA_MODE_IN ?
+ DPLL_PIN_DIRECTION_INPUT :
+ DPLL_PIN_DIRECTION_OUTPUT;
+ return 0;
+}
+
+static int ptp_ocp_dpll_direction_set(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_direction direction,
+ struct netlink_ext_ack *extack)
+{
+ struct ptp_ocp_sma_connector *sma = pin_priv;
+ struct ptp_ocp *bp = dpll_priv;
+ enum ptp_ocp_sma_mode mode;
+ int sma_nr = (sma - bp->sma);
+
+ if (sma->fixed_dir)
+ return -EOPNOTSUPP;
+ mode = direction == DPLL_PIN_DIRECTION_INPUT ?
+ SMA_MODE_IN : SMA_MODE_OUT;
+ return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);
+}
+
+static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u64 frequency,
+ struct netlink_ext_ack *extack)
+{
+ struct ptp_ocp_sma_connector *sma = pin_priv;
+ struct ptp_ocp *bp = dpll_priv;
+ const struct ocp_selector *tbl;
+ int sma_nr = (sma - bp->sma);
+ int val, i;
+
+ if (sma->fixed_fcn)
+ return -EOPNOTSUPP;
+
+ tbl = bp->sma_op->tbl[sma->mode];
+ for (i = 0; tbl[i].name; i++)
+ if (tbl[i].frequency == frequency)
+ return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);
+ return -EINVAL;
+}
+
+static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv, u64 *frequency,
+ struct netlink_ext_ack *extack)
+{
+ struct ptp_ocp_sma_connector *sma = pin_priv;
+ struct ptp_ocp *bp = dpll_priv;
+ const struct ocp_selector *tbl;
+ int sma_nr = (sma - bp->sma);
+ u32 val;
+ int i;
+
+ val = bp->sma_op->get(bp, sma_nr);
+ tbl = bp->sma_op->tbl[sma->mode];
+ for (i = 0; tbl[i].name; i++)
+ if (val == tbl[i].value) {
+ *frequency = tbl[i].frequency;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct dpll_device_ops dpll_ops = {
+ .lock_status_get = ptp_ocp_dpll_lock_status_get,
+ .source_pin_idx_get = ptp_ocp_dpll_source_idx_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,
+};
+
static int
ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct devlink *devlink;
struct ptp_ocp *bp;
- int err;
+ int err, i;
+ u64 clkid;

devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
if (!devlink) {
@@ -4226,8 +4411,39 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)

ptp_ocp_info(bp);
devlink_register(devlink);
- return 0;

+ clkid = pci_get_dsn(pdev);
+ bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
+ if (IS_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))
+ 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;
+ }
+ }
+
+ return 0;
+out_dpll:
+ 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);
out:
ptp_ocp_detach(bp);
out_disable:
@@ -4242,7 +4458,16 @@ ptp_ocp_remove(struct pci_dev *pdev)
{
struct ptp_ocp *bp = pci_get_drvdata(pdev);
struct devlink *devlink = priv_to_devlink(bp);
+ int i;

+ for (i = 0; i < OCP_SMA_NUM; i++) {
+ if (bp->sma[i].dpll_pin) {
+ dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp);
+ dpll_pin_put(bp->sma[i].dpll_pin);
+ }
+ }
+ dpll_device_unregister(bp->dpll, &dpll_ops, bp);
+ dpll_device_put(bp->dpll);
devlink_unregister(devlink);
ptp_ocp_detach(bp);
pci_disable_device(pdev);
--
2.37.3


2023-06-09 12:33:22

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

From: Vadim Fedorenko <[email protected]>

Add documentation explaining common netlink interface to configure DPLL
devices and monitoring events. Common way to implement DPLL device in
a driver is also covered.

Signed-off-by: Vadim Fedorenko <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
Documentation/driver-api/dpll.rst | 458 +++++++++++++++++++++++++++++
Documentation/driver-api/index.rst | 1 +
2 files changed, 459 insertions(+)
create mode 100644 Documentation/driver-api/dpll.rst

diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
new file mode 100644
index 000000000000..8caa4af022ad
--- /dev/null
+++ b/Documentation/driver-api/dpll.rst
@@ -0,0 +1,458 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===============================
+The Linux kernel dpll 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 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.
+
+For other dpll device operating modes there is no pin selection
+mechanics.
+
+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 provide user with additional nested
+attribute ``DPLL_A_PIN_PARENT`` 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',
+ 'pin-dpll-caps': 4,
+ 'pin-id': 13,
+ 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
+ {'pin-id': 3, 'pin-state': 'disconnected'},
+ {'id': 0, 'pin-direction': 'input'},
+ {'id': 1, 'pin-direction': 'input'}],
+ 'pin-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`` nested attribute for requesting configuration
+ related to parent pin
+ ``DPLL_A_PIN_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`` child pin id
+ ``DPLL_A_PIN_PARENT`` nested attribute for requesting configuration
+ related to parent pin
+ ``DPLL_A_ID`` parent dpll id
+ ``DPLL_A_PIN_PRIO`` requested pin prio on parent dpll
+
+Child pin of MUX-type is not capable of automatic input pin selection,
+in order to configure a 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.
+
+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
+=================================================
+
+All constants identifying command types use a ``DPLL_CMD_`` prefix and
+suffix according to command purpose. All 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
+ ``DPLL_CMD_PIN_GET`` command to get pin 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_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_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_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_DIRECTION`` attr direction 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_PARENT`` nested attr for each parent the
+ pin is connected with
+ ``DPLL_A_ID`` attr provided if parent is dpll
+ device
+ ``DPLL_A_PIN_ID`` attr provided if parent is a pin
+ ``DPLL_A_PIN_PRIO`` attr priority of pin on the
+ dpll device
+ ``DPLL_A_PIN_STATE`` attr state of pin on the dpll
+ device or on the parent pin
+ ``DPLL_A_PIN_DPLL_CAPS`` attr bitmask of pin-dpll
+ capabilities
+ ``DPLL_CMD_PIN_SET`` command to set pins configuration
+ ``DPLL_A_PIN_ID`` attr unique a pin ID
+ ``DPLL_A_PIN_DIRECTION`` attr requested direction of a pin
+ ``DPLL_A_PIN_FREQUENCY`` attr requested frequency of a pin
+ ``DPLL_A_PIN_PARENT`` nested attr for each parent
+ related configuration of a pin
+ requested
+ ``DPLL_A_ID`` attr provided if parent is dpll
+ device
+ ``DPLL_A_PIN_ID`` attr provided if parent is 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 or on the 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 has to 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_DIRECTION`` or ``DPLL_A_PIN_FREQUENCY`` are configured,
+this affects all the dpll device they are connected, that is why those
+attributes shall not be enclosed in ``DPLL_A_PIN_PARENT``.
+Other attributes:
+``DPLL_A_PIN_PRIO`` or ``DPLL_A_PIN_STATE`` must be enclosed in
+``DPLL_A_PIN_PARENT`` as their configuration relates to only one
+parent dpll or parent pin.
+Nested attribute of either ``DPLL_A_ID`` or ``DPLL_A_PIN_ID`` determines
+if configuration was requested on a dpll device or on a pin
+respectively.
+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.
+
+Device level configuration pre-defined enums
+=================================================
+
+Values for ``DPLL_A_LOCK_STATUS`` attribute:
+
+ ================================== ===================================
+ ``DPLL_LOCK_STATUS_UNLOCKED`` dpll device is in freerun, not
+ locked to any input pin
+ ``DPLL_LOCK_STATUS_LOCKED`` dpll device is locked to the input
+ but no holdover capability yet
+ acquired
+ ``DPLL_LOCK_STATUS_LOCKED_HO_ACQ`` dpll device is locked to the input
+ pin with holdover capability
+ acquired
+ ``DPLL_LOCK_STATUS_HOLDOVER`` dpll device lost a lock, using its
+ frequency holdover capabilities
+
+Values for ``DPLL_A_MODE`` attribute:
+
+ ======================= ==============================================
+ ``DPLL_MODE_MANUAL`` input pin is manually selected by setting pin
+ state to ``DPLL_PIN_STATE_CONNECTED`` on a
+ dpll device
+ ``DPLL_MODE_AUTOMATIC`` input pin is auto selected according to
+ configured pin priorities and input signal
+ validity
+ ``DPLL_MODE_HOLDOVER`` force holdover mode of dpll
+ ``DPLL_MODE_FREERUN`` dpll device is driven by supplied system clock
+ without holdover capabilities
+
+Values for ``DPLL_A_TYPE`` attribute:
+
+ ================= ===================================================
+ ``DPLL_TYPE_PPS`` dpll device used to provide pulse-per-second output
+ ``DPLL_TYPE_EEC`` dpll device used to drive ethernet equipment clock
+
+Pin level configuration pre-defined enums
+=========================================
+
+Values for ``DPLL_A_PIN_STATE`` attribute:
+
+ =============================== ======================================
+ ``DPLL_PIN_STATE_CONNECTED`` Pin used as active input for a dpll
+ device or for a parent pin
+ ``DPLL_PIN_STATE_DISCONNECTED`` Pin disconnected from a dpll device or
+ from a parent pin
+ ``DPLL_PIN_STATE_SELECTABLE`` Pin enabled for automatic selection
+
+Values for ``DPLL_A_PIN_DIRECTION`` attribute:
+
+ ============================= ====================================
+ ``DPLL_PIN_DIRECTION_INPUT`` used to provide its signal to a dpll
+ device
+ ``DPLL_PIN_DIRECTION_OUTPUT`` used to output the signal from a dpll
+ device
+
+Values for ``DPLL_A_PIN_TYPE`` attributes:
+
+ ================================ =====================================
+ ``DPLL_PIN_TYPE_MUX`` MUX type pin, connected pins shall
+ have their own types
+ ``DPLL_PIN_TYPE_EXT`` External pin
+ ``DPLL_PIN_TYPE_SYNCE_ETH_PORT`` SyncE on Ethernet port
+ ``DPLL_PIN_TYPE_INT_OSCILLATOR`` Internal Oscillator (i.e. Holdover
+ with Atomic Clock as an input)
+ ``DPLL_PIN_TYPE_GNSS`` GNSS 1PPS input
+
+Values for ``DPLL_A_PIN_DPLL_CAPS`` attributes:
+
+ ====================================== ===============================
+ ``DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE`` Bit present if direction of
+ pin can change
+ ``DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE`` Bit present if priority of pin
+ can change
+ ``DPLL_PIN_CAPS_STATE_CAN_CHANGE`` Bit present if state of pin can
+ change
+
+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. Neverthelessi, there are few required to be
+implemented.
+Required dpll device level callback operations:
+- ``.mode_get``,
+- ``.lock_status_get``.
+
+Required pin level callback operations:
+- ``.state_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
+``-ENOTSUPP`` is returned in case of absence of specific handler.
+
+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:
+netdev_dpll_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``.
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
index ff9aa1afdc62..8c017dc51514 100644
--- a/Documentation/driver-api/index.rst
+++ b/Documentation/driver-api/index.rst
@@ -113,6 +113,7 @@ available subsections can be seen below.
xillybus
zorro
hte/index
+ dpll

.. only:: subproject and html

--
2.37.3


2023-06-09 12:47:38

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: [RFC PATCH v8 10/10] mlx5: Implement SyncE support using DPLL infrastructure

From: Jiri Pirko <[email protected]>

Implement SyncE support using newly introduced DPLL support.
Make sure that each PFs/VFs/SFs probed with appropriate capability
will spawn a dpll auxiliary device and register appropriate dpll device
and pin instances.

Signed-off-by: Jiri Pirko <[email protected]>
Signed-off-by: Arkadiusz Kubalewski <[email protected]>
---
.../net/ethernet/mellanox/mlx5/core/Kconfig | 8 +
.../net/ethernet/mellanox/mlx5/core/Makefile | 3 +
drivers/net/ethernet/mellanox/mlx5/core/dev.c | 17 +
.../net/ethernet/mellanox/mlx5/core/dpll.c | 432 ++++++++++++++++++
include/linux/mlx5/driver.h | 2 +
include/linux/mlx5/mlx5_ifc.h | 59 ++-
6 files changed, 520 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/dpll.c

diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
index bb1d7b039a7e..15a48d376eb3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig
@@ -188,3 +188,11 @@ config MLX5_SF_MANAGER
port is managed through devlink. A subfunction supports RDMA, netdevice
and vdpa device. It is similar to a SRIOV VF but it doesn't require
SRIOV support.
+
+config MLX5_DPLL
+ tristate "Mellanox 5th generation network adapters (ConnectX series) DPLL support"
+ depends on NETDEVICES && ETHERNET && PCI && MLX5_CORE
+ select DPLL
+ help
+ DPLL support in Mellanox Technologies ConnectX NICs.
+
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
index ddf1e352f51d..69434e8c2fb1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile
+++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile
@@ -123,3 +123,6 @@ mlx5_core-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o irq_
# SF manager
#
mlx5_core-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o
+
+obj-$(CONFIG_MLX5_DPLL) += mlx5_dpll.o
+mlx5_dpll-y := dpll.o
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
index 1b33533b15de..7d677332a18b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
@@ -211,6 +211,19 @@ static bool is_ib_enabled(struct mlx5_core_dev *dev)
return err ? false : val.vbool;
}

+static bool is_dpll_supported(struct mlx5_core_dev *dev)
+{
+ if (!IS_ENABLED(CONFIG_MLX5_DPLL))
+ return false;
+
+ if (!MLX5_CAP_MCAM_REG2(dev, synce_registers)) {
+ mlx5_core_warn(dev, "Missing SyncE capability\n");
+ return false;
+ }
+
+ return true;
+}
+
enum {
MLX5_INTERFACE_PROTOCOL_ETH,
MLX5_INTERFACE_PROTOCOL_ETH_REP,
@@ -220,6 +233,8 @@ enum {
MLX5_INTERFACE_PROTOCOL_MPIB,

MLX5_INTERFACE_PROTOCOL_VNET,
+
+ MLX5_INTERFACE_PROTOCOL_DPLL,
};

static const struct mlx5_adev_device {
@@ -242,6 +257,8 @@ static const struct mlx5_adev_device {
.is_supported = &is_ib_rep_supported },
[MLX5_INTERFACE_PROTOCOL_MPIB] = { .suffix = "multiport",
.is_supported = &is_mp_supported },
+ [MLX5_INTERFACE_PROTOCOL_DPLL] = { .suffix = "dpll",
+ .is_supported = &is_dpll_supported },
};

int mlx5_adev_idx_alloc(void)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
new file mode 100644
index 000000000000..9bff7044c614
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#include <linux/dpll.h>
+#include <linux/mlx5/driver.h>
+
+/* This structure represents a reference to DPLL, one is created
+ * per mdev instance.
+ */
+struct mlx5_dpll {
+ struct dpll_device *dpll;
+ struct dpll_pin *dpll_pin;
+ struct mlx5_core_dev *mdev;
+ struct workqueue_struct *wq;
+ struct delayed_work work;
+ struct {
+ bool valid;
+ enum dpll_lock_status lock_status;
+ enum dpll_pin_state pin_state;
+ } last;
+ struct notifier_block mdev_nb;
+ struct net_device *tracking_netdev;
+};
+
+static int mlx5_dpll_clock_id_get(struct mlx5_core_dev *mdev, u64 *clock_id)
+{
+ u32 out[MLX5_ST_SZ_DW(msecq_reg)] = {};
+ u32 in[MLX5_ST_SZ_DW(msecq_reg)] = {};
+ int err;
+
+ err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+ MLX5_REG_MSECQ, 0, 0);
+ if (err)
+ return err;
+ *clock_id = MLX5_GET64(msecq_reg, out, local_clock_identity);
+ return 0;
+}
+
+static int
+mlx5_dpll_synce_status_get(struct mlx5_core_dev *mdev,
+ enum mlx5_msees_admin_status *admin_status,
+ enum mlx5_msees_oper_status *oper_status,
+ bool *ho_acq)
+{
+ u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
+ u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
+ int err;
+
+ err = mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+ MLX5_REG_MSEES, 0, 0);
+ if (err)
+ return err;
+ if (admin_status)
+ *admin_status = MLX5_GET(msees_reg, out, admin_status);
+ *oper_status = MLX5_GET(msees_reg, out, oper_status);
+ if (ho_acq)
+ *ho_acq = MLX5_GET(msees_reg, out, ho_acq);
+ return 0;
+}
+
+static int
+mlx5_dpll_synce_status_set(struct mlx5_core_dev *mdev,
+ enum mlx5_msees_admin_status admin_status)
+{
+ u32 out[MLX5_ST_SZ_DW(msees_reg)] = {};
+ u32 in[MLX5_ST_SZ_DW(msees_reg)] = {};
+
+ MLX5_SET(msees_reg, in, field_select,
+ MLX5_MSEES_FIELD_SELECT_ENABLE |
+ MLX5_MSEES_FIELD_SELECT_ADMIN_STATUS);
+ MLX5_SET(msees_reg, in, admin_status, admin_status);
+ return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out),
+ MLX5_REG_MSEES, 0, 1);
+}
+
+static enum dpll_lock_status
+mlx5_dpll_lock_status_get(enum mlx5_msees_oper_status oper_status, bool ho_acq)
+{
+ switch (oper_status) {
+ case MLX5_MSEES_OPER_STATUS_SELF_TRACK:
+ fallthrough;
+ case MLX5_MSEES_OPER_STATUS_OTHER_TRACK:
+ return ho_acq ? DPLL_LOCK_STATUS_LOCKED_HO_ACQ :
+ DPLL_LOCK_STATUS_LOCKED;
+ case MLX5_MSEES_OPER_STATUS_HOLDOVER:
+ fallthrough;
+ case MLX5_MSEES_OPER_STATUS_FAIL_HOLDOVER:
+ return DPLL_LOCK_STATUS_HOLDOVER;
+ default:
+ return DPLL_LOCK_STATUS_UNLOCKED;
+ }
+}
+
+static enum dpll_pin_state
+mlx5_dpll_pin_state_get(enum mlx5_msees_admin_status admin_status,
+ enum mlx5_msees_oper_status oper_status)
+{
+ return (admin_status == MLX5_MSEES_ADMIN_STATUS_TRACK &&
+ (oper_status == MLX5_MSEES_OPER_STATUS_SELF_TRACK ||
+ oper_status == MLX5_MSEES_OPER_STATUS_OTHER_TRACK)) ?
+ DPLL_PIN_STATE_CONNECTED : DPLL_PIN_STATE_DISCONNECTED;
+}
+
+static int mlx5_dpll_device_lock_status_get(const struct dpll_device *dpll,
+ void *priv,
+ enum dpll_lock_status *status,
+ struct netlink_ext_ack *extack)
+{
+ enum mlx5_msees_oper_status oper_status;
+ struct mlx5_dpll *mdpll = priv;
+ bool ho_acq;
+ int err;
+
+ err = mlx5_dpll_synce_status_get(mdpll->mdev, NULL,
+ &oper_status, &ho_acq);
+ if (err)
+ return err;
+
+ *status = mlx5_dpll_lock_status_get(oper_status, ho_acq);
+ return 0;
+}
+
+static int mlx5_dpll_device_mode_get(const struct dpll_device *dpll,
+ void *priv,
+ u32 *mode, struct netlink_ext_ack *extack)
+{
+ *mode = DPLL_MODE_MANUAL;
+ return 0;
+}
+
+static bool mlx5_dpll_device_mode_supported(const struct dpll_device *dpll,
+ void *priv,
+ enum dpll_mode mode,
+ struct netlink_ext_ack *extack)
+{
+ return mode == DPLL_MODE_MANUAL;
+}
+
+static const struct dpll_device_ops mlx5_dpll_device_ops = {
+ .lock_status_get = mlx5_dpll_device_lock_status_get,
+ .mode_get = mlx5_dpll_device_mode_get,
+ .mode_supported = mlx5_dpll_device_mode_supported,
+};
+
+static int mlx5_dpll_pin_direction_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_direction *direction,
+ struct netlink_ext_ack *extack)
+{
+ *direction = DPLL_PIN_DIRECTION_INPUT;
+ return 0;
+}
+
+static int mlx5_dpll_state_on_dpll_get(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state *state,
+ struct netlink_ext_ack *extack)
+{
+ enum mlx5_msees_admin_status admin_status;
+ enum mlx5_msees_oper_status oper_status;
+ struct mlx5_dpll *mdpll = pin_priv;
+ int err;
+
+ err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status,
+ &oper_status, NULL);
+ if (err)
+ return err;
+ *state = mlx5_dpll_pin_state_get(admin_status, oper_status);
+ return 0;
+}
+
+static int mlx5_dpll_state_on_dpll_set(const struct dpll_pin *pin,
+ void *pin_priv,
+ const struct dpll_device *dpll,
+ void *dpll_priv,
+ enum dpll_pin_state state,
+ struct netlink_ext_ack *extack)
+{
+ struct mlx5_dpll *mdpll = pin_priv;
+
+ return mlx5_dpll_synce_status_set(mdpll->mdev,
+ state == DPLL_PIN_STATE_CONNECTED ?
+ MLX5_MSEES_ADMIN_STATUS_TRACK :
+ MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
+}
+
+static const struct dpll_pin_ops mlx5_dpll_pins_ops = {
+ .direction_get = mlx5_dpll_pin_direction_get,
+ .state_on_dpll_get = mlx5_dpll_state_on_dpll_get,
+ .state_on_dpll_set = mlx5_dpll_state_on_dpll_set,
+};
+
+static const struct dpll_pin_properties mlx5_dpll_pin_properties = {
+ .type = DPLL_PIN_TYPE_SYNCE_ETH_PORT,
+ .capabilities = DPLL_PIN_CAPS_STATE_CAN_CHANGE,
+};
+
+#define MLX5_DPLL_PERIODIC_WORK_INTERVAL 500 /* ms */
+
+static void mlx5_dpll_periodic_work_queue(struct mlx5_dpll *mdpll)
+{
+ queue_delayed_work(mdpll->wq, &mdpll->work,
+ msecs_to_jiffies(MLX5_DPLL_PERIODIC_WORK_INTERVAL));
+}
+
+static void mlx5_dpll_periodic_work(struct work_struct *work)
+{
+ struct mlx5_dpll *mdpll = container_of(work, struct mlx5_dpll,
+ work.work);
+ enum mlx5_msees_admin_status admin_status;
+ enum mlx5_msees_oper_status oper_status;
+ enum dpll_lock_status lock_status;
+ enum dpll_pin_state pin_state;
+ bool ho_acq;
+ int err;
+
+ err = mlx5_dpll_synce_status_get(mdpll->mdev, &admin_status,
+ &oper_status, &ho_acq);
+ if (err)
+ goto err_out;
+ lock_status = mlx5_dpll_lock_status_get(oper_status, ho_acq);
+ pin_state = mlx5_dpll_pin_state_get(admin_status, oper_status);
+
+ if (!mdpll->last.valid)
+ goto invalid_out;
+
+ if (mdpll->last.lock_status != lock_status)
+ dpll_device_change_ntf(mdpll->dpll);
+ if (mdpll->last.pin_state != pin_state)
+ dpll_pin_change_ntf(mdpll->dpll_pin);
+
+invalid_out:
+ mdpll->last.lock_status = lock_status;
+ mdpll->last.pin_state = pin_state;
+ mdpll->last.valid = true;
+err_out:
+ mlx5_dpll_periodic_work_queue(mdpll);
+}
+
+static void mlx5_dpll_netdev_dpll_pin_set(struct mlx5_dpll *mdpll,
+ struct net_device *netdev)
+{
+ if (mdpll->tracking_netdev)
+ return;
+ netdev_dpll_pin_set(netdev, mdpll->dpll_pin);
+ mdpll->tracking_netdev = netdev;
+}
+
+static void mlx5_dpll_netdev_dpll_pin_clear(struct mlx5_dpll *mdpll)
+{
+ if (!mdpll->tracking_netdev)
+ return;
+ netdev_dpll_pin_clear(mdpll->tracking_netdev);
+ mdpll->tracking_netdev = NULL;
+}
+
+static int mlx5_dpll_mdev_notifier_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct mlx5_dpll *mdpll = container_of(nb, struct mlx5_dpll, mdev_nb);
+ struct net_device *netdev = data;
+
+ switch (event) {
+ case MLX5_DRIVER_EVENT_UPLINK_NETDEV:
+ if (netdev)
+ mlx5_dpll_netdev_dpll_pin_set(mdpll, netdev);
+ else
+ mlx5_dpll_netdev_dpll_pin_clear(mdpll);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static void mlx5_dpll_mdev_netdev_track(struct mlx5_dpll *mdpll,
+ struct mlx5_core_dev *mdev)
+{
+ mdpll->mdev_nb.notifier_call = mlx5_dpll_mdev_notifier_event;
+ mlx5_blocking_notifier_register(mdev, &mdpll->mdev_nb);
+ mlx5_core_uplink_netdev_event_replay(mdev);
+}
+
+static void mlx5_dpll_mdev_netdev_untrack(struct mlx5_dpll *mdpll,
+ struct mlx5_core_dev *mdev)
+{
+ mlx5_blocking_notifier_unregister(mdev, &mdpll->mdev_nb);
+ mlx5_dpll_netdev_dpll_pin_clear(mdpll);
+}
+
+static int mlx5_dpll_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct mlx5_adev *edev = container_of(adev, struct mlx5_adev, adev);
+ struct mlx5_core_dev *mdev = edev->mdev;
+ struct mlx5_dpll *mdpll;
+ u64 clock_id;
+ int err;
+
+ err = mlx5_dpll_synce_status_set(mdev,
+ MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
+ if (err)
+ return err;
+
+ err = mlx5_dpll_clock_id_get(mdev, &clock_id);
+ if (err)
+ return err;
+
+ mdpll = kzalloc(sizeof(*mdpll), GFP_KERNEL);
+ if (!mdpll)
+ return -ENOMEM;
+ mdpll->mdev = mdev;
+ auxiliary_set_drvdata(adev, mdpll);
+
+ /* Multiple mdev instances might share one DPLL device. */
+ mdpll->dpll = dpll_device_get(clock_id, 0, THIS_MODULE);
+ if (IS_ERR(mdpll->dpll)) {
+ err = PTR_ERR(mdpll->dpll);
+ goto err_free_mdpll;
+ }
+
+ err = dpll_device_register(mdpll->dpll, DPLL_TYPE_EEC,
+ &mlx5_dpll_device_ops, mdpll);
+ if (err)
+ goto err_put_dpll_device;
+
+ /* Multiple mdev instances might share one DPLL pin. */
+ mdpll->dpll_pin = dpll_pin_get(clock_id, mlx5_get_dev_index(mdev),
+ THIS_MODULE, &mlx5_dpll_pin_properties);
+ if (IS_ERR(mdpll->dpll_pin)) {
+ err = PTR_ERR(mdpll->dpll_pin);
+ goto err_unregister_dpll_device;
+ }
+
+ err = dpll_pin_register(mdpll->dpll, mdpll->dpll_pin,
+ &mlx5_dpll_pins_ops, mdpll);
+ if (err)
+ goto err_put_dpll_pin;
+
+ mdpll->wq = create_singlethread_workqueue("mlx5_dpll");
+ if (!mdpll->wq) {
+ err = -ENOMEM;
+ goto err_unregister_dpll_pin;
+ }
+
+ mlx5_dpll_mdev_netdev_track(mdpll, mdev);
+
+ INIT_DELAYED_WORK(&mdpll->work, &mlx5_dpll_periodic_work);
+ mlx5_dpll_periodic_work_queue(mdpll);
+
+ return 0;
+
+err_unregister_dpll_pin:
+ dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
+ &mlx5_dpll_pins_ops, mdpll);
+err_put_dpll_pin:
+ dpll_pin_put(mdpll->dpll_pin);
+err_unregister_dpll_device:
+ dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
+err_put_dpll_device:
+ dpll_device_put(mdpll->dpll);
+err_free_mdpll:
+ kfree(mdpll);
+ return err;
+}
+
+static void mlx5_dpll_remove(struct auxiliary_device *adev)
+{
+ struct mlx5_dpll *mdpll = auxiliary_get_drvdata(adev);
+ struct mlx5_core_dev *mdev = mdpll->mdev;
+
+ cancel_delayed_work(&mdpll->work);
+ mlx5_dpll_mdev_netdev_untrack(mdpll, mdev);
+ destroy_workqueue(mdpll->wq);
+ dpll_pin_unregister(mdpll->dpll, mdpll->dpll_pin,
+ &mlx5_dpll_pins_ops, mdpll);
+ dpll_pin_put(mdpll->dpll_pin);
+ dpll_device_unregister(mdpll->dpll, &mlx5_dpll_device_ops, mdpll);
+ dpll_device_put(mdpll->dpll);
+ kfree(mdpll);
+
+ mlx5_dpll_synce_status_set(mdev,
+ MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING);
+}
+
+static int mlx5_dpll_suspend(struct auxiliary_device *adev, pm_message_t state)
+{
+ return 0;
+}
+
+static int mlx5_dpll_resume(struct auxiliary_device *adev)
+{
+ return 0;
+}
+
+static const struct auxiliary_device_id mlx5_dpll_id_table[] = {
+ { .name = MLX5_ADEV_NAME ".dpll", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(auxiliary, mlx5_dpll_id_table);
+
+static struct auxiliary_driver mlx5_dpll_driver = {
+ .name = "dpll",
+ .probe = mlx5_dpll_probe,
+ .remove = mlx5_dpll_remove,
+ .suspend = mlx5_dpll_suspend,
+ .resume = mlx5_dpll_resume,
+ .id_table = mlx5_dpll_id_table,
+};
+
+static int __init mlx5_dpll_init(void)
+{
+ return auxiliary_driver_register(&mlx5_dpll_driver);
+}
+
+static void __exit mlx5_dpll_exit(void)
+{
+ auxiliary_driver_unregister(&mlx5_dpll_driver);
+}
+
+module_init(mlx5_dpll_init);
+module_exit(mlx5_dpll_exit);
+
+MODULE_AUTHOR("Jiri Pirko <[email protected]>");
+MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) DPLL driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index 94d2be5848ae..e063af87b552 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -154,6 +154,8 @@ enum {
MLX5_REG_MCC = 0x9062,
MLX5_REG_MCDA = 0x9063,
MLX5_REG_MCAM = 0x907f,
+ MLX5_REG_MSECQ = 0x9155,
+ MLX5_REG_MSEES = 0x9156,
MLX5_REG_MIRC = 0x9162,
MLX5_REG_SBCAM = 0xB01F,
MLX5_REG_RESOURCE_DUMP = 0xC000,
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index b89778d0d326..1381c668684f 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -10193,7 +10193,9 @@ struct mlx5_ifc_mcam_access_reg_bits2 {
u8 mirc[0x1];
u8 regs_97_to_96[0x2];

- u8 regs_95_to_64[0x20];
+ u8 regs_95_to_87[0x09];
+ u8 synce_registers[0x2];
+ u8 regs_84_to_64[0x15];

u8 regs_63_to_32[0x20];

@@ -12555,4 +12557,59 @@ struct mlx5_ifc_modify_page_track_obj_in_bits {
struct mlx5_ifc_page_track_bits obj_context;
};

+struct mlx5_ifc_msecq_reg_bits {
+ u8 reserved_at_0[0x20];
+
+ u8 reserved_at_20[0x12];
+ u8 network_option[0x2];
+ u8 local_ssm_code[0x4];
+ u8 local_enhanced_ssm_code[0x8];
+
+ u8 local_clock_identity[0x40];
+
+ u8 reserved_at_80[0x180];
+};
+
+enum {
+ MLX5_MSEES_FIELD_SELECT_ENABLE = BIT(0),
+ MLX5_MSEES_FIELD_SELECT_ADMIN_STATUS = BIT(1),
+ MLX5_MSEES_FIELD_SELECT_ADMIN_FREQ_MEASURE = BIT(2),
+};
+
+enum mlx5_msees_admin_status {
+ MLX5_MSEES_ADMIN_STATUS_FREE_RUNNING = 0x0,
+ MLX5_MSEES_ADMIN_STATUS_TRACK = 0x1,
+};
+
+enum mlx5_msees_oper_status {
+ MLX5_MSEES_OPER_STATUS_FREE_RUNNING = 0x0,
+ MLX5_MSEES_OPER_STATUS_SELF_TRACK = 0x1,
+ MLX5_MSEES_OPER_STATUS_OTHER_TRACK = 0x2,
+ MLX5_MSEES_OPER_STATUS_HOLDOVER = 0x3,
+ MLX5_MSEES_OPER_STATUS_FAIL_HOLDOVER = 0x4,
+ MLX5_MSEES_OPER_STATUS_FAIL_FREE_RUNNING = 0x5,
+};
+
+struct mlx5_ifc_msees_reg_bits {
+ u8 reserved_at_0[0x8];
+ u8 local_port[0x8];
+ u8 pnat[0x2];
+ u8 lp_msb[0x2];
+ u8 reserved_at_14[0xc];
+
+ u8 field_select[0x20];
+
+ u8 admin_status[0x4];
+ u8 oper_status[0x4];
+ u8 ho_acq[0x1];
+ u8 reserved_at_49[0xc];
+ u8 admin_freq_measure[0x1];
+ u8 oper_freq_measure[0x1];
+ u8 failure_reason[0x9];
+
+ u8 frequency_diff[0x20];
+
+ u8 reserved_at_80[0x180];
+};
+
#endif /* MLX5_IFC_H */
--
2.37.3


2023-06-09 23:45:11

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH v8 00/10] Create common DPLL configuration API

On Fri, 9 Jun 2023 14:18:43 +0200 Arkadiusz Kubalewski wrote:
> Implement common API for clock/DPLL configuration and status reporting.
> The API utilises netlink interface as transport for commands and event
> notifications. This API aim to extend current pin configuration and
> make it flexible and easy to cover special configurations.
>
> Netlink interface is based on ynl spec, it allows use of in-kernel
> tools/net/ynl/cli.py application to control the interface with properly
> formated command and json attribute strings. Here are few command
> examples of how it works with `ice` driver on supported NIC:

Now I see why you sent the ynl-gen patch you sent :) It's the
combination of nest and multi-attr which doesn't populate the right
policy. Also the enums are not policed right by ynl-gen.

I pushed two fixes to my local tree:
https://github.com/kuba-moo/linux/commits/ynl-c
in case you need them. I'll submit them on Monday.

I also tried to run the user space C code gen (for generating C
API/lib as described in [1]). It seems to work just fine, FWIW, so
just LMK if C bindings would be useful, I can give more instructions.

[1] https://docs.kernel.org/next/userspace-api/netlink/intro-specs.html#ynl-lib

2023-06-10 03:51:18

by Bagas Sanjaya

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

On Fri, Jun 09, 2023 at 02:18:44PM +0200, Arkadiusz Kubalewski wrote:
> diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
> new file mode 100644
> index 000000000000..8caa4af022ad
> --- /dev/null
> +++ b/Documentation/driver-api/dpll.rst
> @@ -0,0 +1,458 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +===============================
> +The Linux kernel dpll 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 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.
> +
> +For other dpll device operating modes there is no pin selection
> +mechanics.
> +
> +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 provide user with additional nested
> +attribute ``DPLL_A_PIN_PARENT`` 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',
> + 'pin-dpll-caps': 4,
> + 'pin-id': 13,
> + 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
> + {'pin-id': 3, 'pin-state': 'disconnected'},
> + {'id': 0, 'pin-direction': 'input'},
> + {'id': 1, 'pin-direction': 'input'}],
> + 'pin-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`` nested attribute for requesting configuration
> + related to parent pin
> + ``DPLL_A_PIN_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`` child pin id
> + ``DPLL_A_PIN_PARENT`` nested attribute for requesting configuration
> + related to parent pin
> + ``DPLL_A_ID`` parent dpll id
> + ``DPLL_A_PIN_PRIO`` requested pin prio on parent dpll
> +
> +Child pin of MUX-type is not capable of automatic input pin selection,
> +in order to configure a 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.
> +
> +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
> +=================================================
> +
> +All constants identifying command types use a ``DPLL_CMD_`` prefix and
> +suffix according to command purpose. All 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
> + ``DPLL_CMD_PIN_GET`` command to get pin 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_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_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_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_DIRECTION`` attr direction 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_PARENT`` nested attr for each parent the
> + pin is connected with
> + ``DPLL_A_ID`` attr provided if parent is dpll
> + device
> + ``DPLL_A_PIN_ID`` attr provided if parent is a pin
> + ``DPLL_A_PIN_PRIO`` attr priority of pin on the
> + dpll device
> + ``DPLL_A_PIN_STATE`` attr state of pin on the dpll
> + device or on the parent pin
> + ``DPLL_A_PIN_DPLL_CAPS`` attr bitmask of pin-dpll
> + capabilities
> + ``DPLL_CMD_PIN_SET`` command to set pins configuration
> + ``DPLL_A_PIN_ID`` attr unique a pin ID
> + ``DPLL_A_PIN_DIRECTION`` attr requested direction of a pin
> + ``DPLL_A_PIN_FREQUENCY`` attr requested frequency of a pin
> + ``DPLL_A_PIN_PARENT`` nested attr for each parent
> + related configuration of a pin
> + requested
> + ``DPLL_A_ID`` attr provided if parent is dpll
> + device
> + ``DPLL_A_PIN_ID`` attr provided if parent is 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 or on the 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 has to 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_DIRECTION`` or ``DPLL_A_PIN_FREQUENCY`` are configured,
> +this affects all the dpll device they are connected, that is why those
> +attributes shall not be enclosed in ``DPLL_A_PIN_PARENT``.
> +Other attributes:
> +``DPLL_A_PIN_PRIO`` or ``DPLL_A_PIN_STATE`` must be enclosed in
> +``DPLL_A_PIN_PARENT`` as their configuration relates to only one
> +parent dpll or parent pin.
> +Nested attribute of either ``DPLL_A_ID`` or ``DPLL_A_PIN_ID`` determines
> +if configuration was requested on a dpll device or on a pin
> +respectively.
> +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.
> +
> +Device level configuration pre-defined enums
> +=================================================
> +
> +Values for ``DPLL_A_LOCK_STATUS`` attribute:
> +
> + ================================== ===================================
> + ``DPLL_LOCK_STATUS_UNLOCKED`` dpll device is in freerun, not
> + locked to any input pin
> + ``DPLL_LOCK_STATUS_LOCKED`` dpll device is locked to the input
> + but no holdover capability yet
> + acquired
> + ``DPLL_LOCK_STATUS_LOCKED_HO_ACQ`` dpll device is locked to the input
> + pin with holdover capability
> + acquired
> + ``DPLL_LOCK_STATUS_HOLDOVER`` dpll device lost a lock, using its
> + frequency holdover capabilities
> +
> +Values for ``DPLL_A_MODE`` attribute:
> +
> + ======================= ==============================================
> + ``DPLL_MODE_MANUAL`` input pin is manually selected by setting pin
> + state to ``DPLL_PIN_STATE_CONNECTED`` on a
> + dpll device
> + ``DPLL_MODE_AUTOMATIC`` input pin is auto selected according to
> + configured pin priorities and input signal
> + validity
> + ``DPLL_MODE_HOLDOVER`` force holdover mode of dpll
> + ``DPLL_MODE_FREERUN`` dpll device is driven by supplied system clock
> + without holdover capabilities
> +
> +Values for ``DPLL_A_TYPE`` attribute:
> +
> + ================= ===================================================
> + ``DPLL_TYPE_PPS`` dpll device used to provide pulse-per-second output
> + ``DPLL_TYPE_EEC`` dpll device used to drive ethernet equipment clock
> +
> +Pin level configuration pre-defined enums
> +=========================================
> +
> +Values for ``DPLL_A_PIN_STATE`` attribute:
> +
> + =============================== ======================================
> + ``DPLL_PIN_STATE_CONNECTED`` Pin used as active input for a dpll
> + device or for a parent pin
> + ``DPLL_PIN_STATE_DISCONNECTED`` Pin disconnected from a dpll device or
> + from a parent pin
> + ``DPLL_PIN_STATE_SELECTABLE`` Pin enabled for automatic selection
> +
> +Values for ``DPLL_A_PIN_DIRECTION`` attribute:
> +
> + ============================= ====================================
> + ``DPLL_PIN_DIRECTION_INPUT`` used to provide its signal to a dpll
> + device
> + ``DPLL_PIN_DIRECTION_OUTPUT`` used to output the signal from a dpll
> + device
> +
> +Values for ``DPLL_A_PIN_TYPE`` attributes:
> +
> + ================================ =====================================
> + ``DPLL_PIN_TYPE_MUX`` MUX type pin, connected pins shall
> + have their own types
> + ``DPLL_PIN_TYPE_EXT`` External pin
> + ``DPLL_PIN_TYPE_SYNCE_ETH_PORT`` SyncE on Ethernet port
> + ``DPLL_PIN_TYPE_INT_OSCILLATOR`` Internal Oscillator (i.e. Holdover
> + with Atomic Clock as an input)
> + ``DPLL_PIN_TYPE_GNSS`` GNSS 1PPS input
> +
> +Values for ``DPLL_A_PIN_DPLL_CAPS`` attributes:
> +
> + ====================================== ===============================
> + ``DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE`` Bit present if direction of
> + pin can change
> + ``DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE`` Bit present if priority of pin
> + can change
> + ``DPLL_PIN_CAPS_STATE_CAN_CHANGE`` Bit present if state of pin can
> + change
> +
> +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. Neverthelessi, there are few required to be
> +implemented.
> +Required dpll device level callback operations:
> +- ``.mode_get``,
> +- ``.lock_status_get``.
> +
> +Required pin level callback operations:
> +- ``.state_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
> +``-ENOTSUPP`` is returned in case of absence of specific handler.
> +
> +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:
> +netdev_dpll_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``.

There are countless htmldocs warnings, so I have to fix them up:

---- >8 ----
diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
index 8caa4af022ad82..5b2d3e3b9f8893 100644
--- a/Documentation/driver-api/dpll.rst
+++ b/Documentation/driver-api/dpll.rst
@@ -63,16 +63,19 @@ 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.
@@ -85,6 +88,7 @@ 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
@@ -103,31 +107,32 @@ 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:
+attributes with current state related to each parent, like::

-``'pin': [{
- {'clock-id': 282574471561216,
- 'module-name': 'ice',
- 'pin-dpll-caps': 4,
- 'pin-id': 13,
- 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
- {'pin-id': 3, 'pin-state': 'disconnected'},
- {'id': 0, 'pin-direction': 'input'},
- {'id': 1, 'pin-direction': 'input'}],
- 'pin-type': 'synce-eth-port'}
-}]``
+ 'pin': [{
+ {'clock-id': 282574471561216,
+ 'module-name': 'ice',
+ 'pin-dpll-caps': 4,
+ 'pin-id': 13,
+ 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
+ {'pin-id': 3, 'pin-state': 'disconnected'},
+ {'id': 0, 'pin-direction': 'input'},
+ {'id': 1, 'pin-direction': 'input'}],
+ 'pin-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`` nested attribute for requesting configuration
related to parent pin
``DPLL_A_PIN_ID`` parent pin id
``DPLL_A_PIN_STATE`` requested pin state on parent
+ ====================== =============================================

Pin priority
============
@@ -149,6 +154,7 @@ device. Example of netlink `set priority on parent pin` message format:
related to parent pin
``DPLL_A_ID`` parent dpll id
``DPLL_A_PIN_PRIO`` requested pin prio on parent dpll
+ ===================== =============================================

Child pin of MUX-type is not capable of automatic input pin selection,
in order to configure a input of a MUX-type pin, the user needs to
@@ -254,6 +260,7 @@ prefix and suffix according to attribute purpose:
``DPLL_A_PIN_STATE`` attr requested state of pin on
the dpll device or on the parent
pin
+ ==================================== =================================

Netlink dump requests
=====================
@@ -303,6 +310,7 @@ Values for ``DPLL_A_LOCK_STATUS`` attribute:
acquired
``DPLL_LOCK_STATUS_HOLDOVER`` dpll device lost a lock, using its
frequency holdover capabilities
+ ================================== ===================================

Values for ``DPLL_A_MODE`` attribute:

@@ -316,12 +324,14 @@ Values for ``DPLL_A_MODE`` attribute:
``DPLL_MODE_HOLDOVER`` force holdover mode of dpll
``DPLL_MODE_FREERUN`` dpll device is driven by supplied system clock
without holdover capabilities
+ ======================= ==============================================

Values for ``DPLL_A_TYPE`` attribute:

================= ===================================================
``DPLL_TYPE_PPS`` dpll device used to provide pulse-per-second output
``DPLL_TYPE_EEC`` dpll device used to drive ethernet equipment clock
+ ================= ===================================================

Pin level configuration pre-defined enums
=========================================
@@ -334,6 +344,7 @@ Values for ``DPLL_A_PIN_STATE`` attribute:
``DPLL_PIN_STATE_DISCONNECTED`` Pin disconnected from a dpll device or
from a parent pin
``DPLL_PIN_STATE_SELECTABLE`` Pin enabled for automatic selection
+ =============================== ======================================

Values for ``DPLL_A_PIN_DIRECTION`` attribute:

@@ -342,6 +353,7 @@ Values for ``DPLL_A_PIN_DIRECTION`` attribute:
device
``DPLL_PIN_DIRECTION_OUTPUT`` used to output the signal from a dpll
device
+ ============================= ====================================

Values for ``DPLL_A_PIN_TYPE`` attributes:

@@ -353,6 +365,7 @@ Values for ``DPLL_A_PIN_TYPE`` attributes:
``DPLL_PIN_TYPE_INT_OSCILLATOR`` Internal Oscillator (i.e. Holdover
with Atomic Clock as an input)
``DPLL_PIN_TYPE_GNSS`` GNSS 1PPS input
+ ================================ =====================================

Values for ``DPLL_A_PIN_DPLL_CAPS`` attributes:

@@ -363,6 +376,7 @@ Values for ``DPLL_A_PIN_DPLL_CAPS`` attributes:
can change
``DPLL_PIN_CAPS_STATE_CAN_CHANGE`` Bit present if state of pin can
change
+ ====================================== ===============================

Notifications
=============
@@ -381,6 +395,7 @@ Notifications messages:
``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
@@ -413,6 +428,7 @@ 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.

@@ -422,6 +438,7 @@ 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
@@ -431,10 +448,11 @@ The device driver using dpll interface is not required to implement all
the callback operation. Neverthelessi, there are few required to be
implemented.
Required dpll device level callback operations:
+
- ``.mode_get``,
- ``.lock_status_get``.

-Required pin level callback operations:
+oRequired pin level callback operations:
- ``.state_get`` (pins registered with dpll device),
- ``.state_on_pin_get`` (pins registered with parent pin),
- ``.direction_get``.
@@ -451,8 +469,8 @@ 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:
-netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin);
+netdevice itself with
+``netdev_dpll_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``.

(but because the fix diff above is quite large, Co-developed-by: from
me may qualify).

Thanks.

--
An old man doll... just what I always wanted! - Clara


Attachments:
(No filename) (32.73 kB)
signature.asc (235.00 B)
Download all attachments

2023-06-10 07:35:43

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 05/10] dpll: api header: Add DPLL framework base functions

Fri, Jun 09, 2023 at 02:18:48PM CEST, [email protected] wrote:
>From: Vadim Fedorenko <[email protected]>

[...]

>+struct dpll_device_ops {
>+ int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_mode *mode, struct netlink_ext_ack *extack);
>+ int (*mode_set)(const struct dpll_device *dpll, void *dpll_priv,
>+ const enum dpll_mode mode,
>+ struct netlink_ext_ack *extack);
>+ bool (*mode_supported)(const struct dpll_device *dpll, void *dpll_priv,
>+ const enum dpll_mode mode,
>+ struct netlink_ext_ack *extack);
>+ int (*source_pin_idx_get)(const struct dpll_device *dpll,
>+ void *dpll_priv,
>+ u32 *pin_idx,
>+ struct netlink_ext_ack *extack);

I'm pretty sure I wrote this to the last patchset version as well.
You call this op from anywhere, it's a leftover, please remove it.
In ptp_ocp remove it as well and implement the state_on_dpll pin op
instead. I'm pretty sure no one tested ptp_ocp with this patchset
version otherwise this would show-up :/

[...]

2023-06-10 07:35:46

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 05/10] dpll: api header: Add DPLL framework base functions

Fri, Jun 09, 2023 at 02:18:48PM CEST, [email protected] wrote:
>From: Vadim Fedorenko <[email protected]>
>
>DPLL framework is used to represent and configure DPLL devices
>in systems. Each device that has DPLL and can configure sources
>and outputs can use this framework. Netlink interface is used to
>provide configuration data and to receive notification messages
>about changes in the configuration or status of DPLL device.
>Inputs and outputs of the DPLL device are represented as special
>objects which could be dynamically added to and removed from DPLL
>device.
>
>Add kernel api header, make dpll subsystem available to device drivers.
>
>Add/update makefiles/Kconfig to allow compilation of dpll subsystem.
>
>Co-developed-by: Milena Olech <[email protected]>
>Signed-off-by: Milena Olech <[email protected]>
>Co-developed-by: Michal Michalik <[email protected]>
>Signed-off-by: Michal Michalik <[email protected]>
>Signed-off-by: Vadim Fedorenko <[email protected]>
>Co-developed-by: Arkadiusz Kubalewski <[email protected]>
>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>---
> MAINTAINERS | 8 +++
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/dpll/Kconfig | 7 ++
> drivers/dpll/Makefile | 9 +++
> include/linux/dpll.h | 144 ++++++++++++++++++++++++++++++++++++++++++
> 6 files changed, 171 insertions(+)
> create mode 100644 drivers/dpll/Kconfig
> create mode 100644 drivers/dpll/Makefile
> create mode 100644 include/linux/dpll.h
>
>diff --git a/MAINTAINERS b/MAINTAINERS
>index 288d9a5edb9d..0e69429ecc55 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -6306,6 +6306,14 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive
> F: drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
> F: drivers/net/ethernet/freescale/dpaa2/dpsw*
>
>+DPLL CLOCK SUBSYSTEM
>+M: Vadim Fedorenko <[email protected]>
>+L: [email protected]
>+S: Maintained

I think status should be rather "Supported":
"Supported: Someone is actually paid to look after this."

Also, I think that it would be good to have Arkadiusz Kubalewski
listed here, as he is the one that knows the subsystem by heart.

Also, if you don't mind, I would be happy as a co-maintainer of the
subsystem to be listed here, as I helped to shape the code and
interfaces and I also know it pretty good.



>+F: drivers/dpll/*
>+F: include/net/dpll.h
>+F: include/uapi/linux/dpll.h
>+
> DRBD DRIVER
> M: Philipp Reisner <[email protected]>
> M: Lars Ellenberg <[email protected]>
>diff --git a/drivers/Kconfig b/drivers/Kconfig
>index 514ae6b24cb2..ce5f63918eba 100644
>--- a/drivers/Kconfig
>+++ b/drivers/Kconfig
>@@ -243,4 +243,6 @@ source "drivers/hte/Kconfig"
>
> source "drivers/cdx/Kconfig"
>
>+source "drivers/dpll/Kconfig"
>+
> endmenu
>diff --git a/drivers/Makefile b/drivers/Makefile
>index 7241d80a7b29..6fea42a6dd05 100644
>--- a/drivers/Makefile
>+++ b/drivers/Makefile
>@@ -195,3 +195,4 @@ obj-$(CONFIG_PECI) += peci/
> obj-$(CONFIG_HTE) += hte/
> obj-$(CONFIG_DRM_ACCEL) += accel/
> obj-$(CONFIG_CDX_BUS) += cdx/
>+obj-$(CONFIG_DPLL) += dpll/
>diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
>new file mode 100644
>index 000000000000..a4cae73f20d3
>--- /dev/null
>+++ b/drivers/dpll/Kconfig
>@@ -0,0 +1,7 @@
>+# SPDX-License-Identifier: GPL-2.0-only
>+#
>+# Generic DPLL drivers configuration
>+#
>+
>+config DPLL
>+ bool
>diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>new file mode 100644
>index 000000000000..2e5b27850110
>--- /dev/null
>+++ b/drivers/dpll/Makefile
>@@ -0,0 +1,9 @@
>+# SPDX-License-Identifier: GPL-2.0
>+#
>+# Makefile for DPLL drivers.
>+#
>+
>+obj-$(CONFIG_DPLL) += dpll.o
>+dpll-y += dpll_core.o
>+dpll-y += dpll_netlink.o
>+dpll-y += dpll_nl.o
>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>new file mode 100644
>index 000000000000..a18bcaa13553
>--- /dev/null
>+++ b/include/linux/dpll.h
>@@ -0,0 +1,144 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>+ * Copyright (c) 2023 Intel and affiliates
>+ */
>+
>+#ifndef __DPLL_H__
>+#define __DPLL_H__
>+
>+#include <uapi/linux/dpll.h>
>+#include <linux/device.h>
>+#include <linux/netlink.h>
>+
>+struct dpll_device;
>+struct dpll_pin;
>+
>+struct dpll_device_ops {
>+ int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_mode *mode, struct netlink_ext_ack *extack);
>+ int (*mode_set)(const struct dpll_device *dpll, void *dpll_priv,
>+ const enum dpll_mode mode,
>+ struct netlink_ext_ack *extack);
>+ bool (*mode_supported)(const struct dpll_device *dpll, void *dpll_priv,
>+ const enum dpll_mode mode,
>+ struct netlink_ext_ack *extack);
>+ int (*source_pin_idx_get)(const struct dpll_device *dpll,
>+ void *dpll_priv,
>+ u32 *pin_idx,
>+ struct netlink_ext_ack *extack);
>+ int (*lock_status_get)(const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_lock_status *status,
>+ struct netlink_ext_ack *extack);
>+ int (*temp_get)(const struct dpll_device *dpll, void *dpll_priv,
>+ s32 *temp, struct netlink_ext_ack *extack);
>+};
>+
>+struct dpll_pin_ops {
>+ int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ const u64 frequency,
>+ struct netlink_ext_ack *extack);
>+ int (*frequency_get)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ u64 *frequency, struct netlink_ext_ack *extack);
>+ int (*direction_set)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ const enum dpll_pin_direction direction,
>+ struct netlink_ext_ack *extack);
>+ int (*direction_get)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_pin_direction *direction,
>+ struct netlink_ext_ack *extack);
>+ int (*state_on_pin_get)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_pin *parent_pin,
>+ void *parent_pin_priv,
>+ enum dpll_pin_state *state,
>+ struct netlink_ext_ack *extack);
>+ int (*state_on_dpll_get)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll,
>+ void *dpll_priv, enum dpll_pin_state *state,
>+ struct netlink_ext_ack *extack);
>+ int (*state_on_pin_set)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_pin *parent_pin,
>+ void *parent_pin_priv,
>+ const enum dpll_pin_state state,
>+ struct netlink_ext_ack *extack);
>+ int (*state_on_dpll_set)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll,
>+ void *dpll_priv,
>+ const enum dpll_pin_state state,
>+ struct netlink_ext_ack *extack);
>+ int (*prio_get)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ u32 *prio, struct netlink_ext_ack *extack);
>+ int (*prio_set)(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ const u32 prio, struct netlink_ext_ack *extack);
>+};
>+
>+struct dpll_pin_frequency {
>+ u64 min;
>+ u64 max;
>+};
>+
>+#define DPLL_PIN_FREQUENCY_RANGE(_min, _max) \
>+ { \
>+ .min = _min, \
>+ .max = _max, \
>+ }
>+
>+#define DPLL_PIN_FREQUENCY(_val) DPLL_PIN_FREQUENCY_RANGE(_val, _val)
>+#define DPLL_PIN_FREQUENCY_1PPS \
>+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_1_HZ)
>+#define DPLL_PIN_FREQUENCY_10MHZ \
>+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_MHZ)
>+#define DPLL_PIN_FREQUENCY_IRIG_B \
>+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_KHZ)
>+#define DPLL_PIN_FREQUENCY_DCF77 \
>+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_77_5_KHZ)
>+
>+struct dpll_pin_properties {
>+ const char *board_label;
>+ const char *panel_label;
>+ const char *package_label;
>+ enum dpll_pin_type type;
>+ unsigned long capabilities;
>+ u32 freq_supported_num;
>+ struct dpll_pin_frequency *freq_supported;
>+};
>+
>+struct dpll_device
>+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
>+
>+void dpll_device_put(struct dpll_device *dpll);
>+
>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+ const struct dpll_device_ops *ops, void *priv);
>+
>+void dpll_device_unregister(struct dpll_device *dpll,
>+ const struct dpll_device_ops *ops, void *priv);
>+
>+struct dpll_pin
>+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>+ const struct dpll_pin_properties *prop);
>+
>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv);
>+
>+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv);
>+
>+void dpll_pin_put(struct dpll_pin *pin);
>+
>+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv);
>+
>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv);
>+
>+int dpll_device_change_ntf(struct dpll_device *dpll);
>+
>+int dpll_pin_change_ntf(struct dpll_pin *pin);

Why exactly did you split this into a separate patch? To me, it does not
make any sense. Please squash this header addition to the


>+
>+#endif
>--
>2.37.3
>

2023-06-10 07:39:21

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 05/10] dpll: api header: Add DPLL framework base functions

Sat, Jun 10, 2023 at 09:25:59AM CEST, [email protected] wrote:
>Fri, Jun 09, 2023 at 02:18:48PM CEST, [email protected] wrote:
>>From: Vadim Fedorenko <[email protected]>
>>
>>DPLL framework is used to represent and configure DPLL devices
>>in systems. Each device that has DPLL and can configure sources
>>and outputs can use this framework. Netlink interface is used to
>>provide configuration data and to receive notification messages
>>about changes in the configuration or status of DPLL device.
>>Inputs and outputs of the DPLL device are represented as special
>>objects which could be dynamically added to and removed from DPLL
>>device.
>>
>>Add kernel api header, make dpll subsystem available to device drivers.
>>
>>Add/update makefiles/Kconfig to allow compilation of dpll subsystem.
>>
>>Co-developed-by: Milena Olech <[email protected]>
>>Signed-off-by: Milena Olech <[email protected]>
>>Co-developed-by: Michal Michalik <[email protected]>
>>Signed-off-by: Michal Michalik <[email protected]>
>>Signed-off-by: Vadim Fedorenko <[email protected]>
>>Co-developed-by: Arkadiusz Kubalewski <[email protected]>
>>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>>---
>> MAINTAINERS | 8 +++
>> drivers/Kconfig | 2 +
>> drivers/Makefile | 1 +
>> drivers/dpll/Kconfig | 7 ++
>> drivers/dpll/Makefile | 9 +++
>> include/linux/dpll.h | 144 ++++++++++++++++++++++++++++++++++++++++++
>> 6 files changed, 171 insertions(+)
>> create mode 100644 drivers/dpll/Kconfig
>> create mode 100644 drivers/dpll/Makefile
>> create mode 100644 include/linux/dpll.h
>>
>>diff --git a/MAINTAINERS b/MAINTAINERS
>>index 288d9a5edb9d..0e69429ecc55 100644
>>--- a/MAINTAINERS
>>+++ b/MAINTAINERS
>>@@ -6306,6 +6306,14 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive
>> F: drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
>> F: drivers/net/ethernet/freescale/dpaa2/dpsw*
>>
>>+DPLL CLOCK SUBSYSTEM
>>+M: Vadim Fedorenko <[email protected]>
>>+L: [email protected]
>>+S: Maintained
>
>I think status should be rather "Supported":
>"Supported: Someone is actually paid to look after this."
>
>Also, I think that it would be good to have Arkadiusz Kubalewski
>listed here, as he is the one that knows the subsystem by heart.
>
>Also, if you don't mind, I would be happy as a co-maintainer of the
>subsystem to be listed here, as I helped to shape the code and
>interfaces and I also know it pretty good.
>
>
>
>>+F: drivers/dpll/*
>>+F: include/net/dpll.h
>>+F: include/uapi/linux/dpll.h
>>+
>> DRBD DRIVER
>> M: Philipp Reisner <[email protected]>
>> M: Lars Ellenberg <[email protected]>
>>diff --git a/drivers/Kconfig b/drivers/Kconfig
>>index 514ae6b24cb2..ce5f63918eba 100644
>>--- a/drivers/Kconfig
>>+++ b/drivers/Kconfig
>>@@ -243,4 +243,6 @@ source "drivers/hte/Kconfig"
>>
>> source "drivers/cdx/Kconfig"
>>
>>+source "drivers/dpll/Kconfig"
>>+
>> endmenu
>>diff --git a/drivers/Makefile b/drivers/Makefile
>>index 7241d80a7b29..6fea42a6dd05 100644
>>--- a/drivers/Makefile
>>+++ b/drivers/Makefile
>>@@ -195,3 +195,4 @@ obj-$(CONFIG_PECI) += peci/
>> obj-$(CONFIG_HTE) += hte/
>> obj-$(CONFIG_DRM_ACCEL) += accel/
>> obj-$(CONFIG_CDX_BUS) += cdx/
>>+obj-$(CONFIG_DPLL) += dpll/
>>diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
>>new file mode 100644
>>index 000000000000..a4cae73f20d3
>>--- /dev/null
>>+++ b/drivers/dpll/Kconfig
>>@@ -0,0 +1,7 @@
>>+# SPDX-License-Identifier: GPL-2.0-only
>>+#
>>+# Generic DPLL drivers configuration
>>+#
>>+
>>+config DPLL
>>+ bool
>>diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>>new file mode 100644
>>index 000000000000..2e5b27850110
>>--- /dev/null
>>+++ b/drivers/dpll/Makefile
>>@@ -0,0 +1,9 @@
>>+# SPDX-License-Identifier: GPL-2.0
>>+#
>>+# Makefile for DPLL drivers.
>>+#
>>+
>>+obj-$(CONFIG_DPLL) += dpll.o
>>+dpll-y += dpll_core.o
>>+dpll-y += dpll_netlink.o
>>+dpll-y += dpll_nl.o
>>diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>>new file mode 100644
>>index 000000000000..a18bcaa13553
>>--- /dev/null
>>+++ b/include/linux/dpll.h
>>@@ -0,0 +1,144 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>>+ * Copyright (c) 2023 Intel and affiliates
>>+ */
>>+
>>+#ifndef __DPLL_H__
>>+#define __DPLL_H__
>>+
>>+#include <uapi/linux/dpll.h>
>>+#include <linux/device.h>
>>+#include <linux/netlink.h>
>>+
>>+struct dpll_device;
>>+struct dpll_pin;
>>+
>>+struct dpll_device_ops {
>>+ int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_mode *mode, struct netlink_ext_ack *extack);
>>+ int (*mode_set)(const struct dpll_device *dpll, void *dpll_priv,
>>+ const enum dpll_mode mode,
>>+ struct netlink_ext_ack *extack);
>>+ bool (*mode_supported)(const struct dpll_device *dpll, void *dpll_priv,
>>+ const enum dpll_mode mode,
>>+ struct netlink_ext_ack *extack);
>>+ int (*source_pin_idx_get)(const struct dpll_device *dpll,
>>+ void *dpll_priv,
>>+ u32 *pin_idx,
>>+ struct netlink_ext_ack *extack);
>>+ int (*lock_status_get)(const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_lock_status *status,
>>+ struct netlink_ext_ack *extack);
>>+ int (*temp_get)(const struct dpll_device *dpll, void *dpll_priv,
>>+ s32 *temp, struct netlink_ext_ack *extack);
>>+};
>>+
>>+struct dpll_pin_ops {
>>+ int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ const u64 frequency,
>>+ struct netlink_ext_ack *extack);
>>+ int (*frequency_get)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ u64 *frequency, struct netlink_ext_ack *extack);
>>+ int (*direction_set)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ const enum dpll_pin_direction direction,
>>+ struct netlink_ext_ack *extack);
>>+ int (*direction_get)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_pin_direction *direction,
>>+ struct netlink_ext_ack *extack);
>>+ int (*state_on_pin_get)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_pin *parent_pin,
>>+ void *parent_pin_priv,
>>+ enum dpll_pin_state *state,
>>+ struct netlink_ext_ack *extack);
>>+ int (*state_on_dpll_get)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll,
>>+ void *dpll_priv, enum dpll_pin_state *state,
>>+ struct netlink_ext_ack *extack);
>>+ int (*state_on_pin_set)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_pin *parent_pin,
>>+ void *parent_pin_priv,
>>+ const enum dpll_pin_state state,
>>+ struct netlink_ext_ack *extack);
>>+ int (*state_on_dpll_set)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll,
>>+ void *dpll_priv,
>>+ const enum dpll_pin_state state,
>>+ struct netlink_ext_ack *extack);
>>+ int (*prio_get)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ u32 *prio, struct netlink_ext_ack *extack);
>>+ int (*prio_set)(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ const u32 prio, struct netlink_ext_ack *extack);
>>+};
>>+
>>+struct dpll_pin_frequency {
>>+ u64 min;
>>+ u64 max;
>>+};
>>+
>>+#define DPLL_PIN_FREQUENCY_RANGE(_min, _max) \
>>+ { \
>>+ .min = _min, \
>>+ .max = _max, \
>>+ }
>>+
>>+#define DPLL_PIN_FREQUENCY(_val) DPLL_PIN_FREQUENCY_RANGE(_val, _val)
>>+#define DPLL_PIN_FREQUENCY_1PPS \
>>+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_1_HZ)
>>+#define DPLL_PIN_FREQUENCY_10MHZ \
>>+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_MHZ)
>>+#define DPLL_PIN_FREQUENCY_IRIG_B \
>>+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_KHZ)
>>+#define DPLL_PIN_FREQUENCY_DCF77 \
>>+ DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_77_5_KHZ)
>>+
>>+struct dpll_pin_properties {
>>+ const char *board_label;
>>+ const char *panel_label;
>>+ const char *package_label;
>>+ enum dpll_pin_type type;
>>+ unsigned long capabilities;
>>+ u32 freq_supported_num;
>>+ struct dpll_pin_frequency *freq_supported;
>>+};
>>+
>>+struct dpll_device
>>+*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
>>+
>>+void dpll_device_put(struct dpll_device *dpll);
>>+
>>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>>+ const struct dpll_device_ops *ops, void *priv);
>>+
>>+void dpll_device_unregister(struct dpll_device *dpll,
>>+ const struct dpll_device_ops *ops, void *priv);
>>+
>>+struct dpll_pin
>>+*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>>+ const struct dpll_pin_properties *prop);
>>+
>>+int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv);
>>+
>>+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv);
>>+
>>+void dpll_pin_put(struct dpll_pin *pin);
>>+
>>+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv);
>>+
>>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv);
>>+
>>+int dpll_device_change_ntf(struct dpll_device *dpll);
>>+
>>+int dpll_pin_change_ntf(struct dpll_pin *pin);
>
>Why exactly did you split this into a separate patch? To me, it does not
>make any sense. Please squash this header addition to the

..Hit send be mistake.

Please squash this header addition to the patch where you actually
introcude the functions. Since you define a lot of structures here, I
believe that without this patch things are not compilable and breat
dissection. Makes me wonder why you did this split...


>
>
>>+
>>+#endif
>>--
>>2.37.3
>>

2023-06-10 08:30:15

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 09/10] ptp_ocp: implement DPLL ops

Fri, Jun 09, 2023 at 02:18:52PM CEST, [email protected] wrote:
>From: Vadim Fedorenko <[email protected]>
>
>Implement basic DPLL operations in ptp_ocp driver as the
>simplest example of using new subsystem.
>
>Signed-off-by: Vadim Fedorenko <[email protected]>
>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>---
> drivers/ptp/Kconfig | 1 +
> drivers/ptp/ptp_ocp.c | 329 +++++++++++++++++++++++++++++++++++-------
> 2 files changed, 278 insertions(+), 52 deletions(-)
>
>diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
>index b00201d81313..e3575c2e34dc 100644
>--- a/drivers/ptp/Kconfig
>+++ b/drivers/ptp/Kconfig
>@@ -177,6 +177,7 @@ config PTP_1588_CLOCK_OCP
> depends on COMMON_CLK
> select NET_DEVLINK
> select CRC16
>+ select DPLL
> help
> This driver adds support for an OpenCompute time card.
>
>diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
>index ab8cab4d1560..40a1ab7053d4 100644
>--- a/drivers/ptp/ptp_ocp.c
>+++ b/drivers/ptp/ptp_ocp.c
>@@ -23,6 +23,7 @@
> #include <linux/mtd/mtd.h>
> #include <linux/nvmem-consumer.h>
> #include <linux/crc16.h>
>+#include <linux/dpll.h>
>
> #define PCI_VENDOR_ID_FACEBOOK 0x1d9b
> #define PCI_DEVICE_ID_FACEBOOK_TIMECARD 0x0400
>@@ -260,12 +261,21 @@ enum ptp_ocp_sma_mode {
> SMA_MODE_OUT,
> };
>
>+static struct dpll_pin_frequency ptp_ocp_sma_freq[] = {
>+ DPLL_PIN_FREQUENCY_1PPS,
>+ DPLL_PIN_FREQUENCY_10MHZ,
>+ DPLL_PIN_FREQUENCY_IRIG_B,
>+ DPLL_PIN_FREQUENCY_DCF77,
>+};
>+
> struct ptp_ocp_sma_connector {
> enum ptp_ocp_sma_mode mode;
> bool fixed_fcn;
> bool fixed_dir;
> bool disabled;
> u8 default_fcn;
>+ struct dpll_pin *dpll_pin;
>+ struct dpll_pin_properties dpll_prop;
> };
>
> struct ocp_attr_group {
>@@ -294,6 +304,7 @@ struct ptp_ocp_serial_port {
>
> #define OCP_BOARD_ID_LEN 13
> #define OCP_SERIAL_LEN 6
>+#define OCP_SMA_NUM 4
>
> struct ptp_ocp {
> struct pci_dev *pdev;
>@@ -350,8 +361,9 @@ struct ptp_ocp {
> u32 ts_window_adjust;
> u64 fw_cap;
> struct ptp_ocp_signal signal[4];
>- struct ptp_ocp_sma_connector sma[4];
>+ struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];

Strictly speaking, this 4->OCP_SMA_NUM replacement should
be done as a separate patch.


> const struct ocp_sma_op *sma_op;
>+ struct dpll_device *dpll;
> };
>
> #define OCP_REQ_TIMESTAMP BIT(0)
>@@ -835,6 +847,7 @@ static DEFINE_IDR(ptp_ocp_idr);
> struct ocp_selector {
> const char *name;
> int value;
>+ u64 frequency;
> };
>
> static const struct ocp_selector ptp_ocp_clock[] = {
>@@ -855,31 +868,31 @@ static const struct ocp_selector ptp_ocp_clock[] = {
> #define SMA_SELECT_MASK GENMASK(14, 0)
>
> static const struct ocp_selector ptp_ocp_sma_in[] = {
>- { .name = "10Mhz", .value = 0x0000 },
>- { .name = "PPS1", .value = 0x0001 },
>- { .name = "PPS2", .value = 0x0002 },
>- { .name = "TS1", .value = 0x0004 },
>- { .name = "TS2", .value = 0x0008 },
>- { .name = "IRIG", .value = 0x0010 },
>- { .name = "DCF", .value = 0x0020 },
>- { .name = "TS3", .value = 0x0040 },
>- { .name = "TS4", .value = 0x0080 },
>- { .name = "FREQ1", .value = 0x0100 },
>- { .name = "FREQ2", .value = 0x0200 },
>- { .name = "FREQ3", .value = 0x0400 },
>- { .name = "FREQ4", .value = 0x0800 },
>- { .name = "None", .value = SMA_DISABLE },
>+ { .name = "10Mhz", .value = 0x0000, .frequency = 10000000 },
>+ { .name = "PPS1", .value = 0x0001, .frequency = 1 },
>+ { .name = "PPS2", .value = 0x0002, .frequency = 1 },
>+ { .name = "TS1", .value = 0x0004, .frequency = 0 },
>+ { .name = "TS2", .value = 0x0008, .frequency = 0 },
>+ { .name = "IRIG", .value = 0x0010, .frequency = 10000 },
>+ { .name = "DCF", .value = 0x0020, .frequency = 77500 },
>+ { .name = "TS3", .value = 0x0040, .frequency = 0 },
>+ { .name = "TS4", .value = 0x0080, .frequency = 0 },
>+ { .name = "FREQ1", .value = 0x0100, .frequency = 0 },
>+ { .name = "FREQ2", .value = 0x0200, .frequency = 0 },
>+ { .name = "FREQ3", .value = 0x0400, .frequency = 0 },
>+ { .name = "FREQ4", .value = 0x0800, .frequency = 0 },
>+ { .name = "None", .value = SMA_DISABLE, .frequency = 0 },
> { }
> };
>
> static const struct ocp_selector ptp_ocp_sma_out[] = {
>- { .name = "10Mhz", .value = 0x0000 },
>- { .name = "PHC", .value = 0x0001 },
>- { .name = "MAC", .value = 0x0002 },
>- { .name = "GNSS1", .value = 0x0004 },
>- { .name = "GNSS2", .value = 0x0008 },
>- { .name = "IRIG", .value = 0x0010 },
>- { .name = "DCF", .value = 0x0020 },
>+ { .name = "10Mhz", .value = 0x0000, .frequency = 10000000 },
>+ { .name = "PHC", .value = 0x0001, .frequency = 1 },
>+ { .name = "MAC", .value = 0x0002, .frequency = 1 },
>+ { .name = "GNSS1", .value = 0x0004, .frequency = 1 },
>+ { .name = "GNSS2", .value = 0x0008, .frequency = 1 },
>+ { .name = "IRIG", .value = 0x0010, .frequency = 10000 },
>+ { .name = "DCF", .value = 0x0020, .frequency = 77000 },
> { .name = "GEN1", .value = 0x0040 },
> { .name = "GEN2", .value = 0x0080 },
> { .name = "GEN3", .value = 0x0100 },
>@@ -890,15 +903,15 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
> };
>
> static const struct ocp_selector ptp_ocp_art_sma_in[] = {
>- { .name = "PPS1", .value = 0x0001 },
>- { .name = "10Mhz", .value = 0x0008 },
>+ { .name = "PPS1", .value = 0x0001, .frequency = 1 },
>+ { .name = "10Mhz", .value = 0x0008, .frequency = 1000000 },
> { }
> };
>
> static const struct ocp_selector ptp_ocp_art_sma_out[] = {
>- { .name = "PHC", .value = 0x0002 },
>- { .name = "GNSS", .value = 0x0004 },
>- { .name = "10Mhz", .value = 0x0010 },
>+ { .name = "PHC", .value = 0x0002, .frequency = 1 },
>+ { .name = "GNSS", .value = 0x0004, .frequency = 1 },
>+ { .name = "10Mhz", .value = 0x0010, .frequency = 10000000 },
> { }
> };
>
>@@ -2282,22 +2295,35 @@ ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
> static void
> ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
> {
>+ struct dpll_pin_properties prop = {
>+ .board_label = NULL,

It's NULL, remove this poitless init.


>+ .type = DPLL_PIN_TYPE_EXT,
>+ .capabilities = DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE,
>+ .freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>+ .freq_supported = ptp_ocp_sma_freq,
>+
>+ };
> u32 reg;
> int i;
>
> /* defaults */
>+ for (i = 0; i < OCP_SMA_NUM; i++) {
>+ bp->sma[i].default_fcn = i & 1;
>+ bp->sma[i].dpll_prop = prop;
>+ bp->sma[i].dpll_prop.board_label =
>+ bp->ptp_info.pin_config[i].name;

It is really label on a board? Isn't it rather label on a front panel?


>+ }
> bp->sma[0].mode = SMA_MODE_IN;
> bp->sma[1].mode = SMA_MODE_IN;
> bp->sma[2].mode = SMA_MODE_OUT;
> bp->sma[3].mode = SMA_MODE_OUT;
>- for (i = 0; i < 4; i++)
>- bp->sma[i].default_fcn = i & 1;
>-
> /* If no SMA1 map, the pin functions and directions are fixed. */
> if (!bp->sma_map1) {
>- for (i = 0; i < 4; i++) {
>+ for (i = 0; i < OCP_SMA_NUM; i++) {
> bp->sma[i].fixed_fcn = true;
> bp->sma[i].fixed_dir = true;
>+ bp->sma[1].dpll_prop.capabilities &=
>+ ~DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
> }
> return;
> }
>@@ -2307,7 +2333,7 @@ ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
> */
> reg = ioread32(&bp->sma_map2->gpio2);
> if (reg == 0xffffffff) {
>- for (i = 0; i < 4; i++)
>+ for (i = 0; i < OCP_SMA_NUM; i++)
> bp->sma[i].fixed_dir = true;
> } else {
> reg = ioread32(&bp->sma_map1->gpio1);
>@@ -2329,7 +2355,7 @@ static const struct ocp_sma_op ocp_fb_sma_op = {
> };
>
> static int
>-ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
>+ptp_ocp_set_pins(struct ptp_ocp *bp)
> {
> struct ptp_pin_desc *config;
> int i;
>@@ -2396,16 +2422,16 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
>
> ptp_ocp_tod_init(bp);
> ptp_ocp_nmea_out_init(bp);
>- ptp_ocp_sma_init(bp);
> ptp_ocp_signal_init(bp);
>
> err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
> if (err)
> return err;
>
>- err = ptp_ocp_fb_set_pins(bp);
>+ err = ptp_ocp_set_pins(bp);
> if (err)
> return err;
>+ ptp_ocp_sma_init(bp);
>
> return ptp_ocp_init_clock(bp);
> }
>@@ -2445,6 +2471,14 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
> static void
> ptp_ocp_art_sma_init(struct ptp_ocp *bp)
> {
>+ struct dpll_pin_properties prop = {
>+ .board_label = NULL,

It's NULL, remove this pointless init.


>+ .type = DPLL_PIN_TYPE_EXT,
>+ .capabilities = 0,

Again, it is zero, drop it.


>+ .freq_supported_num = ARRAY_SIZE(ptp_ocp_sma_freq),
>+ .freq_supported = ptp_ocp_sma_freq,
>+
>+ };
> u32 reg;
> int i;
>
>@@ -2459,16 +2493,17 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
> bp->sma[2].default_fcn = 0x10; /* OUT: 10Mhz */
> bp->sma[3].default_fcn = 0x02; /* OUT: PHC */
>
>- /* If no SMA map, the pin functions and directions are fixed. */
>- if (!bp->art_sma) {
>- for (i = 0; i < 4; i++) {
>+
>+ for (i = 0; i < OCP_SMA_NUM; i++) {
>+ /* If no SMA map, the pin functions and directions are fixed. */
>+ bp->sma[i].dpll_prop = prop;
>+ bp->sma[i].dpll_prop.board_label =

It is really label on a board? Isn't it rather label on a front panel?


>+ bp->ptp_info.pin_config[i].name;
>+ if (!bp->art_sma) {
> bp->sma[i].fixed_fcn = true;
> bp->sma[i].fixed_dir = true;
>+ continue;
> }
>- return;
>- }
>-
>- for (i = 0; i < 4; i++) {
> reg = ioread32(&bp->art_sma->map[i].gpio);
>
> switch (reg & 0xff) {
>@@ -2479,9 +2514,13 @@ ptp_ocp_art_sma_init(struct ptp_ocp *bp)
> case 1:
> case 8:
> bp->sma[i].mode = SMA_MODE_IN;
>+ bp->sma[i].dpll_prop.capabilities =
>+ DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
> break;
> default:
> bp->sma[i].mode = SMA_MODE_OUT;
>+ bp->sma[i].dpll_prop.capabilities =
>+ DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE;
> break;
> }
> }
>@@ -2548,6 +2587,9 @@ ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
> /* Enable MAC serial port during initialisation */
> iowrite32(1, &bp->board_config->mro50_serial_activate);
>
>+ err = ptp_ocp_set_pins(bp);
>+ if (err)
>+ return err;
> ptp_ocp_sma_init(bp);
>
> err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
>@@ -2689,16 +2731,9 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
> }
>
> static int
>-ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>+ptp_ocp_sma_store_val(struct ptp_ocp *bp, int val, enum ptp_ocp_sma_mode mode, int sma_nr)
> {
> struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>- enum ptp_ocp_sma_mode mode;
>- int val;
>-
>- mode = sma->mode;
>- val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>- if (val < 0)
>- return val;
>
> if (sma->fixed_dir && (mode != sma->mode || val & SMA_DISABLE))
> return -EOPNOTSUPP;
>@@ -2733,6 +2768,20 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
> return val;
> }
>
>+static int
>+ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
>+{
>+ struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
>+ enum ptp_ocp_sma_mode mode;
>+ int val;
>+
>+ mode = sma->mode;
>+ val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
>+ if (val < 0)
>+ return val;
>+ return ptp_ocp_sma_store_val(bp, val, mode, sma_nr);
>+}
>+
> static ssize_t
> sma1_store(struct device *dev, struct device_attribute *attr,
> const char *buf, size_t count)
>@@ -4171,12 +4220,148 @@ ptp_ocp_detach(struct ptp_ocp *bp)
> device_unregister(&bp->dev);
> }
>
>+static int ptp_ocp_dpll_lock_status_get(const struct dpll_device *dpll,
>+ void *priv,
>+ enum dpll_lock_status *status,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct ptp_ocp *bp = priv;
>+ int sync;
>+
>+ sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
>+ *status = sync ? DPLL_LOCK_STATUS_LOCKED : DPLL_LOCK_STATUS_UNLOCKED;
>+
>+ return 0;
>+}
>+
>+static int ptp_ocp_dpll_source_idx_get(const struct dpll_device *dpll,
>+ void *priv, u32 *idx,
>+ struct netlink_ext_ack *extack)

As I wrote in the other reply, this should be removed as it is a
leftover and is never called. You should implement pin op to get a state
on dpll instead.


>+{
>+ struct ptp_ocp *bp = priv;
>+
>+ if (bp->pps_select) {
>+ *idx = ioread32(&bp->pps_select->gpio1);
>+ return 0;
>+ }
>+ return -EINVAL;
>+}
>+
>+static int ptp_ocp_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>+ u32 *mode, struct netlink_ext_ack *extack)
>+{
>+ *mode = DPLL_MODE_AUTOMATIC;
>+ return 0;
>+}
>+
>+static bool ptp_ocp_dpll_mode_supported(const struct dpll_device *dpll,
>+ void *priv, const enum dpll_mode mode,
>+ struct netlink_ext_ack *extack)
>+{
>+ return mode == DPLL_MODE_AUTOMATIC;
>+}
>+
>+static int ptp_ocp_dpll_direction_get(const struct dpll_pin *pin,
>+ void *pin_priv,
>+ const struct dpll_device *dpll,
>+ void *priv,
>+ enum dpll_pin_direction *direction,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct ptp_ocp_sma_connector *sma = pin_priv;
>+
>+ *direction = sma->mode == SMA_MODE_IN ?
>+ DPLL_PIN_DIRECTION_INPUT :
>+ DPLL_PIN_DIRECTION_OUTPUT;
>+ return 0;
>+}
>+
>+static int ptp_ocp_dpll_direction_set(const struct dpll_pin *pin,
>+ void *pin_priv,
>+ const struct dpll_device *dpll,
>+ void *dpll_priv,
>+ enum dpll_pin_direction direction,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct ptp_ocp_sma_connector *sma = pin_priv;
>+ struct ptp_ocp *bp = dpll_priv;
>+ enum ptp_ocp_sma_mode mode;
>+ int sma_nr = (sma - bp->sma);
>+
>+ if (sma->fixed_dir)
>+ return -EOPNOTSUPP;
>+ mode = direction == DPLL_PIN_DIRECTION_INPUT ?
>+ SMA_MODE_IN : SMA_MODE_OUT;
>+ return ptp_ocp_sma_store_val(bp, 0, mode, sma_nr);

Fill the extack message to give user a clue about what's wrong in case
of an error. You can pass the extack all the way down to
ptp_ocp_sma_store_val() and fill-up the msg there.
Call it with NULL from ptp_ocp_sma_store().


>+}
>+
>+static int ptp_ocp_dpll_frequency_set(const struct dpll_pin *pin,
>+ void *pin_priv,
>+ const struct dpll_device *dpll,
>+ void *dpll_priv, u64 frequency,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct ptp_ocp_sma_connector *sma = pin_priv;
>+ struct ptp_ocp *bp = dpll_priv;
>+ const struct ocp_selector *tbl;
>+ int sma_nr = (sma - bp->sma);

No need for "()" here. Drop it.


>+ int val, i;
>+
>+ if (sma->fixed_fcn)
>+ return -EOPNOTSUPP;

Fill the extack message to give user a clue about what's wrong.


>+
>+ tbl = bp->sma_op->tbl[sma->mode];
>+ for (i = 0; tbl[i].name; i++)
>+ if (tbl[i].frequency == frequency)
>+ return ptp_ocp_sma_store_val(bp, val, sma->mode, sma_nr);

Fill the extack message to give user a clue about what's wrong in case
of an error.


>+ return -EINVAL;

Fill the extack message to give user a clue about what's wrong.


>+}
>+
>+static int ptp_ocp_dpll_frequency_get(const struct dpll_pin *pin,
>+ void *pin_priv,
>+ const struct dpll_device *dpll,
>+ void *dpll_priv, u64 *frequency,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct ptp_ocp_sma_connector *sma = pin_priv;
>+ struct ptp_ocp *bp = dpll_priv;
>+ const struct ocp_selector *tbl;
>+ int sma_nr = (sma - bp->sma);

No need for "()" here. Drop it.


>+ u32 val;
>+ int i;
>+
>+ val = bp->sma_op->get(bp, sma_nr);
>+ tbl = bp->sma_op->tbl[sma->mode];
>+ for (i = 0; tbl[i].name; i++)
>+ if (val == tbl[i].value) {
>+ *frequency = tbl[i].frequency;
>+ return 0;
>+ }
>+
>+ return -EINVAL;

How can this happen? Isn't it a driver bug if it does? In that case,
please consider WARN_ON_ONCE() here.
Of if this can happen, fill the extack message to give user a clue
about what's wrong.


>+}
>+
>+static const struct dpll_device_ops dpll_ops = {
>+ .lock_status_get = ptp_ocp_dpll_lock_status_get,
>+ .source_pin_idx_get = ptp_ocp_dpll_source_idx_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,
>+};

For the record, you still miss the notifications. I know that you wrote
you plan to add them, just don't want to forget about them.


>+
> static int
> ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> {
> struct devlink *devlink;
> struct ptp_ocp *bp;
>- int err;
>+ int err, i;
>+ u64 clkid;
>
> devlink = devlink_alloc(&ptp_ocp_devlink_ops, sizeof(*bp), &pdev->dev);
> if (!devlink) {
>@@ -4226,8 +4411,39 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>
> ptp_ocp_info(bp);
> devlink_register(devlink);
>- return 0;
>
>+ clkid = pci_get_dsn(pdev);
>+ bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
>+ if (IS_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))
>+ 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);

You should do this in error path, not here. Have and extra later to jump
in between dpll_pin_unregister() and dpll_pin_put().


>+ goto out_dpll;
>+ }
>+ }
>+
>+ return 0;
>+out_dpll:
>+ while (i) {
>+ --i;

while (i--) {

instead?


>+ dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
>+ dpll_pin_put(bp->sma[i].dpll_pin);
>+ }

You are missing dpll_device_unregister() here. Please fix your error
path.

>+ dpll_device_put(bp->dpll);

You are missing devlink_unregister() here. Please fix your error path.

A hint: The error path should be in sync with what you have in
ptp_ocp_remove().



> out:
> ptp_ocp_detach(bp);
> out_disable:
>@@ -4242,7 +4458,16 @@ ptp_ocp_remove(struct pci_dev *pdev)
> {
> struct ptp_ocp *bp = pci_get_drvdata(pdev);
> struct devlink *devlink = priv_to_devlink(bp);
>+ int i;
>
>+ for (i = 0; i < OCP_SMA_NUM; i++) {
>+ if (bp->sma[i].dpll_pin) {
>+ dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, bp);
>+ dpll_pin_put(bp->sma[i].dpll_pin);
>+ }
>+ }
>+ dpll_device_unregister(bp->dpll, &dpll_ops, bp);
>+ dpll_device_put(bp->dpll);
> devlink_unregister(devlink);
> ptp_ocp_detach(bp);
> pci_disable_device(pdev);
>--
>2.37.3
>

2023-06-10 09:28:46

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 07/10] ice: add admin commands to access cgu configuration

Fri, Jun 09, 2023 at 02:18:50PM CEST, [email protected] wrote:
>Add firmware admin command to access clock generation unit
>configuration, it is required to enable Extended PTP and SyncE features
>in the driver.

Empty line here perhaps?


>Add definitions of possible hardware variations of input and output pins
>related to clock generation unit and functions to access the data.
>
>Signed-off-by: Arkadiusz Kubalewski <[email protected]>

I just skimmed over this, not really give it much of a time. Couple of
nits:


>+
>+#define MAX_NETLIST_SIZE 10

Prefix perhaps?

[...]


>+/**
>+ * convert_s48_to_s64 - convert 48 bit value to 64 bit value
>+ * @signed_48: signed 64 bit variable storing signed 48 bit value
>+ *
>+ * Convert signed 48 bit value to its 64 bit representation.
>+ *
>+ * Return: signed 64 bit representation of signed 48 bit value.
>+ */
>+static inline

Never use "inline" in a c file.


>+s64 convert_s48_to_s64(s64 signed_48)
>+{
>+ const s64 MASK_SIGN_BITS = GENMASK_ULL(63, 48);

variable with capital letters? Not nice. Define? You have that multiple
times in the patch.


>+ const s64 SIGN_BIT_47 = BIT_ULL(47);
>+
>+ return ((signed_48 & SIGN_BIT_47) ? (s64)(MASK_SIGN_BITS | signed_48)

Pointless cast, isn't it?

You don't need () around "signed_48 & SIGN_BIT_47"


>+ : signed_48);

Return is not a function. Drop the outer "()"s.


The whole fuction can look like:
static s64 convert_s48_to_s64(s64 signed_48)
{
return signed_48 & BIT_ULL(47) ? signed_48 | GENMASK_ULL(63, 48) :
signed_48;
}

Nicer?


[...]



>+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx)
>+{
>+ struct ice_aqc_get_link_topo cmd;
>+ u8 node_part_number;
>+ u16 node_handle;
>+ int status;
>+ u8 ctx;
>+
>+ if (hw->mac_type != ICE_MAC_E810)
>+ return -ENODEV;
>+
>+ if (hw->device_id != ICE_DEV_ID_E810C_QSFP) {
>+ *idx = C827_0;
>+ return 0;
>+ }
>+
>+ memset(&cmd, 0, sizeof(cmd));
>+
>+ ctx = ICE_AQC_LINK_TOPO_NODE_TYPE_PHY << ICE_AQC_LINK_TOPO_NODE_TYPE_S;
>+ ctx |= ICE_AQC_LINK_TOPO_NODE_CTX_PORT << ICE_AQC_LINK_TOPO_NODE_CTX_S;
>+ cmd.addr.topo_params.node_type_ctx = ctx;
>+ cmd.addr.topo_params.index = 0;

You zeroed the struct 4 lines above...


>+
>+ status = ice_aq_get_netlist_node(hw, &cmd, &node_part_number,
>+ &node_handle);
>+ if (status || node_part_number != ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827)
>+ return -ENOENT;
>+
>+ if (node_handle == E810C_QSFP_C827_0_HANDLE)
>+ *idx = C827_0;
>+ else if (node_handle == E810C_QSFP_C827_1_HANDLE)
>+ *idx = C827_1;
>+ else
>+ return -EIO;
>+
>+ return 0;
>+}
>+

[...]

2023-06-10 10:18:28

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

Fri, Jun 09, 2023 at 02:18:51PM CEST, [email protected] wrote:
>Control over clock generation unit is required for further development
>of Synchronous Ethernet feature. Interface provides ability to obtain
>current state of a dpll, its sources and outputs which are pins, and
>allows their configuration.
>
>Co-developed-by: Milena Olech <[email protected]>
>Signed-off-by: Milena Olech <[email protected]>
>Co-developed-by: Michal Michalik <[email protected]>
>Signed-off-by: Michal Michalik <[email protected]>
>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>---
> drivers/net/ethernet/intel/Kconfig | 1 +
> drivers/net/ethernet/intel/ice/Makefile | 3 +-
> drivers/net/ethernet/intel/ice/ice.h | 4 +
> drivers/net/ethernet/intel/ice/ice_dpll.c | 2015 +++++++++++++++++++++
> drivers/net/ethernet/intel/ice/ice_dpll.h | 102 ++
> drivers/net/ethernet/intel/ice/ice_main.c | 7 +
> 6 files changed, 2131 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
>
>diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
>index 9bc0a9519899..913dcf928d15 100644
>--- a/drivers/net/ethernet/intel/Kconfig
>+++ b/drivers/net/ethernet/intel/Kconfig
>@@ -284,6 +284,7 @@ config ICE
> select DIMLIB
> select NET_DEVLINK
> select PLDMFW
>+ select DPLL
> help
> This driver supports Intel(R) Ethernet Connection E800 Series of
> devices. For more information on how to identify your adapter, go
>diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
>index 817977e3039d..85d6366d1f5b 100644
>--- a/drivers/net/ethernet/intel/ice/Makefile
>+++ b/drivers/net/ethernet/intel/ice/Makefile
>@@ -34,7 +34,8 @@ ice-y := ice_main.o \
> ice_lag.o \
> ice_ethtool.o \
> ice_repr.o \
>- ice_tc_lib.o
>+ ice_tc_lib.o \
>+ ice_dpll.o
> ice-$(CONFIG_PCI_IOV) += \
> ice_sriov.o \
> ice_virtchnl.o \
>diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
>index ae58d7499955..8a110272a799 100644
>--- a/drivers/net/ethernet/intel/ice/ice.h
>+++ b/drivers/net/ethernet/intel/ice/ice.h
>@@ -76,6 +76,7 @@
> #include "ice_vsi_vlan_ops.h"
> #include "ice_gnss.h"
> #include "ice_irq.h"
>+#include "ice_dpll.h"
>
> #define ICE_BAR0 0
> #define ICE_REQ_DESC_MULTIPLE 32
>@@ -198,6 +199,7 @@
> enum ice_feature {
> ICE_F_DSCP,
> ICE_F_PTP_EXTTS,
>+ ICE_F_PHY_RCLK,
> ICE_F_SMA_CTRL,
> ICE_F_CGU,
> ICE_F_GNSS,
>@@ -506,6 +508,7 @@ enum ice_pf_flags {
> ICE_FLAG_UNPLUG_AUX_DEV,
> ICE_FLAG_MTU_CHANGED,
> ICE_FLAG_GNSS, /* GNSS successfully initialized */
>+ ICE_FLAG_DPLL, /* SyncE/PTP dplls initialized */
> ICE_PF_FLAGS_NBITS /* must be last */
> };
>
>@@ -628,6 +631,7 @@ struct ice_pf {
> #define ICE_VF_AGG_NODE_ID_START 65
> #define ICE_MAX_VF_AGG_NODES 32
> struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
>+ struct ice_dplls dplls;
> };
>
> struct ice_netdev_priv {
>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
>new file mode 100644
>index 000000000000..22a69197188a
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>@@ -0,0 +1,2015 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#include "ice.h"
>+#include "ice_lib.h"
>+#include "ice_trace.h"
>+#include <linux/dpll.h>
>+
>+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD 50
>+#define ICE_DPLL_LOCK_TRIES 1000
>+#define ICE_DPLL_PIN_IDX_INVALID 0xff
>+#define ICE_DPLL_RCLK_NUM_PER_PF 1
>+
>+/**
>+ * dpll_lock_status - map ice cgu states into dpll's subsystem lock status
>+ */
>+static const enum dpll_lock_status
>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>+ [ICE_CGU_STATE_INVALID] = 0,

It's already 0, drop this pointless assign.


>+ [ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>+ [ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_LOCKED,
>+ [ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED_HO_ACQ,
>+ [ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>+};
>+
>+/**
>+ * ice_dpll_pin_type - enumerate ice pin types
>+ */
>+enum ice_dpll_pin_type {
>+ ICE_DPLL_PIN_INVALID = 0,

Not needed to set 0 here, it is implicit.


>+ ICE_DPLL_PIN_TYPE_INPUT,
>+ ICE_DPLL_PIN_TYPE_OUTPUT,
>+ ICE_DPLL_PIN_TYPE_RCLK_INPUT,
>+};
>+
>+/**
>+ * pin_type_name - string names of ice pin types
>+ */
>+static const char * const pin_type_name[] = {
>+ [ICE_DPLL_PIN_TYPE_INPUT] = "input",
>+ [ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>+ [ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
>+};
>+
>+/**
>+ * ice_dpll_cb_lock - lock dplls mutex in callback context
>+ * @pf: private board structure
>+ *
>+ * Lock the mutex from the callback operations invoked by dpll subsystem.
>+ * Prevent dead lock caused by `rmmod ice` when dpll callbacks are under stress

"dead lock", really? Which one? Didn't you want to write "livelock"?

If this is livelock prevention, is this something you really see or
just an assumption? Seems to me unlikely.

Plus, see my note in ice_dpll_init(). If you remove taking the lock from
ice_dpll_init() and ice_dpll_deinit(), do you still need this? I don't
think so.


>+ * tests.
>+ *
>+ * Return:
>+ * 0 - if lock acquired
>+ * negative - lock not acquired or dpll was deinitialized
>+ */
>+static int ice_dpll_cb_lock(struct ice_pf *pf)
>+{
>+ int i;
>+
>+ for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>+ if (mutex_trylock(&pf->dplls.lock))
>+ return 0;
>+ usleep_range(100, 150);
>+ if (!test_bit(ICE_FLAG_DPLL, pf->flags))

How exactly could this happen? I don't think it can. Drop it.


>+ return -EFAULT;
>+ }
>+
>+ return -EBUSY;
>+}
>+
>+/**
>+ * ice_dpll_cb_unlock - unlock dplls mutex in callback context
>+ * @pf: private board structure
>+ *
>+ * Unlock the mutex from the callback operations invoked by dpll subsystem.
>+ */
>+static void ice_dpll_cb_unlock(struct ice_pf *pf)
>+{
>+ mutex_unlock(&pf->dplls.lock);
>+}
>+
>+/**
>+ * ice_dpll_pin_freq_set - set pin's frequency
>+ * @pf: private board structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being configured
>+ * @freq: frequency to be set
>+ *
>+ * Set requested frequency on a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error on AQ or wrong pin type given
>+ */
>+static int
>+ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+ enum ice_dpll_pin_type pin_type, const u32 freq)
>+{
>+ int ret = -EINVAL;
>+ u8 flags;
>+
>+ switch (pin_type) {
>+ case ICE_DPLL_PIN_TYPE_INPUT:
>+ flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>+ ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>+ pin->flags[0], freq, 0);
>+ break;
>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>+ flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>+ ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>+ 0, freq, 0);
>+ break;
>+ default:
>+ break;
>+ }
>+ if (ret) {
>+ dev_err(ice_pf_to_dev(pf),
>+ "err:%d %s failed to set pin freq:%u on pin:%u\n",
>+ ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>+ freq, pin->idx);

Why you need dev_err here? Why can't this be rather put to the extack
message? Much better. Try to avoid polluting dmesg.


>+ return ret;
>+ }
>+ pin->freq = freq;
>+
>+ return 0;
>+}
>+
>+/**
>+ * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ * @pin_type: type of pin being configured
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ const u32 frequency,
>+ struct netlink_ext_ack *extack,
>+ enum ice_dpll_pin_type pin_type)
>+{
>+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;

Rather do:
struct ice_dpll *dpll = dpll_priv;
struct ice_pf *pf = dpll->pf;
And avoid the cast. Easier to read as well.
Same on other places.


>+ struct ice_dpll_pin *p = pin_priv;
>+ int ret = -EINVAL;
>+
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret)
>+ return ret;
>+ ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency);
>+ ice_dpll_cb_unlock(pf);
>+ if (ret)
>+ NL_SET_ERR_MSG(extack, "frequency was not set");

Yeah, that is stating the obvious as the use got error value, but tell
him some other details. Fill this in ice_dpll_pin_freq_set() by the
message you have there for dev_err() instead.



>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_input_frequency_set - input pin callback for set frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ u64 frequency, struct netlink_ext_ack *extack)
>+{
>+ return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>+ extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_output_frequency_set - output pin callback for set frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: frequency to be set
>+ * @extack: error reporting
>+ *
>+ * Wraps internal set frequency command on a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't set in hw
>+ */
>+static int
>+ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ u64 frequency, struct netlink_ext_ack *extack)
>+{
>+ return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv, frequency,
>+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: on success holds pin's frequency
>+ * @extack: error reporting
>+ * @pin_type: type of pin being configured
>+ *
>+ * Wraps internal get frequency command of a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ u64 *frequency, struct netlink_ext_ack *extack,
>+ enum ice_dpll_pin_type pin_type)
>+{
>+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
>+ struct ice_dpll_pin *p = pin_priv;
>+ int ret = -EINVAL;
>+
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret)
>+ return ret;
>+ *frequency = p->freq;
>+ ice_dpll_cb_unlock(pf);
>+
>+ return 0;
>+}
>+
>+/**
>+ * ice_dpll_input_frequency_get - input pin callback for get frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: on success holds pin's frequency
>+ * @extack: error reporting
>+ *
>+ * Wraps internal get frequency command of a input pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ u64 *frequency, struct netlink_ext_ack *extack)
>+{
>+ return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>+ extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_output_frequency_get - output pin callback for get frequency
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: pointer to dpll
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @frequency: on success holds pin's frequency
>+ * @extack: error reporting
>+ *
>+ * Wraps internal get frequency command of a pin.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - error pin not found or couldn't get from hw
>+ */
>+static int
>+ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ u64 *frequency, struct netlink_ext_ack *extack)
>+{
>+ return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv, frequency,
>+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_enable - enable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being enabled
>+ *
>+ * Enable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+ enum ice_dpll_pin_type pin_type)
>+{
>+ int ret = -EINVAL;
>+ u8 flags = 0;
>+
>+ switch (pin_type) {
>+ case ICE_DPLL_PIN_TYPE_INPUT:
>+ if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>+ flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>+ flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>+ ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+ break;
>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>+ if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>+ flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>+ flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>+ ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+ break;
>+ default:
>+ break;
>+ }
>+ if (ret)
>+ dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+ "err:%d %s failed to enable %s pin:%u\n",
>+ ret, ice_aq_str(hw->adminq.sq_last_status),
>+ pin_type_name[pin_type], pin->idx);
>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_disable - disable a pin on dplls
>+ * @hw: board private hw structure
>+ * @pin: pointer to a pin
>+ * @pin_type: type of pin being disabled
>+ *
>+ * Disable a pin on both dplls. Store current state in pin->flags.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>+ enum ice_dpll_pin_type pin_type)
>+{
>+ int ret = -EINVAL;
>+ u8 flags = 0;
>+
>+ switch (pin_type) {
>+ case ICE_DPLL_PIN_TYPE_INPUT:
>+ if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>+ flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>+ ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>+ break;
>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>+ if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>+ flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>+ ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>+ break;
>+ default:
>+ break;
>+ }
>+ if (ret)
>+ dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>+ "err:%d %s failed to disable %s pin:%u\n",
>+ ret, ice_aq_str(hw->adminq.sq_last_status),
>+ pin_type_name[pin_type], pin->idx);
>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_pin_state_update - update pin's state
>+ * @hw: private board struct
>+ * @pin: structure with pin attributes to be updated
>+ * @pin_type: type of pin being updated
>+ *
>+ * Determine pin current state and frequency, then update struct
>+ * holding the pin info. For input pin states are separated for each
>+ * dpll, for rclk pins states are separated for each parent.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - OK
>+ * * negative - error
>+ */
>+int
>+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+ enum ice_dpll_pin_type pin_type)
>+{
>+ int ret = -EINVAL;
>+
>+ switch (pin_type) {
>+ case ICE_DPLL_PIN_TYPE_INPUT:
>+ ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
>+ NULL, &pin->flags[0],
>+ &pin->freq, NULL);
>+ if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
>+ if (pin->pin) {
>+ pin->state[pf->dplls.eec.dpll_idx] =
>+ pin->pin == pf->dplls.eec.active_input ?
>+ DPLL_PIN_STATE_CONNECTED :
>+ DPLL_PIN_STATE_SELECTABLE;
>+ pin->state[pf->dplls.pps.dpll_idx] =
>+ pin->pin == pf->dplls.pps.active_input ?
>+ DPLL_PIN_STATE_CONNECTED :
>+ DPLL_PIN_STATE_SELECTABLE;
>+ } else {
>+ pin->state[pf->dplls.eec.dpll_idx] =
>+ DPLL_PIN_STATE_SELECTABLE;
>+ pin->state[pf->dplls.pps.dpll_idx] =
>+ DPLL_PIN_STATE_SELECTABLE;
>+ }
>+ } else {
>+ pin->state[pf->dplls.eec.dpll_idx] =
>+ DPLL_PIN_STATE_DISCONNECTED;
>+ pin->state[pf->dplls.pps.dpll_idx] =
>+ DPLL_PIN_STATE_DISCONNECTED;
>+ }
>+ break;
>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>+ ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
>+ &pin->flags[0], NULL,
>+ &pin->freq, NULL);
>+ if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
>+ pin->state[0] = DPLL_PIN_STATE_CONNECTED;
>+ else
>+ pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
>+ break;
>+ case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>+ u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
>+
>+ for (parent = 0; parent < pf->dplls.rclk.num_parents;
>+ parent++) {
>+ ret = ice_aq_get_phy_rec_clk_out(&pf->hw, parent,
>+ &port_num,
>+ &pin->flags[parent],
>+ &pin->freq);
>+ if (ret)
>+ return ret;
>+ if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
>+ pin->flags[parent])
>+ pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
>+ else
>+ pin->state[parent] =
>+ DPLL_PIN_STATE_DISCONNECTED;
>+ }
>+ break;
>+ default:
>+ break;
>+ }
>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_find_dpll - find ice_dpll on a pf
>+ * @pf: private board structure
>+ * @dpll: kernel's dpll_device pointer to be searched
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * pointer if ice_dpll with given device dpll pointer is found
>+ * * NULL if not found
>+ */
>+static struct ice_dpll
>+*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
>+{
>+ if (!pf || !dpll)
>+ return NULL;
>+
>+ return dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
>+ dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL;
>+}
>+
>+/**
>+ * ice_dpll_hw_input_prio_set - set input priority value in hardware
>+ * @pf: board private structure
>+ * @dpll: ice dpll pointer
>+ * @pin: ice pin pointer
>+ * @prio: priority value being set on a dpll
>+ *
>+ * Internal wrapper for setting the priority in the hardware.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
>+ struct ice_dpll_pin *pin, const u32 prio)
>+{
>+ int ret;
>+
>+ ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
>+ (u8)prio);
>+ if (ret)
>+ dev_err(ice_pf_to_dev(pf),
>+ "err:%d %s failed to set pin prio:%u on pin:%u\n",
>+ ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>+ prio, pin->idx);
>+ else
>+ dpll->input_prio[pin->idx] = prio;
>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_lock_status_get - get dpll lock status callback
>+ * @dpll: registered dpll pointer
>+ * @status: on success holds dpll's lock status
>+ *
>+ * Dpll subsystem callback, provides dpll's lock status.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_lock_status_get(const struct dpll_device *dpll, void *priv,
>+ enum dpll_lock_status *status,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct ice_dpll *d = priv;
>+ struct ice_pf *pf = d->pf;
>+ int ret = -EINVAL;
>+
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret)
>+ return ret;
>+ *status = ice_dpll_status[d->dpll_state];
>+ ice_dpll_cb_unlock(pf);
>+ dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p, ret:%d\n", __func__,
>+ dpll, pf, ret);
>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_mode_get - get dpll's working mode
>+ * @dpll: registered dpll pointer
>+ * @priv: private data pointer passed on dpll registration
>+ * @mode: on success holds current working mode of dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Provides working mode of dpll.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>+ enum dpll_mode *mode,
>+ struct netlink_ext_ack *extack)
>+{
>+ *mode = DPLL_MODE_AUTOMATIC;
>+
>+ return 0;
>+}
>+
>+/**
>+ * ice_dpll_mode_get - check if dpll's working mode is supported
>+ * @dpll: registered dpll pointer
>+ * @priv: private data pointer passed on dpll registration
>+ * @mode: mode to be checked for support
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Provides information if working mode is supported
>+ * by dpll.
>+ *
>+ * Return:
>+ * * true - mode is supported
>+ * * false - mode is not supported
>+ */
>+static bool ice_dpll_mode_supported(const struct dpll_device *dpll, void *priv,
>+ enum dpll_mode mode,
>+ struct netlink_ext_ack *extack)
>+{
>+ if (mode == DPLL_MODE_AUTOMATIC)
>+ return true;
>+
>+ return false;
>+}
>+
>+/**
>+ * ice_dpll_pin_state_set - set pin's state on dpll
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: state of pin to be set
>+ * @extack: error reporting
>+ * @pin_type: type of a pin
>+ *
>+ * Set pin state on a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - OK or no change required
>+ * * negative - error
>+ */
>+static int
>+ice_dpll_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ bool enable, struct netlink_ext_ack *extack,
>+ enum ice_dpll_pin_type pin_type)
>+{
>+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
>+ struct ice_dpll_pin *p = pin_priv;
>+ int ret = -EINVAL;
>+
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret)
>+ return ret;
>+ if (enable)
>+ ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>+ else
>+ ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
>+ if (!ret)
>+ ret = ice_dpll_pin_state_update(pf, p, pin_type);
>+ ice_dpll_cb_unlock(pf);
>+ if (ret)
>+ dev_err(ice_pf_to_dev(pf),

You have another dev_err inside ice_dpll_pin_enable(). Please avoid
redundancies like that.


>+ "%s: dpll:%p, pin:%p, p:%p pf:%p enable:%d ret:%d\n",
>+ __func__, dpll, pin, p, pf, enable, ret);

Quite cryptic. Make it more readable and use extack to pass the error
message to the user.

Actually, I don't want to repeat myself, but could you please do this
conversion in the rest of the ops as well. I mean, if you have extack,
just fill it up properly so the user knows what is wrong. Avoid the
dev_errs() in that cases.


>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_state_set - enable/disable output pin on dpll device
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: dpll being configured
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: state of pin to be set
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Set given state on output type pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int
>+ice_dpll_output_state_set(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_pin_state state,
>+ struct netlink_ext_ack *extack)
>+{
>+ bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;

Just:
bool enable = state == DPLL_PIN_STATE_CONNECTED;


>+
>+ return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
>+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_input_state_set - enable/disable input pin on dpll levice
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: dpll being configured
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: state of pin to be set
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Enables given mode on input type pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - successfully enabled mode
>+ * * negative - failed to enable mode
>+ */
>+static int
>+ice_dpll_input_state_set(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_pin_state state,
>+ struct netlink_ext_ack *extack)
>+{
>+ bool enable = state == DPLL_PIN_STATE_SELECTABLE ? true : false;

Just:
bool enable = state == DPLL_PIN_STATE_SELECTABLE;

>+
>+ return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
>+ extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_pin_state_get - set pin's state on dpll
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: on success holds state of the pin
>+ * @extack: error reporting
>+ * @pin_type: type of questioned pin
>+ *
>+ * Determine pin state set it on a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_pin_state_get(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_pin_state *state,
>+ struct netlink_ext_ack *extack,
>+ enum ice_dpll_pin_type pin_type)
>+{
>+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
>+ struct ice_dpll_pin *p = pin_priv;
>+ struct ice_dpll *d;
>+ int ret = -EINVAL;

Pointless init, drop it.

>+
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret)
>+ return ret;
>+ d = ice_find_dpll(pf, dpll);

This is a leftover. please remove, use dpll_priv and remove
ice_find_dpll() entirely.


>+ if (!d)
>+ goto unlock;
>+ ret = ice_dpll_pin_state_update(pf, p, pin_type);
>+ if (ret)
>+ goto unlock;
>+ if (pin_type == ICE_DPLL_PIN_TYPE_INPUT)
>+ *state = p->state[d->dpll_idx];
>+ else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
>+ *state = p->state[0];
>+ ret = 0;
>+unlock:
>+ ice_dpll_cb_unlock(pf);
>+ if (ret)
>+ dev_err(ice_pf_to_dev(pf),
>+ "%s: dpll:%p, pin:%p, pf:%p state: %d ret:%d\n",
>+ __func__, dpll, pin, pf, *state, ret);
>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_output_state_get - get output pin state on dpll device
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: on success holds state of the pin
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Check state of a pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_output_state_get(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_pin_state *state,
>+ struct netlink_ext_ack *extack)
>+{
>+ return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
>+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>+}
>+
>+/**
>+ * ice_dpll_input_state_get - get input pin state on dpll device
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @state: on success holds state of the pin
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Check state of a input pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failed to get state
>+ */
>+static int
>+ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_pin_state *state,
>+ struct netlink_ext_ack *extack)
>+{
>+ return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
>+ extack, ICE_DPLL_PIN_TYPE_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_input_prio_get - get dpll's input prio
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @prio: on success - returns input priority on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting priority of a input pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_input_prio_get(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ u32 *prio, struct netlink_ext_ack *extack)
>+{
>+ struct ice_dpll_pin *p = pin_priv;
>+ struct ice_dpll *d = dpll_priv;
>+ struct ice_pf *pf = d->pf;
>+ int ret = -EINVAL;
>+
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret)
>+ return ret;
>+ *prio = d->input_prio[p->idx];
>+ ice_dpll_cb_unlock(pf);
>+ dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>+ __func__, dpll, pin, pf, ret);

What exactly is this good for?


>+
>+ return 0;
>+}
>+
>+/**
>+ * ice_dpll_input_prio_set - set dpll input prio
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @prio: input priority to be set on dpll
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for setting priority of a input pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_input_prio_set(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ u32 prio, struct netlink_ext_ack *extack)
>+{
>+ struct ice_dpll_pin *p = pin_priv;
>+ struct ice_dpll *d = dpll_priv;
>+ struct ice_pf *pf = d->pf;
>+ int ret = -EINVAL;
>+
>+ if (prio > ICE_DPLL_PRIO_MAX) {
>+ NL_SET_ERR_MSG_FMT(extack, "prio out of supported range 0-%d",
>+ ICE_DPLL_PRIO_MAX);
>+ return ret;
>+ }
>+
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret)
>+ return ret;
>+ ret = ice_dpll_hw_input_prio_set(pf, d, p, prio);
>+ if (ret)
>+ NL_SET_ERR_MSG_FMT(extack, "unable to set prio: %u", prio);
>+ ice_dpll_cb_unlock(pf);
>+ dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>+ __func__, dpll, pin, pf, ret);

Same here.


>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_input_direction - callback for get input pin direction
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @direction: holds input pin direction
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting direction of a input pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ */
>+static int
>+ice_dpll_input_direction(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_pin_direction *direction,
>+ struct netlink_ext_ack *extack)
>+{
>+ *direction = DPLL_PIN_DIRECTION_INPUT;
>+
>+ return 0;
>+}
>+
>+/**
>+ * ice_dpll_output_direction - callback for get output pin direction
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @dpll: registered dpll pointer
>+ * @dpll_priv: private data pointer passed on dpll registration
>+ * @direction: holds output pin direction
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback. Handler for getting direction of an output pin.
>+ *
>+ * Return:
>+ * * 0 - success
>+ */
>+static int
>+ice_dpll_output_direction(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_device *dpll, void *dpll_priv,
>+ enum dpll_pin_direction *direction,
>+ struct netlink_ext_ack *extack)
>+{
>+ *direction = DPLL_PIN_DIRECTION_OUTPUT;
>+
>+ return 0;
>+}
>+
>+/**
>+ * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @parent_pin: pin parent pointer
>+ * @state: state to be set on pin
>+ * @extack: error reporting
>+ *
>+ * Dpll subsystem callback, set a state of a rclk pin on a parent pin
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_pin *parent_pin,
>+ void *parent_pin_priv,
>+ enum dpll_pin_state state,
>+ struct netlink_ext_ack *extack)
>+{
>+ bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;

You have an odd pattern of this assing. Please avoid "? true : false"
in the whole patch.


>+ struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
>+ struct ice_pf *pf = p->pf;
>+ int ret = -EINVAL;

Also you have an odd patter of initializing "ret" variable and assign
value to it 2 lines below. Please avoid it.


>+ u32 hw_idx;
>+
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret)
>+ return ret;
>+ hw_idx = parent->idx - pf->dplls.base_rclk_idx;
>+ if (hw_idx >= pf->dplls.num_inputs)
>+ goto unlock;
>+
>+ if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
>+ (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
>+ ret = -EINVAL;

Extack.


>+ goto unlock;
>+ }
>+ ret = ice_aq_set_phy_rec_clk_out(&pf->hw, hw_idx, enable,
>+ &p->freq);
>+unlock:
>+ ice_dpll_cb_unlock(pf);
>+ dev_dbg(ice_pf_to_dev(pf),
>+ "%s: parent:%p, pin:%p, pf:%p hw_idx:%u enable:%d ret:%d\n",
>+ __func__, parent_pin, pin, pf, hw_idx, enable, ret);

What is this good for? Again, lots of debug messages like this in the
whole patch. Do you need it, for what? If not, remove please.


>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_rclk_state_on_pin_get - get a state of rclk pin
>+ * @pin: pointer to a pin
>+ * @pin_priv: private data pointer passed on pin registration
>+ * @parent_pin: pin parent pointer
>+ * @state: on success holds pin state on parent pin
>+ * @extack: error reporting
>+ *
>+ * dpll subsystem callback, get a state of a recovered clock pin.
>+ *
>+ * Context: Acquires pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - failure
>+ */
>+static int
>+ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
>+ const struct dpll_pin *parent_pin,
>+ void *parent_pin_priv,
>+ enum dpll_pin_state *state,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
>+ struct ice_pf *pf = p->pf;
>+ int ret = -EFAULT;
>+ u32 hw_idx;
>+
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret)
>+ return ret;
>+ hw_idx = parent->idx - pf->dplls.base_rclk_idx;
>+ if (hw_idx >= pf->dplls.num_inputs)
>+ goto unlock;
>+
>+ ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>+ if (ret)
>+ goto unlock;
>+
>+ *state = p->state[hw_idx];
>+ ret = 0;
>+unlock:
>+ ice_dpll_cb_unlock(pf);
>+ dev_dbg(ice_pf_to_dev(pf),
>+ "%s: parent:%p, pin:%p, pf:%p hw_idx:%u state:%u ret:%d\n",
>+ __func__, parent_pin, pin, pf, hw_idx, *state, ret);
>+
>+ return ret;
>+}
>+
>+static const struct dpll_pin_ops ice_dpll_rclk_ops = {
>+ .state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
>+ .state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
>+ .direction_get = ice_dpll_input_direction,
>+};
>+
>+static const struct dpll_pin_ops ice_dpll_input_ops = {
>+ .frequency_get = ice_dpll_input_frequency_get,
>+ .frequency_set = ice_dpll_input_frequency_set,
>+ .state_on_dpll_get = ice_dpll_input_state_get,
>+ .state_on_dpll_set = ice_dpll_input_state_set,
>+ .prio_get = ice_dpll_input_prio_get,
>+ .prio_set = ice_dpll_input_prio_set,
>+ .direction_get = ice_dpll_input_direction,
>+};
>+
>+static const struct dpll_pin_ops ice_dpll_output_ops = {
>+ .frequency_get = ice_dpll_output_frequency_get,
>+ .frequency_set = ice_dpll_output_frequency_set,
>+ .state_on_dpll_get = ice_dpll_output_state_get,
>+ .state_on_dpll_set = ice_dpll_output_state_set,
>+ .direction_get = ice_dpll_output_direction,
>+};
>+
>+static const struct dpll_device_ops ice_dpll_ops = {
>+ .lock_status_get = ice_dpll_lock_status_get,
>+ .mode_get = ice_dpll_mode_get,
>+ .mode_supported = ice_dpll_mode_supported,
>+};
>+
>+/**
>+ * ice_dpll_deinit_info - release memory allocated for pins info
>+ * @pf: board private structure
>+ *
>+ * Release memory allocated for pins by ice_dpll_init_info function.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void ice_dpll_deinit_info(struct ice_pf *pf)
>+{
>+ kfree(pf->dplls.inputs);
>+ pf->dplls.inputs = NULL;
>+ kfree(pf->dplls.outputs);
>+ pf->dplls.outputs = NULL;
>+ kfree(pf->dplls.eec.input_prio);
>+ pf->dplls.eec.input_prio = NULL;
>+ kfree(pf->dplls.pps.input_prio);
>+ pf->dplls.pps.input_prio = NULL;

Why you NULL the pointers? Do you use them later on? If not, please
drop it, it's confusing.



>+}
>+
>+/**
>+ * ice_dpll_deinit_rclk_pin - release rclk pin resources
>+ * @pf: board private structure
>+ *
>+ * Deregister rclk pin from parent pins and release resources in dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
>+{
>+ struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>+ struct ice_vsi *vsi = ice_get_main_vsi(pf);
>+ struct dpll_pin *parent;
>+ int i;
>+
>+ for (i = 0; i < rclk->num_parents; i++) {
>+ parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
>+ if (!parent)
>+ continue;
>+ if (!IS_ERR_OR_NULL(rclk->pin))
>+ dpll_pin_on_pin_unregister(parent, rclk->pin,
>+ &ice_dpll_rclk_ops, rclk);
>+ }
>+ if (WARN_ON_ONCE(!vsi || !vsi->netdev))
>+ return;
>+ netdev_dpll_pin_clear(vsi->netdev);
>+ dpll_pin_put(rclk->pin);
>+ rclk->pin = NULL;

Why you need to NULL it? If don't, please drop to avoid confusions.
Same goes to the rest of the NULLing occurances in deinit() functions.


>+}
>+
>+/**
>+ * ice_dpll_unregister_pins - unregister pins from a dpll
>+ * @dpll: dpll device pointer
>+ * @pins: pointer to pins array
>+ * @ops: callback ops registered with the pins
>+ * @count: number of pins
>+ *
>+ * Unregister pins of a given array of pins from given dpll device registered in
>+ * dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void
>+ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
>+ const struct dpll_pin_ops *ops, int count)
>+{
>+ struct ice_dpll_pin *p;
>+ int i;
>+
>+ for (i = 0; i < count; i++) {
>+ p = &pins[i];
>+ if (p && !IS_ERR_OR_NULL(p->pin))

How can the p be NULL? I don't think it can.

Please void the whole check here.
Do the error path rollback in init() function properly.


>+ dpll_pin_unregister(dpll, p->pin, ops, p);
>+ }
>+}
>+
>+/**
>+ * ice_dpll_release_pins - release pins resources from dpll subsystem
>+ * @pf: board private structure
>+ * @pins: pointer to pins array
>+ * @count: number of pins
>+ *
>+ * Release resources of given pins array in the dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
>+{
>+ struct ice_dpll_pin *p;
>+ int i;
>+
>+ for (i = 0; i < count; i++) {
>+ p = &pins[i];
>+ if (p && !IS_ERR_OR_NULL(p->pin)) {

Same here.

Please void the whole check here.
Do the error path rollback in init() function properly.


>+ dpll_pin_put(p->pin);
>+ p->pin = NULL;
>+ }
>+ }
>+}
>+
>+/**
>+ * ice_dpll_get_pins - get pins from dpll subsystem
>+ * @pf: board private structure
>+ * @pins: pointer to pins array
>+ * @start_idx: get starts from this pin idx value
>+ * @count: number of pins
>+ * @clock_id: clock_id of dpll device
>+ *
>+ * Get pins - allocate - in dpll subsystem, store them in pin field of given
>+ * pins array.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - allocation failure reason
>+ */
>+static int
>+ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
>+ int start_idx, int count, u64 clock_id)
>+{
>+ int i, ret;
>+
>+ for (i = 0; i < count; i++) {
>+ pins[i].pin = dpll_pin_get(clock_id, i + start_idx, THIS_MODULE,
>+ &pins[i].prop);
>+ if (IS_ERR(pins[i].pin)) {
>+ ret = PTR_ERR(pins[i].pin);
>+ goto release_pins;
>+ }
>+ }
>+
>+ return 0;
>+
>+release_pins:
>+ ice_dpll_release_pins(pins, i);


Please call dpll_pin_put() in a loop here.


>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_register_pins - register pins with a dpll
>+ * @dpll: dpll pointer to register pins with
>+ * @pins: pointer to pins array
>+ * @ops: callback ops registered with the pins
>+ * @count: number of pins
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Register pins of a given array with given dpll in dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - registration failure reason
>+ */
>+static int
>+ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin *pins,
>+ const struct dpll_pin_ops *ops, int count)
>+{
>+ int ret, i;
>+
>+ for (i = 0; i < count; i++) {
>+ ret = dpll_pin_register(dpll, pins[i].pin, ops, &pins[i]);
>+ if (ret)
>+ goto unregister_pins;
>+ }
>+
>+ return 0;
>+
>+unregister_pins:
>+ ice_dpll_unregister_pins(dpll, pins, ops, i);

Please call dpll_pin_unregister() in a loop here.

>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_init_direct_pins - initialize direct pins
>+ * @dpll: dpll pointer to register pins with
>+ * @cgu: if cgu is present and controlled by this NIC
>+ * @pins: pointer to pins array
>+ * @start_idx: on which index shall allocation start in dpll subsystem
>+ * @count: number of pins
>+ * @ops: callback ops registered with the pins
>+ * @first: dpll device pointer
>+ * @second: dpll device pointer
>+ *
>+ * Allocate directly connected pins of a given array in dpll subsystem.
>+ * If cgu is owned register allocated pins with given dplls.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - registration failure reason
>+ */
>+static int
>+ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
>+ struct ice_dpll_pin *pins, int start_idx, int count,
>+ const struct dpll_pin_ops *ops,
>+ struct dpll_device *first, struct dpll_device *second)
>+{
>+ int ret;
>+
>+ ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf->dplls.clock_id);
>+ if (ret)
>+ return ret;
>+ if (cgu) {
>+ ret = ice_dpll_register_pins(first, pins, ops, count);
>+ if (ret)
>+ goto release_pins;
>+ ret = ice_dpll_register_pins(second, pins, ops, count);
>+ if (ret)
>+ goto unregister_first;
>+ }
>+
>+ return 0;
>+
>+unregister_first:
>+ ice_dpll_unregister_pins(first, pins, ops, count);
>+release_pins:
>+ ice_dpll_release_pins(pins, count);
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_deinit_direct_pins - deinitialize direct pins
>+ * @cgu: if cgu is present and controlled by this NIC
>+ * @pins: pointer to pins array
>+ * @count: number of pins
>+ * @ops: callback ops registered with the pins
>+ * @first: dpll device pointer
>+ * @second: dpll device pointer
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * If cgu is owned unregister pins from given dplls.
>+ * Release pins resources to the dpll subsystem.
>+ */
>+static void
>+ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int count,
>+ const struct dpll_pin_ops *ops,
>+ struct dpll_device *first,
>+ struct dpll_device *second)
>+{
>+ if (cgu) {
>+ ice_dpll_unregister_pins(first, pins, ops, count);
>+ ice_dpll_unregister_pins(second, pins, ops, count);
>+ }
>+ ice_dpll_release_pins(pins, count);
>+}
>+
>+/**
>+ * ice_dpll_init_rclk_pins - initialize recovered clock pin
>+ * @dpll: dpll pointer to register pins with
>+ * @cgu: if cgu is present and controlled by this NIC
>+ * @pins: pointer to pins array
>+ * @start_idx: on which index shall allocation start in dpll subsystem
>+ * @count: number of pins
>+ * @ops: callback ops registered with the pins
>+ *
>+ * Allocate resource for recovered clock pin in dpll subsystem. Register the
>+ * pin with the parents it has in the info. Register pin with the pf's main vsi
>+ * netdev.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - registration failure reason
>+ */
>+static int
>+ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
>+ int start_idx, const struct dpll_pin_ops *ops)

It is a good practise to have the init/cleanup functions of the same
thing one behind the another. Could you please reorder the code to
achieve that?


>+{
>+ struct ice_vsi *vsi = ice_get_main_vsi(pf);
>+ struct dpll_pin *parent;
>+ int ret, i;
>+
>+ ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
>+ pf->dplls.clock_id);
>+ if (ret)
>+ return ret;
>+ for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>+ parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>+ if (!parent) {
>+ ret = -ENODEV;
>+ goto unregister_pins;
>+ }
>+ ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>+ ops, &pf->dplls.rclk);
>+ if (ret)
>+ goto unregister_pins;
>+ }
>+ if (WARN_ON((!vsi || !vsi->netdev)))
>+ return -EINVAL;
>+ netdev_dpll_pin_set(vsi->netdev, pf->dplls.rclk.pin);
>+
>+ return 0;
>+
>+unregister_pins:
>+ while (i) {
>+ parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
>+ dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
>+ &ice_dpll_rclk_ops, &pf->dplls.rclk);
>+ }
>+ ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_init_pins - init pins and register pins with a dplls
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Initialize directly connected pf's pins within pf's dplls in a Linux dpll
>+ * subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - initialization failure reason
>+ */
>+static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
>+{
>+ u32 rclk_idx;
>+ int ret;
>+
>+ ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
>+ pf->dplls.num_inputs,
>+ &ice_dpll_input_ops,
>+ pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>+ if (ret)
>+ return ret;
>+ if (cgu) {
>+ ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
>+ pf->dplls.num_inputs,
>+ pf->dplls.num_outputs,
>+ &ice_dpll_output_ops,
>+ pf->dplls.eec.dpll,
>+ pf->dplls.pps.dpll);
>+ if (ret)
>+ goto deinit_inputs;
>+ }
>+ rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf->hw.pf_id;
>+ ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
>+ &ice_dpll_rclk_ops);
>+ if (ret)
>+ goto deinit_outputs;
>+
>+ return 0;
>+deinit_outputs:
>+ ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
>+ pf->dplls.num_outputs,
>+ &ice_dpll_output_ops, pf->dplls.pps.dpll,
>+ pf->dplls.eec.dpll);
>+deinit_inputs:
>+ ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf->dplls.num_inputs,
>+ &ice_dpll_input_ops, pf->dplls.pps.dpll,
>+ pf->dplls.eec.dpll);
>+ return ret;
>+}
>+
>+/**
>+ * ice_generate_clock_id - generates unique clock_id for registering dpll.
>+ * @pf: board private structure
>+ *
>+ * Generates unique (per board) clock_id for allocation and search of dpll
>+ * devices in Linux dpll subsystem.
>+ *
>+ * Return: generated clock id for the board
>+ */
>+static u64 ice_generate_clock_id(struct ice_pf *pf)
>+{
>+ return pci_get_dsn(pf->pdev);
>+}
>+
>+/**
>+ * ice_dpll_init_dpll - initialize dpll device in dpll subsystem
>+ * @pf: board private structure
>+ * @d: dpll to be initialized
>+ * @cgu: if cgu is present and controlled by this NIC
>+ * @type: type of dpll being initialized
>+ *
>+ * Allocate dpll instance for this board in dpll subsystem, if cgu is controlled
>+ * by this NIC, register dpll with the callback ops.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - initialization failure reason
>+ */
>+static int
>+ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
>+ enum dpll_type type)
>+{
>+ u64 clock_id = pf->dplls.clock_id;
>+ int ret;
>+
>+ d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
>+ if (IS_ERR(d->dpll)) {
>+ ret = PTR_ERR(d->dpll);
>+ dev_err(ice_pf_to_dev(pf),
>+ "dpll_device_get failed (%p) err=%d\n", d, ret);
>+ return ret;
>+ }
>+ d->pf = pf;
>+ if (cgu) {
>+ ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d);
>+ if (ret) {
>+ dpll_device_put(d->dpll);
>+ return ret;
>+ }
>+ }
>+
>+ return 0;
>+}
>+
>+/**
>+ * ice_dpll_update_state - update dpll state
>+ * @pf: pf private structure
>+ * @d: pointer to queried dpll device
>+ *
>+ * Poll current state of dpll from hw and update ice_dpll struct.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - AQ failure
>+ */
>+static int
>+ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
>+{
>+ struct ice_dpll_pin *p;
>+ int ret;
>+
>+ ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
>+ &d->input_idx, &d->ref_state, &d->eec_mode,
>+ &d->phase_offset, &d->dpll_state);
>+
>+ dev_dbg(ice_pf_to_dev(pf),
>+ "update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d, prev:%d\n",
>+ d->dpll_idx, d->prev_input_idx, d->input_idx,
>+ d->dpll_state, d->prev_dpll_state);
>+ if (ret) {
>+ dev_err(ice_pf_to_dev(pf),
>+ "update dpll=%d state failed, ret=%d %s\n",
>+ d->dpll_idx, ret,
>+ ice_aq_str(pf->hw.adminq.sq_last_status));
>+ return ret;
>+ }
>+ if (init) {
>+ if (d->dpll_state == ICE_CGU_STATE_LOCKED &&
>+ d->dpll_state == ICE_CGU_STATE_LOCKED_HO_ACQ)
>+ d->active_input = pf->dplls.inputs[d->input_idx].pin;
>+ p = &pf->dplls.inputs[d->input_idx];
>+ return ice_dpll_pin_state_update(pf, p,
>+ ICE_DPLL_PIN_TYPE_INPUT);
>+ }
>+ if (d->dpll_state == ICE_CGU_STATE_HOLDOVER ||
>+ d->dpll_state == ICE_CGU_STATE_FREERUN) {
>+ d->active_input = NULL;
>+ p = &pf->dplls.inputs[d->input_idx];
>+ d->prev_input_idx = ICE_DPLL_PIN_IDX_INVALID;
>+ d->input_idx = ICE_DPLL_PIN_IDX_INVALID;
>+ ret = ice_dpll_pin_state_update(pf, p,
>+ ICE_DPLL_PIN_TYPE_INPUT);
>+ } else if (d->input_idx != d->prev_input_idx) {
>+ p = &pf->dplls.inputs[d->prev_input_idx];
>+ ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_INPUT);
>+ p = &pf->dplls.inputs[d->input_idx];
>+ d->active_input = p->pin;
>+ ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_INPUT);
>+ d->prev_input_idx = d->input_idx;
>+ }
>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_notify_changes - notify dpll subsystem about changes
>+ * @d: pointer do dpll
>+ *
>+ * Once change detected appropriate event is submitted to the dpll subsystem.
>+ */
>+static void ice_dpll_notify_changes(struct ice_dpll *d)
>+{
>+ if (d->prev_dpll_state != d->dpll_state) {
>+ d->prev_dpll_state = d->dpll_state;
>+ dpll_device_change_ntf(d->dpll);
>+ }
>+ if (d->prev_input != d->active_input) {
>+ if (d->prev_input)
>+ dpll_pin_change_ntf(d->prev_input);
>+ d->prev_input = d->active_input;
>+ if (d->active_input)
>+ dpll_pin_change_ntf(d->active_input);
>+ }
>+}
>+
>+/**
>+ * ice_dpll_periodic_work - DPLLs periodic worker
>+ * @work: pointer to kthread_work structure
>+ *
>+ * DPLLs periodic worker is responsible for polling state of dpll.
>+ * Context: Holds pf->dplls.lock
>+ */
>+static void ice_dpll_periodic_work(struct kthread_work *work)
>+{
>+ struct ice_dplls *d = container_of(work, struct ice_dplls, work.work);
>+ struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>+ struct ice_dpll *de = &pf->dplls.eec;
>+ struct ice_dpll *dp = &pf->dplls.pps;
>+ int ret = 0;
>+
>+ if (!test_bit(ICE_FLAG_DPLL, pf->flags))

How exactly could this happen? I don't think it can. Drop it.


>+ return;
>+ ret = ice_dpll_cb_lock(pf);
>+ if (ret) {
>+ d->lock_err_num++;

Drop this struct field, you don't use it anywhere.


>+ goto resched;
>+ }
>+ ret = ice_dpll_update_state(pf, de, false);
>+ if (!ret)
>+ ret = ice_dpll_update_state(pf, dp, false);
>+ if (ret) {
>+ d->cgu_state_acq_err_num++;
>+ /* stop rescheduling this worker */
>+ if (d->cgu_state_acq_err_num >
>+ ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
>+ dev_err(ice_pf_to_dev(pf),
>+ "EEC/PPS DPLLs periodic work disabled\n");
>+ return;
>+ }
>+ }
>+ ice_dpll_cb_unlock(pf);
>+ ice_dpll_notify_changes(de);
>+ ice_dpll_notify_changes(dp);
>+resched:
>+ /* Run twice a second or reschedule if update failed */
>+ kthread_queue_delayed_work(d->kworker, &d->work,
>+ ret ? msecs_to_jiffies(10) :
>+ msecs_to_jiffies(500));
>+}
>+
>+/**
>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>+ * @pf: board private structure
>+ *
>+ * Create and start DPLLs periodic worker.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ * Return:
>+ * * 0 - success
>+ * * negative - create worker failure
>+ */
>+static int ice_dpll_init_worker(struct ice_pf *pf)
>+{
>+ struct ice_dplls *d = &pf->dplls;
>+ struct kthread_worker *kworker;
>+
>+ ice_dpll_update_state(pf, &d->eec, true);
>+ ice_dpll_update_state(pf, &d->pps, true);
>+ kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>+ kworker = kthread_create_worker(0, "ice-dplls-%s",
>+ dev_name(ice_pf_to_dev(pf)));
>+ if (IS_ERR(kworker))
>+ return PTR_ERR(kworker);
>+ d->kworker = kworker;
>+ d->cgu_state_acq_err_num = 0;
>+ kthread_queue_delayed_work(d->kworker, &d->work, 0);
>+
>+ return 0;
>+}
>+
>+/**
>+ * ice_dpll_deinit_pins - deinitialize direct pins
>+ * @pf: board private structure
>+ * @cgu: if cgu is controlled by this pf
>+ *
>+ * If cgu is owned unregister directly connected pins from the dplls.
>+ * Release resources of directly connected pins from the dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
>+{
>+ struct ice_dpll_pin *outputs = pf->dplls.outputs;
>+ struct ice_dpll_pin *inputs = pf->dplls.inputs;
>+ int num_outputs = pf->dplls.num_outputs;
>+ int num_inputs = pf->dplls.num_inputs;
>+ struct ice_dplls *d = &pf->dplls;
>+ struct ice_dpll *de = &d->eec;
>+ struct ice_dpll *dp = &d->pps;
>+
>+ ice_dpll_deinit_rclk_pin(pf);
>+ if (cgu) {
>+ ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
>+ num_inputs);
>+ ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
>+ num_inputs);
>+ }
>+ ice_dpll_release_pins(inputs, num_inputs);
>+ if (cgu) {
>+ ice_dpll_unregister_pins(dp->dpll, outputs,
>+ &ice_dpll_output_ops, num_outputs);
>+ ice_dpll_unregister_pins(de->dpll, outputs,
>+ &ice_dpll_output_ops, num_outputs);
>+ ice_dpll_release_pins(outputs, num_outputs);
>+ }
>+}
>+
>+/**
>+ * ice_dpll_deinit_dpll - deinitialize dpll device
>+ * @pf: board private structure
>+ *
>+ * If cgu is owned unregister the dpll from dpll subsystem.
>+ * Release resources of dpll device from dpll subsystem.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void
>+ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
>+{
>+ if (!IS_ERR(d->dpll)) {
>+ if (cgu)
>+ dpll_device_unregister(d->dpll, &ice_dpll_ops, d);
>+ dpll_device_put(d->dpll);
>+ dev_dbg(ice_pf_to_dev(pf), "(%p) dpll removed\n", d);
>+ }
>+}
>+
>+/**
>+ * ice_dpll_deinit_worker - deinitialize dpll kworker
>+ * @pf: board private structure
>+ *
>+ * Stop dpll's kworker, release it's resources.
>+ *
>+ * Context: Called under pf->dplls.lock
>+ */
>+static void ice_dpll_deinit_worker(struct ice_pf *pf)
>+{
>+ struct ice_dplls *d = &pf->dplls;
>+
>+ kthread_cancel_delayed_work_sync(&d->work);
>+ if (!IS_ERR_OR_NULL(d->kworker)) {
>+ kthread_destroy_worker(d->kworker);
>+ d->kworker = NULL;
>+ dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");

What is this msg good for?


>+ }
>+}
>+
>+/**
>+ * ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
>+ * the dpll device.
>+ * @pf: board private structure
>+ *
>+ * Handles the cleanup work required after dpll initialization,freeing resources
>+ * and unregistering the dpll, pin and all resources used for handling them.
>+ *
>+ * Context: Function holds pf->dplls.lock mutex.
>+ */
>+void ice_dpll_deinit(struct ice_pf *pf)
>+{
>+ bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>+
>+ if (test_bit(ICE_FLAG_DPLL, pf->flags)) {

How about avoiding the indent are rather do:
if (!test_bit(ICE_FLAG_DPLL, pf->flags))
return;

?

>+ mutex_lock(&pf->dplls.lock);

Related to my question in ice_dpll_init(), why do you need to lock the mutex
here?


>+ ice_dpll_deinit_pins(pf, cgu);
>+ ice_dpll_deinit_info(pf);
>+ ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>+ ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);

Please reorder to match error path in ice_dpll_init()

>+ if (cgu)

In ice_dpll_init() you call this "cgu_present". Please be consistent in
naming.


>+ ice_dpll_deinit_worker(pf);
>+ clear_bit(ICE_FLAG_DPLL, pf->flags);
>+ mutex_unlock(&pf->dplls.lock);
>+ mutex_destroy(&pf->dplls.lock);
>+ }
>+}
>+
>+/**
>+ * ice_dpll_init_info_direct_pins - initializes direct pins info
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Init information for directly connected pins, cache them in pf's pins
>+ * structures.
>+ *
>+ * Context: Function initializes and holds pf->dplls.lock mutex.
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int
>+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>+ enum ice_dpll_pin_type pin_type)
>+{
>+ struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>+ int num_pins, i, ret = -EINVAL;
>+ struct ice_hw *hw = &pf->hw;
>+ struct ice_dpll_pin *pins;
>+ u8 freq_supp_num;
>+ bool input;
>+
>+ switch (pin_type) {
>+ case ICE_DPLL_PIN_TYPE_INPUT:
>+ pins = pf->dplls.inputs;
>+ num_pins = pf->dplls.num_inputs;
>+ input = true;
>+ break;
>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>+ pins = pf->dplls.outputs;
>+ num_pins = pf->dplls.num_outputs;
>+ input = false;
>+ break;
>+ default:
>+ return ret;
>+ }
>+
>+ for (i = 0; i < num_pins; i++) {
>+ pins[i].idx = i;
>+ pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>+ pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>+ if (input) {
>+ ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>+ &de->input_prio[i]);
>+ if (ret)
>+ return ret;
>+ ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>+ &dp->input_prio[i]);
>+ if (ret)
>+ return ret;
>+ pins[i].prop.capabilities |=
>+ DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>+ }
>+ pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>+ ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type);
>+ if (ret)
>+ return ret;
>+ pins[i].prop.freq_supported =
>+ ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>+ pins[i].prop.freq_supported_num = freq_supp_num;
>+ pins[i].pf = pf;
>+ }
>+
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_init_rclk_pin - initializes rclk pin information
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Init information for rclk pin, cache them in pf->dplls.rclk.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int ice_dpll_init_rclk_pin(struct ice_pf *pf)
>+{
>+ struct ice_dpll_pin *pin = &pf->dplls.rclk;
>+ struct device *dev = ice_pf_to_dev(pf);
>+
>+ pin->prop.board_label = dev_name(dev);

What??? Must be some sort of joke, correct?
"board_label" should be an actual writing on a board. For syncE, I don't
think it makes sense to fill any label. The connection to the netdev
should be enough. That is what I do in mlx5.

Please drop this.



>+ pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>+ pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>+ pin->pf = pf;
>+
>+ return ice_dpll_pin_state_update(pf, pin,
>+ ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>+}
>+
>+/**
>+ * ice_dpll_init_pins_info - init pins info wrapper
>+ * @pf: board private structure
>+ * @pin_type: type of pins being initialized
>+ *
>+ * Wraps functions for pin initialization.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int
>+ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type pin_type)
>+{
>+ switch (pin_type) {
>+ case ICE_DPLL_PIN_TYPE_INPUT:
>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>+ return ice_dpll_init_info_direct_pins(pf, pin_type);
>+ case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>+ return ice_dpll_init_rclk_pin(pf);
>+ default:
>+ return -EINVAL;
>+ }
>+}
>+
>+/**
>+ * ice_dpll_init_info - prepare pf's dpll information structure
>+ * @pf: board private structure
>+ * @cgu: if cgu is present and controlled by this NIC
>+ *
>+ * Acquire (from HW) and set basic dpll information (on pf->dplls struct).
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
>+{
>+ struct ice_aqc_get_cgu_abilities abilities;
>+ struct ice_dpll *de = &pf->dplls.eec;
>+ struct ice_dpll *dp = &pf->dplls.pps;
>+ struct ice_dplls *d = &pf->dplls;
>+ struct ice_hw *hw = &pf->hw;
>+ int ret, alloc_size, i;
>+
>+ d->clock_id = ice_generate_clock_id(pf);
>+ ret = ice_aq_get_cgu_abilities(hw, &abilities);
>+ if (ret) {
>+ dev_err(ice_pf_to_dev(pf),
>+ "err:%d %s failed to read cgu abilities\n",
>+ ret, ice_aq_str(hw->adminq.sq_last_status));
>+ return ret;
>+ }
>+
>+ de->dpll_idx = abilities.eec_dpll_idx;
>+ dp->dpll_idx = abilities.pps_dpll_idx;
>+ d->num_inputs = abilities.num_inputs;
>+ d->num_outputs = abilities.num_outputs;
>+
>+ alloc_size = sizeof(*d->inputs) * d->num_inputs;
>+ d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>+ if (!d->inputs)
>+ return -ENOMEM;
>+
>+ alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>+ de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+ if (!de->input_prio)
>+ return -ENOMEM;
>+
>+ dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>+ if (!dp->input_prio)
>+ return -ENOMEM;
>+
>+ ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
>+ if (ret)
>+ goto deinit_info;
>+
>+ if (cgu) {
>+ alloc_size = sizeof(*d->outputs) * d->num_outputs;
>+ d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>+ if (!d->outputs)
>+ goto deinit_info;
>+
>+ ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>+ if (ret)
>+ goto deinit_info;
>+ }
>+
>+ ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
>+ &pf->dplls.rclk.num_parents);
>+ if (ret)
>+ return ret;
>+ for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>+ pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
>+ ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>+ if (ret)
>+ return ret;
>+
>+ dev_dbg(ice_pf_to_dev(pf),
>+ "%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
>+ __func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
>+
>+ return 0;
>+
>+deinit_info:
>+ dev_err(ice_pf_to_dev(pf),
>+ "%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p, d->outputs:%p\n",
>+ __func__, d->inputs, de->input_prio,
>+ dp->input_prio, d->outputs);
>+ ice_dpll_deinit_info(pf);
>+ return ret;
>+}
>+
>+/**
>+ * ice_dpll_init - initialize support for dpll subsystem
>+ * @pf: board private structure
>+ *
>+ * Set up the device dplls, register them and pins connected within Linux dpll
>+ * subsystem. Allow userpsace to obtain state of DPLL and handling of DPLL
>+ * configuration requests.
>+ *
>+ * Return:
>+ * * 0 - success
>+ * * negative - init failure reason
>+ */
>+int ice_dpll_init(struct ice_pf *pf)
>+{
>+ bool cgu_present = ice_is_feature_supported(pf, ICE_F_CGU);
>+ struct ice_dplls *d = &pf->dplls;
>+ int err = 0;
>+
>+ mutex_init(&d->lock);
>+ mutex_lock(&d->lock);

Seeing pattern like this always triggers questions.
Why exactly do you need to lock the mutex here?


>+ err = ice_dpll_init_info(pf, cgu_present);
>+ if (err)
>+ goto err_exit;
>+ err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu_present,
>+ DPLL_TYPE_EEC);
>+ if (err)
>+ goto deinit_info;
>+ err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu_present,
>+ DPLL_TYPE_PPS);
>+ if (err)
>+ goto deinit_eec;
>+ err = ice_dpll_init_pins(pf, cgu_present);
>+ if (err)
>+ goto deinit_pps;
>+ set_bit(ICE_FLAG_DPLL, pf->flags);
>+ if (cgu_present) {
>+ err = ice_dpll_init_worker(pf);
>+ if (err)
>+ goto deinit_pins;
>+ }
>+ mutex_unlock(&d->lock);
>+ dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");

What is this good for? Please avoid polluting dmesg and drop this.


>+
>+ return err;
>+
>+deinit_pins:
>+ ice_dpll_deinit_pins(pf, cgu_present);
>+deinit_pps:
>+ ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu_present);
>+deinit_eec:
>+ ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu_present);
>+deinit_info:
>+ ice_dpll_deinit_info(pf);
>+err_exit:
>+ clear_bit(ICE_FLAG_DPLL, pf->flags);
>+ mutex_unlock(&d->lock);
>+ mutex_destroy(&d->lock);
>+ dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:\n");

You are missing the err. But why do you need the message?


>+
>+ return err;
>+}
>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h
>new file mode 100644
>index 000000000000..287892825deb
>--- /dev/null
>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>@@ -0,0 +1,102 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/* Copyright (C) 2022, Intel Corporation. */
>+
>+#ifndef _ICE_DPLL_H_
>+#define _ICE_DPLL_H_
>+
>+#include "ice.h"
>+
>+#define ICE_DPLL_PRIO_MAX 0xF
>+#define ICE_DPLL_RCLK_NUM_MAX 4
>+/** ice_dpll_pin - store info about pins
>+ * @pin: dpll pin structure
>+ * @pf: pointer to pf, which has registered the dpll_pin
>+ * @flags: pin flags returned from HW
>+ * @idx: ice pin private idx
>+ * @state: state of a pin
>+ * @type: type of a pin
>+ * @freq_mask: mask of supported frequencies
>+ * @freq: current frequency of a pin
>+ * @caps: capabilities of a pin
>+ * @name: pin name
>+ */
>+struct ice_dpll_pin {
>+ struct dpll_pin *pin;
>+ struct ice_pf *pf;
>+ u8 idx;
>+ u8 num_parents;
>+ u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
>+ u8 flags[ICE_DPLL_RCLK_NUM_MAX];
>+ u8 state[ICE_DPLL_RCLK_NUM_MAX];
>+ struct dpll_pin_properties prop;
>+ u32 freq;
>+};
>+
>+/** ice_dpll - store info required for DPLL control
>+ * @dpll: pointer to dpll dev
>+ * @pf: pointer to pf, which has registered the dpll_device
>+ * @dpll_idx: index of dpll on the NIC
>+ * @input_idx: currently selected input index
>+ * @prev_input_idx: previously selected input index
>+ * @ref_state: state of dpll reference signals
>+ * @eec_mode: eec_mode dpll is configured for
>+ * @phase_offset: phase delay of a dpll
>+ * @input_prio: priorities of each input
>+ * @dpll_state: current dpll sync state
>+ * @prev_dpll_state: last dpll sync state
>+ * @active_input: pointer to active input pin
>+ * @prev_input: pointer to previous active input pin
>+ */
>+struct ice_dpll {
>+ struct dpll_device *dpll;
>+ struct ice_pf *pf;
>+ int dpll_idx;
>+ u8 input_idx;
>+ u8 prev_input_idx;
>+ u8 ref_state;
>+ u8 eec_mode;
>+ s64 phase_offset;
>+ u8 *input_prio;
>+ enum ice_cgu_state dpll_state;
>+ enum ice_cgu_state prev_dpll_state;
>+ struct dpll_pin *active_input;
>+ struct dpll_pin *prev_input;
>+};
>+
>+/** ice_dplls - store info required for CCU (clock controlling unit)
>+ * @kworker: periodic worker
>+ * @work: periodic work
>+ * @lock: locks access to configuration of a dpll
>+ * @eec: pointer to EEC dpll dev
>+ * @pps: pointer to PPS dpll dev
>+ * @inputs: input pins pointer
>+ * @outputs: output pins pointer
>+ * @rclk: recovered pins pointer
>+ * @num_inputs: number of input pins available on dpll
>+ * @num_outputs: number of output pins available on dpll
>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>+ * @base_rclk_idx: idx of first pin used for clock revocery pins
>+ * @clock_id: clock_id of dplls
>+ */
>+struct ice_dplls {
>+ struct kthread_worker *kworker;
>+ struct kthread_delayed_work work;
>+ struct mutex lock;
>+ struct ice_dpll eec;
>+ struct ice_dpll pps;
>+ struct ice_dpll_pin *inputs;
>+ struct ice_dpll_pin *outputs;
>+ struct ice_dpll_pin rclk;
>+ u32 num_inputs;
>+ u32 num_outputs;
>+ int cgu_state_acq_err_num;
>+ int lock_err_num;
>+ u8 base_rclk_idx;
>+ u64 clock_id;
>+};
>+
>+int ice_dpll_init(struct ice_pf *pf);
>+
>+void ice_dpll_deinit(struct ice_pf *pf);
>+
>+#endif
>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
>index 62e91512aeab..ba5f3bc9075a 100644
>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>@@ -4595,6 +4595,10 @@ static void ice_init_features(struct ice_pf *pf)
> if (ice_is_feature_supported(pf, ICE_F_GNSS))
> ice_gnss_init(pf);
>
>+ if (ice_is_feature_supported(pf, ICE_F_CGU) ||
>+ ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>+ ice_dpll_init(pf);

Why do you have the function returning int when you don't check it here?


>+
> /* Note: Flow director init failure is non-fatal to load */
> if (ice_init_fdir(pf))
> dev_err(dev, "could not initialize flow director\n");
>@@ -4621,6 +4625,9 @@ static void ice_deinit_features(struct ice_pf *pf)
> ice_gnss_exit(pf);
> if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
> ice_ptp_release(pf);
>+ if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK) ||
>+ ice_is_feature_supported(pf, ICE_F_CGU))
>+ ice_dpll_deinit(pf);
> }
>
> static void ice_init_wakeup(struct ice_pf *pf)
>--
>2.37.3
>

2023-06-10 16:38:25

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 02/10] dpll: spec: Add Netlink spec in YAML

Fri, Jun 09, 2023 at 02:18:45PM CEST, [email protected] wrote:
>Add a protocol spec for DPLL.
>Add code generated from the spec.
>
>Signed-off-by: Jakub Kicinski <[email protected]>
>Signed-off-by: Michal Michalik <[email protected]>
>Signed-off-by: Vadim Fedorenko <[email protected]>
>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>---
> Documentation/netlink/specs/dpll.yaml | 466 ++++++++++++++++++++++++++
> drivers/dpll/dpll_nl.c | 161 +++++++++
> drivers/dpll/dpll_nl.h | 50 +++
> include/uapi/linux/dpll.h | 184 ++++++++++
> 4 files changed, 861 insertions(+)
> create mode 100644 Documentation/netlink/specs/dpll.yaml
> create mode 100644 drivers/dpll/dpll_nl.c
> create mode 100644 drivers/dpll/dpll_nl.h
> create mode 100644 include/uapi/linux/dpll.h
>
>diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml
>new file mode 100644
>index 000000000000..f7317003d312
>--- /dev/null
>+++ b/Documentation/netlink/specs/dpll.yaml
>@@ -0,0 +1,466 @@
>+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
>+
>+name: dpll
>+
>+doc: DPLL subsystem.
>+
>+definitions:
>+ -
>+ type: enum
>+ name: mode
>+ doc: |
>+ working-modes a dpll can support, differentiate if and how dpll selects

s/working-modes/working modes/
s/differentiate/differentiates/
?


>+ one of its inputs to syntonize with it, valid values for DPLL_A_MODE
>+ attribute
>+ entries:
>+ -
>+ name: manual
>+ doc: input can be only selected by sending a request to dpll
>+ value: 1
>+ -
>+ name: automatic
>+ doc: highest prio, valid input, auto selected by dpll

s/valid input, auto selected by dpll/input pin auto selected by dpll/
?


>+ -
>+ name: holdover
>+ doc: dpll forced into holdover mode
>+ -
>+ name: freerun
>+ doc: dpll driven on system clk

Thinking about modes "holdover" and "freerun".
1) You don't use them anywhere in this patchset, please remove them
until they are needed. ptp_ocp and ice uses automatic, mlx5 uses
manual. Btw, are there any other unused parts of UAPI? If yes, could
you please remove them too?

2) I don't think it is correct to have them.
a) to achieve holdover:
if state is LOCKED_HO_ACQ you just disconnect all input pins.
b) to achieve freerun:
if state LOCKED you just disconnect all input pins.
So don't mangle the mode with status.


>+ render-max: true
>+ -
>+ type: enum
>+ name: lock-status
>+ doc: |
>+ provides information of dpll device lock status, valid values for
>+ DPLL_A_LOCK_STATUS attribute
>+ entries:
>+ -
>+ name: unlocked
>+ doc: |
>+ dpll was not yet locked to any valid input (or is in mode:
>+ DPLL_MODE_FREERUN)

Don't forget to remove the mention of mode freerun from here.


>+ value: 1
>+ -
>+ name: locked
>+ doc: |
>+ dpll is locked to a valid signal, but no holdover available
>+ -
>+ name: locked-ho-acq
>+ doc: |
>+ dpll is locked and holdover acquired
>+ -
>+ name: holdover
>+ doc: |
>+ dpll is in holdover state - lost a valid lock or was forced
>+ by selecting DPLL_MODE_HOLDOVER mode (latter possible only
>+ when dpll lock-state was already DPLL_LOCK_STATUS_LOCKED,
>+ if dpll lock-state was not DPLL_LOCK_STATUS_LOCKED, the
>+ dpll's lock-state shall remain DPLL_LOCK_STATUS_UNLOCKED
>+ even if DPLL_MODE_HOLDOVER was requested)

Don't forget to remove the mention of mode holdover from here.


>+ render-max: true
>+ -
>+ type: const
>+ name: temp-divider
>+ value: 1000
>+ doc: |
>+ temperature divider allowing userspace to calculate the
>+ temperature as float with three digit decimal precision.
>+ Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
>+ temperature value.
>+ Value of (DPLL_A_TEMP % DPLL_TEMP_DIVIDER) is fractional part of
>+ temperature value.
>+ -
>+ type: enum
>+ name: type
>+ doc: type of dpll, valid values for DPLL_A_TYPE attribute
>+ entries:
>+ -
>+ name: pps
>+ doc: dpll produces Pulse-Per-Second signal
>+ value: 1
>+ -
>+ name: eec
>+ doc: dpll drives the Ethernet Equipment Clock
>+ render-max: true
>+ -
>+ type: enum
>+ name: pin-type
>+ doc: |
>+ defines possible types of a pin, valid values for DPLL_A_PIN_TYPE
>+ attribute
>+ entries:
>+ -
>+ name: mux
>+ doc: aggregates another layer of selectable pins
>+ value: 1
>+ -
>+ name: ext
>+ doc: external input
>+ -
>+ name: synce-eth-port
>+ doc: ethernet port PHY's recovered clock
>+ -
>+ name: int-oscillator
>+ doc: device internal oscillator
>+ -
>+ name: gnss
>+ doc: GNSS recovered clock
>+ render-max: true
>+ -
>+ type: enum
>+ name: pin-direction
>+ doc: |
>+ defines possible direction of a pin, valid values for
>+ DPLL_A_PIN_DIRECTION attribute
>+ entries:
>+ -
>+ name: input
>+ doc: pin used as a input of a signal

I don't think I have any objections against "input", but out of
curiosity, why you changed that from "source"?


>+ value: 1
>+ -
>+ name: output
>+ doc: pin used to output the signal
>+ render-max: true
>+ -
>+ type: const
>+ name: pin-frequency-1-hz
>+ value: 1
>+ -
>+ type: const
>+ name: pin-frequency-10-khz
>+ value: 10000
>+ -
>+ type: const
>+ name: pin-frequency-77_5-khz
>+ value: 77500
>+ -
>+ type: const
>+ name: pin-frequency-10-mhz
>+ value: 10000000
>+ -
>+ type: enum
>+ name: pin-state
>+ doc: |
>+ defines possible states of a pin, valid values for
>+ DPLL_A_PIN_STATE attribute
>+ entries:
>+ -
>+ name: connected
>+ doc: pin connected, active input of phase locked loop
>+ value: 1
>+ -
>+ name: disconnected
>+ doc: pin disconnected, not considered as a valid input
>+ -
>+ name: selectable
>+ doc: pin enabled for automatic input selection
>+ render-max: true
>+ -
>+ type: flags
>+ name: pin-caps
>+ doc: |
>+ defines possible capabilities of a pin, valid flags on
>+ DPLL_A_PIN_CAPS attribute
>+ entries:
>+ -
>+ name: direction-can-change
>+ -
>+ name: priority-can-change
>+ -
>+ name: state-can-change
>+
>+attribute-sets:
>+ -
>+ name: dpll
>+ enum-name: dpll_a
>+ attributes:
>+ -
>+ name: id
>+ type: u32
>+ value: 1
>+ -
>+ name: module-name
>+ type: string
>+ -
>+ name: clock-id
>+ type: u64
>+ -
>+ name: mode
>+ type: u8
>+ enum: mode
>+ -
>+ name: mode-supported
>+ type: u8
>+ enum: mode
>+ multi-attr: true
>+ -
>+ name: lock-status
>+ type: u8
>+ enum: lock-status
>+ -
>+ name: temp
>+ type: s32
>+ -
>+ name: type
>+ type: u8
>+ enum: type
>+ -
>+ name: pin-id
>+ type: u32
>+ -
>+ name: pin-board-label
>+ type: string
>+ -
>+ name: pin-panel-label
>+ type: string
>+ -
>+ name: pin-package-label
>+ type: string

Wouldn't it make sense to add some small documentation blocks to the
attrs? IDK.


>+ -
>+ name: pin-type
>+ type: u8
>+ enum: pin-type
>+ -
>+ name: pin-direction
>+ type: u8
>+ enum: pin-direction
>+ -
>+ name: pin-frequency
>+ type: u64
>+ -
>+ name: pin-frequency-supported
>+ type: nest
>+ multi-attr: true
>+ nested-attributes: pin-frequency-range
>+ -
>+ name: pin-frequency-min
>+ type: u64
>+ -
>+ name: pin-frequency-max
>+ type: u64
>+ -
>+ name: pin-prio
>+ type: u32
>+ -
>+ name: pin-state
>+ type: u8
>+ enum: pin-state
>+ -
>+ name: pin-dpll-caps
>+ type: u32
>+ -
>+ name: pin-parent
>+ type: nest
>+ multi-attr: true
>+ nested-attributes: pin-parent
>+ -
>+ name: pin-parent
>+ subset-of: dpll
>+ attributes:
>+ -
>+ name: id
>+ type: u32
>+ -
>+ name: pin-direction
>+ type: u8
>+ -
>+ name: pin-prio
>+ type: u32
>+ -
>+ name: pin-state
>+ type: u8
>+ -
>+ name: pin-id
>+ type: u32
>+
>+ -
>+ name: pin-frequency-range
>+ subset-of: dpll
>+ attributes:
>+ -
>+ name: pin-frequency-min
>+ type: u64
>+ -
>+ name: pin-frequency-max
>+ type: u64

[...]

2023-06-10 17:09:34

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

Fri, Jun 09, 2023 at 02:18:51PM CEST, [email protected] wrote:

[...]


>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>+ enum dpll_mode *mode,
>+ struct netlink_ext_ack *extack)
>+{
>+ *mode = DPLL_MODE_AUTOMATIC;

I don't understand how the automatic mode could work with SyncE. The
There is one pin exposed for one netdev. The SyncE daemon should select
exacly one pin. How do you achieve that?
Is is by setting DPLL_PIN_STATE_SELECTABLE on the pin-netdev you want to
select and DPLL_PIN_STATE_DISCONNECTED on the rest?


[...]

2023-06-10 17:09:36

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

Fri, Jun 09, 2023 at 02:18:44PM CEST, [email protected] wrote:
>From: Vadim Fedorenko <[email protected]>
>
>Add documentation explaining common netlink interface to configure DPLL
>devices and monitoring events. Common way to implement DPLL device in
>a driver is also covered.
>
>Signed-off-by: Vadim Fedorenko <[email protected]>
>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>---
> Documentation/driver-api/dpll.rst | 458 +++++++++++++++++++++++++++++
> Documentation/driver-api/index.rst | 1 +
> 2 files changed, 459 insertions(+)
> create mode 100644 Documentation/driver-api/dpll.rst

Looks fine to me. I just wonder if the info redundancy of this file and
the netlink yaml could be somehow reduce. IDK.

2023-06-10 19:05:20

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

Fri, Jun 09, 2023 at 02:18:46PM CEST, [email protected] wrote:
>From: Vadim Fedorenko <[email protected]>
>
>DPLL framework is used to represent and configure DPLL devices
>in systems. Each device that has DPLL and can configure inputs
>and outputs can use this framework.
>
>Implement core framework functions for further interactions
>with device drivers implementing dpll subsystem, as well as for
>interactions of DPLL netlink framework part with the subsystem
>itself.
>
>Co-developed-by: Milena Olech <[email protected]>
>Signed-off-by: Milena Olech <[email protected]>
>Co-developed-by: Michal Michalik <[email protected]>
>Signed-off-by: Michal Michalik <[email protected]>
>Signed-off-by: Vadim Fedorenko <[email protected]>
>Co-developed-by: Arkadiusz Kubalewski <[email protected]>
>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>---
> drivers/dpll/dpll_core.c | 953 +++++++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_core.h | 104 +++++

Overall, looks very good! I pinpointed couple of nits below, nothing big.
General question: Why do you put documentation comment to every static
function? Does not make any sense to me. Even for non-exported functions
I think it is overkill. Most of them (if not all) give the reader no
additional information and only make the code a bit harder to read.
Care to drop them?


> 2 files changed, 1057 insertions(+)
> create mode 100644 drivers/dpll/dpll_core.c
> create mode 100644 drivers/dpll/dpll_core.h
>
>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>new file mode 100644
>index 000000000000..ee515b7c18be
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.c
>@@ -0,0 +1,953 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * dpll_core.c - Generic DPLL Management class support.

What's "management class support"? Does not sound recognizable.


>+ *
>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>+ * Copyright (c) 2023 Intel Corporation.
>+ */
>+
>+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>+
>+#include <linux/device.h>
>+#include <linux/err.h>
>+#include <linux/slab.h>
>+#include <linux/string.h>
>+
>+#include "dpll_core.h"
>+
>+DEFINE_MUTEX(dpll_lock);
>+
>+DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
>+DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
>+
>+#define ASSERT_DPLL_REGISTERED(d) \
>+ WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>+#define ASSERT_DPLL_NOT_REGISTERED(d) \
>+ WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>+
>+/**
>+ * dpll_device_get_by_id - find dpll device by it's id
>+ * @id: id of searched dpll
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return:
>+ * * dpll_device struct if found
>+ * * NULL otherwise
>+ */
>+struct dpll_device *dpll_device_get_by_id(int id)
>+{
>+ if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>+ return xa_load(&dpll_device_xa, id);
>+
>+ return NULL;
>+}
>+
>+/**
>+ * dpll_pin_registration_find - find a pin registration record
>+ * @ref: reference between dpll and pin
>+ * @ops: searched pin ops pointer
>+ * @priv: searched pin priv pointer
>+ *
>+ * Search dpll's registered pins for given ops and priv data.
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return:
>+ * * NULL - if pin was not found
>+ * * pointer to `struct dpll_pin_registration` if found
>+ */
>+static struct dpll_pin_registration *
>+dpll_pin_registration_find(struct dpll_pin_ref *ref,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ struct dpll_pin_registration *reg;
>+
>+ list_for_each_entry(reg, &ref->registration_list, list) {
>+ if (reg->ops == ops && reg->priv == priv)
>+ return reg;
>+ }
>+ return NULL;
>+}
>+
>+/**
>+ * dpll_xa_ref_pin_add - add pin reference to a given xarray
>+ * @xa_pins: dpll_pin_ref xarray holding pins
>+ * @pin: pin being added
>+ * @ops: ops for a pin
>+ * @priv: pointer to private data of owner
>+ *
>+ * Allocate and create reference of a pin and enlist a registration
>+ * structure storing ops and priv pointers of a caller registant.
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return:
>+ * * 0 on success
>+ * * -ENOMEM on failed allocation
>+ */
>+static int
>+dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ struct dpll_pin_registration *reg;
>+ struct dpll_pin_ref *ref;
>+ bool ref_exists = false;
>+ unsigned long i;
>+ int ret;
>+
>+ xa_for_each(xa_pins, i, ref) {
>+ if (ref->pin != pin)
>+ continue;
>+ reg = dpll_pin_registration_find(ref, ops, priv);
>+ if (reg) {
>+ refcount_inc(&ref->refcount);
>+ return 0;
>+ }
>+ ref_exists = true;
>+ break;
>+ }
>+
>+ if (!ref_exists) {
>+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>+ if (!ref)
>+ return -ENOMEM;
>+ ref->pin = pin;
>+ INIT_LIST_HEAD(&ref->registration_list);
>+ ret = xa_insert(xa_pins, pin->pin_idx, ref, GFP_KERNEL);
>+ if (ret) {
>+ kfree(ref);
>+ return ret;
>+ }
>+ refcount_set(&ref->refcount, 1);
>+ }
>+
>+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>+ if (!reg) {
>+ if (!ref_exists)
>+ kfree(ref);
>+ return -ENOMEM;
>+ }
>+ reg->ops = ops;
>+ reg->priv = priv;
>+ if (ref_exists)

If you set refcount to 0 couple of lines above, you can drop the check
here.


>+ refcount_inc(&ref->refcount);
>+ list_add_tail(&reg->list, &ref->registration_list);
>+
>+ return 0;
>+}
>+
>+/**
>+ * dpll_xa_ref_pin_del - remove reference of a pin from xarray
>+ * @xa_pins: dpll_pin_ref xarray holding pins
>+ * @pin: pointer to a pin being removed
>+ * @ops: pointer to ops of pin being removed
>+ * @priv: pointer to private data of registerer who invoked pin removal
>+ *
>+ * Decrement refcount of existing pin reference on given xarray.
>+ * If all registrations are lifted delete the reference and free its memory.
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return:
>+ * * 0 on success
>+ * * -EINVAL if reference to a pin was not found
>+ */
>+static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ struct dpll_pin_registration *reg;
>+ struct dpll_pin_ref *ref;
>+ unsigned long i;
>+
>+ xa_for_each(xa_pins, i, ref) {
>+ if (ref->pin != pin)
>+ continue;
>+ reg = dpll_pin_registration_find(ref, ops, priv);
>+ if (WARN_ON(!reg))
>+ return -EINVAL;
>+ if (refcount_dec_and_test(&ref->refcount)) {
>+ list_del(&reg->list);
>+ kfree(reg);
>+ xa_erase(xa_pins, i);
>+ WARN_ON(!list_empty(&ref->registration_list));
>+ kfree(ref);
>+ }
>+ return 0;
>+ }
>+
>+ return -EINVAL;
>+}
>+
>+/**
>+ * dpll_xa_ref_dpll_add - add dpll reference to a given xarray
>+ * @xa_dplls: dpll_pin_ref xarray holding dplls
>+ * @dpll: dpll being added
>+ * @ops: pin-reference ops for a dpll
>+ * @priv: pointer to private data of owner
>+ *
>+ * Allocate and create reference of a dpll-pin ops or increase refcount
>+ * on existing dpll reference on given xarray.
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return:
>+ * * 0 on success
>+ * * -ENOMEM on failed allocation
>+ */
>+static int
>+dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ struct dpll_pin_registration *reg;
>+ struct dpll_pin_ref *ref;
>+ bool ref_exists = false;
>+ unsigned long i;
>+ int ret;
>+
>+ xa_for_each(xa_dplls, i, ref) {
>+ if (ref->dpll != dpll)
>+ continue;
>+ reg = dpll_pin_registration_find(ref, ops, priv);
>+ if (reg) {
>+ refcount_inc(&ref->refcount);
>+ return 0;
>+ }
>+ ref_exists = true;
>+ break;
>+ }
>+
>+ if (!ref_exists) {
>+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>+ if (!ref)
>+ return -ENOMEM;
>+ ref->dpll = dpll;
>+ INIT_LIST_HEAD(&ref->registration_list);
>+ ret = xa_insert(xa_dplls, dpll->device_idx, ref, GFP_KERNEL);
>+ if (ret) {
>+ kfree(ref);
>+ return ret;
>+ }
>+ refcount_set(&ref->refcount, 1);
>+ }
>+
>+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>+ if (!reg) {
>+ if (!ref_exists)
>+ kfree(ref);
>+ return -ENOMEM;
>+ }
>+ reg->ops = ops;
>+ reg->priv = priv;
>+ if (ref_exists)

If you set refcount to 0 couple of lines above, you can drop the check
here.


>+ refcount_inc(&ref->refcount);
>+ list_add_tail(&reg->list, &ref->registration_list);
>+
>+ return 0;
>+}
>+
>+/**
>+ * dpll_xa_ref_dpll_del - remove reference of a dpll from xarray
>+ * @xa_dplls: dpll_pin_ref xarray holding dplls
>+ * @dpll: pointer to a dpll to remove
>+ * @ops: pointer to ops of dpll being removed
>+ * @priv: pointer to private data of registerer who invoked dpll removal
>+ *
>+ * Decrement refcount of existing dpll reference on given xarray.
>+ * If all references are dropped, delete the reference and free its memory.
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ */
>+static void
>+dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ struct dpll_pin_registration *reg;
>+ struct dpll_pin_ref *ref;
>+ unsigned long i;
>+
>+ xa_for_each(xa_dplls, i, ref) {
>+ if (ref->dpll != dpll)
>+ continue;
>+ reg = dpll_pin_registration_find(ref, ops, priv);
>+ if (WARN_ON(!reg))
>+ return;
>+ if (refcount_dec_and_test(&ref->refcount)) {
>+ list_del(&reg->list);
>+ kfree(reg);
>+ xa_erase(xa_dplls, i);
>+ WARN_ON(!list_empty(&ref->registration_list));
>+ kfree(ref);
>+ }
>+ return;
>+ }
>+}
>+
>+/**
>+ * dpll_xa_ref_dpll_find - find dpll reference on xarray
>+ * @xa_refs: dpll_pin_ref xarray holding dpll references
>+ * @dpll: pointer to a dpll being searched
>+ *
>+ * Search for dpll-pin ops reference struct of a given dpll on given xarray.
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return:
>+ * * pin reference struct pointer on success
>+ * * NULL - reference to a pin was not found
>+ */
>+struct dpll_pin_ref *
>+dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll)
>+{
>+ struct dpll_pin_ref *ref;
>+ unsigned long i;
>+
>+ xa_for_each(xa_refs, i, ref) {
>+ if (ref->dpll == dpll)
>+ return ref;
>+ }
>+
>+ return NULL;
>+}
>+
>+/**
>+ * dpll_xa_ref_dpll_first - find first record of given xarray
>+ * @xa_refs: xarray
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return: first element on given xaaray
>+ */
>+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs)
>+{
>+ struct dpll_pin_ref *ref;
>+ unsigned long i = 0;
>+
>+ ref = xa_find(xa_refs, &i, ULONG_MAX, XA_PRESENT);
>+ WARN_ON(!ref);
>+ return ref;
>+}
>+
>+/**
>+ * dpll_device_alloc - allocate the memory for dpll device
>+ * @clock_id: clock_id of creator
>+ * @device_idx: id given by dev driver
>+ * @module: reference to registering module
>+ *
>+ * Allocates memory and initialize dpll device, hold its reference on global
>+ * xarray.
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return:
>+ * * valid dpll_device struct pointer if succeeded
>+ * * ERR_PTR(-ENOMEM) - failed memory allocation
>+ * * ERR_PTR(X) - failed allocation on dpll's xa
>+ */
>+static struct dpll_device *
>+dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
>+{
>+ struct dpll_device *dpll;
>+ int ret;
>+
>+ dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>+ if (!dpll)
>+ return ERR_PTR(-ENOMEM);
>+ refcount_set(&dpll->refcount, 1);
>+ INIT_LIST_HEAD(&dpll->registration_list);
>+ dpll->device_idx = device_idx;
>+ dpll->clock_id = clock_id;
>+ dpll->module = module;
>+ ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll, xa_limit_16b,
>+ GFP_KERNEL);
>+ if (ret) {
>+ kfree(dpll);
>+ return ERR_PTR(ret);
>+ }
>+ xa_init_flags(&dpll->pin_refs, XA_FLAGS_ALLOC);
>+
>+ return dpll;
>+}
>+
>+/**
>+ * dpll_device_get - find existing or create new dpll device
>+ * @clock_id: clock_id of creator
>+ * @device_idx: idx given by device driver
>+ * @module: reference to registering module
>+ *
>+ * Get existing object of a dpll device, unique for given arguments.
>+ * Create new if doesn't exist yet.
>+ *
>+ * Context: Acquires a lock (dpll_lock)
>+ * Return:
>+ * * valid dpll_device struct pointer if succeeded
>+ * * ERR_PTR(-ENOMEM) - failed memory allocation
>+ * * ERR_PTR(X) - failed allocation on dpll's xa
>+ */
>+struct dpll_device *
>+dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
>+{
>+ struct dpll_device *dpll, *ret = NULL;
>+ unsigned long index;
>+
>+ mutex_lock(&dpll_lock);
>+ xa_for_each(&dpll_device_xa, index, dpll) {
>+ if (dpll->clock_id == clock_id &&
>+ dpll->device_idx == device_idx &&
>+ dpll->module == module) {
>+ ret = dpll;
>+ refcount_inc(&ret->refcount);
>+ break;
>+ }
>+ }
>+ if (!ret)
>+ ret = dpll_device_alloc(clock_id, device_idx, module);
>+ mutex_unlock(&dpll_lock);
>+
>+ return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_get);
>+
>+/**
>+ * dpll_device_put - decrease the refcount and free memory if possible
>+ * @dpll: dpll_device struct pointer
>+ *
>+ * Context: Acquires a lock (dpll_lock)
>+ * Drop reference for a dpll device, if all references are gone, delete
>+ * dpll device object.
>+ */
>+void dpll_device_put(struct dpll_device *dpll)
>+{
>+ if (!dpll)

Drop this check and rely on a basic driver sanity. I'm pretty sure I
asked you to drop checks like this during the last version review.


>+ return;
>+ mutex_lock(&dpll_lock);
>+ if (refcount_dec_and_test(&dpll->refcount)) {
>+ ASSERT_DPLL_NOT_REGISTERED(dpll);
>+ WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>+ xa_destroy(&dpll->pin_refs);
>+ xa_erase(&dpll_device_xa, dpll->id);
>+ WARN_ON(!list_empty(&dpll->registration_list));
>+ kfree(dpll);
>+ }
>+ mutex_unlock(&dpll_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_put);
>+
>+static struct dpll_device_registration *
>+dpll_device_registration_find(struct dpll_device *dpll,
>+ const struct dpll_device_ops *ops, void *priv)
>+{
>+ struct dpll_device_registration *reg;
>+
>+ list_for_each_entry(reg, &dpll->registration_list, list) {
>+ if (reg->ops == ops && reg->priv == priv)
>+ return reg;
>+ }
>+ return NULL;
>+}
>+
>+/**
>+ * dpll_device_register - register the dpll device in the subsystem
>+ * @dpll: pointer to a dpll
>+ * @type: type of a dpll
>+ * @ops: ops for a dpll device
>+ * @priv: pointer to private information of owner
>+ *
>+ * Make dpll device available for user space.
>+ *
>+ * Context: Acquires a lock (dpll_lock)
>+ * Return:
>+ * * 0 on success
>+ * * -EINVAL on failure due to wrong arguments provided
>+ * * -EEXIST if device was already registered
>+ */
>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+ const struct dpll_device_ops *ops, void *priv)
>+{
>+ struct dpll_device_registration *reg;
>+ bool first_registration = false;
>+
>+ if (WARN_ON(!ops))
>+ return -EINVAL;
>+ if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX))
>+ return -EINVAL;
>+
>+ mutex_lock(&dpll_lock);
>+ reg = dpll_device_registration_find(dpll, ops, priv);
>+ if (reg) {
>+ mutex_unlock(&dpll_lock);
>+ return -EEXIST;
>+ }
>+
>+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>+ if (!reg) {
>+ mutex_unlock(&dpll_lock);
>+ return -EEXIST;

-ENOMEM;


>+ }
>+ reg->ops = ops;
>+ reg->priv = priv;
>+ dpll->type = type;
>+ first_registration = list_empty(&dpll->registration_list);
>+ list_add_tail(&reg->list, &dpll->registration_list);
>+ if (!first_registration) {
>+ mutex_unlock(&dpll_lock);
>+ return 0;
>+ }
>+
>+ xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+ mutex_unlock(&dpll_lock);
>+ dpll_device_create_ntf(dpll);
>+
>+ return 0;
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_register);
>+
>+/**
>+ * dpll_device_unregister - unregister dpll device
>+ * @dpll: registered dpll pointer
>+ * @ops: ops for a dpll device
>+ * @priv: pointer to private information of owner
>+ *
>+ * Unregister device, make it unavailable for userspace.
>+ * Note: It does not free the memory
>+ * Context: Acquires a lock (dpll_lock)
>+ */
>+void dpll_device_unregister(struct dpll_device *dpll,
>+ const struct dpll_device_ops *ops, void *priv)
>+{
>+ struct dpll_device_registration *reg;
>+
>+ mutex_lock(&dpll_lock);
>+ ASSERT_DPLL_REGISTERED(dpll);
>+ dpll_device_delete_ntf(dpll);
>+ reg = dpll_device_registration_find(dpll, ops, priv);
>+ if (WARN_ON(!reg)) {
>+ mutex_unlock(&dpll_lock);
>+ return;
>+ }
>+ list_del(&reg->list);
>+ kfree(reg);
>+
>+ if (!list_empty(&dpll->registration_list)) {
>+ mutex_unlock(&dpll_lock);
>+ return;
>+ }
>+ xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+ mutex_unlock(&dpll_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_unregister);
>+
>+/**
>+ * dpll_pin_alloc - allocate the memory for dpll pin
>+ * @clock_id: clock_id of creator
>+ * @pin_idx: idx given by dev driver
>+ * @module: reference to registering module
>+ * @prop: dpll pin properties
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return:
>+ * * valid allocated dpll_pin struct pointer if succeeded
>+ * * ERR_PTR(-ENOMEM) - failed memory allocation
>+ * * ERR_PTR(-EINVAL) - wrong pin type property value
>+ */
>+static struct dpll_pin *
>+dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
>+ const struct dpll_pin_properties *prop)
>+{
>+ struct dpll_pin *pin;
>+ int ret;
>+
>+ pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>+ if (!pin)
>+ return ERR_PTR(-ENOMEM);
>+ pin->pin_idx = pin_idx;
>+ pin->clock_id = clock_id;
>+ pin->module = module;
>+ if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX ||
>+ prop->type > DPLL_PIN_TYPE_MAX)) {
>+ ret = -EINVAL;
>+ goto err;
>+ }
>+ pin->prop = prop;
>+ refcount_set(&pin->refcount, 1);
>+ xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC);
>+ xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC);
>+ ret = xa_alloc(&dpll_pin_xa, &pin->id, pin, xa_limit_16b, GFP_KERNEL);
>+ if (ret)
>+ goto err;
>+ return pin;
>+err:
>+ xa_destroy(&pin->dpll_refs);
>+ xa_destroy(&pin->parent_refs);
>+ kfree(pin);
>+ return ERR_PTR(ret);
>+}
>+
>+/**
>+ * dpll_pin_get - find existing or create new dpll pin
>+ * @clock_id: clock_id of creator
>+ * @pin_idx: idx given by dev driver
>+ * @module: reference to registering module
>+ * @prop: dpll pin properties
>+ *
>+ * Get existing object of a pin (unique for given arguments) or create new
>+ * if doesn't exist yet.
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return:
>+ * * valid allocated dpll_pin struct pointer if succeeded
>+ * * ERR_PTR of an error
>+ */
>+struct dpll_pin *
>+dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>+ const struct dpll_pin_properties *prop)
>+{
>+ struct dpll_pin *pos, *ret = NULL;
>+ unsigned long i;
>+
>+ xa_for_each(&dpll_pin_xa, i, pos) {
>+ if (pos->clock_id == clock_id &&
>+ pos->pin_idx == pin_idx &&
>+ pos->module == module) {
>+ ret = pos;
>+ refcount_inc(&ret->refcount);
>+ break;
>+ }
>+ }
>+ if (!ret)
>+ ret = dpll_pin_alloc(clock_id, pin_idx, module, prop);
>+
>+ return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_get);
>+
>+/**
>+ * dpll_pin_put - decrease the refcount and free memory if possible
>+ * @pin: pointer to a pin to be put
>+ *
>+ * Drop reference for a pin, if all references are gone, delete pin object.
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ */
>+void dpll_pin_put(struct dpll_pin *pin)
>+{
>+ if (!pin)

Drop this check.


>+ return;
>+ if (refcount_dec_and_test(&pin->refcount)) {
>+ xa_destroy(&pin->dpll_refs);
>+ xa_destroy(&pin->parent_refs);
>+ xa_erase(&dpll_pin_xa, pin->id);
>+ kfree(pin);
>+ }
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_put);
>+
>+static int
>+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ int ret;
>+
>+ if (WARN_ON(!ops))
>+ return -EINVAL;
>+
>+ ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
>+ if (ret)
>+ return ret;
>+ ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
>+ if (ret)
>+ goto ref_pin_del;
>+ else
>+ dpll_pin_create_ntf(pin);
>+
>+ return ret;
>+
>+ref_pin_del:
>+ dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
>+ return ret;
>+}
>+
>+/**
>+ * dpll_pin_register - register the dpll pin in the subsystem
>+ * @dpll: pointer to a dpll
>+ * @pin: pointer to a dpll pin
>+ * @ops: ops for a dpll pin ops
>+ * @priv: pointer to private information of owner
>+ *
>+ * Context: Acquires a lock (dpll_lock)
>+ * Return:
>+ * * 0 on success
>+ * * -EINVAL - missing pin ops
>+ * * -ENOMEM - failed to allocate memory
>+ */
>+int
>+dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ int ret;
>+
>+ mutex_lock(&dpll_lock);
>+ if (WARN_ON(!(dpll->module == pin->module &&
>+ dpll->clock_id == pin->clock_id)))
>+ ret = -EFAULT;

In other cases like this you return -EINVAL. I think it is better. One
way or another, be consistent please.


>+ else
>+ ret = __dpll_pin_register(dpll, pin, ops, priv);
>+ mutex_unlock(&dpll_lock);
>+
>+ return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>+
>+static void
>+__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
>+ dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv);
>+}
>+
>+/**
>+ * dpll_pin_unregister - unregister dpll pin from dpll device
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ * @ops: ops for a dpll pin
>+ * @priv: pointer to private information of owner
>+ *
>+ * Note: It does not free the memory
>+ * Context: Acquires a lock (dpll_lock)
>+ */
>+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ if (WARN_ON(xa_empty(&dpll->pin_refs)))
>+ return;
>+
>+ mutex_lock(&dpll_lock);
>+ __dpll_pin_unregister(dpll, pin, ops, priv);
>+ mutex_unlock(&dpll_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_unregister);
>+
>+/**
>+ * dpll_pin_on_pin_register - register a pin with a parent pin
>+ * @parent: pointer to a parent pin
>+ * @pin: pointer to a pin
>+ * @ops: ops for a dpll pin
>+ * @priv: pointer to private information of owner
>+ *
>+ * Register a pin with a parent pin, create references between them and
>+ * between newly registered pin and dplls connected with a parent pin.
>+ *
>+ * Context: Acquires a lock (dpll_lock)
>+ * Return:
>+ * * 0 on success
>+ * * -EINVAL missing pin or parent
>+ * * -ENOMEM failed allocation
>+ * * -EPERM if parent is not allowed
>+ */
>+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ struct dpll_pin_ref *ref;
>+ unsigned long i, stop;
>+ int ret;
>+
>+ if (WARN_ON(parent->prop->type != DPLL_PIN_TYPE_MUX))
>+ return -EINVAL;
>+ ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv);
>+ if (ret)
>+ goto unlock;
>+ refcount_inc(&pin->refcount);
>+ xa_for_each(&parent->dpll_refs, i, ref) {
>+ mutex_lock(&dpll_lock);
>+ ret = __dpll_pin_register(ref->dpll, pin, ops, priv);
>+ mutex_unlock(&dpll_lock);
>+ if (ret) {
>+ stop = i;
>+ goto dpll_unregister;
>+ }
>+ dpll_pin_create_ntf(pin);
>+ }
>+
>+ return ret;
>+
>+dpll_unregister:
>+ xa_for_each(&parent->dpll_refs, i, ref) {
>+ if (i < stop) {
>+ mutex_lock(&dpll_lock);
>+ __dpll_pin_unregister(ref->dpll, pin, ops, priv);
>+ mutex_unlock(&dpll_lock);
>+ }
>+ }
>+ refcount_dec(&pin->refcount);
>+ dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
>+unlock:
>+ return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register);
>+
>+/**
>+ * dpll_pin_on_pin_unregister - unregister dpll pin from a parent pin
>+ * @parent: pointer to a parent pin
>+ * @pin: pointer to a pin
>+ * @ops: ops for a dpll pin
>+ * @priv: pointer to private information of owner
>+ *
>+ * Context: Acquires a lock (dpll_lock)
>+ * Note: It does not free the memory
>+ */
>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ struct dpll_pin_ref *ref;
>+ unsigned long i;
>+
>+ mutex_lock(&dpll_lock);
>+ dpll_pin_delete_ntf(pin);
>+ dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
>+ refcount_dec(&pin->refcount);
>+ xa_for_each(&pin->dpll_refs, i, ref) {
>+ __dpll_pin_unregister(ref->dpll, pin, ops, priv);
>+ }

Drop "{}"s for one line statement. Didn't checkpack warn you?


>+ mutex_unlock(&dpll_lock);
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>+
>+
>+/**
>+ * dpll_device_registration_first - get first registration of dpll device
>+ * @dpll: pointer to a dpll
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return: pointer to the first registration of a dpll
>+ */
>+static struct dpll_device_registration *
>+dpll_device_registration_first(struct dpll_device *dpll)
>+{
>+ struct dpll_device_registration *reg;
>+
>+ reg = list_first_entry_or_null((struct list_head *) &dpll->registration_list,
>+ struct dpll_device_registration, list);
>+ WARN_ON(!reg);
>+ return reg;
>+}
>+
>+/**
>+ * dpll_priv - get the dpll device private owner data
>+ * @dpll: registered dpll pointer
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return: pointer to the first registration priv data
>+ */
>+void *dpll_priv(struct dpll_device *dpll)
>+{
>+ struct dpll_device_registration *reg;
>+
>+ reg = dpll_device_registration_first(dpll);
>+ return reg->priv;
>+}
>+
>+/**
>+ * dpll_device_ops - get the dpll device ops pointer
>+ * @dpll: registered dpll pointer
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return: pointer to the first registration ops of the dpll
>+ */
>+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
>+{
>+ struct dpll_device_registration *reg;
>+
>+ reg = dpll_device_registration_first(dpll);
>+ return reg->ops;
>+}
>+
>+/**
>+ * dpll_pin_registration_first - get first registration of dpll pin ref
>+ * @ref: pointer to a pin ref struct
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return: pointer to the first registration of a dpll_pin_ref
>+ */
>+static struct dpll_pin_registration *
>+dpll_pin_registration_first(struct dpll_pin_ref *ref)
>+{
>+ struct dpll_pin_registration *reg;
>+
>+ reg = list_first_entry_or_null(&ref->registration_list,
>+ struct dpll_pin_registration, list);
>+ WARN_ON(!reg);
>+ return reg;
>+}
>+
>+/**
>+ * dpll_pin_on_dpll_priv - get the dpll device private owner data
>+ * @dpll: registered dpll pointer
>+ * @pin: pointer to a pin
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return: pointer to the data
>+ */
>+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll,
>+ struct dpll_pin *pin)
>+{
>+ struct dpll_pin_registration *reg;
>+ struct dpll_pin_ref *ref;
>+
>+ ref = xa_load(&dpll->pin_refs, pin->pin_idx);
>+ if (!ref)
>+ return NULL;
>+ reg = dpll_pin_registration_first(ref);
>+ return reg->priv;
>+}
>+
>+/**
>+ * dpll_pin_on_pin_priv - get the dpll pin private owner data
>+ * @parent: pointer to a parent pin
>+ * @pin: pointer to a pin
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return: pointer to the data
>+ */
>+void *dpll_pin_on_pin_priv(struct dpll_pin *parent,
>+ struct dpll_pin *pin)
>+{
>+ struct dpll_pin_registration *reg;
>+ struct dpll_pin_ref *ref;
>+
>+ ref = xa_load(&pin->parent_refs, parent->pin_idx);
>+ if (!ref)
>+ return NULL;
>+ reg = dpll_pin_registration_first(ref);
>+ return reg->priv;
>+}
>+
>+/**
>+ * dpll_pin_ops - get the pin ops pointer
>+ * @ref: dpll pin ref
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return: pointer to the first ops registered with the pin
>+ */
>+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref)
>+{
>+ struct dpll_pin_registration *reg;
>+
>+ reg = dpll_pin_registration_first(ref);
>+ return reg->ops;
>+}
>+
>+/**
>+ * dpll_init - initialize dpll subsystem
>+ *
>+ * Return:
>+ * 0 - success
>+ * negative - netlink init error
>+ */
>+static int __init dpll_init(void)
>+{
>+ int ret;
>+
>+ ret = dpll_netlink_init();
>+ if (ret)
>+ goto error;
>+
>+ return 0;
>+
>+error:
>+ mutex_destroy(&dpll_lock);
>+ return ret;
>+}
>+subsys_initcall(dpll_init);
>diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
>new file mode 100644
>index 000000000000..ef95e272937c
>--- /dev/null
>+++ b/drivers/dpll/dpll_core.h
>@@ -0,0 +1,104 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>+ * Copyright (c) 2023 Intel and affiliates
>+ */
>+
>+#ifndef __DPLL_CORE_H__
>+#define __DPLL_CORE_H__
>+
>+#include <linux/dpll.h>
>+#include <linux/list.h>
>+#include <linux/refcount.h>
>+#include "dpll_netlink.h"
>+
>+#define DPLL_REGISTERED XA_MARK_1
>+
>+struct dpll_device_registration {
>+ struct list_head list;
>+ const struct dpll_device_ops *ops;
>+ void *priv;

You don't need this struct in the header. Move to .c


>+};
>+
>+/**
>+ * struct dpll_device - stores DPLL device internal data
>+ * @id: unique id number for each device given by kernel

Who's kernel? Say "dpll subsystem" instead.


>+ * @device_idx: id given by dev driver
>+ * @clock_id: unique identifier (clock_id) of a dpll
>+ * @module: module of creator
>+ * @type: type of a dpll
>+ * @pin_refs: stores pins registered within a dpll
>+ * @mode_supported_mask: mask of supported modes
>+ * @refcount: refcount
>+ * @registration_list: list of registered ops and priv data of dpll owners
>+ **/
>+struct dpll_device {
>+ u32 id;
>+ u32 device_idx;
>+ u64 clock_id;
>+ struct module *module;
>+ enum dpll_type type;
>+ struct xarray pin_refs;
>+ unsigned long mode_supported_mask;
>+ refcount_t refcount;
>+ struct list_head registration_list;
>+};
>+
>+/**
>+ * struct dpll_pin - structure for a dpll pin
>+ * @id: unique id number for pin given by kernel

Who's kernel? Say "dpll subsystem" instead.


>+ * @pin_idx: index of a pin given by dev driver
>+ * @clock_id: clock_id of creator
>+ * @module: module of creator
>+ * @dpll_refs: hold referencees to dplls pin was registered with
>+ * @parent_refs: hold references to parent pins pin was registered with
>+ * @prop: pointer to pin properties given by registerer
>+ * @rclk_dev_name: holds name of device when pin can recover clock from it
>+ * @refcount: refcount
>+ **/
>+struct dpll_pin {
>+ u32 id;
>+ u32 pin_idx;
>+ u64 clock_id;
>+ struct module *module;
>+ struct xarray dpll_refs;
>+ struct xarray parent_refs;
>+ const struct dpll_pin_properties *prop;
>+ char *rclk_dev_name;
>+ refcount_t refcount;
>+};
>+
>+struct dpll_pin_registration {
>+ struct list_head list;
>+ const struct dpll_pin_ops *ops;
>+ void *priv;
>+};

You don't need this struct in the header. Move to .c


>+
>+/**
>+ * struct dpll_pin_ref - structure for referencing either dpll or pins
>+ * @dpll: pointer to a dpll
>+ * @pin: pointer to a pin
>+ * @registration_list: list of ops and priv data registered with the ref
>+ * @refcount: refcount
>+ **/
>+struct dpll_pin_ref {
>+ union {
>+ struct dpll_device *dpll;
>+ struct dpll_pin *pin;
>+ };
>+ struct list_head registration_list;
>+ refcount_t refcount;
>+};
>+
>+void *dpll_priv(struct dpll_device *dpll);
>+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll, struct dpll_pin *pin);
>+void *dpll_pin_on_pin_priv(struct dpll_pin *parent, struct dpll_pin *pin);
>+
>+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll);
>+struct dpll_device *dpll_device_get_by_id(int id);
>+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref);
>+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
>+extern struct xarray dpll_device_xa;
>+extern struct xarray dpll_pin_xa;
>+extern struct mutex dpll_lock;
>+#endif
>--
>2.37.3
>

2023-06-11 09:50:44

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

Fri, Jun 09, 2023 at 02:18:46PM CEST, [email protected] wrote:
>From: Vadim Fedorenko <[email protected]>

[...]

>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>+ const struct dpll_device_ops *ops, void *priv)
>+{
>+ struct dpll_device_registration *reg;
>+ bool first_registration = false;
>+
>+ if (WARN_ON(!ops))
>+ return -EINVAL;
>+ if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX))
>+ return -EINVAL;
>+
>+ mutex_lock(&dpll_lock);
>+ reg = dpll_device_registration_find(dpll, ops, priv);
>+ if (reg) {
>+ mutex_unlock(&dpll_lock);
>+ return -EEXIST;
>+ }
>+
>+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>+ if (!reg) {
>+ mutex_unlock(&dpll_lock);
>+ return -EEXIST;
>+ }
>+ reg->ops = ops;
>+ reg->priv = priv;
>+ dpll->type = type;
>+ first_registration = list_empty(&dpll->registration_list);
>+ list_add_tail(&reg->list, &dpll->registration_list);
>+ if (!first_registration) {
>+ mutex_unlock(&dpll_lock);
>+ return 0;
>+ }
>+
>+ xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>+ mutex_unlock(&dpll_lock);
>+ dpll_device_create_ntf(dpll);

This function is introduced in the next patch. Breaks bissection. Make
sure you can compile the code after every patch applied.



>+
>+ return 0;
>+}

[...]

2023-06-11 10:18:58

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

Fri, Jun 09, 2023 at 02:18:46PM CEST, [email protected] wrote:
>From: Vadim Fedorenko <[email protected]>

[...]


>+ * dpll_xa_ref_dpll_first - find first record of given xarray
>+ * @xa_refs: xarray
>+ *
>+ * Context: shall be called under a lock (dpll_lock)
>+ * Return: first element on given xaaray

typo: xarray


>+ */
>+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs)

[...]


>+/**
>+ * dpll_device_get - find existing or create new dpll device
>+ * @clock_id: clock_id of creator
>+ * @device_idx: idx given by device driver
>+ * @module: reference to registering module
>+ *
>+ * Get existing object of a dpll device, unique for given arguments.
>+ * Create new if doesn't exist yet.
>+ *
>+ * Context: Acquires a lock (dpll_lock)
>+ * Return:
>+ * * valid dpll_device struct pointer if succeeded
>+ * * ERR_PTR(-ENOMEM) - failed memory allocation

Yeah, that is kind of obvious, isn't? Really, drop this pointless
coments.


>+ * * ERR_PTR(X) - failed allocation on dpll's xa
>+ */
>+struct dpll_device *
>+dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)

[...]


>+/**
>+ * dpll_pin_register - register the dpll pin in the subsystem
>+ * @dpll: pointer to a dpll
>+ * @pin: pointer to a dpll pin
>+ * @ops: ops for a dpll pin ops
>+ * @priv: pointer to private information of owner
>+ *
>+ * Context: Acquires a lock (dpll_lock)
>+ * Return:
>+ * * 0 on success
>+ * * -EINVAL - missing pin ops
>+ * * -ENOMEM - failed to allocate memory

Does not make sense to assign one errno to one specific error.
Avoid tables like this.


>+ */
>+int
>+dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>+ const struct dpll_pin_ops *ops, void *priv)
>+{
>+ int ret;
>+
>+ mutex_lock(&dpll_lock);
>+ if (WARN_ON(!(dpll->module == pin->module &&
>+ dpll->clock_id == pin->clock_id)))
>+ ret = -EFAULT;

-EINVAL;


>+ else
>+ ret = __dpll_pin_register(dpll, pin, ops, priv);
>+ mutex_unlock(&dpll_lock);
>+
>+ return ret;
>+}

[...]

2023-06-11 12:01:54

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 04/10] dpll: netlink: Add DPLL framework base functions

Fri, Jun 09, 2023 at 02:18:47PM CEST, [email protected] wrote:
>From: Vadim Fedorenko <[email protected]>

Arkadiusz, I think it would be appropriate to change the authorship
of this and other patches to you. I believe that you did vast majority
of the lines by now. Vadim, would you mind?


>
>DPLL framework is used to represent and configure DPLL devices
>in systems. Each device that has DPLL and can configure inputs
>and outputs can use this framework.
>
>Implement dpll netlink framework functions for enablement of dpll
>subsytem netlink family.
>
>Co-developed-by: Milena Olech <[email protected]>
>Signed-off-by: Milena Olech <[email protected]>
>Co-developed-by: Michal Michalik <[email protected]>
>Signed-off-by: Michal Michalik <[email protected]>
>Signed-off-by: Vadim Fedorenko <[email protected]>
>Co-developed-by: Arkadiusz Kubalewski <[email protected]>
>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>---
> drivers/dpll/dpll_netlink.c | 1183 +++++++++++++++++++++++++++++++++++
> drivers/dpll/dpll_netlink.h | 44 ++

Overall, this looks very good. I did take couple of comments below.
Thanks for you work!


> 2 files changed, 1227 insertions(+)
> create mode 100644 drivers/dpll/dpll_netlink.c
> create mode 100644 drivers/dpll/dpll_netlink.h
>
>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>new file mode 100644
>index 000000000000..44d9699c9e6c
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.c
>@@ -0,0 +1,1183 @@
>+// SPDX-License-Identifier: GPL-2.0
>+/*
>+ * Generic netlink for DPLL management framework
>+ *
>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>+ * Copyright (c) 2023 Intel and affiliates
>+ *
>+ */
>+#include <linux/module.h>
>+#include <linux/kernel.h>
>+#include <net/genetlink.h>
>+#include "dpll_core.h"
>+#include "dpll_nl.h"
>+#include <uapi/linux/dpll.h>
>+
>+static int __dpll_pin_change_ntf(struct dpll_pin *pin);

Could you try to reshuffle the code to avoid forward declarations?


>+
>+struct dpll_dump_ctx {
>+ unsigned long idx;
>+};
>+
>+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
>+{
>+ return (struct dpll_dump_ctx *)cb->ctx;
>+}
>+
>+static int
>+dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)

It is odd to see this helper here and the dpll_msg_add_pin_handle() not.
Introduce dpll_msg_add_pin_handle() here right away and only export it
later on in "netdev: expose DPLL pin handle for netdevice".


>+{
>+ if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>+ return -EMSGSIZE;
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
>+ struct netlink_ext_ack *extack)
>+{
>+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>+ enum dpll_mode mode;
>+
>+ if (WARN_ON(!ops->mode_get))
>+ return -EOPNOTSUPP;
>+ if (ops->mode_get(dpll, dpll_priv(dpll), &mode, extack))
>+ return -EFAULT;

I'm pretty sure I commented this before. But again, please get the
value the driver op returned and return it.


>+ if (nla_put_u8(msg, DPLL_A_MODE, mode))
>+ return -EMSGSIZE;
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
>+ struct netlink_ext_ack *extack)
>+{
>+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>+ enum dpll_lock_status status;
>+
>+ if (WARN_ON(!ops->lock_status_get))
>+ return -EOPNOTSUPP;
>+ if (ops->lock_status_get(dpll, dpll_priv(dpll), &status, extack))
>+ return -EFAULT;

please get the value the driver op returned and return it.


>+ if (nla_put_u8(msg, DPLL_A_LOCK_STATUS, status))
>+ return -EMSGSIZE;
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,
>+ struct netlink_ext_ack *extack)
>+{
>+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>+ s32 temp;
>+
>+ if (!ops->temp_get)
>+ return -EOPNOTSUPP;
>+ if (ops->temp_get(dpll, dpll_priv(dpll), &temp, extack))
>+ return -EFAULT;

please get the value the driver op returned and return it.


>+ if (nla_put_s32(msg, DPLL_A_TEMP, temp))
>+ return -EMSGSIZE;
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_prio(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;
>+ u32 prio;
>+
>+ if (!ops->prio_get)
>+ return -EOPNOTSUPP;
>+ if (ops->prio_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>+ dpll_priv(dpll), &prio, extack))
>+ return -EFAULT;

please get the value the driver op returned and return it.


>+ if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
>+ return -EMSGSIZE;
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_on_dpll_state(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;
>+ enum dpll_pin_state state;
>+
>+ if (!ops->state_on_dpll_get)
>+ return -EOPNOTSUPP;
>+ if (ops->state_on_dpll_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>+ dpll_priv(dpll), &state, extack))
>+ return -EFAULT;

please get the value the driver op returned and return it.


>+ if (nla_put_u8(msg, DPLL_A_PIN_STATE, state))
>+ return -EMSGSIZE;
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_direction(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;
>+ enum dpll_pin_direction direction;
>+
>+ if (!ops->direction_get)
>+ return -EOPNOTSUPP;
>+ if (ops->direction_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>+ dpll_priv(dpll), &direction, extack))
>+ return -EFAULT;

please get the value the driver op returned and return it.


>+ if (nla_put_u8(msg, DPLL_A_PIN_DIRECTION, direction))
>+ 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,
>+ bool dump_freq_supported)
>+{
>+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>+ struct dpll_device *dpll = ref->dpll;
>+ struct nlattr *nest;
>+ u64 freq;
>+ int fs;
>+
>+ if (!ops->frequency_get)
>+ return -EOPNOTSUPP;

Return 0 and avoid the check of -EOPNOTSUPP in the caller.


>+ if (ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>+ dpll_priv(dpll), &freq, extack))
>+ return -EFAULT;

please get the value the driver op returned and return it.


>+ if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq, 0))
>+ return -EMSGSIZE;
>+ if (!dump_freq_supported)
>+ return 0;
>+ for (fs = 0; fs < pin->prop->freq_supported_num; fs++) {
>+ nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);
>+ if (!nest)
>+ return -EMSGSIZE;
>+ freq = pin->prop->freq_supported[fs].min;
>+ if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),
>+ &freq, 0)) {
>+ nla_nest_cancel(msg, nest);
>+ return -EMSGSIZE;
>+ }
>+ freq = pin->prop->freq_supported[fs].max;
>+ if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),
>+ &freq, 0)) {
>+ nla_nest_cancel(msg, nest);
>+ return -EMSGSIZE;
>+ }
>+ nla_nest_end(msg, nest);
>+ }
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
>+ struct dpll_pin_ref *dpll_ref,
>+ struct netlink_ext_ack *extack)
>+{
>+ enum dpll_pin_state state;
>+ struct dpll_pin_ref *ref;
>+ struct dpll_pin *ppin;
>+ struct nlattr *nest;
>+ unsigned long index;
>+ int ret;
>+
>+ xa_for_each(&pin->parent_refs, index, ref) {
>+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>+ void *parent_priv;
>+
>+ ppin = ref->pin;
>+ parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, ppin);
>+ if (WARN_ON(!ops->state_on_pin_get))

Wait, so you WARN during user comment on something that driver didn't
fill up? Plese move the check and WARN to the registration function.


>+ return -EFAULT;
>+ ret = ops->state_on_pin_get(pin,
>+ dpll_pin_on_pin_priv(ppin, pin),
>+ ppin, parent_priv, &state, extack);
>+ if (ret)
>+ return -EFAULT;

Return ret please.


>+ nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
>+ if (!nest)
>+ return -EMSGSIZE;
>+ if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
>+ ret = -EMSGSIZE;
>+ goto nest_cancel;
>+ }
>+ if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>+ ret = -EMSGSIZE;
>+ goto nest_cancel;
>+ }
>+ nla_nest_end(msg, nest);
>+ }
>+
>+ return 0;
>+
>+nest_cancel:
>+ nla_nest_cancel(msg, nest);
>+ return ret;
>+}
>+
>+static int
>+dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct dpll_pin_ref *ref;
>+ struct nlattr *attr;
>+ unsigned long index;
>+ int ret;
>+
>+ xa_for_each(&pin->dpll_refs, index, ref) {
>+ attr = nla_nest_start(msg, DPLL_A_PIN_PARENT);
>+ if (!attr)
>+ return -EMSGSIZE;
>+ ret = dpll_msg_add_dev_handle(msg, ref->dpll);
>+ if (ret)
>+ goto nest_cancel;
>+ ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>+ if (ret && ret != -EOPNOTSUPP)
>+ goto nest_cancel;
>+ ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>+ if (ret && ret != -EOPNOTSUPP)
>+ goto nest_cancel;
>+ ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
>+ if (ret)
>+ goto nest_cancel;
>+ nla_nest_end(msg, attr);
>+ }
>+
>+ return 0;
>+
>+nest_cancel:
>+ nla_nest_end(msg, attr);
>+ return ret;
>+}
>+
>+static int
>+dpll_cmd_pin_fill_details(struct sk_buff *msg, struct dpll_pin *pin,

"details"? Sound odd. I don't think that "DPLL_A_PIN_ID" is a detail
for example. Why don't you inline this in the __dpll_cmd_pin_dump_one()
function below?


>+ struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
>+{
>+ const struct dpll_pin_properties *prop = pin->prop;
>+ int ret;
>+
>+ if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
>+ return -EMSGSIZE;
>+ if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(pin->module)))
>+ return -EMSGSIZE;
>+ if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(pin->clock_id),
>+ &pin->clock_id, 0))
>+ return -EMSGSIZE;
>+ if (prop->board_label &&
>+ nla_put_string(msg, DPLL_A_PIN_BOARD_LABEL, prop->board_label))
>+ return -EMSGSIZE;
>+ if (prop->panel_label &&
>+ nla_put_string(msg, DPLL_A_PIN_PANEL_LABEL, prop->panel_label))
>+ return -EMSGSIZE;
>+ if (prop->package_label &&
>+ nla_put_string(msg, DPLL_A_PIN_PACKAGE_LABEL,
>+ prop->package_label))
>+ return -EMSGSIZE;
>+ if (nla_put_u8(msg, DPLL_A_PIN_TYPE, prop->type))
>+ return -EMSGSIZE;
>+ if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, prop->capabilities))
>+ return -EMSGSIZE;
>+ ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
>+ if (ret && ret != -EOPNOTSUPP)
>+ return ret;
>+ return 0;
>+}
>+
>+static int
>+__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
>+ struct netlink_ext_ack *extack)

To be consistent with dpll_device_get_one(), call this function
dpll_pin_get_one() please.


>+{
>+ struct dpll_pin_ref *ref;
>+ int ret;
>+
>+ ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
>+ if (!ref)
>+ return -EFAULT;

-EINVAL. But it should never happen anyway. Perhaps better to avoid the
check entirely.


>+ ret = dpll_cmd_pin_fill_details(msg, pin, ref, extack);
>+ if (ret)
>+ return ret;
>+ ret = dpll_msg_add_pin_parents(msg, pin, ref, extack);
>+ if (ret)
>+ return ret;
>+ if (!xa_empty(&pin->dpll_refs)) {

Drop this check, not needed.


>+ ret = dpll_msg_add_pin_dplls(msg, pin, extack);
>+ if (ret)
>+ return ret;
>+ }
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
>+ struct netlink_ext_ack *extack)
>+{
>+ enum dpll_mode mode;
>+ int ret;
>+
>+ ret = dpll_msg_add_dev_handle(msg, dpll);
>+ if (ret)
>+ return ret;
>+ if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(dpll->module)))
>+ return -EMSGSIZE;
>+ if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
>+ &dpll->clock_id, 0))
>+ return -EMSGSIZE;
>+ ret = dpll_msg_add_temp(msg, dpll, extack);
>+ if (ret && ret != -EOPNOTSUPP)
>+ return ret;
>+ ret = dpll_msg_add_lock_status(msg, dpll, extack);
>+ if (ret)
>+ return ret;
>+ ret = dpll_msg_add_mode(msg, dpll, extack);
>+ if (ret)
>+ return ret;
>+ for (mode = DPLL_MODE_MANUAL; mode <= DPLL_MODE_MAX; mode++)
>+ if (test_bit(mode, &dpll->mode_supported_mask))
>+ if (nla_put_s32(msg, DPLL_A_MODE_SUPPORTED, mode))
>+ return -EMSGSIZE;
>+ if (nla_put_u8(msg, DPLL_A_TYPE, dpll->type))
>+ return -EMSGSIZE;
>+
>+ return ret;
>+}
>+
>+static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
>+{
>+ int fs;
>+
>+ for (fs = 0; fs < pin->prop->freq_supported_num; fs++)
>+ if (freq >= pin->prop->freq_supported[fs].min &&

Avoid double space here ^^


>+ freq <= pin->prop->freq_supported[fs].max)

Avoid double space here ^^


>+ return true;
>+ return false;
>+}
>+
>+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;
>+ unsigned long i;
>+ int ret;
>+
>+ if (!dpll_pin_is_freq_supported(pin, freq))

Fill a proper extack telling the user what's wrong please.
Could you please check the rest of the cmd attr checks and make sure
the extack is always filled with meaningful message?


>+ 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->frequency_set)
>+ return -EOPNOTSUPP;
>+ ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>+ dpll, dpll_priv(dpll), freq, extack);
>+ if (ret)
>+ return -EFAULT;

return "ret"


>+ __dpll_pin_change_ntf(pin);
>+ }
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
>+ enum dpll_pin_state state,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct dpll_pin_ref *parent_ref;
>+ const struct dpll_pin_ops *ops;
>+ struct dpll_pin_ref *dpll_ref;
>+ struct dpll_pin *parent;
>+ unsigned long i;
>+
>+ if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities))
>+ return -EOPNOTSUPP;
>+ parent = xa_load(&dpll_pin_xa, parent_idx);
>+ if (!parent)
>+ return -EINVAL;
>+ parent_ref = xa_load(&pin->parent_refs, parent->pin_idx);
>+ if (!parent_ref)
>+ return -EINVAL;
>+ xa_for_each(&parent->dpll_refs, i, dpll_ref) {
>+ ops = dpll_pin_ops(parent_ref);
>+ if (!ops->state_on_pin_set)
>+ return -EOPNOTSUPP;
>+ if (ops->state_on_pin_set(pin,
>+ dpll_pin_on_pin_priv(parent, pin),
>+ parent,
>+ dpll_pin_on_dpll_priv(dpll_ref->dpll,
>+ parent),
>+ state, extack))
>+ return -EFAULT;

please get the value the driver op returned and return it.


>+ }
>+ __dpll_pin_change_ntf(pin);
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
>+ enum dpll_pin_state state,
>+ struct netlink_ext_ack *extack)
>+{
>+ const struct dpll_pin_ops *ops;
>+ struct dpll_pin_ref *ref;
>+
>+ if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities))
>+ return -EOPNOTSUPP;
>+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+ if (!ref)
>+ return -EFAULT;

-EINVAL. But looks like this should never happen. Perhaps just
WARN_ON(!ref) and don't check-return.


>+ ops = dpll_pin_ops(ref);
>+ if (!ops->state_on_dpll_set)
>+ return -EOPNOTSUPP;
>+ if (ops->state_on_dpll_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>+ dpll_priv(dpll), state, extack))
>+ return -EINVAL;
>+ __dpll_pin_change_ntf(pin);
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
>+ u32 prio, struct netlink_ext_ack *extack)
>+{
>+ const struct dpll_pin_ops *ops;
>+ struct dpll_pin_ref *ref;
>+
>+ if (!(DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE & pin->prop->capabilities))
>+ return -EOPNOTSUPP;
>+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+ if (!ref)
>+ return -EFAULT;

Same here.


>+ ops = dpll_pin_ops(ref);
>+ if (!ops->prio_set)
>+ return -EOPNOTSUPP;
>+ if (ops->prio_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>+ dpll_priv(dpll), prio, extack))
>+ return -EINVAL;
>+ __dpll_pin_change_ntf(pin);
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
>+ enum dpll_pin_direction direction,
>+ struct netlink_ext_ack *extack)
>+{
>+ const struct dpll_pin_ops *ops;
>+ struct dpll_pin_ref *ref;
>+
>+ if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop->capabilities))
>+ return -EOPNOTSUPP;
>+
>+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+ if (!ref)
>+ return -EFAULT;

Same here. This calls for a helper :)


>+ ops = dpll_pin_ops(ref);
>+ if (!ops->direction_set)
>+ return -EOPNOTSUPP;
>+ if (ops->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>+ dpll, dpll_priv(dpll), direction,
>+ extack))
>+ return -EFAULT;

please get the value the driver op returned and return it.


>+ __dpll_pin_change_ntf(pin);
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_pin_parent_set(struct dpll_pin *pin, struct nlattr *parent_nest,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct nlattr *tb[DPLL_A_MAX + 1];
>+ enum dpll_pin_direction direction;
>+ u32 ppin_idx, pdpll_idx, prio;
>+ enum dpll_pin_state state;
>+ struct dpll_pin_ref *ref;
>+ struct dpll_device *dpll;
>+ int ret;
>+
>+ nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
>+ NULL, extack);
>+ if ((tb[DPLL_A_ID] && tb[DPLL_A_PIN_ID]) ||
>+ !(tb[DPLL_A_ID] || tb[DPLL_A_PIN_ID])) {
>+ NL_SET_ERR_MSG(extack, "one parent id expected");
>+ return -EINVAL;
>+ }
>+ if (tb[DPLL_A_ID]) {
>+ pdpll_idx = nla_get_u32(tb[DPLL_A_ID]);
>+ dpll = xa_load(&dpll_device_xa, pdpll_idx);
>+ if (!dpll)
>+ return -EINVAL;
>+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>+ if (!ref)
>+ return -EINVAL;
>+ if (tb[DPLL_A_PIN_STATE]) {
>+ state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
>+ ret = dpll_pin_state_set(dpll, pin, state, extack);
>+ if (ret)
>+ return ret;
>+ }
>+ if (tb[DPLL_A_PIN_PRIO]) {
>+ prio = nla_get_u8(tb[DPLL_A_PIN_PRIO]);
>+ ret = dpll_pin_prio_set(dpll, pin, prio, extack);
>+ if (ret)
>+ return ret;
>+ }
>+ if (tb[DPLL_A_PIN_DIRECTION]) {
>+ direction = nla_get_u8(tb[DPLL_A_PIN_DIRECTION]);
>+ ret = dpll_pin_direction_set(pin, dpll, direction,
>+ extack);
>+ if (ret)
>+ return ret;
>+ }
>+ } else if (tb[DPLL_A_PIN_ID]) {
>+ ppin_idx = nla_get_u32(tb[DPLL_A_PIN_ID]);
>+ state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
>+ ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);
>+ if (ret)
>+ return ret;
>+ }
>+
>+ return 0;
>+}
>+
>+static int
>+dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
>+{
>+ int rem, ret = -EINVAL;
>+ struct nlattr *a;
>+
>+ nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>+ genlmsg_len(info->genlhdr), rem) {
>+ switch (nla_type(a)) {
>+ case DPLL_A_PIN_FREQUENCY:
>+ ret = dpll_pin_freq_set(pin, a, info->extack);
>+ if (ret)
>+ return ret;
>+ break;
>+ case DPLL_A_PIN_PARENT:
>+ ret = dpll_pin_parent_set(pin, a, info->extack);
>+ if (ret)
>+ return ret;
>+ break;
>+ case DPLL_A_PIN_ID:
>+ case DPLL_A_ID:
>+ break;
>+ default:
>+ NL_SET_ERR_MSG_FMT(info->extack,
>+ "unsupported attribute (%d)",
>+ nla_type(a));
>+ return -EINVAL;
>+ }
>+ }
>+
>+ return 0;
>+}
>+
>+static struct dpll_pin *
>+dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,
>+ enum dpll_pin_type type, struct nlattr *board_label,
>+ struct nlattr *panel_label, struct nlattr *package_label)
>+{
>+ bool board_match, panel_match, package_match;
>+ struct dpll_pin *pin_match = NULL, *pin;
>+ const struct dpll_pin_properties *prop;
>+ bool cid_match, mod_match, type_match;
>+ unsigned long i;
>+
>+ xa_for_each(&dpll_pin_xa, i, pin) {
>+ if (xa_empty(&pin->dpll_refs))

This filters out unregistered, right? Could you please introduce a
"REGISTERED" mark and iterate only over list of registered? Similar to
what you have for device.


>+ continue;
>+ prop = pin->prop;
>+ cid_match = clock_id ? pin->clock_id == clock_id : true;
>+ mod_match = mod_name_attr && module_name(pin->module) ?
>+ !nla_strcmp(mod_name_attr,
>+ module_name(pin->module)) : true;
>+ type_match = type ? prop->type == type : true;
>+ board_match = board_label && prop->board_label ?
>+ !nla_strcmp(board_label, prop->board_label) : true;
>+ panel_match = panel_label && prop->panel_label ?
>+ !nla_strcmp(panel_label, prop->panel_label) : true;
>+ package_match = package_label && prop->package_label ?
>+ !nla_strcmp(package_label,
>+ prop->package_label) : true;
>+ if (cid_match && mod_match && type_match && board_match &&
>+ panel_match && package_match) {
>+ if (pin_match)

Double match, rigth? Fillup the extack telling the user what happened.


>+ return NULL;
>+ pin_match = pin;
>+ };
>+ }
>+
>+ return pin_match;
>+}
>+
>+static int
>+dpll_pin_find_from_nlattr(struct genl_info *info, struct sk_buff *skb)
>+{
>+ struct nlattr *attr, *mod_name_attr = NULL, *board_label_attr = NULL,
>+ *panel_label_attr = NULL, *package_label_attr = NULL;
>+ struct dpll_pin *pin = NULL;
>+ enum dpll_pin_type type = 0;
>+ u64 clock_id = 0;
>+ int rem = 0;
>+
>+ nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
>+ genlmsg_len(info->genlhdr), rem) {
>+ switch (nla_type(attr)) {
>+ case DPLL_A_CLOCK_ID:
>+ if (clock_id)
>+ return -EINVAL;

Extack


>+ clock_id = nla_get_u64(attr);
>+ break;
>+ case DPLL_A_MODULE_NAME:
>+ if (mod_name_attr)
>+ return -EINVAL;

Extack


>+ mod_name_attr = attr;
>+ break;
>+ case DPLL_A_PIN_TYPE:
>+ if (type)
>+ return -EINVAL;

Extack


>+ type = nla_get_u8(attr);
>+ break;
>+ case DPLL_A_PIN_BOARD_LABEL:
>+ if (board_label_attr)
>+ return -EINVAL;

Extack


>+ board_label_attr = attr;
>+ break;
>+ case DPLL_A_PIN_PANEL_LABEL:
>+ if (panel_label_attr)
>+ return -EINVAL;

Extack


>+ panel_label_attr = attr;
>+ break;
>+ case DPLL_A_PIN_PACKAGE_LABEL:
>+ if (package_label_attr)
>+ return -EINVAL;

Extack

You can use goto with one "duplicate attribute" message.


>+ package_label_attr = attr;
>+ break;
>+ default:
>+ break;
>+ }
>+ }
>+ if (!(clock_id || mod_name_attr || board_label_attr ||
>+ panel_label_attr || package_label_attr))
>+ return -EINVAL;
>+ pin = dpll_pin_find(clock_id, mod_name_attr, type, board_label_attr,
>+ panel_label_attr, package_label_attr);

Error is either "notfound" of "duplicate match". Have the function
dpll_pin_find() return ERR_PTR with -ENODEV / -EINVAL and let
the function dpll_pin_find() also fill-up the proper extack inside.


>+ if (!pin)
>+ return -EINVAL;
>+ if (nla_put_u32(skb, DPLL_A_PIN_ID, pin->id))

Please move this call to the caller. This function should return ERR_PTR
or dpll_pin pointer.


>+ return -EMSGSIZE;
>+ return 0;
>+}
>+
>+int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+ struct sk_buff *msg;
>+ struct nlattr *hdr;
>+ int ret;
>+
>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+ if (!msg)
>+ return -ENOMEM;
>+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
>+ DPLL_CMD_PIN_ID_GET);
>+ if (!hdr)
>+ return -EMSGSIZE;
>+
>+ ret = dpll_pin_find_from_nlattr(info, msg);
>+ if (ret) {
>+ nlmsg_free(msg);
>+ return ret;
>+ }
>+ genlmsg_end(msg, hdr);


This does not seem to be working:
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do device-id-get --json '{"module-name": "mlx5_dpll"}'
{'id': 0}
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-id-get --json '{"module-name": "mlx5_dpll"}'
Traceback (most recent call last):
File "/mnt/share156/jiri/net-next/./tools/net/ynl/cli.py", line 52, in <module>
main()
File "/mnt/share156/jiri/net-next/./tools/net/ynl/cli.py", line 40, in main
reply = ynl.do(args.do, attrs)
File "/mnt/share156/jiri/net-next/tools/net/ynl/lib/ynl.py", line 596, in do
return self._op(method, vals)
File "/mnt/share156/jiri/net-next/tools/net/ynl/lib/ynl.py", line 567, in _op
raise NlError(nl_msg)
lib.ynl.NlError: Netlink error: Invalid argument
nl_len = 36 (20) nl_flags = 0x100 nl_type = 2
error: -22
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do device-id-get --json '{"clock-id": "630763432553410540"}'
{'id': 0}
$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml --do pin-id-get --json '{"clock-id": "630763432553410540"}'
Traceback (most recent call last):
File "/mnt/share156/jiri/net-next/./tools/net/ynl/cli.py", line 52, in <module>
main()
File "/mnt/share156/jiri/net-next/./tools/net/ynl/cli.py", line 40, in main
reply = ynl.do(args.do, attrs)
File "/mnt/share156/jiri/net-next/tools/net/ynl/lib/ynl.py", line 596, in do
return self._op(method, vals)
File "/mnt/share156/jiri/net-next/tools/net/ynl/lib/ynl.py", line 567, in _op
raise NlError(nl_msg)
lib.ynl.NlError: Netlink error: Invalid argument
nl_len = 36 (20) nl_flags = 0x100 nl_type = 2
error: -22



>+
>+ return genlmsg_reply(msg, info);
>+}
>+
>+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+ struct dpll_pin *pin = info->user_ptr[0];
>+ struct sk_buff *msg;
>+ struct nlattr *hdr;
>+ int ret;
>+
>+ if (!pin)
>+ return -ENODEV;
>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+ if (!msg)
>+ return -ENOMEM;
>+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
>+ DPLL_CMD_PIN_GET);
>+ if (!hdr)
>+ return -EMSGSIZE;
>+ ret = __dpll_cmd_pin_dump_one(msg, pin, info->extack);
>+ if (ret) {
>+ nlmsg_free(msg);
>+ return ret;
>+ }
>+ genlmsg_end(msg, hdr);
>+
>+ return genlmsg_reply(msg, info);
>+}
>+
>+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+ struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>+ struct dpll_pin *pin;
>+ struct nlattr *hdr;
>+ unsigned long i;
>+ int ret = 0;
>+
>+ xa_for_each_start(&dpll_pin_xa, i, pin, ctx->idx) {
>+ if (xa_empty(&pin->dpll_refs))

Same here, also use REGISTERED mark and iterate over them.


>+ continue;
>+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
>+ cb->nlh->nlmsg_seq,
>+ &dpll_nl_family, NLM_F_MULTI,
>+ DPLL_CMD_PIN_GET);
>+ if (!hdr) {
>+ ret = -EMSGSIZE;
>+ break;
>+ }
>+ ret = __dpll_cmd_pin_dump_one(skb, pin, cb->extack);
>+ if (ret) {
>+ genlmsg_cancel(skb, hdr);
>+ break;
>+ }
>+ genlmsg_end(skb, hdr);
>+ }
>+ if (ret == -EMSGSIZE) {
>+ ctx->idx = i;
>+ return skb->len;
>+ }
>+ return ret;
>+}
>+
>+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+ struct dpll_pin *pin = info->user_ptr[0];
>+
>+ return dpll_pin_set_from_nlattr(pin, info);
>+}
>+
>+static struct dpll_device *
>+dpll_device_find(u64 clock_id, struct nlattr *mod_name_attr,
>+ enum dpll_type type)
>+{
>+ struct dpll_device *dpll_match = NULL, *dpll;
>+ bool cid_match, mod_match, type_match;
>+ unsigned long i;
>+
>+ xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>+ cid_match = clock_id ? dpll->clock_id == clock_id : true;
>+ mod_match = mod_name_attr && module_name(dpll->module) ?
>+ !nla_strcmp(mod_name_attr,
>+ module_name(dpll->module)) : true;
>+ type_match = type ? dpll->type == type : true;
>+ if (cid_match && mod_match && type_match) {
>+ if (dpll_match)

Double match, rigth? Fillup the extack telling the user what happened.


>+ return NULL;
>+ dpll_match = dpll;
>+ }
>+ }
>+
>+ return dpll_match;
>+}
>+
>+static int
>+dpll_device_find_from_nlattr(struct genl_info *info, struct sk_buff *skb)
>+{
>+ struct nlattr *attr, *mod_name_attr = NULL;
>+ struct dpll_device *dpll = NULL;
>+ enum dpll_type type = 0;
>+ u64 clock_id = 0;
>+ int rem = 0;
>+
>+ nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
>+ genlmsg_len(info->genlhdr), rem) {
>+ switch (nla_type(attr)) {
>+ case DPLL_A_CLOCK_ID:
>+ if (clock_id)
>+ return -EINVAL;

Extack


>+ clock_id = nla_get_u64(attr);
>+ break;
>+ case DPLL_A_MODULE_NAME:
>+ if (mod_name_attr)
>+ return -EINVAL;

Extack


>+ mod_name_attr = attr;
>+ break;
>+ case DPLL_A_TYPE:
>+ if (type)
>+ return -EINVAL;

Extack

You can use goto with one "duplicate attribute" message.


>+ type = nla_get_u8(attr);
>+ break;
>+ default:
>+ break;
>+ }
>+ }
>+
>+ if (!clock_id && !mod_name_attr && !type)
>+ return -EINVAL;
>+ dpll = dpll_device_find(clock_id, mod_name_attr, type);

Error is either "notfound" of "duplicate match". Have the function
dpll_device_find() return ERR_PTR with -ENODEV / -EINVAL and let
the function dpll_device_find() also fill-up the proper extack inside.


>+ if (!dpll)
>+ return -EINVAL;
>+
>+ return dpll_msg_add_dev_handle(skb, dpll);

Please move this call to the caller. This function should return ERR_PTR
or dpll_device pointer.


>+}
>+
>+static int
>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)

Nit: Please move this function above dpll_device_find() to maintain the
same functions ordering as there is for similar pin functions above.


>+{
>+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>+ struct nlattr *tb[DPLL_A_MAX + 1];
>+ int ret = 0;

Drop pointless init.


>+
>+ nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
>+ genlmsg_len(info->genlhdr), NULL, info->extack);
>+ if (tb[DPLL_A_MODE]) {
>+ ret = ops->mode_set(dpll, dpll_priv(dpll),
>+ nla_get_u8(tb[DPLL_A_MODE]), info->extack);
>+ if (ret)
>+ return ret;
>+ }
>+
>+ return 0;
>+}
>+
>+int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+ struct sk_buff *msg;
>+ struct nlattr *hdr;
>+ int ret;
>+
>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+ if (!msg)
>+ return -ENOMEM;
>+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
>+ DPLL_CMD_DEVICE_ID_GET);
>+ if (!hdr)
>+ return -EMSGSIZE;
>+
>+ ret = dpll_device_find_from_nlattr(info, msg);
>+ if (ret) {
>+ nlmsg_free(msg);
>+ return ret;
>+ }
>+ genlmsg_end(msg, hdr);
>+
>+ return genlmsg_reply(msg, info);
>+}
>+
>+int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+ struct dpll_device *dpll = info->user_ptr[0];
>+ struct sk_buff *msg;
>+ struct nlattr *hdr;
>+ int ret;
>+
>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+ if (!msg)
>+ return -ENOMEM;
>+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
>+ DPLL_CMD_DEVICE_GET);
>+ if (!hdr)
>+ return -EMSGSIZE;
>+
>+ ret = dpll_device_get_one(dpll, msg, info->extack);
>+ if (ret) {
>+ nlmsg_free(msg);
>+ return ret;
>+ }
>+ genlmsg_end(msg, hdr);
>+
>+ return genlmsg_reply(msg, info);
>+}
>+
>+int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info)
>+{
>+ struct dpll_device *dpll = info->user_ptr[0];
>+
>+ return dpll_set_from_nlattr(dpll, info);
>+}
>+
>+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
>+{
>+ struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>+ struct dpll_device *dpll;
>+ struct nlattr *hdr;
>+ unsigned long i;
>+ int ret = 0;
>+
>+ xa_for_each_start(&dpll_device_xa, i, dpll, ctx->idx) {
>+ if (!xa_get_mark(&dpll_device_xa, i, DPLL_REGISTERED))

Hmm, did you consider adding xa_for_each_marked_start?


>+ continue;
>+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
>+ cb->nlh->nlmsg_seq, &dpll_nl_family,
>+ NLM_F_MULTI, DPLL_CMD_DEVICE_GET);
>+ if (!hdr) {
>+ ret = -EMSGSIZE;
>+ break;
>+ }
>+ ret = dpll_device_get_one(dpll, skb, cb->extack);
>+ if (ret) {
>+ genlmsg_cancel(skb, hdr);
>+ break;
>+ }
>+ genlmsg_end(skb, hdr);
>+ }
>+ if (ret == -EMSGSIZE) {
>+ ctx->idx = i;
>+ return skb->len;
>+ }
>+ return ret;
>+}
>+
>+int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+ struct genl_info *info)
>+{
>+ struct dpll_device *dpll_id = NULL;
>+ u32 id;
>+
>+ if (!info->attrs[DPLL_A_ID])
>+ return -EINVAL;
>+
>+ mutex_lock(&dpll_lock);
>+ id = nla_get_u32(info->attrs[DPLL_A_ID]);
>+
>+ dpll_id = dpll_device_get_by_id(id);
>+ if (!dpll_id)
>+ goto unlock;
>+ info->user_ptr[0] = dpll_id;
>+ return 0;
>+unlock:
>+ mutex_unlock(&dpll_lock);
>+ return -ENODEV;
>+}
>+
>+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+ struct genl_info *info)
>+{
>+ mutex_unlock(&dpll_lock);
>+}
>+
>+int
>+dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+ struct genl_info *info)
>+{
>+ mutex_lock(&dpll_lock);
>+
>+ return 0;
>+}
>+
>+void
>+dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+ struct genl_info *info)
>+{
>+ mutex_unlock(&dpll_lock);
>+}
>+
>+int dpll_lock_dumpit(struct netlink_callback *cb)
>+{
>+ mutex_lock(&dpll_lock);
>+
>+ return 0;
>+}
>+
>+int dpll_unlock_dumpit(struct netlink_callback *cb)
>+{
>+ mutex_unlock(&dpll_lock);
>+
>+ return 0;
>+}
>+
>+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+ struct genl_info *info)
>+{
>+ int ret;
>+
>+ mutex_lock(&dpll_lock);
>+ if (!info->attrs[DPLL_A_PIN_ID]) {

Use GENL_REQ_ATTR_CHECK(info, DPLL_A_PIN_ID);
If fills-up the extack info about missing attr giving the user info
about what went wrong.


>+ ret = -EINVAL;
>+ goto unlock_dev;
>+ }
>+ info->user_ptr[0] = xa_load(&dpll_pin_xa,
>+ nla_get_u32(info->attrs[DPLL_A_PIN_ID]));
>+ if (!info->user_ptr[0]) {

Fill-up the extack message please.


>+ ret = -ENODEV;
>+ goto unlock_dev;
>+ }
>+
>+ return 0;
>+
>+unlock_dev:
>+ mutex_unlock(&dpll_lock);
>+ return ret;
>+}
>+
>+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>+ struct genl_info *info)
>+{
>+ mutex_unlock(&dpll_lock);
>+}
>+
>+static int
>+dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)
>+{
>+ struct sk_buff *msg;
>+ int ret = -EMSGSIZE;

Drop the pointless init.


>+ void *hdr;
>+
>+ if (!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED))

WARN_ON? The driver is buggy when he calls this.


>+ return -ENODEV;
>+
>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+ if (!msg)
>+ return -ENOMEM;
>+ hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
>+ if (!hdr)
>+ goto out_free_msg;

"err_free_msg" so that is clear is an error path.


>+ ret = dpll_device_get_one(dpll, msg, NULL);
>+ if (ret)
>+ goto out_cancel_msg;

Same here, "err_cancel_msg"


>+ genlmsg_end(msg, hdr);
>+ genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
>+
>+ return 0;
>+
>+out_cancel_msg:
>+ genlmsg_cancel(msg, hdr);
>+out_free_msg:
>+ nlmsg_free(msg);
>+
>+ return ret;
>+}
>+
>+int dpll_device_create_ntf(struct dpll_device *dpll)
>+{
>+ return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
>+}
>+
>+int dpll_device_delete_ntf(struct dpll_device *dpll)
>+{
>+ return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
>+}
>+

This is an exported function, documentation commentary perhaps?
I mean, you sometimes have it for static functions, here you don't. Very
odd.

Let's have that for all exported functions please.


>+int dpll_device_change_ntf(struct dpll_device *dpll)
>+{
>+ int ret = -EINVAL;
>+
>+ if (WARN_ON(!dpll))
>+ return ret;

Rely on basic driver sanity and drop this check. don't forget to remove
the ret initialization.


>+
>+ mutex_lock(&dpll_lock);
>+ ret = dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
>+ mutex_unlock(&dpll_lock);
>+
>+ return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_device_change_ntf);
>+
>+static int
>+dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
>+{
>+ struct dpll_pin *pin_verify;
>+ struct sk_buff *msg;
>+ int ret = -EMSGSIZE;

Drop the pointless init.


>+ void *hdr;
>+
>+ pin_verify = xa_load(&dpll_pin_xa, pin->id);
>+ if (pin != pin_verify)

I don't follow. What is the purpose for this check? Once you have
REGISTERED mark for pin, you can check it here and be consistent with
dpll_device_event_send()


>+ return -ENODEV;
>+
>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>+ if (!msg)
>+ return -ENOMEM;
>+
>+ hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
>+ if (!hdr)
>+ goto out_free_msg;

"err_free_msg" so that is clear is an error path.


>+ ret = __dpll_cmd_pin_dump_one(msg, pin, NULL);
>+ if (ret)
>+ goto out_cancel_msg;

Same here, "err_cancel_msg"


>+ genlmsg_end(msg, hdr);
>+ genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
>+
>+ return 0;
>+
>+out_cancel_msg:
>+ genlmsg_cancel(msg, hdr);
>+out_free_msg:
>+ nlmsg_free(msg);
>+
>+ return ret;
>+}
>+
>+int dpll_pin_create_ntf(struct dpll_pin *pin)
>+{
>+ return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
>+}
>+
>+int dpll_pin_delete_ntf(struct dpll_pin *pin)
>+{
>+ return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
>+}
>+
>+static int __dpll_pin_change_ntf(struct dpll_pin *pin)
>+{
>+ return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
>+}
>+
>+int dpll_pin_change_ntf(struct dpll_pin *pin)
>+{
>+ int ret = -EINVAL;
>+
>+ if (WARN_ON(!pin))
>+ return ret;

Remove this check and expect basic sanity from driver. Also, don't
forget to drop the "ret" initialization.


>+
>+ mutex_lock(&dpll_lock);
>+ ret = __dpll_pin_change_ntf(pin);
>+ mutex_unlock(&dpll_lock);
>+
>+ return ret;
>+}
>+EXPORT_SYMBOL_GPL(dpll_pin_change_ntf);
>+
>+int __init dpll_netlink_init(void)
>+{
>+ return genl_register_family(&dpll_nl_family);
>+}
>+
>+void dpll_netlink_finish(void)
>+{
>+ genl_unregister_family(&dpll_nl_family);
>+}
>+
>+void __exit dpll_netlink_fini(void)
>+{
>+ dpll_netlink_finish();
>+}
>diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
>new file mode 100644
>index 000000000000..b5f9bfc88c9e
>--- /dev/null
>+++ b/drivers/dpll/dpll_netlink.h
>@@ -0,0 +1,44 @@
>+/* SPDX-License-Identifier: GPL-2.0 */
>+/*
>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>+ * Copyright (c) 2023 Intel and affiliates
>+ */
>+
>+/**
>+ * dpll_device_create_ntf - notify that the device has been created
>+ * @dpll: registered dpll pointer
>+ *
>+ * Context: caller shall hold dpll_xa_lock.
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_device_create_ntf(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_device_delete_ntf - notify that the device has been deleted
>+ * @dpll: registered dpll pointer
>+ *
>+ * Context: caller shall hold dpll_xa_lock.
>+ * Return: 0 if succeeds, error code otherwise.
>+ */

Again, I'm going to repeat myself. Please have this kdoc comments once,
in the .c file. Header should not contain this.



>+int dpll_device_delete_ntf(struct dpll_device *dpll);
>+
>+/**
>+ * dpll_pin_create_ntf - notify that the pin has been created
>+ * @pin: registered pin pointer
>+ *
>+ * Context: caller shall hold dpll_xa_lock.
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_pin_create_ntf(struct dpll_pin *pin);
>+
>+/**
>+ * dpll_pin_delete_ntf - notify that the pin has been deleted
>+ * @pin: registered pin pointer
>+ *
>+ * Context: caller shall hold dpll_xa_lock.
>+ * Return: 0 if succeeds, error code otherwise.
>+ */
>+int dpll_pin_delete_ntf(struct dpll_pin *pin);
>+
>+int __init dpll_netlink_init(void);
>+void dpll_netlink_finish(void);
>--
>2.37.3
>

2023-06-12 07:52:51

by Paolo Abeni

[permalink] [raw]
Subject: Re: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

On Sun, 2023-06-11 at 11:36 +0200, Jiri Pirko wrote:
> Fri, Jun 09, 2023 at 02:18:46PM CEST, [email protected] wrote:
> > From: Vadim Fedorenko <[email protected]>
>
> [...]
>
> > +int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
> > + const struct dpll_device_ops *ops, void *priv)
> > +{
> > + struct dpll_device_registration *reg;
> > + bool first_registration = false;
> > +
> > + if (WARN_ON(!ops))
> > + return -EINVAL;
> > + if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX))
> > + return -EINVAL;
> > +
> > + mutex_lock(&dpll_lock);
> > + reg = dpll_device_registration_find(dpll, ops, priv);
> > + if (reg) {
> > + mutex_unlock(&dpll_lock);
> > + return -EEXIST;
> > + }
> > +
> > + reg = kzalloc(sizeof(*reg), GFP_KERNEL);
> > + if (!reg) {
> > + mutex_unlock(&dpll_lock);
> > + return -EEXIST;
> > + }
> > + reg->ops = ops;
> > + reg->priv = priv;
> > + dpll->type = type;
> > + first_registration = list_empty(&dpll->registration_list);
> > + list_add_tail(&reg->list, &dpll->registration_list);
> > + if (!first_registration) {
> > + mutex_unlock(&dpll_lock);
> > + return 0;
> > + }
> > +
> > + xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
> > + mutex_unlock(&dpll_lock);
> > + dpll_device_create_ntf(dpll);
>
> This function is introduced in the next patch. Breaks bissection. Make
> sure you can compile the code after every patch applied.

WRT, I think the easiest way to solve the above is adding the function
call in the next patch.

Cheers,

Paolo


2023-06-12 09:33:29

by Petr Oros

[permalink] [raw]
Subject: Re: [RFC PATCH v8 06/10] netdev: expose DPLL pin handle for netdevice

Arkadiusz Kubalewski píše v Pá 09. 06. 2023 v 14:18 +0200:
> From: Jiri Pirko <[email protected]>
>
> In case netdevice represents a SyncE port, the user needs to
> understand
> the connection between netdevice and associated DPLL pin. There might
> me
> multiple netdevices pointing to the same pin, in case of VF/SF
> implementation.
>
> Add a IFLA Netlink attribute to nest the DPLL pin handle, similar to
> how it is implemented for devlink port. Add a struct dpll_pin pointer
> to netdev and protect access to it by RTNL. Expose
> netdev_dpll_pin_set()
> and netdev_dpll_pin_clear() helpers to the drivers so they can
> set/clear
> the DPLL pin relationship to netdev.
>
> Note that during the lifetime of struct dpll_pin the pin handle does
> not
> change. Therefore it is save to access it lockless. It is drivers
> responsibility to call netdev_dpll_pin_clear() before dpll_pin_put().
>
> Signed-off-by: Jiri Pirko <[email protected]>
> Signed-off-by: Arkadiusz Kubalewski <[email protected]>
> ---
>  drivers/dpll/dpll_netlink.c  | 28 ++++++++++++++++++--------
>  include/linux/dpll.h         | 20 +++++++++++++++++++
>  include/linux/netdevice.h    | 10 ++++++++++
>  include/uapi/linux/if_link.h |  2 ++
>  net/core/dev.c               | 22 +++++++++++++++++++++
>  net/core/rtnetlink.c         | 38
> ++++++++++++++++++++++++++++++++++++
>  6 files changed, 112 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/dpll/dpll_netlink.c
> b/drivers/dpll/dpll_netlink.c
> index 44d9699c9e6c..e6efc17aaf26 100644
> --- a/drivers/dpll/dpll_netlink.c
> +++ b/drivers/dpll/dpll_netlink.c
> @@ -214,10 +214,9 @@ dpll_msg_add_pin_parents(struct sk_buff *msg,
> struct dpll_pin *pin,
>                 nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
>                 if (!nest)
>                         return -EMSGSIZE;
> -               if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
> -                       ret = -EMSGSIZE;
> +               ret = dpll_msg_add_pin_handle(msg, ppin);
> +               if (ret)
>                         goto nest_cancel;
> -               }
>                 if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>                         ret = -EMSGSIZE;
>                         goto nest_cancel;
> @@ -274,8 +273,9 @@ dpll_cmd_pin_fill_details(struct sk_buff *msg,
> struct dpll_pin *pin,
>         const struct dpll_pin_properties *prop = pin->prop;
>         int ret;
>  
> -       if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
> -               return -EMSGSIZE;
> +       ret = dpll_msg_add_pin_handle(msg, pin);
> +       if (ret)
> +               return ret;
>         if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(pin-
> >module)))
>                 return -EMSGSIZE;
>         if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(pin-
> >clock_id),
> @@ -301,6 +301,20 @@ dpll_cmd_pin_fill_details(struct sk_buff *msg,
> struct dpll_pin *pin,
>         return 0;
>  }
>  
> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
> +{
> +       return nla_total_size(4); /* DPLL_A_PIN_ID */
> +}
> +EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
> +
> +int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin
> *pin)
> +{
> +       if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
> +               return -EMSGSIZE;
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(dpll_msg_add_pin_handle);
> +
>  static int
>  __dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
>                         struct netlink_ext_ack *extack)
> @@ -690,9 +704,7 @@ dpll_pin_find_from_nlattr(struct genl_info *info,
> struct sk_buff *skb)
>                             panel_label_attr, package_label_attr);
>         if (!pin)
>                 return -EINVAL;
> -       if (nla_put_u32(skb, DPLL_A_PIN_ID, pin->id))
> -               return -EMSGSIZE;
> -       return 0;
> +       return dpll_msg_add_pin_handle(skb, pin);
>  }
>  
>  int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info
> *info)
> diff --git a/include/linux/dpll.h b/include/linux/dpll.h
> index a18bcaa13553..8d085dc92cdd 100644
> --- a/include/linux/dpll.h
> +++ b/include/linux/dpll.h
> @@ -108,6 +108,26 @@ struct dpll_pin_properties {
>         struct dpll_pin_frequency *freq_supported;
>  };
>  
> +#if IS_ENABLED(CONFIG_DPLL)
> +
> +size_t dpll_msg_pin_handle_size(struct dpll_pin *pin);
> +
> +int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin
> *pin);
> +
> +#else
> +
> +static inline size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
> +{
> +       return 0;
> +}
> +
> +static inline int dpll_msg_add_pin_handle(struct sk_buff *msg,
> struct dpll_pin *pin)
> +{
> +       return 0;
> +}
> +
> +#endif
> +
>  struct dpll_device
>  *dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module
> *module);
>  
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 08fbd4622ccf..c57723b12f75 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -34,6 +34,7 @@
>  #include <linux/rculist.h>
>  #include <linux/workqueue.h>
>  #include <linux/dynamic_queue_limits.h>
> +#include <linux/dpll.h>
>  
>  #include <net/net_namespace.h>
>  #ifdef CONFIG_DCB
> @@ -2055,6 +2056,9 @@ enum netdev_ml_priv_type {
>   *                     SET_NETDEV_DEVLINK_PORT macro. This pointer
> is static
>   *                     during the time netdevice is registered.
>   *
> + *     @dpll_pin: Pointer to the SyncE source pin of a DPLL
> subsystem,
> + *                where the clock is recovered.
> + *
>   *     FIXME: cleanup struct net_device such that network protocol
> info
>   *     moves out.
>   */
> @@ -2411,6 +2415,10 @@ struct net_device {
>         struct rtnl_hw_stats64  *offload_xstats_l3;
>  
>         struct devlink_port     *devlink_port;
> +
> +#if IS_ENABLED(CONFIG_DPLL)
> +       struct dpll_pin         *dpll_pin;
> +#endif
>  };
>  #define to_net_dev(d) container_of(d, struct net_device, dev)
>  
> @@ -3954,6 +3962,8 @@ int dev_get_mac_address(struct sockaddr *sa,
> struct net *net, char *dev_name);
>  int dev_get_port_parent_id(struct net_device *dev,
>                            struct netdev_phys_item_id *ppid, bool
> recurse);
>  bool netdev_port_same_parent_id(struct net_device *a, struct
> net_device *b);
> +void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin
> *dpll_pin);
> +void netdev_dpll_pin_clear(struct net_device *dev);
>  struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct
> net_device *dev, bool *again);
>  struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct
> net_device *dev,
>                                     struct netdev_queue *txq, int
> *ret);
> diff --git a/include/uapi/linux/if_link.h
> b/include/uapi/linux/if_link.h
> index 0f6a0fe09bdb..be03c8292cd7 100644
> --- a/include/uapi/linux/if_link.h
> +++ b/include/uapi/linux/if_link.h
> @@ -377,6 +377,8 @@ enum {
>         IFLA_GSO_IPV4_MAX_SIZE,
>         IFLA_GRO_IPV4_MAX_SIZE,
>  
> +       IFLA_DPLL_PIN,
> +
>         __IFLA_MAX
>  };
>  
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 99d99b247bc9..7ae0ce75a5c7 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -8993,6 +8993,28 @@ bool netdev_port_same_parent_id(struct
> net_device *a, struct net_device *b)
>  }
>  EXPORT_SYMBOL(netdev_port_same_parent_id);
>  
> +static void netdev_dpll_pin_assign(struct net_device *dev, struct
> dpll_pin *dpll_pin)
> +{
> +#if IS_ENABLED(CONFIG_DPLL)
> +       rtnl_lock();
> +       dev->dpll_pin = dpll_pin;
> +       rtnl_unlock();
> +#endif
> +}
> +
> +void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin
> *dpll_pin)
> +{
> +       WARN_ON(!dpll_pin);
> +       netdev_dpll_pin_assign(dev, dpll_pin);
> +}
> +EXPORT_SYMBOL(netdev_dpll_pin_set);
> +
> +void netdev_dpll_pin_clear(struct net_device *dev)
> +{
> +       netdev_dpll_pin_assign(dev, NULL);
> +}
> +EXPORT_SYMBOL(netdev_dpll_pin_clear);
> +
>  /**
>   *     dev_change_proto_down - set carrier according to proto_down.
>   *
> diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
> index 41de3a2f29e1..ebe9ae8608fc 100644
> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -1052,6 +1052,16 @@ static size_t rtnl_devlink_port_size(const
> struct net_device *dev)
>         return size;
>  }
>  
> +static size_t rtnl_dpll_pin_size(const struct net_device *dev)
> +{
> +       size_t size = nla_total_size(0); /* nest IFLA_DPLL_PIN */
> +
> +       if (dev->dpll_pin)
> +               size += dpll_msg_pin_handle_size(dev->dpll_pin);

Hi Arkadiusz,

net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL)
But the code in net/core/rtnetlink.c doesn't respect that.
If CONFIG_DPLL is not set, net/core/rtnetlink.c cannot be compiled.

Regards,
Petr

> +
> +       return size;
> +}
> +
>  static noinline size_t if_nlmsg_size(const struct net_device *dev,
>                                      u32 ext_filter_mask)
>  {
> @@ -1108,6 +1118,7 @@ static noinline size_t if_nlmsg_size(const
> struct net_device *dev,
>                + rtnl_prop_list_size(dev)
>                + nla_total_size(MAX_ADDR_LEN) /* IFLA_PERM_ADDRESS */
>                + rtnl_devlink_port_size(dev)
> +              + rtnl_dpll_pin_size(dev)
>                + 0;
>  }
>  
> @@ -1769,6 +1780,30 @@ static int rtnl_fill_devlink_port(struct
> sk_buff *skb,
>         return ret;
>  }
>  
> +static int rtnl_fill_dpll_pin(struct sk_buff *skb,
> +                             const struct net_device *dev)
> +{
> +       struct nlattr *dpll_pin_nest;
> +       int ret;
> +
> +       dpll_pin_nest = nla_nest_start(skb, IFLA_DPLL_PIN);
> +       if (!dpll_pin_nest)
> +               return -EMSGSIZE;
> +
> +       if (dev->dpll_pin) {
> +               ret = dpll_msg_add_pin_handle(skb, dev->dpll_pin);
> +               if (ret < 0)
> +                       goto nest_cancel;
> +       }
> +
> +       nla_nest_end(skb, dpll_pin_nest);
> +       return 0;
> +
> +nest_cancel:
> +       nla_nest_cancel(skb, dpll_pin_nest);
> +       return ret;
> +}
> +
>  static int rtnl_fill_ifinfo(struct sk_buff *skb,
>                             struct net_device *dev, struct net
> *src_net,
>                             int type, u32 pid, u32 seq, u32 change,
> @@ -1911,6 +1946,9 @@ static int rtnl_fill_ifinfo(struct sk_buff
> *skb,
>         if (rtnl_fill_devlink_port(skb, dev))
>                 goto nla_put_failure;
>  
> +       if (rtnl_fill_dpll_pin(skb, dev))
> +               goto nla_put_failure;
> +
>         nlmsg_end(skb, nlh);
>         return 0;
>  

2023-06-12 15:24:17

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

>From: Bagas Sanjaya <[email protected]>
>Sent: Saturday, June 10, 2023 5:23 AM
>
>On Fri, Jun 09, 2023 at 02:18:44PM +0200, Arkadiusz Kubalewski wrote:
>> diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-
>api/dpll.rst
>> new file mode 100644
>> index 000000000000..8caa4af022ad
>> --- /dev/null
>> +++ b/Documentation/driver-api/dpll.rst
>> @@ -0,0 +1,458 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +===============================
>> +The Linux kernel dpll 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 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.
>> +
>> +For other dpll device operating modes there is no pin selection
>> +mechanics.
>> +
>> +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 provide user with additional nested
>> +attribute ``DPLL_A_PIN_PARENT`` 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',
>> + 'pin-dpll-caps': 4,
>> + 'pin-id': 13,
>> + 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
>> + {'pin-id': 3, 'pin-state': 'disconnected'},
>> + {'id': 0, 'pin-direction': 'input'},
>> + {'id': 1, 'pin-direction': 'input'}],
>> + 'pin-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`` nested attribute for requesting configuration
>> + related to parent pin
>> + ``DPLL_A_PIN_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`` child pin id
>> + ``DPLL_A_PIN_PARENT`` nested attribute for requesting configuration
>> + related to parent pin
>> + ``DPLL_A_ID`` parent dpll id
>> + ``DPLL_A_PIN_PRIO`` requested pin prio on parent dpll
>> +
>> +Child pin of MUX-type is not capable of automatic input pin selection,
>> +in order to configure a 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.
>> +
>> +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
>> +=================================================
>> +
>> +All constants identifying command types use a ``DPLL_CMD_`` prefix and
>> +suffix according to command purpose. All 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
>> + ``DPLL_CMD_PIN_GET`` command to get pin 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_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_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_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_DIRECTION`` attr direction 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_PARENT`` nested attr for each parent the
>> + pin is connected with
>> + ``DPLL_A_ID`` attr provided if parent is dpll
>> + device
>> + ``DPLL_A_PIN_ID`` attr provided if parent is a pin
>> + ``DPLL_A_PIN_PRIO`` attr priority of pin on the
>> + dpll device
>> + ``DPLL_A_PIN_STATE`` attr state of pin on the dpll
>> + device or on the parent pin
>> + ``DPLL_A_PIN_DPLL_CAPS`` attr bitmask of pin-dpll
>> + capabilities
>> + ``DPLL_CMD_PIN_SET`` command to set pins configuration
>> + ``DPLL_A_PIN_ID`` attr unique a pin ID
>> + ``DPLL_A_PIN_DIRECTION`` attr requested direction of a pin
>> + ``DPLL_A_PIN_FREQUENCY`` attr requested frequency of a pin
>> + ``DPLL_A_PIN_PARENT`` nested attr for each parent
>> + related configuration of a pin
>> + requested
>> + ``DPLL_A_ID`` attr provided if parent is dpll
>> + device
>> + ``DPLL_A_PIN_ID`` attr provided if parent is 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 or on the 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 has to 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_DIRECTION`` or ``DPLL_A_PIN_FREQUENCY`` are configured,
>> +this affects all the dpll device they are connected, that is why those
>> +attributes shall not be enclosed in ``DPLL_A_PIN_PARENT``.
>> +Other attributes:
>> +``DPLL_A_PIN_PRIO`` or ``DPLL_A_PIN_STATE`` must be enclosed in
>> +``DPLL_A_PIN_PARENT`` as their configuration relates to only one
>> +parent dpll or parent pin.
>> +Nested attribute of either ``DPLL_A_ID`` or ``DPLL_A_PIN_ID`` determines
>> +if configuration was requested on a dpll device or on a pin
>> +respectively.
>> +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.
>> +
>> +Device level configuration pre-defined enums
>> +=================================================
>> +
>> +Values for ``DPLL_A_LOCK_STATUS`` attribute:
>> +
>> + ================================== ===================================
>> + ``DPLL_LOCK_STATUS_UNLOCKED`` dpll device is in freerun, not
>> + locked to any input pin
>> + ``DPLL_LOCK_STATUS_LOCKED`` dpll device is locked to the input
>> + but no holdover capability yet
>> + acquired
>> + ``DPLL_LOCK_STATUS_LOCKED_HO_ACQ`` dpll device is locked to the input
>> + pin with holdover capability
>> + acquired
>> + ``DPLL_LOCK_STATUS_HOLDOVER`` dpll device lost a lock, using its
>> + frequency holdover capabilities
>> +
>> +Values for ``DPLL_A_MODE`` attribute:
>> +
>> + ======================= ==============================================
>> + ``DPLL_MODE_MANUAL`` input pin is manually selected by setting pin
>> + state to ``DPLL_PIN_STATE_CONNECTED`` on a
>> + dpll device
>> + ``DPLL_MODE_AUTOMATIC`` input pin is auto selected according to
>> + configured pin priorities and input signal
>> + validity
>> + ``DPLL_MODE_HOLDOVER`` force holdover mode of dpll
>> + ``DPLL_MODE_FREERUN`` dpll device is driven by supplied system clock
>> + without holdover capabilities
>> +
>> +Values for ``DPLL_A_TYPE`` attribute:
>> +
>> + ================= ===================================================
>> + ``DPLL_TYPE_PPS`` dpll device used to provide pulse-per-second output
>> + ``DPLL_TYPE_EEC`` dpll device used to drive ethernet equipment clock
>> +
>> +Pin level configuration pre-defined enums
>> +=========================================
>> +
>> +Values for ``DPLL_A_PIN_STATE`` attribute:
>> +
>> + =============================== ======================================
>> + ``DPLL_PIN_STATE_CONNECTED`` Pin used as active input for a dpll
>> + device or for a parent pin
>> + ``DPLL_PIN_STATE_DISCONNECTED`` Pin disconnected from a dpll device or
>> + from a parent pin
>> + ``DPLL_PIN_STATE_SELECTABLE`` Pin enabled for automatic selection
>> +
>> +Values for ``DPLL_A_PIN_DIRECTION`` attribute:
>> +
>> + ============================= ====================================
>> + ``DPLL_PIN_DIRECTION_INPUT`` used to provide its signal to a dpll
>> + device
>> + ``DPLL_PIN_DIRECTION_OUTPUT`` used to output the signal from a dpll
>> + device
>> +
>> +Values for ``DPLL_A_PIN_TYPE`` attributes:
>> +
>> + ================================ =====================================
>> + ``DPLL_PIN_TYPE_MUX`` MUX type pin, connected pins shall
>> + have their own types
>> + ``DPLL_PIN_TYPE_EXT`` External pin
>> + ``DPLL_PIN_TYPE_SYNCE_ETH_PORT`` SyncE on Ethernet port
>> + ``DPLL_PIN_TYPE_INT_OSCILLATOR`` Internal Oscillator (i.e. Holdover
>> + with Atomic Clock as an input)
>> + ``DPLL_PIN_TYPE_GNSS`` GNSS 1PPS input
>> +
>> +Values for ``DPLL_A_PIN_DPLL_CAPS`` attributes:
>> +
>> + ====================================== ===============================
>> + ``DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE`` Bit present if direction of
>> + pin can change
>> + ``DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE`` Bit present if priority of pin
>> + can change
>> + ``DPLL_PIN_CAPS_STATE_CAN_CHANGE`` Bit present if state of pin can
>> + change
>> +
>> +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. Neverthelessi, there are few required to be
>> +implemented.
>> +Required dpll device level callback operations:
>> +- ``.mode_get``,
>> +- ``.lock_status_get``.
>> +
>> +Required pin level callback operations:
>> +- ``.state_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
>> +``-ENOTSUPP`` is returned in case of absence of specific handler.
>> +
>> +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:
>> +netdev_dpll_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``.
>
>There are countless htmldocs warnings, so I have to fix them up:
>
>---- >8 ----
>diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-
>api/dpll.rst
>index 8caa4af022ad82..5b2d3e3b9f8893 100644
>--- a/Documentation/driver-api/dpll.rst
>+++ b/Documentation/driver-api/dpll.rst
>@@ -63,16 +63,19 @@ 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.
>@@ -85,6 +88,7 @@ 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
>@@ -103,31 +107,32 @@ 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:
>+attributes with current state related to each parent, like::
>
>-``'pin': [{
>- {'clock-id': 282574471561216,
>- 'module-name': 'ice',
>- 'pin-dpll-caps': 4,
>- 'pin-id': 13,
>- 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
>- {'pin-id': 3, 'pin-state': 'disconnected'},
>- {'id': 0, 'pin-direction': 'input'},
>- {'id': 1, 'pin-direction': 'input'}],
>- 'pin-type': 'synce-eth-port'}
>-}]``
>+ 'pin': [{
>+ {'clock-id': 282574471561216,
>+ 'module-name': 'ice',
>+ 'pin-dpll-caps': 4,
>+ 'pin-id': 13,
>+ 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
>+ {'pin-id': 3, 'pin-state': 'disconnected'},
>+ {'id': 0, 'pin-direction': 'input'},
>+ {'id': 1, 'pin-direction': 'input'}],
>+ 'pin-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`` nested attribute for requesting configuration
> related to parent pin
> ``DPLL_A_PIN_ID`` parent pin id
> ``DPLL_A_PIN_STATE`` requested pin state on parent
>+ ====================== =============================================
>
> Pin priority
> ============
>@@ -149,6 +154,7 @@ device. Example of netlink `set priority on parent pin`
>message format:
> related to parent pin
> ``DPLL_A_ID`` parent dpll id
> ``DPLL_A_PIN_PRIO`` requested pin prio on parent dpll
>+ ===================== =============================================
>
> Child pin of MUX-type is not capable of automatic input pin selection,
> in order to configure a input of a MUX-type pin, the user needs to
>@@ -254,6 +260,7 @@ prefix and suffix according to attribute purpose:
> ``DPLL_A_PIN_STATE`` attr requested state of pin on
> the dpll device or on the parent
> pin
>+ ==================================== =================================
>
> Netlink dump requests
> =====================
>@@ -303,6 +310,7 @@ Values for ``DPLL_A_LOCK_STATUS`` attribute:
> acquired
> ``DPLL_LOCK_STATUS_HOLDOVER`` dpll device lost a lock, using its
> frequency holdover capabilities
>+ ================================== ===================================
>
> Values for ``DPLL_A_MODE`` attribute:
>
>@@ -316,12 +324,14 @@ Values for ``DPLL_A_MODE`` attribute:
> ``DPLL_MODE_HOLDOVER`` force holdover mode of dpll
> ``DPLL_MODE_FREERUN`` dpll device is driven by supplied system clock
> without holdover capabilities
>+ ======================= ==============================================
>
> Values for ``DPLL_A_TYPE`` attribute:
>
> ================= ===================================================
> ``DPLL_TYPE_PPS`` dpll device used to provide pulse-per-second output
> ``DPLL_TYPE_EEC`` dpll device used to drive ethernet equipment clock
>+ ================= ===================================================
>
> Pin level configuration pre-defined enums
> =========================================
>@@ -334,6 +344,7 @@ Values for ``DPLL_A_PIN_STATE`` attribute:
> ``DPLL_PIN_STATE_DISCONNECTED`` Pin disconnected from a dpll device or
> from a parent pin
> ``DPLL_PIN_STATE_SELECTABLE`` Pin enabled for automatic selection
>+ =============================== ======================================
>
> Values for ``DPLL_A_PIN_DIRECTION`` attribute:
>
>@@ -342,6 +353,7 @@ Values for ``DPLL_A_PIN_DIRECTION`` attribute:
> device
> ``DPLL_PIN_DIRECTION_OUTPUT`` used to output the signal from a dpll
> device
>+ ============================= ====================================
>
> Values for ``DPLL_A_PIN_TYPE`` attributes:
>
>@@ -353,6 +365,7 @@ Values for ``DPLL_A_PIN_TYPE`` attributes:
> ``DPLL_PIN_TYPE_INT_OSCILLATOR`` Internal Oscillator (i.e. Holdover
> with Atomic Clock as an input)
> ``DPLL_PIN_TYPE_GNSS`` GNSS 1PPS input
>+ ================================ =====================================
>
> Values for ``DPLL_A_PIN_DPLL_CAPS`` attributes:
>
>@@ -363,6 +376,7 @@ Values for ``DPLL_A_PIN_DPLL_CAPS`` attributes:
> can change
> ``DPLL_PIN_CAPS_STATE_CAN_CHANGE`` Bit present if state of pin can
> change
>+ ====================================== ===============================
>
> Notifications
> =============
>@@ -381,6 +395,7 @@ Notifications messages:
> ``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
>@@ -413,6 +428,7 @@ 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.
>
>@@ -422,6 +438,7 @@ 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
>@@ -431,10 +448,11 @@ The device driver using dpll interface is not
>required to implement all
> the callback operation. Neverthelessi, there are few required to be
> implemented.
> Required dpll device level callback operations:
>+
> - ``.mode_get``,
> - ``.lock_status_get``.
>
>-Required pin level callback operations:
>+oRequired pin level callback operations:

This seems wrong here? I mean the "o" in front of required?
Added new line after the sentences, like:

+ Required pin level callback operations:
+
+ -``.state_get`` (pins registered with dpll device),

> - ``.state_get`` (pins registered with dpll device),
> - ``.state_on_pin_get`` (pins registered with parent pin),
> - ``.direction_get``.
>@@ -451,8 +469,8 @@ 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:
>-netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin);
>+netdevice itself with
>+``netdev_dpll_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``.
>
>(but because the fix diff above is quite large, Co-developed-by: from
>me may qualify).
>
>Thanks.

Many thanks for pointing this out, will do.

Thank you!
Arkadiusz

>
>--
>An old man doll... just what I always wanted! - Clara

2023-06-12 15:52:55

by Vadim Fedorenko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 05/10] dpll: api header: Add DPLL framework base functions

On 10/06/2023 08:25, Jiri Pirko wrote:
> Fri, Jun 09, 2023 at 02:18:48PM CEST, [email protected] wrote:
>> From: Vadim Fedorenko <[email protected]>
>>
>> DPLL framework is used to represent and configure DPLL devices
>> in systems. Each device that has DPLL and can configure sources
>> and outputs can use this framework. Netlink interface is used to
>> provide configuration data and to receive notification messages
>> about changes in the configuration or status of DPLL device.
>> Inputs and outputs of the DPLL device are represented as special
>> objects which could be dynamically added to and removed from DPLL
>> device.
>>
>> Add kernel api header, make dpll subsystem available to device drivers.
>>
>> Add/update makefiles/Kconfig to allow compilation of dpll subsystem.
>>
>> Co-developed-by: Milena Olech <[email protected]>
>> Signed-off-by: Milena Olech <[email protected]>
>> Co-developed-by: Michal Michalik <[email protected]>
>> Signed-off-by: Michal Michalik <[email protected]>
>> Signed-off-by: Vadim Fedorenko <[email protected]>
>> Co-developed-by: Arkadiusz Kubalewski <[email protected]>
>> Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>> ---
>> MAINTAINERS | 8 +++
>> drivers/Kconfig | 2 +
>> drivers/Makefile | 1 +
>> drivers/dpll/Kconfig | 7 ++
>> drivers/dpll/Makefile | 9 +++
>> include/linux/dpll.h | 144 ++++++++++++++++++++++++++++++++++++++++++
>> 6 files changed, 171 insertions(+)
>> create mode 100644 drivers/dpll/Kconfig
>> create mode 100644 drivers/dpll/Makefile
>> create mode 100644 include/linux/dpll.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 288d9a5edb9d..0e69429ecc55 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6306,6 +6306,14 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive
>> F: drivers/net/ethernet/freescale/dpaa2/dpaa2-switch*
>> F: drivers/net/ethernet/freescale/dpaa2/dpsw*
>>
>> +DPLL CLOCK SUBSYSTEM
>> +M: Vadim Fedorenko <[email protected]>
>> +L: [email protected]
>> +S: Maintained
>
> I think status should be rather "Supported":
> "Supported: Someone is actually paid to look after this."
>
> Also, I think that it would be good to have Arkadiusz Kubalewski
> listed here, as he is the one that knows the subsystem by heart.
>
> Also, if you don't mind, I would be happy as a co-maintainer of the
> subsystem to be listed here, as I helped to shape the code and
> interfaces and I also know it pretty good.
>

Yes, sure, I'll update the list of maintainers to add both yourself and
Arkadiusz to the list, and of course the status to Supported.
>
>
>> +F: drivers/dpll/*
>> +F: include/net/dpll.h
>> +F: include/uapi/linux/dpll.h
>> +
>> DRBD DRIVER
>> M: Philipp Reisner <[email protected]>
>> M: Lars Ellenberg <[email protected]>
>> diff --git a/drivers/Kconfig b/drivers/Kconfig
>> index 514ae6b24cb2..ce5f63918eba 100644
>> --- a/drivers/Kconfig
>> +++ b/drivers/Kconfig
>> @@ -243,4 +243,6 @@ source "drivers/hte/Kconfig"
>>
>> source "drivers/cdx/Kconfig"
>>
>> +source "drivers/dpll/Kconfig"
>> +
>> endmenu
>> diff --git a/drivers/Makefile b/drivers/Makefile
>> index 7241d80a7b29..6fea42a6dd05 100644
>> --- a/drivers/Makefile
>> +++ b/drivers/Makefile
>> @@ -195,3 +195,4 @@ obj-$(CONFIG_PECI) += peci/
>> obj-$(CONFIG_HTE) += hte/
>> obj-$(CONFIG_DRM_ACCEL) += accel/
>> obj-$(CONFIG_CDX_BUS) += cdx/
>> +obj-$(CONFIG_DPLL) += dpll/
>> diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig
>> new file mode 100644
>> index 000000000000..a4cae73f20d3
>> --- /dev/null
>> +++ b/drivers/dpll/Kconfig
>> @@ -0,0 +1,7 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +#
>> +# Generic DPLL drivers configuration
>> +#
>> +
>> +config DPLL
>> + bool
>> diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile
>> new file mode 100644
>> index 000000000000..2e5b27850110
>> --- /dev/null
>> +++ b/drivers/dpll/Makefile
>> @@ -0,0 +1,9 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +#
>> +# Makefile for DPLL drivers.
>> +#
>> +
>> +obj-$(CONFIG_DPLL) += dpll.o
>> +dpll-y += dpll_core.o
>> +dpll-y += dpll_netlink.o
>> +dpll-y += dpll_nl.o
>> diff --git a/include/linux/dpll.h b/include/linux/dpll.h
>> new file mode 100644
>> index 000000000000..a18bcaa13553
>> --- /dev/null
>> +++ b/include/linux/dpll.h
>> @@ -0,0 +1,144 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>> + * Copyright (c) 2023 Intel and affiliates
>> + */
>> +
>> +#ifndef __DPLL_H__
>> +#define __DPLL_H__
>> +
>> +#include <uapi/linux/dpll.h>
>> +#include <linux/device.h>
>> +#include <linux/netlink.h>
>> +
>> +struct dpll_device;
>> +struct dpll_pin;
>> +
>> +struct dpll_device_ops {
>> + int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
>> + enum dpll_mode *mode, struct netlink_ext_ack *extack);
>> + int (*mode_set)(const struct dpll_device *dpll, void *dpll_priv,
>> + const enum dpll_mode mode,
>> + struct netlink_ext_ack *extack);
>> + bool (*mode_supported)(const struct dpll_device *dpll, void *dpll_priv,
>> + const enum dpll_mode mode,
>> + struct netlink_ext_ack *extack);
>> + int (*source_pin_idx_get)(const struct dpll_device *dpll,
>> + void *dpll_priv,
>> + u32 *pin_idx,
>> + struct netlink_ext_ack *extack);
>> + int (*lock_status_get)(const struct dpll_device *dpll, void *dpll_priv,
>> + enum dpll_lock_status *status,
>> + struct netlink_ext_ack *extack);
>> + int (*temp_get)(const struct dpll_device *dpll, void *dpll_priv,
>> + s32 *temp, struct netlink_ext_ack *extack);
>> +};
>> +
>> +struct dpll_pin_ops {
>> + int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_device *dpll, void *dpll_priv,
>> + const u64 frequency,
>> + struct netlink_ext_ack *extack);
>> + int (*frequency_get)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_device *dpll, void *dpll_priv,
>> + u64 *frequency, struct netlink_ext_ack *extack);
>> + int (*direction_set)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_device *dpll, void *dpll_priv,
>> + const enum dpll_pin_direction direction,
>> + struct netlink_ext_ack *extack);
>> + int (*direction_get)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_device *dpll, void *dpll_priv,
>> + enum dpll_pin_direction *direction,
>> + struct netlink_ext_ack *extack);
>> + int (*state_on_pin_get)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_pin *parent_pin,
>> + void *parent_pin_priv,
>> + enum dpll_pin_state *state,
>> + struct netlink_ext_ack *extack);
>> + int (*state_on_dpll_get)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_device *dpll,
>> + void *dpll_priv, enum dpll_pin_state *state,
>> + struct netlink_ext_ack *extack);
>> + int (*state_on_pin_set)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_pin *parent_pin,
>> + void *parent_pin_priv,
>> + const enum dpll_pin_state state,
>> + struct netlink_ext_ack *extack);
>> + int (*state_on_dpll_set)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_device *dpll,
>> + void *dpll_priv,
>> + const enum dpll_pin_state state,
>> + struct netlink_ext_ack *extack);
>> + int (*prio_get)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_device *dpll, void *dpll_priv,
>> + u32 *prio, struct netlink_ext_ack *extack);
>> + int (*prio_set)(const struct dpll_pin *pin, void *pin_priv,
>> + const struct dpll_device *dpll, void *dpll_priv,
>> + const u32 prio, struct netlink_ext_ack *extack);
>> +};
>> +
>> +struct dpll_pin_frequency {
>> + u64 min;
>> + u64 max;
>> +};
>> +
>> +#define DPLL_PIN_FREQUENCY_RANGE(_min, _max) \
>> + { \
>> + .min = _min, \
>> + .max = _max, \
>> + }
>> +
>> +#define DPLL_PIN_FREQUENCY(_val) DPLL_PIN_FREQUENCY_RANGE(_val, _val)
>> +#define DPLL_PIN_FREQUENCY_1PPS \
>> + DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_1_HZ)
>> +#define DPLL_PIN_FREQUENCY_10MHZ \
>> + DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_MHZ)
>> +#define DPLL_PIN_FREQUENCY_IRIG_B \
>> + DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_10_KHZ)
>> +#define DPLL_PIN_FREQUENCY_DCF77 \
>> + DPLL_PIN_FREQUENCY(DPLL_PIN_FREQUENCY_77_5_KHZ)
>> +
>> +struct dpll_pin_properties {
>> + const char *board_label;
>> + const char *panel_label;
>> + const char *package_label;
>> + enum dpll_pin_type type;
>> + unsigned long capabilities;
>> + u32 freq_supported_num;
>> + struct dpll_pin_frequency *freq_supported;
>> +};
>> +
>> +struct dpll_device
>> +*dpll_device_get(u64 clock_id, u32 dev_driver_id, struct module *module);
>> +
>> +void dpll_device_put(struct dpll_device *dpll);
>> +
>> +int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>> + const struct dpll_device_ops *ops, void *priv);
>> +
>> +void dpll_device_unregister(struct dpll_device *dpll,
>> + const struct dpll_device_ops *ops, void *priv);
>> +
>> +struct dpll_pin
>> +*dpll_pin_get(u64 clock_id, u32 dev_driver_id, struct module *module,
>> + const struct dpll_pin_properties *prop);
>> +
>> +int dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>> + const struct dpll_pin_ops *ops, void *priv);
>> +
>> +void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>> + const struct dpll_pin_ops *ops, void *priv);
>> +
>> +void dpll_pin_put(struct dpll_pin *pin);
>> +
>> +int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>> + const struct dpll_pin_ops *ops, void *priv);
>> +
>> +void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin *pin,
>> + const struct dpll_pin_ops *ops, void *priv);
>> +
>> +int dpll_device_change_ntf(struct dpll_device *dpll);
>> +
>> +int dpll_pin_change_ntf(struct dpll_pin *pin);
>
> Why exactly did you split this into a separate patch? To me, it does not
> make any sense. Please squash this header addition to the
>

Sounds reasonable.

>
>> +
>> +#endif
>> --
>> 2.37.3
>>


2023-06-12 16:30:16

by Bart Van Assche

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

On 6/9/23 05:18, Arkadiusz Kubalewski wrote:
> diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
> new file mode 100644
> index 000000000000..8caa4af022ad
> --- /dev/null
> +++ b/Documentation/driver-api/dpll.rst
> @@ -0,0 +1,458 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +===============================
> +The Linux kernel dpll 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 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.

A section that explains what "DPLL" stands for is missing. Please add
such a section.

Thanks,

Bart.


2023-06-12 16:36:20

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

>From: Jiri Pirko <[email protected]>
>Sent: Saturday, June 10, 2023 6:29 PM
>
>Fri, Jun 09, 2023 at 02:18:44PM CEST, [email protected] wrote:
>>From: Vadim Fedorenko <[email protected]>
>>
>>Add documentation explaining common netlink interface to configure DPLL
>>devices and monitoring events. Common way to implement DPLL device in
>>a driver is also covered.
>>
>>Signed-off-by: Vadim Fedorenko <[email protected]>
>>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>>---
>> Documentation/driver-api/dpll.rst | 458 +++++++++++++++++++++++++++++
>> Documentation/driver-api/index.rst | 1 +
>> 2 files changed, 459 insertions(+)
>> create mode 100644 Documentation/driver-api/dpll.rst
>
>Looks fine to me. I just wonder if the info redundancy of this file and
>the netlink yaml could be somehow reduce. IDK.

Good question, will try to incorporate the dpll uapi header docs here,
instead of having redundant descriptions.

Thank you!
Arkadiusz

2023-06-12 16:53:28

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

>From: Bart Van Assche <[email protected]>
>Sent: Monday, June 12, 2023 5:30 PM
>
>On 6/9/23 05:18, Arkadiusz Kubalewski wrote:
>> diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-
>api/dpll.rst
>> new file mode 100644
>> index 000000000000..8caa4af022ad
>> --- /dev/null
>> +++ b/Documentation/driver-api/dpll.rst
>> @@ -0,0 +1,458 @@
>> +.. SPDX-License-Identifier: GPL-2.0
>> +
>> +===============================
>> +The Linux kernel dpll 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 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.
>
>A section that explains what "DPLL" stands for is missing. Please add
>such a section.
>
>Thanks,
>
>Bart.
>

Sure, will add.

Thank you!
Arkadiusz

2023-06-12 22:42:49

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

On Sat, 10 Jun 2023 18:28:45 +0200 Jiri Pirko wrote:
> Fri, Jun 09, 2023 at 02:18:44PM CEST, [email protected] wrote:
> >From: Vadim Fedorenko <[email protected]>
> >
> >Add documentation explaining common netlink interface to configure DPLL
> >devices and monitoring events. Common way to implement DPLL device in
> >a driver is also covered.
> >
> >Signed-off-by: Vadim Fedorenko <[email protected]>
> >Signed-off-by: Arkadiusz Kubalewski <[email protected]>
> >---
> > Documentation/driver-api/dpll.rst | 458 +++++++++++++++++++++++++++++
> > Documentation/driver-api/index.rst | 1 +
> > 2 files changed, 459 insertions(+)
> > create mode 100644 Documentation/driver-api/dpll.rst
>
> Looks fine to me. I just wonder if the info redundancy of this file and
> the netlink yaml could be somehow reduce. IDK.

Certainly possible, I even talked to Peter of the pyroute2 fame about
this. Should be fairly easy to generate a .rst file, and/or Sphinx
plugin to go directly from the YAML. Is it on my TODO list? Nope :)

2023-06-12 23:23:18

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

On Fri, 9 Jun 2023 14:18:44 +0200 Arkadiusz Kubalewski wrote:
> From: Vadim Fedorenko <[email protected]>
>
> Add documentation explaining common netlink interface to configure DPLL
> devices and monitoring events. Common way to implement DPLL device in
> a driver is also covered.

> +``'pin': [{
> + {'clock-id': 282574471561216,
> + 'module-name': 'ice',
> + 'pin-dpll-caps': 4,
> + 'pin-id': 13,
> + 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
> + {'pin-id': 3, 'pin-state': 'disconnected'},
> + {'id': 0, 'pin-direction': 'input'},
> + {'id': 1, 'pin-direction': 'input'}],
> + 'pin-type': 'synce-eth-port'}
> +}]``

It seems like pin-parent is overloaded, can we split it into two
different nests?

> +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 has to 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_DIRECTION`` or ``DPLL_A_PIN_FREQUENCY`` are configured,
> +this affects all the dpll device they are connected, that is why those
> +attributes shall not be enclosed in ``DPLL_A_PIN_PARENT``.
> +Other attributes:
> +``DPLL_A_PIN_PRIO`` or ``DPLL_A_PIN_STATE`` must be enclosed in
> +``DPLL_A_PIN_PARENT`` as their configuration relates to only one
> +parent dpll or parent pin.

Also sounds like setting pin attrs and pin-parent attrs should be
different commands.

2023-06-12 23:59:38

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

On Fri, 9 Jun 2023 14:18:46 +0200 Arkadiusz Kubalewski wrote:
> + xa_for_each(xa_pins, i, ref) {
> + if (ref->pin != pin)
> + continue;
> + reg = dpll_pin_registration_find(ref, ops, priv);
> + if (reg) {
> + refcount_inc(&ref->refcount);
> + return 0;
> + }
> + ref_exists = true;
> + break;
> + }
> +
> + if (!ref_exists) {
> + ref = kzalloc(sizeof(*ref), GFP_KERNEL);
> + if (!ref)
> + return -ENOMEM;
> + ref->pin = pin;
> + INIT_LIST_HEAD(&ref->registration_list);
> + ret = xa_insert(xa_pins, pin->pin_idx, ref, GFP_KERNEL);
> + if (ret) {
> + kfree(ref);
> + return ret;
> + }
> + refcount_set(&ref->refcount, 1);
> + }
> +
> + reg = kzalloc(sizeof(*reg), GFP_KERNEL);

Why do we have two structures - ref and reg?

> + if (!reg) {
> + if (!ref_exists)
> + kfree(ref);

ref has already been inserted into xa_pins

> + return -ENOMEM;

2023-06-13 00:12:57

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

On Fri, 9 Jun 2023 14:18:44 +0200 Arkadiusz Kubalewski wrote:
> +Every other operation handler is checked for existence and
> +``-ENOTSUPP`` is returned in case of absence of specific handler.

EOPNOTSUPP

2023-06-13 10:02:57

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

Tue, Jun 13, 2023 at 12:43:29AM CEST, [email protected] wrote:
>On Fri, 9 Jun 2023 14:18:44 +0200 Arkadiusz Kubalewski wrote:
>> From: Vadim Fedorenko <[email protected]>
>>
>> Add documentation explaining common netlink interface to configure DPLL
>> devices and monitoring events. Common way to implement DPLL device in
>> a driver is also covered.
>
>> +``'pin': [{
>> + {'clock-id': 282574471561216,
>> + 'module-name': 'ice',
>> + 'pin-dpll-caps': 4,
>> + 'pin-id': 13,
>> + 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
>> + {'pin-id': 3, 'pin-state': 'disconnected'},
>> + {'id': 0, 'pin-direction': 'input'},
>> + {'id': 1, 'pin-direction': 'input'}],
>> + 'pin-type': 'synce-eth-port'}
>> +}]``
>
>It seems like pin-parent is overloaded, can we split it into two
>different nests?

Yeah, we had it as two and converged to this one. The thing is, the rest
of the attrs are the same for both parent pin and parent device. I link
it this way a bit better. No strong feeling.


>
>> +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 has to 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_DIRECTION`` or ``DPLL_A_PIN_FREQUENCY`` are configured,
>> +this affects all the dpll device they are connected, that is why those
>> +attributes shall not be enclosed in ``DPLL_A_PIN_PARENT``.
>> +Other attributes:
>> +``DPLL_A_PIN_PRIO`` or ``DPLL_A_PIN_STATE`` must be enclosed in
>> +``DPLL_A_PIN_PARENT`` as their configuration relates to only one
>> +parent dpll or parent pin.
>
>Also sounds like setting pin attrs and pin-parent attrs should be
>different commands.

Could be, but what't the benefit? Also, you are not configuring
pin-parent. You are configuring pin:pin-parent tuple. Basically the pin
configuration as a child. So this is mainly config of the pin itsest
Therefore does not really make sense to me to split to two comments.

2023-06-13 14:14:17

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 06/10] netdev: expose DPLL pin handle for netdevice

Mon, Jun 12, 2023 at 11:17:23AM CEST, [email protected] wrote:
>Arkadiusz Kubalewski píše v Pá 09. 06. 2023 v 14:18 +0200:
>> From: Jiri Pirko <[email protected]>

[...]


>> +static size_t rtnl_dpll_pin_size(const struct net_device *dev)
>> +{
>> +       size_t size = nla_total_size(0); /* nest IFLA_DPLL_PIN */
>> +
>> +       if (dev->dpll_pin)
>> +               size += dpll_msg_pin_handle_size(dev->dpll_pin);
>
>Hi Arkadiusz,
>
>net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL)
>But the code in net/core/rtnetlink.c doesn't respect that.
>If CONFIG_DPLL is not set, net/core/rtnetlink.c cannot be compiled.
>
>Regards,
>Petr

You are correct. Here's the squash-patch to fix this. Arkadiusz, could
you please make the squash? Thanks!

diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index e6efc17aaf26..00dc96c3ade4 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -303,12 +303,14 @@ dpll_cmd_pin_fill_details(struct sk_buff *msg, struct dpll_pin *pin,

size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
{
- return nla_total_size(4); /* DPLL_A_PIN_ID */
+ return pin ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
}
EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);

int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
{
+ if (!pin)
+ return 0;
if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
return -EMSGSIZE;
return 0;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index b002e3cc9943..82ad12fd4266 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3967,6 +3967,16 @@ int dev_get_port_parent_id(struct net_device *dev,
bool netdev_port_same_parent_id(struct net_device *a, struct net_device *b);
void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin);
void netdev_dpll_pin_clear(struct net_device *dev);
+
+static inline struct dpll_pin *netdev_dpll_pin(const struct net_device *dev)
+{
+#if IS_ENABLED(CONFIG_DPLL)
+ return dev->dpll_pin;
+#else
+ return NULL;
+#endif
+}
+
struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev, bool *again);
struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, int *ret);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index ebe9ae8608fc..67dd455e15c7 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1056,8 +1056,7 @@ static size_t rtnl_dpll_pin_size(const struct net_device *dev)
{
size_t size = nla_total_size(0); /* nest IFLA_DPLL_PIN */

- if (dev->dpll_pin)
- size += dpll_msg_pin_handle_size(dev->dpll_pin);
+ size += dpll_msg_pin_handle_size(netdev_dpll_pin(dev));

return size;
}
@@ -1790,11 +1789,9 @@ static int rtnl_fill_dpll_pin(struct sk_buff *skb,
if (!dpll_pin_nest)
return -EMSGSIZE;

- if (dev->dpll_pin) {
- ret = dpll_msg_add_pin_handle(skb, dev->dpll_pin);
- if (ret < 0)
- goto nest_cancel;
- }
+ ret = dpll_msg_add_pin_handle(skb, netdev_dpll_pin(dev));
+ if (ret < 0)
+ goto nest_cancel;

nla_nest_end(skb, dpll_pin_nest);
return 0;


2023-06-13 17:17:40

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

On Tue, 13 Jun 2023 11:55:11 +0200 Jiri Pirko wrote:
> >> +``'pin': [{
> >> + {'clock-id': 282574471561216,
> >> + 'module-name': 'ice',
> >> + 'pin-dpll-caps': 4,
> >> + 'pin-id': 13,
> >> + 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
> >> + {'pin-id': 3, 'pin-state': 'disconnected'},
> >> + {'id': 0, 'pin-direction': 'input'},
> >> + {'id': 1, 'pin-direction': 'input'}],
> >> + 'pin-type': 'synce-eth-port'}
> >> +}]``
> >
> >It seems like pin-parent is overloaded, can we split it into two
> >different nests?
>
> Yeah, we had it as two and converged to this one. The thing is, the rest
> of the attrs are the same for both parent pin and parent device. I link
> it this way a bit better. No strong feeling.

Do you mean the same attribute enum / "space" / "set"?
In the example above the attributes present don't seem to overlap.
For user space its an extra if to sift thru the objects under
pin-parent.

> >Also sounds like setting pin attrs and pin-parent attrs should be
> >different commands.
>
> Could be, but what't the benefit? Also, you are not configuring
> pin-parent. You are configuring pin:pin-parent tuple. Basically the pin
> configuration as a child. So this is mainly config of the pin itsest
> Therefore does not really make sense to me to split to two comments.

Clarity of the API. If muxing everything thru few calls was the goal
we should also have very few members in struct dpll_pin_ops, and we
don't.

2023-06-14 09:52:14

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

Tue, Jun 13, 2023 at 06:38:01PM CEST, [email protected] wrote:
>On Tue, 13 Jun 2023 11:55:11 +0200 Jiri Pirko wrote:
>> >> +``'pin': [{
>> >> + {'clock-id': 282574471561216,
>> >> + 'module-name': 'ice',
>> >> + 'pin-dpll-caps': 4,
>> >> + 'pin-id': 13,
>> >> + 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
>> >> + {'pin-id': 3, 'pin-state': 'disconnected'},
>> >> + {'id': 0, 'pin-direction': 'input'},
>> >> + {'id': 1, 'pin-direction': 'input'}],
>> >> + 'pin-type': 'synce-eth-port'}
>> >> +}]``
>> >
>> >It seems like pin-parent is overloaded, can we split it into two
>> >different nests?
>>
>> Yeah, we had it as two and converged to this one. The thing is, the rest
>> of the attrs are the same for both parent pin and parent device. I link
>> it this way a bit better. No strong feeling.
>
>Do you mean the same attribute enum / "space" / "set"?

Yeah, that is my understanding. Arkadiusz, could you please clarify?


>In the example above the attributes present don't seem to overlap.
>For user space its an extra if to sift thru the objects under
>pin-parent.
>
>> >Also sounds like setting pin attrs and pin-parent attrs should be
>> >different commands.
>>
>> Could be, but what't the benefit? Also, you are not configuring
>> pin-parent. You are configuring pin:pin-parent tuple. Basically the pin
>> configuration as a child. So this is mainly config of the pin itsest
>> Therefore does not really make sense to me to split to two comments.
>
>Clarity of the API. If muxing everything thru few calls was the goal
>we should also have very few members in struct dpll_pin_ops, and we
>don't.

How the the internal kernel api related to the uapi? I mean, it's quite
common to have 1:N relationsip between cmd and op. I have to be missing
your point. Could you be more specific please?

Current code presents PIN_SET command with accepts structured set of
attribute to be set. The core-driver api is pretty clear. Squashing to
a single op would be disaster. Having one command per attr looks like an
overkill without any real benefit. How exactly do you propose to change
this?

2023-06-14 12:34:24

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 06/10] netdev: expose DPLL pin handle for netdevice

>From: Jiri Pirko <[email protected]>
>Sent: Tuesday, June 13, 2023 3:52 PM
>
>Mon, Jun 12, 2023 at 11:17:23AM CEST, [email protected] wrote:
>>Arkadiusz Kubalewski píše v Pá 09. 06. 2023 v 14:18 +0200:
>>> From: Jiri Pirko <[email protected]>
>
>[...]
>
>
>>> +static size_t rtnl_dpll_pin_size(const struct net_device *dev)
>>> +{
>>> +       size_t size = nla_total_size(0); /* nest IFLA_DPLL_PIN */
>>> +
>>> +       if (dev->dpll_pin)
>>> +               size += dpll_msg_pin_handle_size(dev->dpll_pin);
>>
>>Hi Arkadiusz,
>>
>>net_device->dpll_pin is only valid if IS_ENABLED(CONFIG_DPLL)
>>But the code in net/core/rtnetlink.c doesn't respect that.
>>If CONFIG_DPLL is not set, net/core/rtnetlink.c cannot be compiled.
>>
>>Regards,
>>Petr
>
>You are correct. Here's the squash-patch to fix this. Arkadiusz, could
>you please make the squash? Thanks!
>

Sure thing, will do.

Thank you!
Arkadiusz

>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>index e6efc17aaf26..00dc96c3ade4 100644
>--- a/drivers/dpll/dpll_netlink.c
>+++ b/drivers/dpll/dpll_netlink.c
>@@ -303,12 +303,14 @@ dpll_cmd_pin_fill_details(struct sk_buff *msg, struct
>dpll_pin *pin,
>
> size_t dpll_msg_pin_handle_size(struct dpll_pin *pin)
> {
>- return nla_total_size(4); /* DPLL_A_PIN_ID */
>+ return pin ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
> }
> EXPORT_SYMBOL_GPL(dpll_msg_pin_handle_size);
>
> int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
> {
>+ if (!pin)
>+ return 0;
> if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
> return -EMSGSIZE;
> return 0;
>diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>index b002e3cc9943..82ad12fd4266 100644
>--- a/include/linux/netdevice.h
>+++ b/include/linux/netdevice.h
>@@ -3967,6 +3967,16 @@ int dev_get_port_parent_id(struct net_device *dev,
> bool netdev_port_same_parent_id(struct net_device *a, struct net_device
>*b);
> void netdev_dpll_pin_set(struct net_device *dev, struct dpll_pin
>*dpll_pin);
> void netdev_dpll_pin_clear(struct net_device *dev);
>+
>+static inline struct dpll_pin *netdev_dpll_pin(const struct net_device
>*dev)
>+{
>+#if IS_ENABLED(CONFIG_DPLL)
>+ return dev->dpll_pin;
>+#else
>+ return NULL;
>+#endif
>+}
>+
> struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct
>net_device *dev, bool *again);
> struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device
>*dev,
> struct netdev_queue *txq, int *ret);
>diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
>index ebe9ae8608fc..67dd455e15c7 100644
>--- a/net/core/rtnetlink.c
>+++ b/net/core/rtnetlink.c
>@@ -1056,8 +1056,7 @@ static size_t rtnl_dpll_pin_size(const struct
>net_device *dev)
> {
> size_t size = nla_total_size(0); /* nest IFLA_DPLL_PIN */
>
>- if (dev->dpll_pin)
>- size += dpll_msg_pin_handle_size(dev->dpll_pin);
>+ size += dpll_msg_pin_handle_size(netdev_dpll_pin(dev));
>
> return size;
> }
>@@ -1790,11 +1789,9 @@ static int rtnl_fill_dpll_pin(struct sk_buff *skb,
> if (!dpll_pin_nest)
> return -EMSGSIZE;
>
>- if (dev->dpll_pin) {
>- ret = dpll_msg_add_pin_handle(skb, dev->dpll_pin);
>- if (ret < 0)
>- goto nest_cancel;
>- }
>+ ret = dpll_msg_add_pin_handle(skb, netdev_dpll_pin(dev));
>+ if (ret < 0)
>+ goto nest_cancel;
>
> nla_nest_end(skb, dpll_pin_nest);
> return 0;

2023-06-14 13:03:27

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

>From: Jiri Pirko <[email protected]>
>Sent: Wednesday, June 14, 2023 11:27 AM
>
>Tue, Jun 13, 2023 at 06:38:01PM CEST, [email protected] wrote:
>>On Tue, 13 Jun 2023 11:55:11 +0200 Jiri Pirko wrote:
>>> >> +``'pin': [{
>>> >> + {'clock-id': 282574471561216,
>>> >> + 'module-name': 'ice',
>>> >> + 'pin-dpll-caps': 4,
>>> >> + 'pin-id': 13,
>>> >> + 'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
>>> >> + {'pin-id': 3, 'pin-state': 'disconnected'},
>>> >> + {'id': 0, 'pin-direction': 'input'},
>>> >> + {'id': 1, 'pin-direction': 'input'}],
>>> >> + 'pin-type': 'synce-eth-port'}
>>> >> +}]``
>>> >
>>> >It seems like pin-parent is overloaded, can we split it into two
>>> >different nests?
>>>
>>> Yeah, we had it as two and converged to this one. The thing is, the rest
>>> of the attrs are the same for both parent pin and parent device. I link
>>> it this way a bit better. No strong feeling.
>>
>>Do you mean the same attribute enum / "space" / "set"?
>
>Yeah, that is my understanding. Arkadiusz, could you please clarify?
>

From user perspective the pin object is configured either:
- by itself (DPLL_A_PIN_FREQUENCY), as this affects all registered pin's dplls
and frequency set ops are called on all of them,
- in a tuples, where ops are called only on particular parent object:
- pin:'dpll device' (DPLL_A_PIN_STATE, DPLL_A_PIN_PRIO, DPLL_A_PIN_DIRECTION),
- pin:'parent MUX-type pin' (DPLL_A_PIN_STATE).

Right now DPLL_A_PIN_PARENT nest can convey both parent types:
- if the nest contains DPLL_A_ID, other attributes describe config with the
parent dpll device,
- if it contains DPLL_A_PIN_ID, the other attributes describe config with the
parent pin.

The above example is actually a bit misleading, as this relates to the muxed
pin. From user perspective the information on parent dpll devices is redundant,
and I think in this case we shall not pass it to the user, as the pin was not
explicitly registered with a device by device driver.
The user shall only get parent pin related info, like:
``'pin': [{
{'clock-id': 282574471561216,
'module-name': 'ice',
'pin-dpll-caps': 4,
'pin-id': 13,
'pin-parent': [{'pin-id': 2, 'pin-state': 'connected'},
{'pin-id': 3, 'pin-state': 'disconnected'},
'pin-type': 'synce-eth-port'}
}]``

Thus will fix this by removing the parent device information from the muxed
pins info.

But to answer the question: for now it seems good enough to have the PIN_PARENT
nest that conveys either parent pin or parent dpll device information.
IMHO the real question here is about the future, are we going to add pin-parent
only config, which would not be a part of pin-device superset and would make
things unclear?
Unfortunately I don't have on my mind anything more that would be needed for
pin:parent_pin tuple..

Surely, we can skip this discussion and split the nest attr into something like:
- PIN_A_PIN_PARENT_DEVICE,
- PIN_A_PIN_PARENT_PIN.

>
>>In the example above the attributes present don't seem to overlap.
>>For user space its an extra if to sift thru the objects under
>>pin-parent.
>>
>>> >Also sounds like setting pin attrs and pin-parent attrs should be
>>> >different commands.
>>>
>>> Could be, but what't the benefit? Also, you are not configuring
>>> pin-parent. You are configuring pin:pin-parent tuple. Basically the pin
>>> configuration as a child. So this is mainly config of the pin itsest
>>> Therefore does not really make sense to me to split to two comments.
>>
>>Clarity of the API. If muxing everything thru few calls was the goal
>>we should also have very few members in struct dpll_pin_ops, and we
>>don't.
>
>How the the internal kernel api related to the uapi? I mean, it's quite
>common to have 1:N relationsip between cmd and op. I have to be missing
>your point. Could you be more specific please?
>
>Current code presents PIN_SET command with accepts structured set of
>attribute to be set. The core-driver api is pretty clear. Squashing to
>a single op would be disaster. Having one command per attr looks like an
>overkill without any real benefit. How exactly do you propose to change
>this?

Well, I agree that one command per attribute might be an overkill.

Thank you,
Arkadiusz

2023-06-14 13:15:11

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

>From: Jakub Kicinski <[email protected]>
>Sent: Tuesday, June 13, 2023 1:49 AM
>
>On Fri, 9 Jun 2023 14:18:44 +0200 Arkadiusz Kubalewski wrote:
>> +Every other operation handler is checked for existence and
>> +``-ENOTSUPP`` is returned in case of absence of specific handler.
>
>EOPNOTSUPP

Sure, will fix.

Thank you!
Arkadiusz

2023-06-14 19:37:21

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

On Wed, 14 Jun 2023 12:21:29 +0000 Kubalewski, Arkadiusz wrote:
> Surely, we can skip this discussion and split the nest attr into something like:
> - PIN_A_PIN_PARENT_DEVICE,
> - PIN_A_PIN_PARENT_PIN.

Yup, exactly. Should a fairly change code wise, if I'm looking right.

2023-06-14 19:38:42

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

On Wed, 14 Jun 2023 12:15:14 -0700 Jakub Kicinski wrote:
> On Wed, 14 Jun 2023 12:21:29 +0000 Kubalewski, Arkadiusz wrote:
> > Surely, we can skip this discussion and split the nest attr into something like:
> > - PIN_A_PIN_PARENT_DEVICE,
> > - PIN_A_PIN_PARENT_PIN.
>
> Yup, exactly. Should a fairly change code wise, if I'm looking right.
^ small

2023-06-15 10:33:00

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

Wed, Jun 14, 2023 at 09:23:48PM CEST, [email protected] wrote:
>On Wed, 14 Jun 2023 12:15:14 -0700 Jakub Kicinski wrote:
>> On Wed, 14 Jun 2023 12:21:29 +0000 Kubalewski, Arkadiusz wrote:
>> > Surely, we can skip this discussion and split the nest attr into something like:
>> > - PIN_A_PIN_PARENT_DEVICE,
>> > - PIN_A_PIN_PARENT_PIN.
>>
>> Yup, exactly. Should a fairly change code wise, if I'm looking right.
> ^ small

Yeah, that is what we had originally. This just pushes out the
different attr selection from the nest one level up to the actualy
nesting attribute.

One downside of this is you will have 2 arrays of parent objects,
one per parent type. Current code neatly groups them into a single array.

I guess this is a matter of personal preference, I'm fine either way.


2023-06-15 13:51:59

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 02/10] dpll: spec: Add Netlink spec in YAML

>From: Jiri Pirko <[email protected]>
>Sent: Saturday, June 10, 2023 6:22 PM
>
>Fri, Jun 09, 2023 at 02:18:45PM CEST, [email protected] wrote:
>>Add a protocol spec for DPLL.
>>Add code generated from the spec.
>>
>>Signed-off-by: Jakub Kicinski <[email protected]>
>>Signed-off-by: Michal Michalik <[email protected]>
>>Signed-off-by: Vadim Fedorenko <[email protected]>
>>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>>---
>> Documentation/netlink/specs/dpll.yaml | 466 ++++++++++++++++++++++++++
>> drivers/dpll/dpll_nl.c | 161 +++++++++
>> drivers/dpll/dpll_nl.h | 50 +++
>> include/uapi/linux/dpll.h | 184 ++++++++++
>> 4 files changed, 861 insertions(+)
>> create mode 100644 Documentation/netlink/specs/dpll.yaml
>> create mode 100644 drivers/dpll/dpll_nl.c
>> create mode 100644 drivers/dpll/dpll_nl.h
>> create mode 100644 include/uapi/linux/dpll.h
>>
>>diff --git a/Documentation/netlink/specs/dpll.yaml
>>b/Documentation/netlink/specs/dpll.yaml
>>new file mode 100644
>>index 000000000000..f7317003d312
>>--- /dev/null
>>+++ b/Documentation/netlink/specs/dpll.yaml
>>@@ -0,0 +1,466 @@
>>+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-
>>Clause)
>>+
>>+name: dpll
>>+
>>+doc: DPLL subsystem.
>>+
>>+definitions:
>>+ -
>>+ type: enum
>>+ name: mode
>>+ doc: |
>>+ working-modes a dpll can support, differentiate if and how dpll
>>selects
>
>s/working-modes/working modes/
>s/differentiate/differentiates/
>?

Fixed.

>
>
>>+ one of its inputs to syntonize with it, valid values for DPLL_A_MODE
>>+ attribute
>>+ entries:
>>+ -
>>+ name: manual
>>+ doc: input can be only selected by sending a request to dpll
>>+ value: 1
>>+ -
>>+ name: automatic
>>+ doc: highest prio, valid input, auto selected by dpll
>
>s/valid input, auto selected by dpll/input pin auto selected by dpll/
>?

Fixed.

>
>
>>+ -
>>+ name: holdover
>>+ doc: dpll forced into holdover mode
>>+ -
>>+ name: freerun
>>+ doc: dpll driven on system clk
>
>Thinking about modes "holdover" and "freerun".
>1) You don't use them anywhere in this patchset, please remove them
> until they are needed. ptp_ocp and ice uses automatic, mlx5 uses
> manual. Btw, are there any other unused parts of UAPI? If yes, could
> you please remove them too?
>
>2) I don't think it is correct to have them.
> a) to achieve holdover:
> if state is LOCKED_HO_ACQ you just disconnect all input pins.
> b) to achieve freerun:
> if state LOCKED you just disconnect all input pins.
> So don't mangle the mode with status.
>

Well this is not entierly true, the mode is not a state.
Technically in those modes the user would not be able to set any states
on the pins.
The modes are supported on the synchronizer chips we are using, altough
the ice driver does not have this support enabled yet.
So I am removing those for now, if they would be needed, we will submit the
patches for it.

>
>>+ render-max: true
>>+ -
>>+ type: enum
>>+ name: lock-status
>>+ doc: |
>>+ provides information of dpll device lock status, valid values for
>>+ DPLL_A_LOCK_STATUS attribute
>>+ entries:
>>+ -
>>+ name: unlocked
>>+ doc: |
>>+ dpll was not yet locked to any valid input (or is in mode:
>>+ DPLL_MODE_FREERUN)
>
>Don't forget to remove the mention of mode freerun from here.
>

Fixed.

>
>>+ value: 1
>>+ -
>>+ name: locked
>>+ doc: |
>>+ dpll is locked to a valid signal, but no holdover available
>>+ -
>>+ name: locked-ho-acq
>>+ doc: |
>>+ dpll is locked and holdover acquired
>>+ -
>>+ name: holdover
>>+ doc: |
>>+ dpll is in holdover state - lost a valid lock or was forced
>>+ by selecting DPLL_MODE_HOLDOVER mode (latter possible only
>>+ when dpll lock-state was already DPLL_LOCK_STATUS_LOCKED,
>>+ if dpll lock-state was not DPLL_LOCK_STATUS_LOCKED, the
>>+ dpll's lock-state shall remain DPLL_LOCK_STATUS_UNLOCKED
>>+ even if DPLL_MODE_HOLDOVER was requested)
>
>Don't forget to remove the mention of mode holdover from here.
>

Fixed.

>
>>+ render-max: true
>>+ -
>>+ type: const
>>+ name: temp-divider
>>+ value: 1000
>>+ doc: |
>>+ temperature divider allowing userspace to calculate the
>>+ temperature as float with three digit decimal precision.
>>+ Value of (DPLL_A_TEMP / DPLL_TEMP_DIVIDER) is integer part of
>>+ temperature value.
>>+ Value of (DPLL_A_TEMP % DPLL_TEMP_DIVIDER) is fractional part of
>>+ temperature value.
>>+ -
>>+ type: enum
>>+ name: type
>>+ doc: type of dpll, valid values for DPLL_A_TYPE attribute
>>+ entries:
>>+ -
>>+ name: pps
>>+ doc: dpll produces Pulse-Per-Second signal
>>+ value: 1
>>+ -
>>+ name: eec
>>+ doc: dpll drives the Ethernet Equipment Clock
>>+ render-max: true
>>+ -
>>+ type: enum
>>+ name: pin-type
>>+ doc: |
>>+ defines possible types of a pin, valid values for DPLL_A_PIN_TYPE
>>+ attribute
>>+ entries:
>>+ -
>>+ name: mux
>>+ doc: aggregates another layer of selectable pins
>>+ value: 1
>>+ -
>>+ name: ext
>>+ doc: external input
>>+ -
>>+ name: synce-eth-port
>>+ doc: ethernet port PHY's recovered clock
>>+ -
>>+ name: int-oscillator
>>+ doc: device internal oscillator
>>+ -
>>+ name: gnss
>>+ doc: GNSS recovered clock
>>+ render-max: true
>>+ -
>>+ type: enum
>>+ name: pin-direction
>>+ doc: |
>>+ defines possible direction of a pin, valid values for
>>+ DPLL_A_PIN_DIRECTION attribute
>>+ entries:
>>+ -
>>+ name: input
>>+ doc: pin used as a input of a signal
>
>I don't think I have any objections against "input", but out of
>curiosity, why you changed that from "source"?
>

Agreed to previous version review comment from Jakub,
to use either: input/output or source/sink naming scheme.

>
>>+ value: 1
>>+ -
>>+ name: output
>>+ doc: pin used to output the signal
>>+ render-max: true
>>+ -
>>+ type: const
>>+ name: pin-frequency-1-hz
>>+ value: 1
>>+ -
>>+ type: const
>>+ name: pin-frequency-10-khz
>>+ value: 10000
>>+ -
>>+ type: const
>>+ name: pin-frequency-77_5-khz
>>+ value: 77500
>>+ -
>>+ type: const
>>+ name: pin-frequency-10-mhz
>>+ value: 10000000
>>+ -
>>+ type: enum
>>+ name: pin-state
>>+ doc: |
>>+ defines possible states of a pin, valid values for
>>+ DPLL_A_PIN_STATE attribute
>>+ entries:
>>+ -
>>+ name: connected
>>+ doc: pin connected, active input of phase locked loop
>>+ value: 1
>>+ -
>>+ name: disconnected
>>+ doc: pin disconnected, not considered as a valid input
>>+ -
>>+ name: selectable
>>+ doc: pin enabled for automatic input selection
>>+ render-max: true
>>+ -
>>+ type: flags
>>+ name: pin-caps
>>+ doc: |
>>+ defines possible capabilities of a pin, valid flags on
>>+ DPLL_A_PIN_CAPS attribute
>>+ entries:
>>+ -
>>+ name: direction-can-change
>>+ -
>>+ name: priority-can-change
>>+ -
>>+ name: state-can-change
>>+
>>+attribute-sets:
>>+ -
>>+ name: dpll
>>+ enum-name: dpll_a
>>+ attributes:
>>+ -
>>+ name: id
>>+ type: u32
>>+ value: 1
>>+ -
>>+ name: module-name
>>+ type: string
>>+ -
>>+ name: clock-id
>>+ type: u64
>>+ -
>>+ name: mode
>>+ type: u8
>>+ enum: mode
>>+ -
>>+ name: mode-supported
>>+ type: u8
>>+ enum: mode
>>+ multi-attr: true
>>+ -
>>+ name: lock-status
>>+ type: u8
>>+ enum: lock-status
>>+ -
>>+ name: temp
>>+ type: s32
>>+ -
>>+ name: type
>>+ type: u8
>>+ enum: type
>>+ -
>>+ name: pin-id
>>+ type: u32
>>+ -
>>+ name: pin-board-label
>>+ type: string
>>+ -
>>+ name: pin-panel-label
>>+ type: string
>>+ -
>>+ name: pin-package-label
>>+ type: string
>
>Wouldn't it make sense to add some small documentation blocks to the
>attrs? IDK.
>

Actually already tried that, but after all they did not generate any docs
for attr enums. So this need also fix in ynl-gen-c.py

I think this would be useful, but only if we could use them in the dpll.rst.
Right now it is not case, but we already try to incorporate other enums
description there, for now it is broken, but will try to fix this in the near
future.

Thank you!
Arkadiusz

>
>>+ -
>>+ name: pin-type
>>+ type: u8
>>+ enum: pin-type
>>+ -
>>+ name: pin-direction
>>+ type: u8
>>+ enum: pin-direction
>>+ -
>>+ name: pin-frequency
>>+ type: u64
>>+ -
>>+ name: pin-frequency-supported
>>+ type: nest
>>+ multi-attr: true
>>+ nested-attributes: pin-frequency-range
>>+ -
>>+ name: pin-frequency-min
>>+ type: u64
>>+ -
>>+ name: pin-frequency-max
>>+ type: u64
>>+ -
>>+ name: pin-prio
>>+ type: u32
>>+ -
>>+ name: pin-state
>>+ type: u8
>>+ enum: pin-state
>>+ -
>>+ name: pin-dpll-caps
>>+ type: u32
>>+ -
>>+ name: pin-parent
>>+ type: nest
>>+ multi-attr: true
>>+ nested-attributes: pin-parent
>>+ -
>>+ name: pin-parent
>>+ subset-of: dpll
>>+ attributes:
>>+ -
>>+ name: id
>>+ type: u32
>>+ -
>>+ name: pin-direction
>>+ type: u8
>>+ -
>>+ name: pin-prio
>>+ type: u32
>>+ -
>>+ name: pin-state
>>+ type: u8
>>+ -
>>+ name: pin-id
>>+ type: u32
>>+
>>+ -
>>+ name: pin-frequency-range
>>+ subset-of: dpll
>>+ attributes:
>>+ -
>>+ name: pin-frequency-min
>>+ type: u64
>>+ -
>>+ name: pin-frequency-max
>>+ type: u64
>
>[...]

2023-06-15 13:52:01

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

>From: Jiri Pirko <[email protected]>
>Sent: Thursday, June 15, 2023 12:18 PM
>
>Wed, Jun 14, 2023 at 09:23:48PM CEST, [email protected] wrote:
>>On Wed, 14 Jun 2023 12:15:14 -0700 Jakub Kicinski wrote:
>>> On Wed, 14 Jun 2023 12:21:29 +0000 Kubalewski, Arkadiusz wrote:
>>> > Surely, we can skip this discussion and split the nest attr into
>something like:
>>> > - PIN_A_PIN_PARENT_DEVICE,
>>> > - PIN_A_PIN_PARENT_PIN.
>>>
>>> Yup, exactly. Should a fairly change code wise, if I'm looking right.
>> ^ small
>
>Yeah, that is what we had originally. This just pushes out the
>different attr selection from the nest one level up to the actualy
>nesting attribute.
>
>One downside of this is you will have 2 arrays of parent objects,
>one per parent type. Current code neatly groups them into a single array.
>
>I guess this is a matter of personal preference, I'm fine either way.

Ok, I will split it.

Thank you!
Arkadiusz

2023-06-15 16:44:01

by Jakub Kicinski

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

On Thu, 15 Jun 2023 12:18:28 +0200 Jiri Pirko wrote:
> Yeah, that is what we had originally. This just pushes out the
> different attr selection from the nest one level up to the actualy
> nesting attribute.

Oh no, no extra nesting. Let me try to fake up the output:

'pin': [{
{'clock-id': 282574471561216,
'module-name': 'ice',
'pin-dpll-caps': 4,
'pin-id': 13,
'parent-device': [{'pin-id': 2, 'pin-state': 'connected'},
{'pin-id': 3, 'pin-state': 'disconnected'}],
'parent-pin': [{'id': 0, 'pin-direction': 'input'},
{'id': 1, 'pin-direction': 'input'}],
'pin-type': 'synce-eth-port'}
}]

> One downside of this is you will have 2 arrays of parent objects,
> one per parent type. Current code neatly groups them into a single array.
>
> I guess this is a matter of personal preference, I'm fine either way.

Yeah, could be.

2023-06-15 21:57:40

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 07/10] ice: add admin commands to access cgu configuration

>From: Jiri Pirko <[email protected]>
>Sent: Saturday, June 10, 2023 10:46 AM
>
>Fri, Jun 09, 2023 at 02:18:50PM CEST, [email protected] wrote:
>>Add firmware admin command to access clock generation unit
>>configuration, it is required to enable Extended PTP and SyncE features
>>in the driver.
>
>Empty line here perhaps?
>

Sure, will do.

>
>>Add definitions of possible hardware variations of input and output pins
>>related to clock generation unit and functions to access the data.
>>
>>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>
>I just skimmed over this, not really give it much of a time. Couple of
>nits:
>
>
>>+
>>+#define MAX_NETLIST_SIZE 10
>
>Prefix perhaps?
>

Fixed.

>[...]
>
>
>>+/**
>>+ * convert_s48_to_s64 - convert 48 bit value to 64 bit value
>>+ * @signed_48: signed 64 bit variable storing signed 48 bit value
>>+ *
>>+ * Convert signed 48 bit value to its 64 bit representation.
>>+ *
>>+ * Return: signed 64 bit representation of signed 48 bit value.
>>+ */
>>+static inline
>
>Never use "inline" in a c file.
>
>
>>+s64 convert_s48_to_s64(s64 signed_48)
>>+{
>>+ const s64 MASK_SIGN_BITS = GENMASK_ULL(63, 48);
>
>variable with capital letters? Not nice. Define? You have that multiple
>times in the patch.
>
>
>>+ const s64 SIGN_BIT_47 = BIT_ULL(47);
>>+
>>+ return ((signed_48 & SIGN_BIT_47) ? (s64)(MASK_SIGN_BITS | signed_48)
>
>Pointless cast, isn't it?
>
>You don't need () around "signed_48 & SIGN_BIT_47"
>
>
>>+ : signed_48);
>
>Return is not a function. Drop the outer "()"s.
>
>
>The whole fuction can look like:
>static s64 convert_s48_to_s64(s64 signed_48)
>{
> return signed_48 & BIT_ULL(47) ? signed_48 | GENMASK_ULL(63, 48) :
> signed_48;
>}
>
>Nicer?
>

Sure, fixed.

>
>[...]
>
>
>
>>+int ice_get_pf_c827_idx(struct ice_hw *hw, u8 *idx)
>>+{
>>+ struct ice_aqc_get_link_topo cmd;
>>+ u8 node_part_number;
>>+ u16 node_handle;
>>+ int status;
>>+ u8 ctx;
>>+
>>+ if (hw->mac_type != ICE_MAC_E810)
>>+ return -ENODEV;
>>+
>>+ if (hw->device_id != ICE_DEV_ID_E810C_QSFP) {
>>+ *idx = C827_0;
>>+ return 0;
>>+ }
>>+
>>+ memset(&cmd, 0, sizeof(cmd));
>>+
>>+ ctx = ICE_AQC_LINK_TOPO_NODE_TYPE_PHY <<
>ICE_AQC_LINK_TOPO_NODE_TYPE_S;
>>+ ctx |= ICE_AQC_LINK_TOPO_NODE_CTX_PORT <<
>>ICE_AQC_LINK_TOPO_NODE_CTX_S;
>>+ cmd.addr.topo_params.node_type_ctx = ctx;
>>+ cmd.addr.topo_params.index = 0;
>
>You zeroed the struct 4 lines above...
>

Fixed.

Thank you!
Arkadiusz

>
>>+
>>+ status = ice_aq_get_netlist_node(hw, &cmd, &node_part_number,
>>+ &node_handle);
>>+ if (status || node_part_number != ICE_ACQ_GET_LINK_TOPO_NODE_NR_C827)
>>+ return -ENOENT;
>>+
>>+ if (node_handle == E810C_QSFP_C827_0_HANDLE)
>>+ *idx = C827_0;
>>+ else if (node_handle == E810C_QSFP_C827_1_HANDLE)
>>+ *idx = C827_1;
>>+ else
>>+ return -EIO;
>>+
>>+ return 0;
>>+}
>>+
>
>[...]

2023-06-17 10:52:36

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 01/10] dpll: documentation on DPLL subsystem interface

Thu, Jun 15, 2023 at 06:31:11PM CEST, [email protected] wrote:
>On Thu, 15 Jun 2023 12:18:28 +0200 Jiri Pirko wrote:
>> Yeah, that is what we had originally. This just pushes out the
>> different attr selection from the nest one level up to the actualy
>> nesting attribute.
>
>Oh no, no extra nesting. Let me try to fake up the output:

I wasn't implying any extra nesting.

>
>'pin': [{
> {'clock-id': 282574471561216,
> 'module-name': 'ice',
> 'pin-dpll-caps': 4,
> 'pin-id': 13,
> 'parent-device': [{'pin-id': 2, 'pin-state': 'connected'},
> {'pin-id': 3, 'pin-state': 'disconnected'}],
> 'parent-pin': [{'id': 0, 'pin-direction': 'input'},
> {'id': 1, 'pin-direction': 'input'}],
> 'pin-type': 'synce-eth-port'}

You messed up a bit. Should be:
parent-device : id
parent-pin : pin-id

That is basically my point. The fact if the parent is either device or
pin is carried inside the nest by either providing "id" or "pin-id".
So you add redundant info which could be source of mixups - as you
already demonstrated :)


>}]
>
>> One downside of this is you will have 2 arrays of parent objects,
>> one per parent type. Current code neatly groups them into a single array.
>>
>> I guess this is a matter of personal preference, I'm fine either way.
>
>Yeah, could be.

2023-06-19 18:22:36

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

>From: Jiri Pirko <[email protected]>
>Sent: Saturday, June 10, 2023 11:57 AM
>
>Fri, Jun 09, 2023 at 02:18:51PM CEST, [email protected] wrote:
>>Control over clock generation unit is required for further development
>>of Synchronous Ethernet feature. Interface provides ability to obtain
>>current state of a dpll, its sources and outputs which are pins, and
>>allows their configuration.
>>
>>Co-developed-by: Milena Olech <[email protected]>
>>Signed-off-by: Milena Olech <[email protected]>
>>Co-developed-by: Michal Michalik <[email protected]>
>>Signed-off-by: Michal Michalik <[email protected]>
>>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>>---
>> drivers/net/ethernet/intel/Kconfig | 1 +
>> drivers/net/ethernet/intel/ice/Makefile | 3 +-
>> drivers/net/ethernet/intel/ice/ice.h | 4 +
>> drivers/net/ethernet/intel/ice/ice_dpll.c | 2015 +++++++++++++++++++++
>> drivers/net/ethernet/intel/ice/ice_dpll.h | 102 ++
>> drivers/net/ethernet/intel/ice/ice_main.c | 7 +
>> 6 files changed, 2131 insertions(+), 1 deletion(-)
>> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.c
>> create mode 100644 drivers/net/ethernet/intel/ice/ice_dpll.h
>>
>>diff --git a/drivers/net/ethernet/intel/Kconfig
>>b/drivers/net/ethernet/intel/Kconfig
>>index 9bc0a9519899..913dcf928d15 100644
>>--- a/drivers/net/ethernet/intel/Kconfig
>>+++ b/drivers/net/ethernet/intel/Kconfig
>>@@ -284,6 +284,7 @@ config ICE
>> select DIMLIB
>> select NET_DEVLINK
>> select PLDMFW
>>+ select DPLL
>> help
>> This driver supports Intel(R) Ethernet Connection E800 Series of
>> devices. For more information on how to identify your adapter, go
>>diff --git a/drivers/net/ethernet/intel/ice/Makefile
>>b/drivers/net/ethernet/intel/ice/Makefile
>>index 817977e3039d..85d6366d1f5b 100644
>>--- a/drivers/net/ethernet/intel/ice/Makefile
>>+++ b/drivers/net/ethernet/intel/ice/Makefile
>>@@ -34,7 +34,8 @@ ice-y := ice_main.o \
>> ice_lag.o \
>> ice_ethtool.o \
>> ice_repr.o \
>>- ice_tc_lib.o
>>+ ice_tc_lib.o \
>>+ ice_dpll.o
>> ice-$(CONFIG_PCI_IOV) += \
>> ice_sriov.o \
>> ice_virtchnl.o \
>>diff --git a/drivers/net/ethernet/intel/ice/ice.h
>>b/drivers/net/ethernet/intel/ice/ice.h
>>index ae58d7499955..8a110272a799 100644
>>--- a/drivers/net/ethernet/intel/ice/ice.h
>>+++ b/drivers/net/ethernet/intel/ice/ice.h
>>@@ -76,6 +76,7 @@
>> #include "ice_vsi_vlan_ops.h"
>> #include "ice_gnss.h"
>> #include "ice_irq.h"
>>+#include "ice_dpll.h"
>>
>> #define ICE_BAR0 0
>> #define ICE_REQ_DESC_MULTIPLE 32
>>@@ -198,6 +199,7 @@
>> enum ice_feature {
>> ICE_F_DSCP,
>> ICE_F_PTP_EXTTS,
>>+ ICE_F_PHY_RCLK,
>> ICE_F_SMA_CTRL,
>> ICE_F_CGU,
>> ICE_F_GNSS,
>>@@ -506,6 +508,7 @@ enum ice_pf_flags {
>> ICE_FLAG_UNPLUG_AUX_DEV,
>> ICE_FLAG_MTU_CHANGED,
>> ICE_FLAG_GNSS, /* GNSS successfully initialized */
>>+ ICE_FLAG_DPLL, /* SyncE/PTP dplls initialized */
>> ICE_PF_FLAGS_NBITS /* must be last */
>> };
>>
>>@@ -628,6 +631,7 @@ struct ice_pf {
>> #define ICE_VF_AGG_NODE_ID_START 65
>> #define ICE_MAX_VF_AGG_NODES 32
>> struct ice_agg_node vf_agg_node[ICE_MAX_VF_AGG_NODES];
>>+ struct ice_dplls dplls;
>> };
>>
>> struct ice_netdev_priv {
>>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c
>>b/drivers/net/ethernet/intel/ice/ice_dpll.c
>>new file mode 100644
>>index 000000000000..22a69197188a
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
>>@@ -0,0 +1,2015 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/* Copyright (C) 2022, Intel Corporation. */
>>+
>>+#include "ice.h"
>>+#include "ice_lib.h"
>>+#include "ice_trace.h"
>>+#include <linux/dpll.h>
>>+
>>+#define ICE_CGU_STATE_ACQ_ERR_THRESHOLD 50
>>+#define ICE_DPLL_LOCK_TRIES 1000
>>+#define ICE_DPLL_PIN_IDX_INVALID 0xff
>>+#define ICE_DPLL_RCLK_NUM_PER_PF 1
>>+
>>+/**
>>+ * dpll_lock_status - map ice cgu states into dpll's subsystem lock
>>status
>>+ */
>>+static const enum dpll_lock_status
>>+ice_dpll_status[__DPLL_LOCK_STATUS_MAX] = {
>>+ [ICE_CGU_STATE_INVALID] = 0,
>
>It's already 0, drop this pointless assign.
>

Fixed.

>
>>+ [ICE_CGU_STATE_FREERUN] = DPLL_LOCK_STATUS_UNLOCKED,
>>+ [ICE_CGU_STATE_LOCKED] = DPLL_LOCK_STATUS_LOCKED,
>>+ [ICE_CGU_STATE_LOCKED_HO_ACQ] = DPLL_LOCK_STATUS_LOCKED_HO_ACQ,
>>+ [ICE_CGU_STATE_HOLDOVER] = DPLL_LOCK_STATUS_HOLDOVER,
>>+};
>>+
>>+/**
>>+ * ice_dpll_pin_type - enumerate ice pin types
>>+ */
>>+enum ice_dpll_pin_type {
>>+ ICE_DPLL_PIN_INVALID = 0,
>
>Not needed to set 0 here, it is implicit.
>

Fixed.

>
>>+ ICE_DPLL_PIN_TYPE_INPUT,
>>+ ICE_DPLL_PIN_TYPE_OUTPUT,
>>+ ICE_DPLL_PIN_TYPE_RCLK_INPUT,
>>+};
>>+
>>+/**
>>+ * pin_type_name - string names of ice pin types
>>+ */
>>+static const char * const pin_type_name[] = {
>>+ [ICE_DPLL_PIN_TYPE_INPUT] = "input",
>>+ [ICE_DPLL_PIN_TYPE_OUTPUT] = "output",
>>+ [ICE_DPLL_PIN_TYPE_RCLK_INPUT] = "rclk-input",
>>+};
>>+
>>+/**
>>+ * ice_dpll_cb_lock - lock dplls mutex in callback context
>>+ * @pf: private board structure
>>+ *
>>+ * Lock the mutex from the callback operations invoked by dpll subsystem.
>>+ * Prevent dead lock caused by `rmmod ice` when dpll callbacks are under
>>stress
>
>"dead lock", really? Which one? Didn't you want to write "livelock"?
>

As explained, rmmod takes and destroys lock, it can happen that
netlink request/ops hangs on trying to lock when rmmod started deinit.

>If this is livelock prevention, is this something you really see or
>just an assumption? Seems to me unlikely.
>
>Plus, see my note in ice_dpll_init(). If you remove taking the lock from
>ice_dpll_init() and ice_dpll_deinit(), do you still need this? I don't
>think so.
>

I won't remove it from there, as the lock shall serialize the access to ice dpll
data, so it must be held on init until everything is ready for netlink access,
or when deinit takes place, until everything is released.

>
>>+ * tests.
>>+ *
>>+ * Return:
>>+ * 0 - if lock acquired
>>+ * negative - lock not acquired or dpll was deinitialized
>>+ */
>>+static int ice_dpll_cb_lock(struct ice_pf *pf)
>>+{
>>+ int i;
>>+
>>+ for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>+ if (mutex_trylock(&pf->dplls.lock))
>>+ return 0;
>>+ usleep_range(100, 150);
>>+ if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>
>How exactly could this happen? I don't think it can. Drop it.
>

Asynchronously called deinit, and kworker tries to update at the same time.

>
>>+ return -EFAULT;
>>+ }
>>+
>>+ return -EBUSY;
>>+}
>>+
>>+/**
>>+ * ice_dpll_cb_unlock - unlock dplls mutex in callback context
>>+ * @pf: private board structure
>>+ *
>>+ * Unlock the mutex from the callback operations invoked by dpll
>>subsystem.
>>+ */
>>+static void ice_dpll_cb_unlock(struct ice_pf *pf)
>>+{
>>+ mutex_unlock(&pf->dplls.lock);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_freq_set - set pin's frequency
>>+ * @pf: private board structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of pin being configured
>>+ * @freq: frequency to be set
>>+ *
>>+ * Set requested frequency on a pin.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error on AQ or wrong pin type given
>>+ */
>>+static int
>>+ice_dpll_pin_freq_set(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>+ enum ice_dpll_pin_type pin_type, const u32 freq)
>>+{
>>+ int ret = -EINVAL;
>>+ u8 flags;
>>+
>>+ switch (pin_type) {
>>+ case ICE_DPLL_PIN_TYPE_INPUT:
>>+ flags = ICE_AQC_SET_CGU_IN_CFG_FLG1_UPDATE_FREQ;
>>+ ret = ice_aq_set_input_pin_cfg(&pf->hw, pin->idx, flags,
>>+ pin->flags[0], freq, 0);
>>+ break;
>>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+ flags = ICE_AQC_SET_CGU_OUT_CFG_UPDATE_FREQ;
>>+ ret = ice_aq_set_output_pin_cfg(&pf->hw, pin->idx, flags,
>>+ 0, freq, 0);
>>+ break;
>>+ default:
>>+ break;
>>+ }
>>+ if (ret) {
>>+ dev_err(ice_pf_to_dev(pf),
>>+ "err:%d %s failed to set pin freq:%u on pin:%u\n",
>>+ ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>>+ freq, pin->idx);
>
>Why you need dev_err here? Why can't this be rather put to the extack
>message? Much better. Try to avoid polluting dmesg.
>

Fixed.

>
>>+ return ret;
>>+ }
>>+ pin->freq = freq;
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_frequency_set - wrapper for pin callback for set frequency
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: pointer to dpll
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @frequency: frequency to be set
>>+ * @extack: error reporting
>>+ * @pin_type: type of pin being configured
>>+ *
>>+ * Wraps internal set frequency command on a pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't set in hw
>>+ */
>>+static int
>>+ice_dpll_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ const u32 frequency,
>>+ struct netlink_ext_ack *extack,
>>+ enum ice_dpll_pin_type pin_type)
>>+{
>>+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
>
>Rather do:
> struct ice_dpll *dpll = dpll_priv;
> struct ice_pf *pf = dpll->pf;
>And avoid the cast. Easier to read as well.
>Same on other places.
>

Fixed.

>
>>+ struct ice_dpll_pin *p = pin_priv;
>>+ int ret = -EINVAL;
>>+
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret)
>>+ return ret;
>>+ ret = ice_dpll_pin_freq_set(pf, p, pin_type, frequency);
>>+ ice_dpll_cb_unlock(pf);
>>+ if (ret)
>>+ NL_SET_ERR_MSG(extack, "frequency was not set");
>
>Yeah, that is stating the obvious as the use got error value, but tell
>him some other details. Fill this in ice_dpll_pin_freq_set() by the
>message you have there for dev_err() instead.
>

Fixed.

>
>
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_input_frequency_set - input pin callback for set frequency
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: pointer to dpll
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @frequency: frequency to be set
>>+ * @extack: error reporting
>>+ *
>>+ * Wraps internal set frequency command on a pin.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't set in hw
>>+ */
>>+static int
>>+ice_dpll_input_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ u64 frequency, struct netlink_ext_ack *extack)
>>+{
>>+ return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>frequency,
>>+ extack, ICE_DPLL_PIN_TYPE_INPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_frequency_set - output pin callback for set frequency
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: pointer to dpll
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @frequency: frequency to be set
>>+ * @extack: error reporting
>>+ *
>>+ * Wraps internal set frequency command on a pin.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't set in hw
>>+ */
>>+static int
>>+ice_dpll_output_frequency_set(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ u64 frequency, struct netlink_ext_ack *extack)
>>+{
>>+ return ice_dpll_frequency_set(pin, pin_priv, dpll, dpll_priv,
>>frequency,
>>+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_frequency_get - wrapper for pin callback for get frequency
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: pointer to dpll
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @frequency: on success holds pin's frequency
>>+ * @extack: error reporting
>>+ * @pin_type: type of pin being configured
>>+ *
>>+ * Wraps internal get frequency command of a pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't get from hw
>>+ */
>>+static int
>>+ice_dpll_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ u64 *frequency, struct netlink_ext_ack *extack,
>>+ enum ice_dpll_pin_type pin_type)
>>+{
>>+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
>>+ struct ice_dpll_pin *p = pin_priv;
>>+ int ret = -EINVAL;
>>+
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret)
>>+ return ret;
>>+ *frequency = p->freq;
>>+ ice_dpll_cb_unlock(pf);
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_input_frequency_get - input pin callback for get frequency
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: pointer to dpll
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @frequency: on success holds pin's frequency
>>+ * @extack: error reporting
>>+ *
>>+ * Wraps internal get frequency command of a input pin.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't get from hw
>>+ */
>>+static int
>>+ice_dpll_input_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ u64 *frequency, struct netlink_ext_ack *extack)
>>+{
>>+ return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>frequency,
>>+ extack, ICE_DPLL_PIN_TYPE_INPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_frequency_get - output pin callback for get frequency
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: pointer to dpll
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @frequency: on success holds pin's frequency
>>+ * @extack: error reporting
>>+ *
>>+ * Wraps internal get frequency command of a pin.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - error pin not found or couldn't get from hw
>>+ */
>>+static int
>>+ice_dpll_output_frequency_get(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ u64 *frequency, struct netlink_ext_ack *extack)
>>+{
>>+ return ice_dpll_frequency_get(pin, pin_priv, dpll, dpll_priv,
>>frequency,
>>+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_enable - enable a pin on dplls
>>+ * @hw: board private hw structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of pin being enabled
>>+ *
>>+ * Enable a pin on both dplls. Store current state in pin->flags.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_enable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+ enum ice_dpll_pin_type pin_type)
>>+{
>>+ int ret = -EINVAL;
>>+ u8 flags = 0;
>>+
>>+ switch (pin_type) {
>>+ case ICE_DPLL_PIN_TYPE_INPUT:
>>+ if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>+ flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>+ flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_INPUT_EN;
>>+ ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+ break;
>>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+ if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>+ flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>+ flags |= ICE_AQC_SET_CGU_OUT_CFG_OUT_EN;
>>+ ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+ break;
>>+ default:
>>+ break;
>>+ }
>>+ if (ret)
>>+ dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+ "err:%d %s failed to enable %s pin:%u\n",
>>+ ret, ice_aq_str(hw->adminq.sq_last_status),
>>+ pin_type_name[pin_type], pin->idx);
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_disable - disable a pin on dplls
>>+ * @hw: board private hw structure
>>+ * @pin: pointer to a pin
>>+ * @pin_type: type of pin being disabled
>>+ *
>>+ * Disable a pin on both dplls. Store current state in pin->flags.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_disable(struct ice_hw *hw, struct ice_dpll_pin *pin,
>>+ enum ice_dpll_pin_type pin_type)
>>+{
>>+ int ret = -EINVAL;
>>+ u8 flags = 0;
>>+
>>+ switch (pin_type) {
>>+ case ICE_DPLL_PIN_TYPE_INPUT:
>>+ if (pin->flags[0] & ICE_AQC_GET_CGU_IN_CFG_FLG2_ESYNC_EN)
>>+ flags |= ICE_AQC_SET_CGU_IN_CFG_FLG2_ESYNC_EN;
>>+ ret = ice_aq_set_input_pin_cfg(hw, pin->idx, 0, flags, 0, 0);
>>+ break;
>>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+ if (pin->flags[0] & ICE_AQC_GET_CGU_OUT_CFG_ESYNC_EN)
>>+ flags |= ICE_AQC_SET_CGU_OUT_CFG_ESYNC_EN;
>>+ ret = ice_aq_set_output_pin_cfg(hw, pin->idx, flags, 0, 0, 0);
>>+ break;
>>+ default:
>>+ break;
>>+ }
>>+ if (ret)
>>+ dev_err(ice_pf_to_dev((struct ice_pf *)(hw->back)),
>>+ "err:%d %s failed to disable %s pin:%u\n",
>>+ ret, ice_aq_str(hw->adminq.sq_last_status),
>>+ pin_type_name[pin_type], pin->idx);
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_state_update - update pin's state
>>+ * @hw: private board struct
>>+ * @pin: structure with pin attributes to be updated
>>+ * @pin_type: type of pin being updated
>>+ *
>>+ * Determine pin current state and frequency, then update struct
>>+ * holding the pin info. For input pin states are separated for each
>>+ * dpll, for rclk pins states are separated for each parent.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - OK
>>+ * * negative - error
>>+ */
>>+int
>>+ice_dpll_pin_state_update(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>+ enum ice_dpll_pin_type pin_type)
>>+{
>>+ int ret = -EINVAL;
>>+
>>+ switch (pin_type) {
>>+ case ICE_DPLL_PIN_TYPE_INPUT:
>>+ ret = ice_aq_get_input_pin_cfg(&pf->hw, pin->idx, NULL, NULL,
>>+ NULL, &pin->flags[0],
>>+ &pin->freq, NULL);
>>+ if (ICE_AQC_GET_CGU_IN_CFG_FLG2_INPUT_EN & pin->flags[0]) {
>>+ if (pin->pin) {
>>+ pin->state[pf->dplls.eec.dpll_idx] =
>>+ pin->pin == pf->dplls.eec.active_input ?
>>+ DPLL_PIN_STATE_CONNECTED :
>>+ DPLL_PIN_STATE_SELECTABLE;
>>+ pin->state[pf->dplls.pps.dpll_idx] =
>>+ pin->pin == pf->dplls.pps.active_input ?
>>+ DPLL_PIN_STATE_CONNECTED :
>>+ DPLL_PIN_STATE_SELECTABLE;
>>+ } else {
>>+ pin->state[pf->dplls.eec.dpll_idx] =
>>+ DPLL_PIN_STATE_SELECTABLE;
>>+ pin->state[pf->dplls.pps.dpll_idx] =
>>+ DPLL_PIN_STATE_SELECTABLE;
>>+ }
>>+ } else {
>>+ pin->state[pf->dplls.eec.dpll_idx] =
>>+ DPLL_PIN_STATE_DISCONNECTED;
>>+ pin->state[pf->dplls.pps.dpll_idx] =
>>+ DPLL_PIN_STATE_DISCONNECTED;
>>+ }
>>+ break;
>>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+ ret = ice_aq_get_output_pin_cfg(&pf->hw, pin->idx,
>>+ &pin->flags[0], NULL,
>>+ &pin->freq, NULL);
>>+ if (ICE_AQC_SET_CGU_OUT_CFG_OUT_EN & pin->flags[0])
>>+ pin->state[0] = DPLL_PIN_STATE_CONNECTED;
>>+ else
>>+ pin->state[0] = DPLL_PIN_STATE_DISCONNECTED;
>>+ break;
>>+ case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>>+ u8 parent, port_num = ICE_AQC_SET_PHY_REC_CLK_OUT_CURR_PORT;
>>+
>>+ for (parent = 0; parent < pf->dplls.rclk.num_parents;
>>+ parent++) {
>>+ ret = ice_aq_get_phy_rec_clk_out(&pf->hw, parent,
>>+ &port_num,
>>+ &pin->flags[parent],
>>+ &pin->freq);
>>+ if (ret)
>>+ return ret;
>>+ if (ICE_AQC_GET_PHY_REC_CLK_OUT_OUT_EN &
>>+ pin->flags[parent])
>>+ pin->state[parent] = DPLL_PIN_STATE_CONNECTED;
>>+ else
>>+ pin->state[parent] =
>>+ DPLL_PIN_STATE_DISCONNECTED;
>>+ }
>>+ break;
>>+ default:
>>+ break;
>>+ }
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_find_dpll - find ice_dpll on a pf
>>+ * @pf: private board structure
>>+ * @dpll: kernel's dpll_device pointer to be searched
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * pointer if ice_dpll with given device dpll pointer is found
>>+ * * NULL if not found
>>+ */
>>+static struct ice_dpll
>>+*ice_find_dpll(struct ice_pf *pf, const struct dpll_device *dpll)
>>+{
>>+ if (!pf || !dpll)
>>+ return NULL;
>>+
>>+ return dpll == pf->dplls.eec.dpll ? &pf->dplls.eec :
>>+ dpll == pf->dplls.pps.dpll ? &pf->dplls.pps : NULL;
>>+}
>>+
>>+/**
>>+ * ice_dpll_hw_input_prio_set - set input priority value in hardware
>>+ * @pf: board private structure
>>+ * @dpll: ice dpll pointer
>>+ * @pin: ice pin pointer
>>+ * @prio: priority value being set on a dpll
>>+ *
>>+ * Internal wrapper for setting the priority in the hardware.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_hw_input_prio_set(struct ice_pf *pf, struct ice_dpll *dpll,
>>+ struct ice_dpll_pin *pin, const u32 prio)
>>+{
>>+ int ret;
>>+
>>+ ret = ice_aq_set_cgu_ref_prio(&pf->hw, dpll->dpll_idx, pin->idx,
>>+ (u8)prio);
>>+ if (ret)
>>+ dev_err(ice_pf_to_dev(pf),
>>+ "err:%d %s failed to set pin prio:%u on pin:%u\n",
>>+ ret, ice_aq_str(pf->hw.adminq.sq_last_status),
>>+ prio, pin->idx);
>>+ else
>>+ dpll->input_prio[pin->idx] = prio;
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_lock_status_get - get dpll lock status callback
>>+ * @dpll: registered dpll pointer
>>+ * @status: on success holds dpll's lock status
>>+ *
>>+ * Dpll subsystem callback, provides dpll's lock status.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_lock_status_get(const struct dpll_device *dpll, void
>>*priv,
>>+ enum dpll_lock_status *status,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ struct ice_dpll *d = priv;
>>+ struct ice_pf *pf = d->pf;
>>+ int ret = -EINVAL;
>>+
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret)
>>+ return ret;
>>+ *status = ice_dpll_status[d->dpll_state];
>>+ ice_dpll_cb_unlock(pf);
>>+ dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pf:%p, ret:%d\n", __func__,
>>+ dpll, pf, ret);
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_mode_get - get dpll's working mode
>>+ * @dpll: registered dpll pointer
>>+ * @priv: private data pointer passed on dpll registration
>>+ * @mode: on success holds current working mode of dpll
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Provides working mode of dpll.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>>+ enum dpll_mode *mode,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ *mode = DPLL_MODE_AUTOMATIC;
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_mode_get - check if dpll's working mode is supported
>>+ * @dpll: registered dpll pointer
>>+ * @priv: private data pointer passed on dpll registration
>>+ * @mode: mode to be checked for support
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Provides information if working mode is
>>supported
>>+ * by dpll.
>>+ *
>>+ * Return:
>>+ * * true - mode is supported
>>+ * * false - mode is not supported
>>+ */
>>+static bool ice_dpll_mode_supported(const struct dpll_device *dpll, void
>>*priv,
>>+ enum dpll_mode mode,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ if (mode == DPLL_MODE_AUTOMATIC)
>>+ return true;
>>+
>>+ return false;
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_state_set - set pin's state on dpll
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: registered dpll pointer
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @state: state of pin to be set
>>+ * @extack: error reporting
>>+ * @pin_type: type of a pin
>>+ *
>>+ * Set pin state on a pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - OK or no change required
>>+ * * negative - error
>>+ */
>>+static int
>>+ice_dpll_pin_state_set(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ bool enable, struct netlink_ext_ack *extack,
>>+ enum ice_dpll_pin_type pin_type)
>>+{
>>+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
>>+ struct ice_dpll_pin *p = pin_priv;
>>+ int ret = -EINVAL;
>>+
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret)
>>+ return ret;
>>+ if (enable)
>>+ ret = ice_dpll_pin_enable(&pf->hw, p, pin_type);
>>+ else
>>+ ret = ice_dpll_pin_disable(&pf->hw, p, pin_type);
>>+ if (!ret)
>>+ ret = ice_dpll_pin_state_update(pf, p, pin_type);
>>+ ice_dpll_cb_unlock(pf);
>>+ if (ret)
>>+ dev_err(ice_pf_to_dev(pf),
>
>You have another dev_err inside ice_dpll_pin_enable(). Please avoid
>redundancies like that.
>

Fixed.

>
>>+ "%s: dpll:%p, pin:%p, p:%p pf:%p enable:%d ret:%d\n",
>>+ __func__, dpll, pin, p, pf, enable, ret);
>
>Quite cryptic. Make it more readable and use extack to pass the error
>message to the user.
>
>Actually, I don't want to repeat myself, but could you please do this
>conversion in the rest of the ops as well. I mean, if you have extack,
>just fill it up properly so the user knows what is wrong. Avoid the
>dev_errs() in that cases.
>

Fixed.

>
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_state_set - enable/disable output pin on dpll device
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: dpll being configured
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @state: state of pin to be set
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Set given state on output type pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int
>>+ice_dpll_output_state_set(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_pin_state state,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
>
>Just:
> bool enable = state == DPLL_PIN_STATE_CONNECTED;
>

Fixed.

>
>>+
>>+ return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
>>+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_input_state_set - enable/disable input pin on dpll levice
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: dpll being configured
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @state: state of pin to be set
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Enables given mode on input type pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - successfully enabled mode
>>+ * * negative - failed to enable mode
>>+ */
>>+static int
>>+ice_dpll_input_state_set(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_pin_state state,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ bool enable = state == DPLL_PIN_STATE_SELECTABLE ? true : false;
>
>Just:
> bool enable = state == DPLL_PIN_STATE_SELECTABLE;

Fixed.

>
>>+
>>+ return ice_dpll_pin_state_set(pin, pin_priv, dpll, dpll_priv, enable,
>>+ extack, ICE_DPLL_PIN_TYPE_INPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_pin_state_get - set pin's state on dpll
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: registered dpll pointer
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @state: on success holds state of the pin
>>+ * @extack: error reporting
>>+ * @pin_type: type of questioned pin
>>+ *
>>+ * Determine pin state set it on a pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failed to get state
>>+ */
>>+static int
>>+ice_dpll_pin_state_get(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_pin_state *state,
>>+ struct netlink_ext_ack *extack,
>>+ enum ice_dpll_pin_type pin_type)
>>+{
>>+ struct ice_pf *pf = ((struct ice_dpll *)dpll_priv)->pf;
>>+ struct ice_dpll_pin *p = pin_priv;
>>+ struct ice_dpll *d;
>>+ int ret = -EINVAL;
>
>Pointless init, drop it.
>

Fixed.

>>+
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret)
>>+ return ret;
>>+ d = ice_find_dpll(pf, dpll);
>
>This is a leftover. please remove, use dpll_priv and remove
>ice_find_dpll() entirely.
>

Fixed.

>
>>+ if (!d)
>>+ goto unlock;
>>+ ret = ice_dpll_pin_state_update(pf, p, pin_type);
>>+ if (ret)
>>+ goto unlock;
>>+ if (pin_type == ICE_DPLL_PIN_TYPE_INPUT)
>>+ *state = p->state[d->dpll_idx];
>>+ else if (pin_type == ICE_DPLL_PIN_TYPE_OUTPUT)
>>+ *state = p->state[0];
>>+ ret = 0;
>>+unlock:
>>+ ice_dpll_cb_unlock(pf);
>>+ if (ret)
>>+ dev_err(ice_pf_to_dev(pf),
>>+ "%s: dpll:%p, pin:%p, pf:%p state: %d ret:%d\n",
>>+ __func__, dpll, pin, pf, *state, ret);
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_state_get - get output pin state on dpll device
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: registered dpll pointer
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @state: on success holds state of the pin
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Check state of a pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failed to get state
>>+ */
>>+static int
>>+ice_dpll_output_state_get(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_pin_state *state,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
>>+ extack, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_input_state_get - get input pin state on dpll device
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: registered dpll pointer
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @state: on success holds state of the pin
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Check state of a input pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failed to get state
>>+ */
>>+static int
>>+ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_pin_state *state,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ return ice_dpll_pin_state_get(pin, pin_priv, dpll, dpll_priv, state,
>>+ extack, ICE_DPLL_PIN_TYPE_INPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_input_prio_get - get dpll's input prio
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: registered dpll pointer
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @prio: on success - returns input priority on dpll
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for getting priority of a input pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_input_prio_get(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ u32 *prio, struct netlink_ext_ack *extack)
>>+{
>>+ struct ice_dpll_pin *p = pin_priv;
>>+ struct ice_dpll *d = dpll_priv;
>>+ struct ice_pf *pf = d->pf;
>>+ int ret = -EINVAL;
>>+
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret)
>>+ return ret;
>>+ *prio = d->input_prio[p->idx];
>>+ ice_dpll_cb_unlock(pf);
>>+ dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>>+ __func__, dpll, pin, pf, ret);
>
>What exactly is this good for?
>

Removed.

>
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_input_prio_set - set dpll input prio
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: registered dpll pointer
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @prio: input priority to be set on dpll
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for setting priority of a input pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_input_prio_set(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ u32 prio, struct netlink_ext_ack *extack)
>>+{
>>+ struct ice_dpll_pin *p = pin_priv;
>>+ struct ice_dpll *d = dpll_priv;
>>+ struct ice_pf *pf = d->pf;
>>+ int ret = -EINVAL;
>>+
>>+ if (prio > ICE_DPLL_PRIO_MAX) {
>>+ NL_SET_ERR_MSG_FMT(extack, "prio out of supported range 0-%d",
>>+ ICE_DPLL_PRIO_MAX);
>>+ return ret;
>>+ }
>>+
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret)
>>+ return ret;
>>+ ret = ice_dpll_hw_input_prio_set(pf, d, p, prio);
>>+ if (ret)
>>+ NL_SET_ERR_MSG_FMT(extack, "unable to set prio: %u", prio);
>>+ ice_dpll_cb_unlock(pf);
>>+ dev_dbg(ice_pf_to_dev(pf), "%s: dpll:%p, pin:%p, pf:%p ret:%d\n",
>>+ __func__, dpll, pin, pf, ret);
>
>Same here.
>

Removed.

>
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_input_direction - callback for get input pin direction
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: registered dpll pointer
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @direction: holds input pin direction
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for getting direction of a input pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ */
>>+static int
>>+ice_dpll_input_direction(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_pin_direction *direction,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ *direction = DPLL_PIN_DIRECTION_INPUT;
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_output_direction - callback for get output pin direction
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @dpll: registered dpll pointer
>>+ * @dpll_priv: private data pointer passed on dpll registration
>>+ * @direction: holds output pin direction
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback. Handler for getting direction of an output
>>pin.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ */
>>+static int
>>+ice_dpll_output_direction(const struct dpll_pin *pin, void *pin_priv,
>>+ const struct dpll_device *dpll, void *dpll_priv,
>>+ enum dpll_pin_direction *direction,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ *direction = DPLL_PIN_DIRECTION_OUTPUT;
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_state_on_pin_set - set a state on rclk pin
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @parent_pin: pin parent pointer
>>+ * @state: state to be set on pin
>>+ * @extack: error reporting
>>+ *
>>+ * Dpll subsystem callback, set a state of a rclk pin on a parent pin
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void
>>*pin_priv,
>>+ const struct dpll_pin *parent_pin,
>>+ void *parent_pin_priv,
>>+ enum dpll_pin_state state,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ bool enable = state == DPLL_PIN_STATE_CONNECTED ? true : false;
>
>You have an odd pattern of this assing. Please avoid "? true : false"
>in the whole patch.
>

Fixed.

>
>>+ struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
>>+ struct ice_pf *pf = p->pf;
>>+ int ret = -EINVAL;
>
>Also you have an odd patter of initializing "ret" variable and assign
>value to it 2 lines below. Please avoid it.
>

Fixed.

>
>>+ u32 hw_idx;
>>+
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret)
>>+ return ret;
>>+ hw_idx = parent->idx - pf->dplls.base_rclk_idx;
>>+ if (hw_idx >= pf->dplls.num_inputs)
>>+ goto unlock;
>>+
>>+ if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) ||
>>+ (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) {
>>+ ret = -EINVAL;
>
>Extack.
>

Fixed.

>
>>+ goto unlock;
>>+ }
>>+ ret = ice_aq_set_phy_rec_clk_out(&pf->hw, hw_idx, enable,
>>+ &p->freq);
>>+unlock:
>>+ ice_dpll_cb_unlock(pf);
>>+ dev_dbg(ice_pf_to_dev(pf),
>>+ "%s: parent:%p, pin:%p, pf:%p hw_idx:%u enable:%d ret:%d\n",
>>+ __func__, parent_pin, pin, pf, hw_idx, enable, ret);
>
>What is this good for? Again, lots of debug messages like this in the
>whole patch. Do you need it, for what? If not, remove please.
>

Removed most of them.

>
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_rclk_state_on_pin_get - get a state of rclk pin
>>+ * @pin: pointer to a pin
>>+ * @pin_priv: private data pointer passed on pin registration
>>+ * @parent_pin: pin parent pointer
>>+ * @state: on success holds pin state on parent pin
>>+ * @extack: error reporting
>>+ *
>>+ * dpll subsystem callback, get a state of a recovered clock pin.
>>+ *
>>+ * Context: Acquires pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - failure
>>+ */
>>+static int
>>+ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void
>>*pin_priv,
>>+ const struct dpll_pin *parent_pin,
>>+ void *parent_pin_priv,
>>+ enum dpll_pin_state *state,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ struct ice_dpll_pin *p = pin_priv, *parent = parent_pin_priv;
>>+ struct ice_pf *pf = p->pf;
>>+ int ret = -EFAULT;
>>+ u32 hw_idx;
>>+
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret)
>>+ return ret;
>>+ hw_idx = parent->idx - pf->dplls.base_rclk_idx;
>>+ if (hw_idx >= pf->dplls.num_inputs)
>>+ goto unlock;
>>+
>>+ ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>>+ if (ret)
>>+ goto unlock;
>>+
>>+ *state = p->state[hw_idx];
>>+ ret = 0;
>>+unlock:
>>+ ice_dpll_cb_unlock(pf);
>>+ dev_dbg(ice_pf_to_dev(pf),
>>+ "%s: parent:%p, pin:%p, pf:%p hw_idx:%u state:%u ret:%d\n",
>>+ __func__, parent_pin, pin, pf, hw_idx, *state, ret);
>>+
>>+ return ret;
>>+}
>>+
>>+static const struct dpll_pin_ops ice_dpll_rclk_ops = {
>>+ .state_on_pin_set = ice_dpll_rclk_state_on_pin_set,
>>+ .state_on_pin_get = ice_dpll_rclk_state_on_pin_get,
>>+ .direction_get = ice_dpll_input_direction,
>>+};
>>+
>>+static const struct dpll_pin_ops ice_dpll_input_ops = {
>>+ .frequency_get = ice_dpll_input_frequency_get,
>>+ .frequency_set = ice_dpll_input_frequency_set,
>>+ .state_on_dpll_get = ice_dpll_input_state_get,
>>+ .state_on_dpll_set = ice_dpll_input_state_set,
>>+ .prio_get = ice_dpll_input_prio_get,
>>+ .prio_set = ice_dpll_input_prio_set,
>>+ .direction_get = ice_dpll_input_direction,
>>+};
>>+
>>+static const struct dpll_pin_ops ice_dpll_output_ops = {
>>+ .frequency_get = ice_dpll_output_frequency_get,
>>+ .frequency_set = ice_dpll_output_frequency_set,
>>+ .state_on_dpll_get = ice_dpll_output_state_get,
>>+ .state_on_dpll_set = ice_dpll_output_state_set,
>>+ .direction_get = ice_dpll_output_direction,
>>+};
>>+
>>+static const struct dpll_device_ops ice_dpll_ops = {
>>+ .lock_status_get = ice_dpll_lock_status_get,
>>+ .mode_get = ice_dpll_mode_get,
>>+ .mode_supported = ice_dpll_mode_supported,
>>+};
>>+
>>+/**
>>+ * ice_dpll_deinit_info - release memory allocated for pins info
>>+ * @pf: board private structure
>>+ *
>>+ * Release memory allocated for pins by ice_dpll_init_info function.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ */
>>+static void ice_dpll_deinit_info(struct ice_pf *pf)
>>+{
>>+ kfree(pf->dplls.inputs);
>>+ pf->dplls.inputs = NULL;
>>+ kfree(pf->dplls.outputs);
>>+ pf->dplls.outputs = NULL;
>>+ kfree(pf->dplls.eec.input_prio);
>>+ pf->dplls.eec.input_prio = NULL;
>>+ kfree(pf->dplls.pps.input_prio);
>>+ pf->dplls.pps.input_prio = NULL;
>
>Why you NULL the pointers? Do you use them later on? If not, please
>drop it, it's confusing.
>

Fixed.

>
>
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_rclk_pin - release rclk pin resources
>>+ * @pf: board private structure
>>+ *
>>+ * Deregister rclk pin from parent pins and release resources in dpll
>>subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ */
>>+static void ice_dpll_deinit_rclk_pin(struct ice_pf *pf)
>>+{
>>+ struct ice_dpll_pin *rclk = &pf->dplls.rclk;
>>+ struct ice_vsi *vsi = ice_get_main_vsi(pf);
>>+ struct dpll_pin *parent;
>>+ int i;
>>+
>>+ for (i = 0; i < rclk->num_parents; i++) {
>>+ parent = pf->dplls.inputs[rclk->parent_idx[i]].pin;
>>+ if (!parent)
>>+ continue;
>>+ if (!IS_ERR_OR_NULL(rclk->pin))
>>+ dpll_pin_on_pin_unregister(parent, rclk->pin,
>>+ &ice_dpll_rclk_ops, rclk);
>>+ }
>>+ if (WARN_ON_ONCE(!vsi || !vsi->netdev))
>>+ return;
>>+ netdev_dpll_pin_clear(vsi->netdev);
>>+ dpll_pin_put(rclk->pin);
>>+ rclk->pin = NULL;
>
>Why you need to NULL it? If don't, please drop to avoid confusions.
>Same goes to the rest of the NULLing occurances in deinit() functions.
>

Fixed.

>
>>+}
>>+
>>+/**
>>+ * ice_dpll_unregister_pins - unregister pins from a dpll
>>+ * @dpll: dpll device pointer
>>+ * @pins: pointer to pins array
>>+ * @ops: callback ops registered with the pins
>>+ * @count: number of pins
>>+ *
>>+ * Unregister pins of a given array of pins from given dpll device
>>registered in
>>+ * dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ */
>>+static void
>>+ice_dpll_unregister_pins(struct dpll_device *dpll, struct ice_dpll_pin
>>*pins,
>>+ const struct dpll_pin_ops *ops, int count)
>>+{
>>+ struct ice_dpll_pin *p;
>>+ int i;
>>+
>>+ for (i = 0; i < count; i++) {
>>+ p = &pins[i];
>>+ if (p && !IS_ERR_OR_NULL(p->pin))
>
>How can the p be NULL? I don't think it can.
>
>Please void the whole check here.
>Do the error path rollback in init() function properly.
>

Fixed.

>
>>+ dpll_pin_unregister(dpll, p->pin, ops, p);
>>+ }
>>+}
>>+
>>+/**
>>+ * ice_dpll_release_pins - release pins resources from dpll subsystem
>>+ * @pf: board private structure
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ *
>>+ * Release resources of given pins array in the dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ */
>>+static void ice_dpll_release_pins(struct ice_dpll_pin *pins, int count)
>>+{
>>+ struct ice_dpll_pin *p;
>>+ int i;
>>+
>>+ for (i = 0; i < count; i++) {
>>+ p = &pins[i];
>>+ if (p && !IS_ERR_OR_NULL(p->pin)) {
>
>Same here.
>
>Please void the whole check here.
>Do the error path rollback in init() function properly.
>

Fixed.

>
>>+ dpll_pin_put(p->pin);
>>+ p->pin = NULL;
>>+ }
>>+ }
>>+}
>>+
>>+/**
>>+ * ice_dpll_get_pins - get pins from dpll subsystem
>>+ * @pf: board private structure
>>+ * @pins: pointer to pins array
>>+ * @start_idx: get starts from this pin idx value
>>+ * @count: number of pins
>>+ * @clock_id: clock_id of dpll device
>>+ *
>>+ * Get pins - allocate - in dpll subsystem, store them in pin field of
>>given
>>+ * pins array.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - allocation failure reason
>>+ */
>>+static int
>>+ice_dpll_get_pins(struct ice_pf *pf, struct ice_dpll_pin *pins,
>>+ int start_idx, int count, u64 clock_id)
>>+{
>>+ int i, ret;
>>+
>>+ for (i = 0; i < count; i++) {
>>+ pins[i].pin = dpll_pin_get(clock_id, i + start_idx,
>>THIS_MODULE,
>>+ &pins[i].prop);
>>+ if (IS_ERR(pins[i].pin)) {
>>+ ret = PTR_ERR(pins[i].pin);
>>+ goto release_pins;
>>+ }
>>+ }
>>+
>>+ return 0;
>>+
>>+release_pins:
>>+ ice_dpll_release_pins(pins, i);
>
>
>Please call dpll_pin_put() in a loop here.
>

Fixed.

>
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_register_pins - register pins with a dpll
>>+ * @dpll: dpll pointer to register pins with
>>+ * @pins: pointer to pins array
>>+ * @ops: callback ops registered with the pins
>>+ * @count: number of pins
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Register pins of a given array with given dpll in dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - registration failure reason
>>+ */
>>+static int
>>+ice_dpll_register_pins(struct dpll_device *dpll, struct ice_dpll_pin
>>*pins,
>>+ const struct dpll_pin_ops *ops, int count)
>>+{
>>+ int ret, i;
>>+
>>+ for (i = 0; i < count; i++) {
>>+ ret = dpll_pin_register(dpll, pins[i].pin, ops, &pins[i]);
>>+ if (ret)
>>+ goto unregister_pins;
>>+ }
>>+
>>+ return 0;
>>+
>>+unregister_pins:
>>+ ice_dpll_unregister_pins(dpll, pins, ops, i);
>
>Please call dpll_pin_unregister() in a loop here.
>

Fixed.

>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_direct_pins - initialize direct pins
>>+ * @dpll: dpll pointer to register pins with
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @pins: pointer to pins array
>>+ * @start_idx: on which index shall allocation start in dpll subsystem
>>+ * @count: number of pins
>>+ * @ops: callback ops registered with the pins
>>+ * @first: dpll device pointer
>>+ * @second: dpll device pointer
>>+ *
>>+ * Allocate directly connected pins of a given array in dpll subsystem.
>>+ * If cgu is owned register allocated pins with given dplls.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - registration failure reason
>>+ */
>>+static int
>>+ice_dpll_init_direct_pins(struct ice_pf *pf, bool cgu,
>>+ struct ice_dpll_pin *pins, int start_idx, int count,
>>+ const struct dpll_pin_ops *ops,
>>+ struct dpll_device *first, struct dpll_device *second)
>>+{
>>+ int ret;
>>+
>>+ ret = ice_dpll_get_pins(pf, pins, start_idx, count, pf-
>>dplls.clock_id);
>>+ if (ret)
>>+ return ret;
>>+ if (cgu) {
>>+ ret = ice_dpll_register_pins(first, pins, ops, count);
>>+ if (ret)
>>+ goto release_pins;
>>+ ret = ice_dpll_register_pins(second, pins, ops, count);
>>+ if (ret)
>>+ goto unregister_first;
>>+ }
>>+
>>+ return 0;
>>+
>>+unregister_first:
>>+ ice_dpll_unregister_pins(first, pins, ops, count);
>>+release_pins:
>>+ ice_dpll_release_pins(pins, count);
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_direct_pins - deinitialize direct pins
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @pins: pointer to pins array
>>+ * @count: number of pins
>>+ * @ops: callback ops registered with the pins
>>+ * @first: dpll device pointer
>>+ * @second: dpll device pointer
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * If cgu is owned unregister pins from given dplls.
>>+ * Release pins resources to the dpll subsystem.
>>+ */
>>+static void
>>+ice_dpll_deinit_direct_pins(bool cgu, struct ice_dpll_pin *pins, int count,
>>+ const struct dpll_pin_ops *ops,
>>+ struct dpll_device *first,
>>+ struct dpll_device *second)
>>+{
>>+ if (cgu) {
>>+ ice_dpll_unregister_pins(first, pins, ops, count);
>>+ ice_dpll_unregister_pins(second, pins, ops, count);
>>+ }
>>+ ice_dpll_release_pins(pins, count);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_rclk_pins - initialize recovered clock pin
>>+ * @dpll: dpll pointer to register pins with
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @pins: pointer to pins array
>>+ * @start_idx: on which index shall allocation start in dpll subsystem
>>+ * @count: number of pins
>>+ * @ops: callback ops registered with the pins
>>+ *
>>+ * Allocate resource for recovered clock pin in dpll subsystem. Register the
>>+ * pin with the parents it has in the info. Register pin with the pf's main
>>vsi
>>+ * netdev.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - registration failure reason
>>+ */
>>+static int
>>+ice_dpll_init_rclk_pins(struct ice_pf *pf, struct ice_dpll_pin *pin,
>>+ int start_idx, const struct dpll_pin_ops *ops)
>
>It is a good practise to have the init/cleanup functions of the same
>thing one behind the another. Could you please reorder the code to
>achieve that?
>

Sure, fixed.

>
>>+{
>>+ struct ice_vsi *vsi = ice_get_main_vsi(pf);
>>+ struct dpll_pin *parent;
>>+ int ret, i;
>>+
>>+ ret = ice_dpll_get_pins(pf, pin, start_idx, ICE_DPLL_RCLK_NUM_PER_PF,
>>+ pf->dplls.clock_id);
>>+ if (ret)
>>+ return ret;
>>+ for (i = 0; i < pf->dplls.rclk.num_parents; i++) {
>>+ parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[i]].pin;
>>+ if (!parent) {
>>+ ret = -ENODEV;
>>+ goto unregister_pins;
>>+ }
>>+ ret = dpll_pin_on_pin_register(parent, pf->dplls.rclk.pin,
>>+ ops, &pf->dplls.rclk);
>>+ if (ret)
>>+ goto unregister_pins;
>>+ }
>>+ if (WARN_ON((!vsi || !vsi->netdev)))
>>+ return -EINVAL;
>>+ netdev_dpll_pin_set(vsi->netdev, pf->dplls.rclk.pin);
>>+
>>+ return 0;
>>+
>>+unregister_pins:
>>+ while (i) {
>>+ parent = pf->dplls.inputs[pf->dplls.rclk.parent_idx[--i]].pin;
>>+ dpll_pin_on_pin_unregister(parent, pf->dplls.rclk.pin,
>>+ &ice_dpll_rclk_ops, &pf->dplls.rclk);
>>+ }
>>+ ice_dpll_release_pins(pin, ICE_DPLL_RCLK_NUM_PER_PF);
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins - init pins and register pins with a dplls
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Initialize directly connected pf's pins within pf's dplls in a Linux dpll
>>+ * subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - initialization failure reason
>>+ */
>>+static int ice_dpll_init_pins(struct ice_pf *pf, bool cgu)
>>+{
>>+ u32 rclk_idx;
>>+ int ret;
>>+
>>+ ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.inputs, 0,
>>+ pf->dplls.num_inputs,
>>+ &ice_dpll_input_ops,
>>+ pf->dplls.eec.dpll, pf->dplls.pps.dpll);
>>+ if (ret)
>>+ return ret;
>>+ if (cgu) {
>>+ ret = ice_dpll_init_direct_pins(pf, cgu, pf->dplls.outputs,
>>+ pf->dplls.num_inputs,
>>+ pf->dplls.num_outputs,
>>+ &ice_dpll_output_ops,
>>+ pf->dplls.eec.dpll,
>>+ pf->dplls.pps.dpll);
>>+ if (ret)
>>+ goto deinit_inputs;
>>+ }
>>+ rclk_idx = pf->dplls.num_inputs + pf->dplls.num_outputs + pf-
>>hw.pf_id;
>>+ ret = ice_dpll_init_rclk_pins(pf, &pf->dplls.rclk, rclk_idx,
>>+ &ice_dpll_rclk_ops);
>>+ if (ret)
>>+ goto deinit_outputs;
>>+
>>+ return 0;
>>+deinit_outputs:
>>+ ice_dpll_deinit_direct_pins(cgu, pf->dplls.outputs,
>>+ pf->dplls.num_outputs,
>>+ &ice_dpll_output_ops, pf->dplls.pps.dpll,
>>+ pf->dplls.eec.dpll);
>>+deinit_inputs:
>>+ ice_dpll_deinit_direct_pins(cgu, pf->dplls.inputs, pf-
>>dplls.num_inputs,
>>+ &ice_dpll_input_ops, pf->dplls.pps.dpll,
>>+ pf->dplls.eec.dpll);
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_generate_clock_id - generates unique clock_id for registering
>>dpll.
>>+ * @pf: board private structure
>>+ *
>>+ * Generates unique (per board) clock_id for allocation and search of
>>dpll
>>+ * devices in Linux dpll subsystem.
>>+ *
>>+ * Return: generated clock id for the board
>>+ */
>>+static u64 ice_generate_clock_id(struct ice_pf *pf)
>>+{
>>+ return pci_get_dsn(pf->pdev);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_dpll - initialize dpll device in dpll subsystem
>>+ * @pf: board private structure
>>+ * @d: dpll to be initialized
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ * @type: type of dpll being initialized
>>+ *
>>+ * Allocate dpll instance for this board in dpll subsystem, if cgu is
>>controlled
>>+ * by this NIC, register dpll with the callback ops.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - initialization failure reason
>>+ */
>>+static int
>>+ice_dpll_init_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu,
>>+ enum dpll_type type)
>>+{
>>+ u64 clock_id = pf->dplls.clock_id;
>>+ int ret;
>>+
>>+ d->dpll = dpll_device_get(clock_id, d->dpll_idx, THIS_MODULE);
>>+ if (IS_ERR(d->dpll)) {
>>+ ret = PTR_ERR(d->dpll);
>>+ dev_err(ice_pf_to_dev(pf),
>>+ "dpll_device_get failed (%p) err=%d\n", d, ret);
>>+ return ret;
>>+ }
>>+ d->pf = pf;
>>+ if (cgu) {
>>+ ret = dpll_device_register(d->dpll, type, &ice_dpll_ops, d);
>>+ if (ret) {
>>+ dpll_device_put(d->dpll);
>>+ return ret;
>>+ }
>>+ }
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_update_state - update dpll state
>>+ * @pf: pf private structure
>>+ * @d: pointer to queried dpll device
>>+ *
>>+ * Poll current state of dpll from hw and update ice_dpll struct.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - AQ failure
>>+ */
>>+static int
>>+ice_dpll_update_state(struct ice_pf *pf, struct ice_dpll *d, bool init)
>>+{
>>+ struct ice_dpll_pin *p;
>>+ int ret;
>>+
>>+ ret = ice_get_cgu_state(&pf->hw, d->dpll_idx, d->prev_dpll_state,
>>+ &d->input_idx, &d->ref_state, &d->eec_mode,
>>+ &d->phase_offset, &d->dpll_state);
>>+
>>+ dev_dbg(ice_pf_to_dev(pf),
>>+ "update dpll=%d, prev_src_idx:%u, src_idx:%u, state:%d,
>>prev:%d\n",
>>+ d->dpll_idx, d->prev_input_idx, d->input_idx,
>>+ d->dpll_state, d->prev_dpll_state);
>>+ if (ret) {
>>+ dev_err(ice_pf_to_dev(pf),
>>+ "update dpll=%d state failed, ret=%d %s\n",
>>+ d->dpll_idx, ret,
>>+ ice_aq_str(pf->hw.adminq.sq_last_status));
>>+ return ret;
>>+ }
>>+ if (init) {
>>+ if (d->dpll_state == ICE_CGU_STATE_LOCKED &&
>>+ d->dpll_state == ICE_CGU_STATE_LOCKED_HO_ACQ)
>>+ d->active_input = pf->dplls.inputs[d->input_idx].pin;
>>+ p = &pf->dplls.inputs[d->input_idx];
>>+ return ice_dpll_pin_state_update(pf, p,
>>+ ICE_DPLL_PIN_TYPE_INPUT);
>>+ }
>>+ if (d->dpll_state == ICE_CGU_STATE_HOLDOVER ||
>>+ d->dpll_state == ICE_CGU_STATE_FREERUN) {
>>+ d->active_input = NULL;
>>+ p = &pf->dplls.inputs[d->input_idx];
>>+ d->prev_input_idx = ICE_DPLL_PIN_IDX_INVALID;
>>+ d->input_idx = ICE_DPLL_PIN_IDX_INVALID;
>>+ ret = ice_dpll_pin_state_update(pf, p,
>>+ ICE_DPLL_PIN_TYPE_INPUT);
>>+ } else if (d->input_idx != d->prev_input_idx) {
>>+ p = &pf->dplls.inputs[d->prev_input_idx];
>>+ ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_INPUT);
>>+ p = &pf->dplls.inputs[d->input_idx];
>>+ d->active_input = p->pin;
>>+ ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_INPUT);
>>+ d->prev_input_idx = d->input_idx;
>>+ }
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_notify_changes - notify dpll subsystem about changes
>>+ * @d: pointer do dpll
>>+ *
>>+ * Once change detected appropriate event is submitted to the dpll
>>subsystem.
>>+ */
>>+static void ice_dpll_notify_changes(struct ice_dpll *d)
>>+{
>>+ if (d->prev_dpll_state != d->dpll_state) {
>>+ d->prev_dpll_state = d->dpll_state;
>>+ dpll_device_change_ntf(d->dpll);
>>+ }
>>+ if (d->prev_input != d->active_input) {
>>+ if (d->prev_input)
>>+ dpll_pin_change_ntf(d->prev_input);
>>+ d->prev_input = d->active_input;
>>+ if (d->active_input)
>>+ dpll_pin_change_ntf(d->active_input);
>>+ }
>>+}
>>+
>>+/**
>>+ * ice_dpll_periodic_work - DPLLs periodic worker
>>+ * @work: pointer to kthread_work structure
>>+ *
>>+ * DPLLs periodic worker is responsible for polling state of dpll.
>>+ * Context: Holds pf->dplls.lock
>>+ */
>>+static void ice_dpll_periodic_work(struct kthread_work *work)
>>+{
>>+ struct ice_dplls *d = container_of(work, struct ice_dplls,
>>work.work);
>>+ struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>>+ struct ice_dpll *de = &pf->dplls.eec;
>>+ struct ice_dpll *dp = &pf->dplls.pps;
>>+ int ret = 0;
>>+
>>+ if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>
>How exactly could this happen? I don't think it can. Drop it.
>

I will change a bit here, ice_dpll_cb_lock returns an error
if that flag was already down, so will use it instead.

>
>>+ return;
>>+ ret = ice_dpll_cb_lock(pf);
>>+ if (ret) {
>>+ d->lock_err_num++;
>
>Drop this struct field, you don't use it anywhere.
>

Sure, fixed.

>
>>+ goto resched;
>>+ }
>>+ ret = ice_dpll_update_state(pf, de, false);
>>+ if (!ret)
>>+ ret = ice_dpll_update_state(pf, dp, false);
>>+ if (ret) {
>>+ d->cgu_state_acq_err_num++;
>>+ /* stop rescheduling this worker */
>>+ if (d->cgu_state_acq_err_num >
>>+ ICE_CGU_STATE_ACQ_ERR_THRESHOLD) {
>>+ dev_err(ice_pf_to_dev(pf),
>>+ "EEC/PPS DPLLs periodic work disabled\n");
>>+ return;
>>+ }
>>+ }
>>+ ice_dpll_cb_unlock(pf);
>>+ ice_dpll_notify_changes(de);
>>+ ice_dpll_notify_changes(dp);
>>+resched:
>>+ /* Run twice a second or reschedule if update failed */
>>+ kthread_queue_delayed_work(d->kworker, &d->work,
>>+ ret ? msecs_to_jiffies(10) :
>>+ msecs_to_jiffies(500));
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_worker - Initialize DPLLs periodic worker
>>+ * @pf: board private structure
>>+ *
>>+ * Create and start DPLLs periodic worker.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - create worker failure
>>+ */
>>+static int ice_dpll_init_worker(struct ice_pf *pf)
>>+{
>>+ struct ice_dplls *d = &pf->dplls;
>>+ struct kthread_worker *kworker;
>>+
>>+ ice_dpll_update_state(pf, &d->eec, true);
>>+ ice_dpll_update_state(pf, &d->pps, true);
>>+ kthread_init_delayed_work(&d->work, ice_dpll_periodic_work);
>>+ kworker = kthread_create_worker(0, "ice-dplls-%s",
>>+ dev_name(ice_pf_to_dev(pf)));
>>+ if (IS_ERR(kworker))
>>+ return PTR_ERR(kworker);
>>+ d->kworker = kworker;
>>+ d->cgu_state_acq_err_num = 0;
>>+ kthread_queue_delayed_work(d->kworker, &d->work, 0);
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_pins - deinitialize direct pins
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is controlled by this pf
>>+ *
>>+ * If cgu is owned unregister directly connected pins from the dplls.
>>+ * Release resources of directly connected pins from the dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ */
>>+static void ice_dpll_deinit_pins(struct ice_pf *pf, bool cgu)
>>+{
>>+ struct ice_dpll_pin *outputs = pf->dplls.outputs;
>>+ struct ice_dpll_pin *inputs = pf->dplls.inputs;
>>+ int num_outputs = pf->dplls.num_outputs;
>>+ int num_inputs = pf->dplls.num_inputs;
>>+ struct ice_dplls *d = &pf->dplls;
>>+ struct ice_dpll *de = &d->eec;
>>+ struct ice_dpll *dp = &d->pps;
>>+
>>+ ice_dpll_deinit_rclk_pin(pf);
>>+ if (cgu) {
>>+ ice_dpll_unregister_pins(dp->dpll, inputs, &ice_dpll_input_ops,
>>+ num_inputs);
>>+ ice_dpll_unregister_pins(de->dpll, inputs, &ice_dpll_input_ops,
>>+ num_inputs);
>>+ }
>>+ ice_dpll_release_pins(inputs, num_inputs);
>>+ if (cgu) {
>>+ ice_dpll_unregister_pins(dp->dpll, outputs,
>>+ &ice_dpll_output_ops, num_outputs);
>>+ ice_dpll_unregister_pins(de->dpll, outputs,
>>+ &ice_dpll_output_ops, num_outputs);
>>+ ice_dpll_release_pins(outputs, num_outputs);
>>+ }
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_dpll - deinitialize dpll device
>>+ * @pf: board private structure
>>+ *
>>+ * If cgu is owned unregister the dpll from dpll subsystem.
>>+ * Release resources of dpll device from dpll subsystem.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ */
>>+static void
>>+ice_dpll_deinit_dpll(struct ice_pf *pf, struct ice_dpll *d, bool cgu)
>>+{
>>+ if (!IS_ERR(d->dpll)) {
>>+ if (cgu)
>>+ dpll_device_unregister(d->dpll, &ice_dpll_ops, d);
>>+ dpll_device_put(d->dpll);
>>+ dev_dbg(ice_pf_to_dev(pf), "(%p) dpll removed\n", d);
>>+ }
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit_worker - deinitialize dpll kworker
>>+ * @pf: board private structure
>>+ *
>>+ * Stop dpll's kworker, release it's resources.
>>+ *
>>+ * Context: Called under pf->dplls.lock
>>+ */
>>+static void ice_dpll_deinit_worker(struct ice_pf *pf)
>>+{
>>+ struct ice_dplls *d = &pf->dplls;
>>+
>>+ kthread_cancel_delayed_work_sync(&d->work);
>>+ if (!IS_ERR_OR_NULL(d->kworker)) {
>>+ kthread_destroy_worker(d->kworker);
>>+ d->kworker = NULL;
>>+ dev_dbg(ice_pf_to_dev(pf), "DPLLs worker removed\n");
>
>What is this msg good for?
>

Will remove it.

>
>>+ }
>>+}
>>+
>>+/**
>>+ * ice_dpll_deinit - Disable the driver/HW support for dpll subsystem
>>+ * the dpll device.
>>+ * @pf: board private structure
>>+ *
>>+ * Handles the cleanup work required after dpll initialization,freeing
>>resources
>>+ * and unregistering the dpll, pin and all resources used for handling
>>them.
>>+ *
>>+ * Context: Function holds pf->dplls.lock mutex.
>>+ */
>>+void ice_dpll_deinit(struct ice_pf *pf)
>>+{
>>+ bool cgu = ice_is_feature_supported(pf, ICE_F_CGU);
>>+
>>+ if (test_bit(ICE_FLAG_DPLL, pf->flags)) {
>
>How about avoiding the indent are rather do:
> if (!test_bit(ICE_FLAG_DPLL, pf->flags))
> return;
>
>?
>

Sure, fixed.

>>+ mutex_lock(&pf->dplls.lock);
>
>Related to my question in ice_dpll_init(), why do you need to lock the
>mutex
>here?

Because before deinit takes place on driver unload the user can still ask
for the info from the dpll subsystem or kworker can try to alter the status.

>
>
>>+ ice_dpll_deinit_pins(pf, cgu);
>>+ ice_dpll_deinit_info(pf);
>>+ ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>+ ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>
>Please reorder to match error path in ice_dpll_init()
>

Fixed.

>>+ if (cgu)
>
>In ice_dpll_init() you call this "cgu_present". Please be consistent in
>naming.
>

Fixed.

>
>>+ ice_dpll_deinit_worker(pf);
>>+ clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+ mutex_unlock(&pf->dplls.lock);
>>+ mutex_destroy(&pf->dplls.lock);
>>+ }
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_info_direct_pins - initializes direct pins info
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Init information for directly connected pins, cache them in pf's pins
>>+ * structures.
>>+ *
>>+ * Context: Function initializes and holds pf->dplls.lock mutex.
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int
>>+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>>+ enum ice_dpll_pin_type pin_type)
>>+{
>>+ struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>>+ int num_pins, i, ret = -EINVAL;
>>+ struct ice_hw *hw = &pf->hw;
>>+ struct ice_dpll_pin *pins;
>>+ u8 freq_supp_num;
>>+ bool input;
>>+
>>+ switch (pin_type) {
>>+ case ICE_DPLL_PIN_TYPE_INPUT:
>>+ pins = pf->dplls.inputs;
>>+ num_pins = pf->dplls.num_inputs;
>>+ input = true;
>>+ break;
>>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+ pins = pf->dplls.outputs;
>>+ num_pins = pf->dplls.num_outputs;
>>+ input = false;
>>+ break;
>>+ default:
>>+ return ret;
>>+ }
>>+
>>+ for (i = 0; i < num_pins; i++) {
>>+ pins[i].idx = i;
>>+ pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>>+ pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>>+ if (input) {
>>+ ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>>+ &de->input_prio[i]);
>>+ if (ret)
>>+ return ret;
>>+ ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>>+ &dp->input_prio[i]);
>>+ if (ret)
>>+ return ret;
>>+ pins[i].prop.capabilities |=
>>+ DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>>+ }
>>+ pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>+ ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type);
>>+ if (ret)
>>+ return ret;
>>+ pins[i].prop.freq_supported =
>>+ ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>>+ pins[i].prop.freq_supported_num = freq_supp_num;
>>+ pins[i].pf = pf;
>>+ }
>>+
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_rclk_pin - initializes rclk pin information
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Init information for rclk pin, cache them in pf->dplls.rclk.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int ice_dpll_init_rclk_pin(struct ice_pf *pf)
>>+{
>>+ struct ice_dpll_pin *pin = &pf->dplls.rclk;
>>+ struct device *dev = ice_pf_to_dev(pf);
>>+
>>+ pin->prop.board_label = dev_name(dev);
>
>What??? Must be some sort of joke, correct?
>"board_label" should be an actual writing on a board. For syncE, I don't
>think it makes sense to fill any label. The connection to the netdev
>should be enough. That is what I do in mlx5.
>
>Please drop this.
>

No, we want a label, as this is recovered clock, will change it to
package_label but the name will stay for now, this is much more meaningful
then i.e. "phy0" or "RCLK".

>
>
>>+ pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>>+ pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>+ pin->pf = pf;
>>+
>>+ return ice_dpll_pin_state_update(pf, pin,
>>+ ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_pins_info - init pins info wrapper
>>+ * @pf: board private structure
>>+ * @pin_type: type of pins being initialized
>>+ *
>>+ * Wraps functions for pin initialization.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int
>>+ice_dpll_init_pins_info(struct ice_pf *pf, enum ice_dpll_pin_type
>>pin_type)
>>+{
>>+ switch (pin_type) {
>>+ case ICE_DPLL_PIN_TYPE_INPUT:
>>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>>+ return ice_dpll_init_info_direct_pins(pf, pin_type);
>>+ case ICE_DPLL_PIN_TYPE_RCLK_INPUT:
>>+ return ice_dpll_init_rclk_pin(pf);
>>+ default:
>>+ return -EINVAL;
>>+ }
>>+}
>>+
>>+/**
>>+ * ice_dpll_init_info - prepare pf's dpll information structure
>>+ * @pf: board private structure
>>+ * @cgu: if cgu is present and controlled by this NIC
>>+ *
>>+ * Acquire (from HW) and set basic dpll information (on pf->dplls
>>struct).
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+static int ice_dpll_init_info(struct ice_pf *pf, bool cgu)
>>+{
>>+ struct ice_aqc_get_cgu_abilities abilities;
>>+ struct ice_dpll *de = &pf->dplls.eec;
>>+ struct ice_dpll *dp = &pf->dplls.pps;
>>+ struct ice_dplls *d = &pf->dplls;
>>+ struct ice_hw *hw = &pf->hw;
>>+ int ret, alloc_size, i;
>>+
>>+ d->clock_id = ice_generate_clock_id(pf);
>>+ ret = ice_aq_get_cgu_abilities(hw, &abilities);
>>+ if (ret) {
>>+ dev_err(ice_pf_to_dev(pf),
>>+ "err:%d %s failed to read cgu abilities\n",
>>+ ret, ice_aq_str(hw->adminq.sq_last_status));
>>+ return ret;
>>+ }
>>+
>>+ de->dpll_idx = abilities.eec_dpll_idx;
>>+ dp->dpll_idx = abilities.pps_dpll_idx;
>>+ d->num_inputs = abilities.num_inputs;
>>+ d->num_outputs = abilities.num_outputs;
>>+
>>+ alloc_size = sizeof(*d->inputs) * d->num_inputs;
>>+ d->inputs = kzalloc(alloc_size, GFP_KERNEL);
>>+ if (!d->inputs)
>>+ return -ENOMEM;
>>+
>>+ alloc_size = sizeof(*de->input_prio) * d->num_inputs;
>>+ de->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+ if (!de->input_prio)
>>+ return -ENOMEM;
>>+
>>+ dp->input_prio = kzalloc(alloc_size, GFP_KERNEL);
>>+ if (!dp->input_prio)
>>+ return -ENOMEM;
>>+
>>+ ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_INPUT);
>>+ if (ret)
>>+ goto deinit_info;
>>+
>>+ if (cgu) {
>>+ alloc_size = sizeof(*d->outputs) * d->num_outputs;
>>+ d->outputs = kzalloc(alloc_size, GFP_KERNEL);
>>+ if (!d->outputs)
>>+ goto deinit_info;
>>+
>>+ ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_OUTPUT);
>>+ if (ret)
>>+ goto deinit_info;
>>+ }
>>+
>>+ ret = ice_get_cgu_rclk_pin_info(&pf->hw, &d->base_rclk_idx,
>>+ &pf->dplls.rclk.num_parents);
>>+ if (ret)
>>+ return ret;
>>+ for (i = 0; i < pf->dplls.rclk.num_parents; i++)
>>+ pf->dplls.rclk.parent_idx[i] = d->base_rclk_idx + i;
>>+ ret = ice_dpll_init_pins_info(pf, ICE_DPLL_PIN_TYPE_RCLK_INPUT);
>>+ if (ret)
>>+ return ret;
>>+
>>+ dev_dbg(ice_pf_to_dev(pf),
>>+ "%s - success, inputs:%u, outputs:%u rclk-parents:%u\n",
>>+ __func__, d->num_inputs, d->num_outputs, d->rclk.num_parents);
>>+
>>+ return 0;
>>+
>>+deinit_info:
>>+ dev_err(ice_pf_to_dev(pf),
>>+ "%s - fail: d->inputs:%p, de->input_prio:%p, dp->input_prio:%p,
>>d->outputs:%p\n",
>>+ __func__, d->inputs, de->input_prio,
>>+ dp->input_prio, d->outputs);
>>+ ice_dpll_deinit_info(pf);
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * ice_dpll_init - initialize support for dpll subsystem
>>+ * @pf: board private structure
>>+ *
>>+ * Set up the device dplls, register them and pins connected within Linux
>>dpll
>>+ * subsystem. Allow userpsace to obtain state of DPLL and handling of
>>DPLL
>>+ * configuration requests.
>>+ *
>>+ * Return:
>>+ * * 0 - success
>>+ * * negative - init failure reason
>>+ */
>>+int ice_dpll_init(struct ice_pf *pf)
>>+{
>>+ bool cgu_present = ice_is_feature_supported(pf, ICE_F_CGU);
>>+ struct ice_dplls *d = &pf->dplls;
>>+ int err = 0;
>>+
>>+ mutex_init(&d->lock);
>>+ mutex_lock(&d->lock);
>
>Seeing pattern like this always triggers questions.
>Why exactly do you need to lock the mutex here?
>

Could do it few lines below before the dplls are inited,
but this would make error path confusing.

>
>>+ err = ice_dpll_init_info(pf, cgu_present);
>>+ if (err)
>>+ goto err_exit;
>>+ err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu_present,
>>+ DPLL_TYPE_EEC);
>>+ if (err)
>>+ goto deinit_info;
>>+ err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu_present,
>>+ DPLL_TYPE_PPS);
>>+ if (err)
>>+ goto deinit_eec;
>>+ err = ice_dpll_init_pins(pf, cgu_present);
>>+ if (err)
>>+ goto deinit_pps;
>>+ set_bit(ICE_FLAG_DPLL, pf->flags);
>>+ if (cgu_present) {
>>+ err = ice_dpll_init_worker(pf);
>>+ if (err)
>>+ goto deinit_pins;
>>+ }
>>+ mutex_unlock(&d->lock);
>>+ dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");
>
>What is this good for? Please avoid polluting dmesg and drop this.
>

Sure, removed.

>
>>+
>>+ return err;
>>+
>>+deinit_pins:
>>+ ice_dpll_deinit_pins(pf, cgu_present);
>>+deinit_pps:
>>+ ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu_present);
>>+deinit_eec:
>>+ ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu_present);
>>+deinit_info:
>>+ ice_dpll_deinit_info(pf);
>>+err_exit:
>>+ clear_bit(ICE_FLAG_DPLL, pf->flags);
>>+ mutex_unlock(&d->lock);
>>+ mutex_destroy(&d->lock);
>>+ dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:\n");
>
>You are missing the err. But why do you need the message?
>

To give a clue that something went wrong on dpll init.

>
>>+
>>+ return err;
>>+}
>>diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h
>>b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>new file mode 100644
>>index 000000000000..287892825deb
>>--- /dev/null
>>+++ b/drivers/net/ethernet/intel/ice/ice_dpll.h
>>@@ -0,0 +1,102 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/* Copyright (C) 2022, Intel Corporation. */
>>+
>>+#ifndef _ICE_DPLL_H_
>>+#define _ICE_DPLL_H_
>>+
>>+#include "ice.h"
>>+
>>+#define ICE_DPLL_PRIO_MAX 0xF
>>+#define ICE_DPLL_RCLK_NUM_MAX 4
>>+/** ice_dpll_pin - store info about pins
>>+ * @pin: dpll pin structure
>>+ * @pf: pointer to pf, which has registered the dpll_pin
>>+ * @flags: pin flags returned from HW
>>+ * @idx: ice pin private idx
>>+ * @state: state of a pin
>>+ * @type: type of a pin
>>+ * @freq_mask: mask of supported frequencies
>>+ * @freq: current frequency of a pin
>>+ * @caps: capabilities of a pin
>>+ * @name: pin name
>>+ */
>>+struct ice_dpll_pin {
>>+ struct dpll_pin *pin;
>>+ struct ice_pf *pf;
>>+ u8 idx;
>>+ u8 num_parents;
>>+ u8 parent_idx[ICE_DPLL_RCLK_NUM_MAX];
>>+ u8 flags[ICE_DPLL_RCLK_NUM_MAX];
>>+ u8 state[ICE_DPLL_RCLK_NUM_MAX];
>>+ struct dpll_pin_properties prop;
>>+ u32 freq;
>>+};
>>+
>>+/** ice_dpll - store info required for DPLL control
>>+ * @dpll: pointer to dpll dev
>>+ * @pf: pointer to pf, which has registered the dpll_device
>>+ * @dpll_idx: index of dpll on the NIC
>>+ * @input_idx: currently selected input index
>>+ * @prev_input_idx: previously selected input index
>>+ * @ref_state: state of dpll reference signals
>>+ * @eec_mode: eec_mode dpll is configured for
>>+ * @phase_offset: phase delay of a dpll
>>+ * @input_prio: priorities of each input
>>+ * @dpll_state: current dpll sync state
>>+ * @prev_dpll_state: last dpll sync state
>>+ * @active_input: pointer to active input pin
>>+ * @prev_input: pointer to previous active input pin
>>+ */
>>+struct ice_dpll {
>>+ struct dpll_device *dpll;
>>+ struct ice_pf *pf;
>>+ int dpll_idx;
>>+ u8 input_idx;
>>+ u8 prev_input_idx;
>>+ u8 ref_state;
>>+ u8 eec_mode;
>>+ s64 phase_offset;
>>+ u8 *input_prio;
>>+ enum ice_cgu_state dpll_state;
>>+ enum ice_cgu_state prev_dpll_state;
>>+ struct dpll_pin *active_input;
>>+ struct dpll_pin *prev_input;
>>+};
>>+
>>+/** ice_dplls - store info required for CCU (clock controlling unit)
>>+ * @kworker: periodic worker
>>+ * @work: periodic work
>>+ * @lock: locks access to configuration of a dpll
>>+ * @eec: pointer to EEC dpll dev
>>+ * @pps: pointer to PPS dpll dev
>>+ * @inputs: input pins pointer
>>+ * @outputs: output pins pointer
>>+ * @rclk: recovered pins pointer
>>+ * @num_inputs: number of input pins available on dpll
>>+ * @num_outputs: number of output pins available on dpll
>>+ * @cgu_state_acq_err_num: number of errors returned during periodic work
>>+ * @base_rclk_idx: idx of first pin used for clock revocery pins
>>+ * @clock_id: clock_id of dplls
>>+ */
>>+struct ice_dplls {
>>+ struct kthread_worker *kworker;
>>+ struct kthread_delayed_work work;
>>+ struct mutex lock;
>>+ struct ice_dpll eec;
>>+ struct ice_dpll pps;
>>+ struct ice_dpll_pin *inputs;
>>+ struct ice_dpll_pin *outputs;
>>+ struct ice_dpll_pin rclk;
>>+ u32 num_inputs;
>>+ u32 num_outputs;
>>+ int cgu_state_acq_err_num;
>>+ int lock_err_num;
>>+ u8 base_rclk_idx;
>>+ u64 clock_id;
>>+};
>>+
>>+int ice_dpll_init(struct ice_pf *pf);
>>+
>>+void ice_dpll_deinit(struct ice_pf *pf);
>>+
>>+#endif
>>diff --git a/drivers/net/ethernet/intel/ice/ice_main.c
>>b/drivers/net/ethernet/intel/ice/ice_main.c
>>index 62e91512aeab..ba5f3bc9075a 100644
>>--- a/drivers/net/ethernet/intel/ice/ice_main.c
>>+++ b/drivers/net/ethernet/intel/ice/ice_main.c
>>@@ -4595,6 +4595,10 @@ static void ice_init_features(struct ice_pf *pf)
>> if (ice_is_feature_supported(pf, ICE_F_GNSS))
>> ice_gnss_init(pf);
>>
>>+ if (ice_is_feature_supported(pf, ICE_F_CGU) ||
>>+ ice_is_feature_supported(pf, ICE_F_PHY_RCLK))
>>+ ice_dpll_init(pf);
>
>Why do you have the function returning int when you don't check it here?
>

Will make it void.

Thank you!
Arkadiusz

>
>>+
>> /* Note: Flow director init failure is non-fatal to load */
>> if (ice_init_fdir(pf))
>> dev_err(dev, "could not initialize flow director\n");
>>@@ -4621,6 +4625,9 @@ static void ice_deinit_features(struct ice_pf *pf)
>> ice_gnss_exit(pf);
>> if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags))
>> ice_ptp_release(pf);
>>+ if (ice_is_feature_supported(pf, ICE_F_PHY_RCLK) ||
>>+ ice_is_feature_supported(pf, ICE_F_CGU))
>>+ ice_dpll_deinit(pf);
>> }
>>
>> static void ice_init_wakeup(struct ice_pf *pf)
>>--
>>2.37.3
>>

2023-06-19 21:07:30

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

>From: Jiri Pirko <[email protected]>
>Sent: Saturday, June 10, 2023 6:37 PM
>
>Fri, Jun 09, 2023 at 02:18:51PM CEST, [email protected] wrote:
>
>[...]
>
>
>>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>>+ enum dpll_mode *mode,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ *mode = DPLL_MODE_AUTOMATIC;
>
>I don't understand how the automatic mode could work with SyncE. The
>There is one pin exposed for one netdev. The SyncE daemon should select
>exacly one pin. How do you achieve that?
>Is is by setting DPLL_PIN_STATE_SELECTABLE on the pin-netdev you want to
>select and DPLL_PIN_STATE_DISCONNECTED on the rest?
>
>
>[...]

AUTOMATIC mode autoselects highest priority valid signal.
As you have pointed out, for SyncE selection, the user must be able to manually
select a pin state to enable recovery of signal from particular port.

In "ice" case there are 2 pins for network PHY clock signal recovery, and both
are parent pins (MUX-type). There are also 4 pins assigned to netdevs (one per
port). Thus passing a signal from PHY to the pin is done through the MUX-pin,
by selecting proper state on pin-parent pair (where parent pins is highest prio
pin on dpll).

Thank you!
Arkadiusz

2023-06-21 11:40:40

by Petr Oros

[permalink] [raw]
Subject: Re: [RFC PATCH v8 04/10] dpll: netlink: Add DPLL framework base functions

Arkadiusz Kubalewski píše v Pá 09. 06. 2023 v 14:18 +0200:
> From: Vadim Fedorenko <[email protected]>
>
> DPLL framework is used to represent and configure DPLL devices
> in systems. Each device that has DPLL and can configure inputs
> and outputs can use this framework.
>
> Implement dpll netlink framework functions for enablement of dpll
> subsytem netlink family.
>
> Co-developed-by: Milena Olech <[email protected]>
> Signed-off-by: Milena Olech <[email protected]>
> Co-developed-by: Michal Michalik <[email protected]>
> Signed-off-by: Michal Michalik <[email protected]>
> Signed-off-by: Vadim Fedorenko <[email protected]>
> Co-developed-by: Arkadiusz Kubalewski
> <[email protected]>
> Signed-off-by: Arkadiusz Kubalewski <[email protected]>
> ---
>  drivers/dpll/dpll_netlink.c | 1183
> +++++++++++++++++++++++++++++++++++
>  drivers/dpll/dpll_netlink.h |   44 ++
>  2 files changed, 1227 insertions(+)
>  create mode 100644 drivers/dpll/dpll_netlink.c
>  create mode 100644 drivers/dpll/dpll_netlink.h
>
> diff --git a/drivers/dpll/dpll_netlink.c
> b/drivers/dpll/dpll_netlink.c
> new file mode 100644
> index 000000000000..44d9699c9e6c
> --- /dev/null
> +++ b/drivers/dpll/dpll_netlink.c
> @@ -0,0 +1,1183 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Generic netlink for DPLL management framework
> + *
> + *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
> + *  Copyright (c) 2023 Intel and affiliates
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <net/genetlink.h>
> +#include "dpll_core.h"
> +#include "dpll_nl.h"
> +#include <uapi/linux/dpll.h>
> +
> +static int __dpll_pin_change_ntf(struct dpll_pin *pin);
> +
> +struct dpll_dump_ctx {
> +       unsigned long idx;
> +};
> +
> +static struct dpll_dump_ctx *dpll_dump_context(struct
> netlink_callback *cb)
> +{
> +       return (struct dpll_dump_ctx *)cb->ctx;
> +}
> +
> +static int
> +dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device
> *dpll)
> +{
> +       if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
> +               return -EMSGSIZE;
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
> +                 struct netlink_ext_ack *extack)
> +{
> +       const struct dpll_device_ops *ops = dpll_device_ops(dpll);
> +       enum dpll_mode mode;
> +
> +       if (WARN_ON(!ops->mode_get))
> +               return -EOPNOTSUPP;
> +       if (ops->mode_get(dpll, dpll_priv(dpll), &mode, extack))
> +               return -EFAULT;
> +       if (nla_put_u8(msg, DPLL_A_MODE, mode))
> +               return -EMSGSIZE;
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device
> *dpll,
> +                        struct netlink_ext_ack *extack)
> +{
> +       const struct dpll_device_ops *ops = dpll_device_ops(dpll);
> +       enum dpll_lock_status status;
> +
> +       if (WARN_ON(!ops->lock_status_get))
> +               return -EOPNOTSUPP;
> +       if (ops->lock_status_get(dpll, dpll_priv(dpll), &status,
> extack))
> +               return -EFAULT;
> +       if (nla_put_u8(msg, DPLL_A_LOCK_STATUS, status))
> +               return -EMSGSIZE;
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,
> +                 struct netlink_ext_ack *extack)
> +{
> +       const struct dpll_device_ops *ops = dpll_device_ops(dpll);
> +       s32 temp;
> +
> +       if (!ops->temp_get)
> +               return -EOPNOTSUPP;
> +       if (ops->temp_get(dpll, dpll_priv(dpll), &temp, extack))
> +               return -EFAULT;
> +       if (nla_put_s32(msg, DPLL_A_TEMP, temp))
> +               return -EMSGSIZE;
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_msg_add_pin_prio(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;
> +       u32 prio;
> +
> +       if (!ops->prio_get)
> +               return -EOPNOTSUPP;
> +       if (ops->prio_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
> dpll,
> +                         dpll_priv(dpll), &prio, extack))
> +               return -EFAULT;
> +       if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
> +               return -EMSGSIZE;
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_msg_add_pin_on_dpll_state(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;
> +       enum dpll_pin_state state;
> +
> +       if (!ops->state_on_dpll_get)
> +               return -EOPNOTSUPP;
> +       if (ops->state_on_dpll_get(pin, dpll_pin_on_dpll_priv(dpll,
> pin), dpll,
> +                                  dpll_priv(dpll), &state, extack))
> +               return -EFAULT;
> +       if (nla_put_u8(msg, DPLL_A_PIN_STATE, state))
> +               return -EMSGSIZE;
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_msg_add_pin_direction(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;
> +       enum dpll_pin_direction direction;
> +
> +       if (!ops->direction_get)
> +               return -EOPNOTSUPP;
> +       if (ops->direction_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
> dpll,
> +                              dpll_priv(dpll), &direction, extack))
> +               return -EFAULT;
> +       if (nla_put_u8(msg, DPLL_A_PIN_DIRECTION, direction))
> +               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,
> +                     bool dump_freq_supported)
> +{
> +       const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
> +       struct dpll_device *dpll = ref->dpll;
> +       struct nlattr *nest;
> +       u64 freq;
> +       int fs;
> +
> +       if (!ops->frequency_get)
> +               return -EOPNOTSUPP;
> +       if (ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
> dpll,
> +                              dpll_priv(dpll), &freq, extack))
> +               return -EFAULT;
> +       if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq),
> &freq, 0))
> +               return -EMSGSIZE;
> +       if (!dump_freq_supported)
> +               return 0;
> +       for (fs = 0; fs < pin->prop->freq_supported_num; fs++) {
> +               nest = nla_nest_start(msg,
> DPLL_A_PIN_FREQUENCY_SUPPORTED);
> +               if (!nest)
> +                       return -EMSGSIZE;
> +               freq = pin->prop->freq_supported[fs].min;
> +               if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN,
> sizeof(freq),
> +                                  &freq, 0)) {
> +                       nla_nest_cancel(msg, nest);
> +                       return -EMSGSIZE;
> +               }
> +               freq = pin->prop->freq_supported[fs].max;
> +               if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX,
> sizeof(freq),
> +                                  &freq, 0)) {
> +                       nla_nest_cancel(msg, nest);
> +                       return -EMSGSIZE;
> +               }
> +               nla_nest_end(msg, nest);
> +       }
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
> +                        struct dpll_pin_ref *dpll_ref,
> +                        struct netlink_ext_ack *extack)
> +{
> +       enum dpll_pin_state state;
> +       struct dpll_pin_ref *ref;
> +       struct dpll_pin *ppin;
> +       struct nlattr *nest;
> +       unsigned long index;
> +       int ret;
> +
> +       xa_for_each(&pin->parent_refs, index, ref) {
> +               const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
> +               void *parent_priv;
> +
> +               ppin = ref->pin;
> +               parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll,
> ppin);
> +               if (WARN_ON(!ops->state_on_pin_get))
> +                       return -EFAULT;
> +               ret = ops->state_on_pin_get(pin,
> +                                          
> dpll_pin_on_pin_priv(ppin, pin),
> +                                           ppin, parent_priv,
> &state, extack);
> +               if (ret)
> +                       return -EFAULT;
> +               nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
> +               if (!nest)
> +                       return -EMSGSIZE;
> +               if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
> +                       ret = -EMSGSIZE;
> +                       goto nest_cancel;
> +               }
> +               if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
> +                       ret = -EMSGSIZE;
> +                       goto nest_cancel;
> +               }
> +               nla_nest_end(msg, nest);
> +       }
> +
> +       return 0;
> +
> +nest_cancel:
> +       nla_nest_cancel(msg, nest);
> +       return ret;
> +}
> +
> +static int
> +dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
> +                      struct netlink_ext_ack *extack)
> +{
> +       struct dpll_pin_ref *ref;
> +       struct nlattr *attr;
> +       unsigned long index;
> +       int ret;
> +
> +       xa_for_each(&pin->dpll_refs, index, ref) {
> +               attr = nla_nest_start(msg, DPLL_A_PIN_PARENT);
> +               if (!attr)
> +                       return -EMSGSIZE;
> +               ret = dpll_msg_add_dev_handle(msg, ref->dpll);
> +               if (ret)
> +                       goto nest_cancel;
> +               ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref,
> extack);
> +               if (ret && ret != -EOPNOTSUPP)
> +                       goto nest_cancel;
> +               ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
> +               if (ret && ret != -EOPNOTSUPP)
> +                       goto nest_cancel;
> +               ret = dpll_msg_add_pin_direction(msg, pin, ref,
> extack);
> +               if (ret)
> +                       goto nest_cancel;
> +               nla_nest_end(msg, attr);
> +       }
> +
> +       return 0;
> +
> +nest_cancel:
> +       nla_nest_end(msg, attr);
> +       return ret;
> +}
> +
> +static int
> +dpll_cmd_pin_fill_details(struct sk_buff *msg, struct dpll_pin *pin,
> +                         struct dpll_pin_ref *ref, struct
> netlink_ext_ack *extack)
> +{
> +       const struct dpll_pin_properties *prop = pin->prop;
> +       int ret;
> +
> +       if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
> +               return -EMSGSIZE;
> +       if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(pin-
> >module)))
> +               return -EMSGSIZE;
> +       if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(pin-
> >clock_id),
> +                         &pin->clock_id, 0))
> +               return -EMSGSIZE;
> +       if (prop->board_label &&
> +           nla_put_string(msg, DPLL_A_PIN_BOARD_LABEL, prop-
> >board_label))
> +               return -EMSGSIZE;
> +       if (prop->panel_label &&
> +           nla_put_string(msg, DPLL_A_PIN_PANEL_LABEL, prop-
> >panel_label))
> +               return -EMSGSIZE;
> +       if (prop->package_label &&
> +           nla_put_string(msg, DPLL_A_PIN_PACKAGE_LABEL,
> +                          prop->package_label))
> +               return -EMSGSIZE;
> +       if (nla_put_u8(msg, DPLL_A_PIN_TYPE, prop->type))
> +               return -EMSGSIZE;
> +       if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, prop-
> >capabilities))
> +               return -EMSGSIZE;
> +       ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
> +       if (ret && ret != -EOPNOTSUPP)
> +               return ret;
> +       return 0;
> +}
> +
> +static int
> +__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
> +                       struct netlink_ext_ack *extack)
> +{
> +       struct dpll_pin_ref *ref;
> +       int ret;
> +
> +       ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
> +       if (!ref)
> +               return -EFAULT;
> +       ret = dpll_cmd_pin_fill_details(msg, pin, ref, extack);
> +       if (ret)
> +               return ret;
> +       ret = dpll_msg_add_pin_parents(msg, pin, ref, extack);
> +       if (ret)
> +               return ret;
> +       if (!xa_empty(&pin->dpll_refs)) {
> +               ret = dpll_msg_add_pin_dplls(msg, pin, extack);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
> +                   struct netlink_ext_ack *extack)
> +{
> +       enum dpll_mode mode;
> +       int ret;
> +
> +       ret = dpll_msg_add_dev_handle(msg, dpll);
> +       if (ret)
> +               return ret;
> +       if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(dpll-
> >module)))
> +               return -EMSGSIZE;
> +       if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll-
> >clock_id),
> +                         &dpll->clock_id, 0))
> +               return -EMSGSIZE;
> +       ret = dpll_msg_add_temp(msg, dpll, extack);
> +       if (ret && ret != -EOPNOTSUPP)
> +               return ret;
> +       ret = dpll_msg_add_lock_status(msg, dpll, extack);
> +       if (ret)
> +               return ret;
> +       ret = dpll_msg_add_mode(msg, dpll, extack);
> +       if (ret)
> +               return ret;
> +       for (mode = DPLL_MODE_MANUAL; mode <= DPLL_MODE_MAX; mode++)
> +               if (test_bit(mode, &dpll->mode_supported_mask))
> +                       if (nla_put_s32(msg, DPLL_A_MODE_SUPPORTED,
> mode))
> +                               return -EMSGSIZE;
> +       if (nla_put_u8(msg, DPLL_A_TYPE, dpll->type))
> +               return -EMSGSIZE;
> +
> +       return ret;
> +}
> +
> +static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32
> freq)
> +{
> +       int fs;
> +
> +       for (fs = 0; fs < pin->prop->freq_supported_num; fs++)
> +               if (freq >=  pin->prop->freq_supported[fs].min &&
> +                   freq <=  pin->prop->freq_supported[fs].max)
> +                       return true;
> +       return false;
> +}
> +
> +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;
> +       unsigned long i;
> +       int ret;
> +
> +       if (!dpll_pin_is_freq_supported(pin, freq))
> +               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->frequency_set)
> +                       return -EOPNOTSUPP;
> +               ret = ops->frequency_set(pin,
> dpll_pin_on_dpll_priv(dpll, pin),
> +                                        dpll, dpll_priv(dpll), freq,
> extack);
> +               if (ret)
> +                       return -EFAULT;
> +               __dpll_pin_change_ntf(pin);
> +       }
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
> +                         enum dpll_pin_state state,
> +                         struct netlink_ext_ack *extack)
> +{
> +       struct dpll_pin_ref *parent_ref;
> +       const struct dpll_pin_ops *ops;
> +       struct dpll_pin_ref *dpll_ref;
> +       struct dpll_pin *parent;
> +       unsigned long i;
> +
> +       if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop-
> >capabilities))
> +               return -EOPNOTSUPP;
> +       parent = xa_load(&dpll_pin_xa, parent_idx);
> +       if (!parent)
> +               return -EINVAL;
> +       parent_ref = xa_load(&pin->parent_refs, parent->pin_idx);
> +       if (!parent_ref)
> +               return -EINVAL;
> +       xa_for_each(&parent->dpll_refs, i, dpll_ref) {
> +               ops = dpll_pin_ops(parent_ref);
> +               if (!ops->state_on_pin_set)
> +                       return -EOPNOTSUPP;
> +               if (ops->state_on_pin_set(pin,
> +                                        
> dpll_pin_on_pin_priv(parent, pin),
> +                                         parent,
> +                                        
> dpll_pin_on_dpll_priv(dpll_ref->dpll,
> +                                                               paren
> t),
> +                                         state, extack))
> +                       return -EFAULT;
> +       }
> +       __dpll_pin_change_ntf(pin);
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
> +                  enum dpll_pin_state state,
> +                  struct netlink_ext_ack *extack)
> +{
> +       const struct dpll_pin_ops *ops;
> +       struct dpll_pin_ref *ref;
> +
> +       if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop-
> >capabilities))
> +               return -EOPNOTSUPP;
> +       ref = xa_load(&pin->dpll_refs, dpll->device_idx);
> +       if (!ref)
> +               return -EFAULT;
> +       ops = dpll_pin_ops(ref);
> +       if (!ops->state_on_dpll_set)
> +               return -EOPNOTSUPP;
> +       if (ops->state_on_dpll_set(pin, dpll_pin_on_dpll_priv(dpll,
> pin), dpll,
> +                                  dpll_priv(dpll), state, extack))
> +               return -EINVAL;
> +       __dpll_pin_change_ntf(pin);
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
> +                 u32 prio, struct netlink_ext_ack *extack)
> +{
> +       const struct dpll_pin_ops *ops;
> +       struct dpll_pin_ref *ref;
> +
> +       if (!(DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE & pin->prop-
> >capabilities))
> +               return -EOPNOTSUPP;
> +       ref = xa_load(&pin->dpll_refs, dpll->device_idx);
> +       if (!ref)
> +               return -EFAULT;
> +       ops = dpll_pin_ops(ref);
> +       if (!ops->prio_set)
> +               return -EOPNOTSUPP;
> +       if (ops->prio_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
> dpll,
> +                         dpll_priv(dpll), prio, extack))
> +               return -EINVAL;
> +       __dpll_pin_change_ntf(pin);
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device
> *dpll,
> +                      enum dpll_pin_direction direction,
> +                      struct netlink_ext_ack *extack)
> +{
> +       const struct dpll_pin_ops *ops;
> +       struct dpll_pin_ref *ref;
> +
> +       if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop-
> >capabilities))
> +               return -EOPNOTSUPP;
> +
> +       ref = xa_load(&pin->dpll_refs, dpll->device_idx);
> +       if (!ref)
> +               return -EFAULT;
> +       ops = dpll_pin_ops(ref);
> +       if (!ops->direction_set)
> +               return -EOPNOTSUPP;
> +       if (ops->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
> +                              dpll, dpll_priv(dpll), direction,
> +                              extack))
> +               return -EFAULT;
> +       __dpll_pin_change_ntf(pin);
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_pin_parent_set(struct dpll_pin *pin, struct nlattr
> *parent_nest,
> +                   struct netlink_ext_ack *extack)
> +{
> +       struct nlattr *tb[DPLL_A_MAX + 1];
> +       enum dpll_pin_direction direction;
> +       u32 ppin_idx, pdpll_idx, prio;
> +       enum dpll_pin_state state;
> +       struct dpll_pin_ref *ref;
> +       struct dpll_device *dpll;
> +       int ret;
> +
> +       nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
> +                        NULL, extack);
> +       if ((tb[DPLL_A_ID] && tb[DPLL_A_PIN_ID]) ||
> +           !(tb[DPLL_A_ID] || tb[DPLL_A_PIN_ID])) {
> +               NL_SET_ERR_MSG(extack, "one parent id expected");
> +               return -EINVAL;
> +       }
> +       if (tb[DPLL_A_ID]) {
> +               pdpll_idx = nla_get_u32(tb[DPLL_A_ID]);
> +               dpll = xa_load(&dpll_device_xa, pdpll_idx);
> +               if (!dpll)
> +                       return -EINVAL;
> +               ref = xa_load(&pin->dpll_refs, dpll->device_idx);
> +               if (!ref)
> +                       return -EINVAL;
> +               if (tb[DPLL_A_PIN_STATE]) {
> +                       state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
> +                       ret = dpll_pin_state_set(dpll, pin, state,
> extack);
> +                       if (ret)
> +                               return ret;
> +               }
> +               if (tb[DPLL_A_PIN_PRIO]) {
> +                       prio = nla_get_u8(tb[DPLL_A_PIN_PRIO]);
> +                       ret = dpll_pin_prio_set(dpll, pin, prio,
> extack);
> +                       if (ret)
> +                               return ret;
> +               }
> +               if (tb[DPLL_A_PIN_DIRECTION]) {
> +                       direction =
> nla_get_u8(tb[DPLL_A_PIN_DIRECTION]);
> +                       ret = dpll_pin_direction_set(pin, dpll,
> direction,
> +                                                    extack);
> +                       if (ret)
> +                               return ret;
> +               }
> +       } else if (tb[DPLL_A_PIN_ID]) {
> +               ppin_idx = nla_get_u32(tb[DPLL_A_PIN_ID]);
> +               state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
> +               ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state,
> extack);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int
> +dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info
> *info)
> +{
> +       int rem, ret = -EINVAL;
> +       struct nlattr *a;
> +
> +       nla_for_each_attr(a, genlmsg_data(info->genlhdr),
> +                         genlmsg_len(info->genlhdr), rem) {
> +               switch (nla_type(a)) {
> +               case DPLL_A_PIN_FREQUENCY:
> +                       ret = dpll_pin_freq_set(pin, a, info-
> >extack);
> +                       if (ret)
> +                               return ret;
> +                       break;
> +               case DPLL_A_PIN_PARENT:
> +                       ret = dpll_pin_parent_set(pin, a, info-
> >extack);
> +                       if (ret)
> +                               return ret;
> +                       break;
> +               case DPLL_A_PIN_ID:
> +               case DPLL_A_ID:
> +                       break;
> +               default:
> +                       NL_SET_ERR_MSG_FMT(info->extack,
> +                                          "unsupported attribute
> (%d)",
> +                                          nla_type(a));
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static struct dpll_pin *
> +dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,
> +             enum dpll_pin_type type, struct nlattr *board_label,
> +             struct nlattr *panel_label, struct nlattr
> *package_label)
> +{
> +       bool board_match, panel_match, package_match;
> +       struct dpll_pin *pin_match = NULL, *pin;
> +       const struct dpll_pin_properties *prop;
> +       bool cid_match, mod_match, type_match;
> +       unsigned long i;
> +
> +       xa_for_each(&dpll_pin_xa, i, pin) {
> +               if (xa_empty(&pin->dpll_refs))
> +                       continue;
> +               prop = pin->prop;
> +               cid_match = clock_id ? pin->clock_id == clock_id :
> true;
> +               mod_match = mod_name_attr && module_name(pin->module)
> ?
> +                       !nla_strcmp(mod_name_attr,
> +                                   module_name(pin->module)) : true;
> +               type_match = type ? prop->type == type : true;
> +               board_match = board_label && prop->board_label ?
> +                       !nla_strcmp(board_label, prop->board_label) :
> true;
> +               panel_match = panel_label && prop->panel_label ?
> +                       !nla_strcmp(panel_label, prop->panel_label) :
> true;
> +               package_match = package_label && prop->package_label
> ?
> +                       !nla_strcmp(package_label,
> +                                   prop->package_label) : true;
> +               if (cid_match && mod_match && type_match &&
> board_match &&
> +                   panel_match && package_match) {
> +                       if (pin_match)
> +                               return NULL;
> +                       pin_match = pin;
> +               };
> +       }
> +
> +       return pin_match;
> +}
> +
> +static int
> +dpll_pin_find_from_nlattr(struct genl_info *info, struct sk_buff
> *skb)
> +{
> +       struct nlattr *attr, *mod_name_attr = NULL, *board_label_attr
> = NULL,
> +               *panel_label_attr = NULL, *package_label_attr = NULL;
> +       struct dpll_pin *pin = NULL;
> +       enum dpll_pin_type type = 0;
> +       u64 clock_id = 0;
> +       int rem = 0;
> +
> +       nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
> +                         genlmsg_len(info->genlhdr), rem) {
> +               switch (nla_type(attr)) {
> +               case DPLL_A_CLOCK_ID:
> +                       if (clock_id)
> +                               return -EINVAL;
> +                       clock_id = nla_get_u64(attr);
> +                       break;
> +               case DPLL_A_MODULE_NAME:
> +                       if (mod_name_attr)
> +                               return -EINVAL;
> +                       mod_name_attr = attr;
> +                       break;
> +               case DPLL_A_PIN_TYPE:
> +                       if (type)
> +                               return -EINVAL;
> +                       type = nla_get_u8(attr);
> +                       break;
> +               case DPLL_A_PIN_BOARD_LABEL:
> +                       if (board_label_attr)
> +                               return -EINVAL;
> +                       board_label_attr = attr;
> +                       break;
> +               case DPLL_A_PIN_PANEL_LABEL:
> +                       if (panel_label_attr)
> +                               return -EINVAL;
> +                       panel_label_attr = attr;
> +                       break;
> +               case DPLL_A_PIN_PACKAGE_LABEL:
> +                       if (package_label_attr)
> +                               return -EINVAL;
> +                       package_label_attr = attr;
> +                       break;
> +               default:
> +                       break;
> +               }
> +       }
> +       if (!(clock_id  || mod_name_attr || board_label_attr ||
> +             panel_label_attr || package_label_attr))
> +               return -EINVAL;
> +       pin = dpll_pin_find(clock_id, mod_name_attr, type,
> board_label_attr,
> +                           panel_label_attr, package_label_attr);
> +       if (!pin)
> +               return -EINVAL;
> +       if (nla_put_u32(skb, DPLL_A_PIN_ID, pin->id))
> +               return -EMSGSIZE;
> +       return 0;
> +}
> +
> +int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info
> *info)
> +{
> +       struct sk_buff *msg;
> +       struct nlattr *hdr;
> +       int ret;
> +
> +       msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> +       if (!msg)
> +               return -ENOMEM;
> +       hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
> +                               DPLL_CMD_PIN_ID_GET);
> +       if (!hdr)
> +               return -EMSGSIZE;
> +
> +       ret = dpll_pin_find_from_nlattr(info, msg);
> +       if (ret) {
> +               nlmsg_free(msg);
> +               return ret;
> +       }
> +       genlmsg_end(msg, hdr);
> +
> +       return genlmsg_reply(msg, info);
> +}
> +
> +int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info
> *info)
> +{
> +       struct dpll_pin *pin = info->user_ptr[0];
> +       struct sk_buff *msg;
> +       struct nlattr *hdr;
> +       int ret;
> +
> +       if (!pin)
> +               return -ENODEV;
> +       msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> +       if (!msg)
> +               return -ENOMEM;
> +       hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
> +                               DPLL_CMD_PIN_GET);
> +       if (!hdr)
> +               return -EMSGSIZE;
> +       ret = __dpll_cmd_pin_dump_one(msg, pin, info->extack);
> +       if (ret) {
> +               nlmsg_free(msg);
> +               return ret;
> +       }
> +       genlmsg_end(msg, hdr);
> +
> +       return genlmsg_reply(msg, info);
> +}
> +
> +int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct
> netlink_callback *cb)
> +{
> +       struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
> +       struct dpll_pin *pin;
> +       struct nlattr *hdr;
> +       unsigned long i;
> +       int ret = 0;
> +
> +       xa_for_each_start(&dpll_pin_xa, i, pin, ctx->idx) {
> +               if (xa_empty(&pin->dpll_refs))
> +                       continue;
> +               hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
> +                                 cb->nlh->nlmsg_seq,
> +                                 &dpll_nl_family, NLM_F_MULTI,
> +                                 DPLL_CMD_PIN_GET);
> +               if (!hdr) {
> +                       ret = -EMSGSIZE;
> +                       break;
> +               }
> +               ret = __dpll_cmd_pin_dump_one(skb, pin, cb->extack);
> +               if (ret) {
> +                       genlmsg_cancel(skb, hdr);
> +                       break;
> +               }
> +               genlmsg_end(skb, hdr);
> +       }
> +       if (ret == -EMSGSIZE) {
> +               ctx->idx = i;
> +               return skb->len;
> +       }
> +       return ret;
> +}
> +
> +int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info
> *info)
> +{
> +       struct dpll_pin *pin = info->user_ptr[0];
> +
> +       return dpll_pin_set_from_nlattr(pin, info);
> +}
> +
> +static struct dpll_device *
> +dpll_device_find(u64 clock_id, struct nlattr *mod_name_attr,
> +                enum dpll_type type)
> +{
> +       struct dpll_device *dpll_match = NULL, *dpll;
> +       bool cid_match, mod_match, type_match;
> +       unsigned long i;
> +
> +       xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED)
> {
> +               cid_match = clock_id ? dpll->clock_id == clock_id :
> true;
> +               mod_match = mod_name_attr && module_name(dpll-
> >module) ?
> +                       !nla_strcmp(mod_name_attr,
> +                                   module_name(dpll->module)) :
> true;
> +               type_match = type ? dpll->type == type : true;
> +               if (cid_match && mod_match && type_match) {
> +                       if (dpll_match)
> +                               return NULL;
> +                       dpll_match = dpll;
> +               }
> +       }
> +
> +       return dpll_match;
> +}
> +
> +static int
> +dpll_device_find_from_nlattr(struct genl_info *info, struct sk_buff
> *skb)
> +{
> +       struct nlattr *attr, *mod_name_attr = NULL;
> +       struct dpll_device *dpll = NULL;
> +       enum dpll_type type = 0;
> +       u64 clock_id = 0;
> +       int rem = 0;
> +
> +       nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
> +                         genlmsg_len(info->genlhdr), rem) {
> +               switch (nla_type(attr)) {
> +               case DPLL_A_CLOCK_ID:
> +                       if (clock_id)
> +                               return -EINVAL;
> +                       clock_id = nla_get_u64(attr);
> +                       break;
> +               case DPLL_A_MODULE_NAME:
> +                       if (mod_name_attr)
> +                               return -EINVAL;
> +                       mod_name_attr = attr;
> +                       break;
> +               case DPLL_A_TYPE:
> +                       if (type)
> +                               return -EINVAL;
> +                       type = nla_get_u8(attr);
> +                       break;
> +               default:
> +                       break;
> +               }
> +       }
> +
> +       if (!clock_id && !mod_name_attr && !type)
> +               return -EINVAL;
> +       dpll = dpll_device_find(clock_id, mod_name_attr, type);
> +       if (!dpll)
> +               return -EINVAL;
> +
> +       return dpll_msg_add_dev_handle(skb, dpll);
> +}
> +
> +static int
> +dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info
> *info)
> +{
> +       const struct dpll_device_ops *ops = dpll_device_ops(dpll);
> +       struct nlattr *tb[DPLL_A_MAX + 1];
> +       int ret = 0;
> +
> +       nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
> +                 genlmsg_len(info->genlhdr), NULL, info->extack);
> +       if (tb[DPLL_A_MODE]) {
Hi,

Here should be something like:
if (!ops->mode_set)
return -EOPNOTSUPP;

Regards,
Petr
> +               ret = ops->mode_set(dpll, dpll_priv(dpll),
> +                                   nla_get_u8(tb[DPLL_A_MODE]),
> info->extack);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info
> *info)
> +{
> +       struct sk_buff *msg;
> +       struct nlattr *hdr;
> +       int ret;
> +
> +       msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> +       if (!msg)
> +               return -ENOMEM;
> +       hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
> +                               DPLL_CMD_DEVICE_ID_GET);
> +       if (!hdr)
> +               return -EMSGSIZE;
> +
> +       ret = dpll_device_find_from_nlattr(info, msg);
> +       if (ret) {
> +               nlmsg_free(msg);
> +               return ret;
> +       }
> +       genlmsg_end(msg, hdr);
> +
> +       return genlmsg_reply(msg, info);
> +}
> +
> +int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info
> *info)
> +{
> +       struct dpll_device *dpll = info->user_ptr[0];
> +       struct sk_buff *msg;
> +       struct nlattr *hdr;
> +       int ret;
> +
> +       msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> +       if (!msg)
> +               return -ENOMEM;
> +       hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
> +                               DPLL_CMD_DEVICE_GET);
> +       if (!hdr)
> +               return -EMSGSIZE;
> +
> +       ret = dpll_device_get_one(dpll, msg, info->extack);
> +       if (ret) {
> +               nlmsg_free(msg);
> +               return ret;
> +       }
> +       genlmsg_end(msg, hdr);
> +
> +       return genlmsg_reply(msg, info);
> +}
> +
> +int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info
> *info)
> +{
> +       struct dpll_device *dpll = info->user_ptr[0];
> +
> +       return dpll_set_from_nlattr(dpll, info);
> +}
> +
> +int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct
> netlink_callback *cb)
> +{
> +       struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
> +       struct dpll_device *dpll;
> +       struct nlattr *hdr;
> +       unsigned long i;
> +       int ret = 0;
> +
> +       xa_for_each_start(&dpll_device_xa, i, dpll, ctx->idx) {
> +               if (!xa_get_mark(&dpll_device_xa, i,
> DPLL_REGISTERED))
> +                       continue;
> +               hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
> +                                 cb->nlh->nlmsg_seq,
> &dpll_nl_family,
> +                                 NLM_F_MULTI, DPLL_CMD_DEVICE_GET);
> +               if (!hdr) {
> +                       ret = -EMSGSIZE;
> +                       break;
> +               }
> +               ret = dpll_device_get_one(dpll, skb, cb->extack);
> +               if (ret) {
> +                       genlmsg_cancel(skb, hdr);
> +                       break;
> +               }
> +               genlmsg_end(skb, hdr);
> +       }
> +       if (ret == -EMSGSIZE) {
> +               ctx->idx = i;
> +               return skb->len;
> +       }
> +       return ret;
> +}
> +
> +int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff
> *skb,
> +                 struct genl_info *info)
> +{
> +       struct dpll_device *dpll_id = NULL;
> +       u32 id;
> +
> +       if (!info->attrs[DPLL_A_ID])
> +               return -EINVAL;
> +
> +       mutex_lock(&dpll_lock);
> +       id = nla_get_u32(info->attrs[DPLL_A_ID]);
> +
> +       dpll_id = dpll_device_get_by_id(id);
> +       if (!dpll_id)
> +               goto unlock;
> +       info->user_ptr[0] = dpll_id;
> +       return 0;
> +unlock:
> +       mutex_unlock(&dpll_lock);
> +       return -ENODEV;
> +}
> +
> +void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff
> *skb,
> +                   struct genl_info *info)
> +{
> +       mutex_unlock(&dpll_lock);
> +}
> +
> +int
> +dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff
> *skb,
> +                    struct genl_info *info)
> +{
> +       mutex_lock(&dpll_lock);
> +
> +       return 0;
> +}
> +
> +void
> +dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff
> *skb,
> +                  struct genl_info *info)
> +{
> +       mutex_unlock(&dpll_lock);
> +}
> +
> +int dpll_lock_dumpit(struct netlink_callback *cb)
> +{
> +       mutex_lock(&dpll_lock);
> +
> +       return 0;
> +}
> +
> +int dpll_unlock_dumpit(struct netlink_callback *cb)
> +{
> +       mutex_unlock(&dpll_lock);
> +
> +       return 0;
> +}
> +
> +int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct
> sk_buff *skb,
> +                     struct genl_info *info)
> +{
> +       int ret;
> +
> +       mutex_lock(&dpll_lock);
> +       if (!info->attrs[DPLL_A_PIN_ID]) {
> +               ret = -EINVAL;
> +               goto unlock_dev;
> +       }
> +       info->user_ptr[0] = xa_load(&dpll_pin_xa,
> +                                   nla_get_u32(info-
> >attrs[DPLL_A_PIN_ID]));
> +       if (!info->user_ptr[0]) {
> +               ret = -ENODEV;
> +               goto unlock_dev;
> +       }
> +
> +       return 0;
> +
> +unlock_dev:
> +       mutex_unlock(&dpll_lock);
> +       return ret;
> +}
> +
> +void dpll_pin_post_doit(const struct genl_split_ops *ops, struct
> sk_buff *skb,
> +                       struct genl_info *info)
> +{
> +       mutex_unlock(&dpll_lock);
> +}
> +
> +static int
> +dpll_device_event_send(enum dpll_cmd event, struct dpll_device
> *dpll)
> +{
> +       struct sk_buff *msg;
> +       int ret = -EMSGSIZE;
> +       void *hdr;
> +
> +       if (!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED))
> +               return -ENODEV;
> +
> +       msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> +       if (!msg)
> +               return -ENOMEM;
> +       hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
> +       if (!hdr)
> +               goto out_free_msg;
> +       ret = dpll_device_get_one(dpll, msg, NULL);
> +       if (ret)
> +               goto out_cancel_msg;
> +       genlmsg_end(msg, hdr);
> +       genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
> +
> +       return 0;
> +
> +out_cancel_msg:
> +       genlmsg_cancel(msg, hdr);
> +out_free_msg:
> +       nlmsg_free(msg);
> +
> +       return ret;
> +}
> +
> +int dpll_device_create_ntf(struct dpll_device *dpll)
> +{
> +       return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF,
> dpll);
> +}
> +
> +int dpll_device_delete_ntf(struct dpll_device *dpll)
> +{
> +       return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF,
> dpll);
> +}
> +
> +int dpll_device_change_ntf(struct dpll_device *dpll)
> +{
> +       int ret = -EINVAL;
> +
> +       if (WARN_ON(!dpll))
> +               return ret;
> +
> +       mutex_lock(&dpll_lock);
> +       ret = dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF,
> dpll);
> +       mutex_unlock(&dpll_lock);
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(dpll_device_change_ntf);
> +
> +static int
> +dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
> +{
> +       struct dpll_pin *pin_verify;
> +       struct sk_buff *msg;
> +       int ret = -EMSGSIZE;
> +       void *hdr;
> +
> +       pin_verify = xa_load(&dpll_pin_xa, pin->id);
> +       if (pin != pin_verify)
> +               return -ENODEV;
> +
> +       msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> +       if (!msg)
> +               return -ENOMEM;
> +
> +       hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
> +       if (!hdr)
> +               goto out_free_msg;
> +       ret = __dpll_cmd_pin_dump_one(msg, pin, NULL);
> +       if (ret)
> +               goto out_cancel_msg;
> +       genlmsg_end(msg, hdr);
> +       genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
> +
> +       return 0;
> +
> +out_cancel_msg:
> +       genlmsg_cancel(msg, hdr);
> +out_free_msg:
> +       nlmsg_free(msg);
> +
> +       return ret;
> +}
> +
> +int dpll_pin_create_ntf(struct dpll_pin *pin)
> +{
> +       return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
> +}
> +
> +int dpll_pin_delete_ntf(struct dpll_pin *pin)
> +{
> +       return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
> +}
> +
> +static int __dpll_pin_change_ntf(struct dpll_pin *pin)
> +{
> +       return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
> +}
> +
> +int dpll_pin_change_ntf(struct dpll_pin *pin)
> +{
> +       int ret = -EINVAL;
> +
> +       if (WARN_ON(!pin))
> +               return ret;
> +
> +       mutex_lock(&dpll_lock);
> +       ret = __dpll_pin_change_ntf(pin);
> +       mutex_unlock(&dpll_lock);
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(dpll_pin_change_ntf);
> +
> +int __init dpll_netlink_init(void)
> +{
> +       return genl_register_family(&dpll_nl_family);
> +}
> +
> +void dpll_netlink_finish(void)
> +{
> +       genl_unregister_family(&dpll_nl_family);
> +}
> +
> +void __exit dpll_netlink_fini(void)
> +{
> +       dpll_netlink_finish();
> +}
> diff --git a/drivers/dpll/dpll_netlink.h
> b/drivers/dpll/dpll_netlink.h
> new file mode 100644
> index 000000000000..b5f9bfc88c9e
> --- /dev/null
> +++ b/drivers/dpll/dpll_netlink.h
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + *  Copyright (c) 2023 Meta Platforms, Inc. and affiliates
> + *  Copyright (c) 2023 Intel and affiliates
> + */
> +
> +/**
> + * dpll_device_create_ntf - notify that the device has been created
> + * @dpll: registered dpll pointer
> + *
> + * Context: caller shall hold dpll_xa_lock.
> + * Return: 0 if succeeds, error code otherwise.
> + */
> +int dpll_device_create_ntf(struct dpll_device *dpll);
> +
> +/**
> + * dpll_device_delete_ntf - notify that the device has been deleted
> + * @dpll: registered dpll pointer
> + *
> + * Context: caller shall hold dpll_xa_lock.
> + * Return: 0 if succeeds, error code otherwise.
> + */
> +int dpll_device_delete_ntf(struct dpll_device *dpll);
> +
> +/**
> + * dpll_pin_create_ntf - notify that the pin has been created
> + * @pin: registered pin pointer
> + *
> + * Context: caller shall hold dpll_xa_lock.
> + * Return: 0 if succeeds, error code otherwise.
> + */
> +int dpll_pin_create_ntf(struct dpll_pin *pin);
> +
> +/**
> + * dpll_pin_delete_ntf - notify that the pin has been deleted
> + * @pin: registered pin pointer
> + *
> + * Context: caller shall hold dpll_xa_lock.
> + * Return: 0 if succeeds, error code otherwise.
> + */
> +int dpll_pin_delete_ntf(struct dpll_pin *pin);
> +
> +int __init dpll_netlink_init(void);
> +void dpll_netlink_finish(void);

2023-06-21 12:09:12

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 04/10] dpll: netlink: Add DPLL framework base functions

Wed, Jun 21, 2023 at 01:18:59PM CEST, [email protected] wrote:
>Arkadiusz Kubalewski píše v Pá 09. 06. 2023 v 14:18 +0200:
>> From: Vadim Fedorenko <[email protected]>

[...]

Could you perhaps cut out the text you don't comment? Saves some time
finding your reply.


>> +static int
>> +dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info
>> *info)
>> +{
>> +       const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>> +       struct nlattr *tb[DPLL_A_MAX + 1];
>> +       int ret = 0;
>> +
>> +       nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
>> +                 genlmsg_len(info->genlhdr), NULL, info->extack);
>> +       if (tb[DPLL_A_MODE]) {
>Hi,
>
>Here should be something like:
> if (!ops->mode_set)
> return -EOPNOTSUPP;

Why? All drivers implement that.
I believe that it's actullaly better that way. For a called setting up
the same mode it is the dpll in, there should be 0 return by the driver.
Note that driver holds this value. I'd like to keep this code as it is.

[...]

2023-06-21 13:01:49

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

Mon, Jun 19, 2023 at 08:08:11PM CEST, [email protected] wrote:
>>From: Jiri Pirko <[email protected]>
>>Sent: Saturday, June 10, 2023 11:57 AM
>>
>>Fri, Jun 09, 2023 at 02:18:51PM CEST, [email protected] wrote:

[...]


>>>+/**
>>>+ * ice_dpll_cb_lock - lock dplls mutex in callback context
>>>+ * @pf: private board structure
>>>+ *
>>>+ * Lock the mutex from the callback operations invoked by dpll subsystem.
>>>+ * Prevent dead lock caused by `rmmod ice` when dpll callbacks are under
>>>stress
>>
>>"dead lock", really? Which one? Didn't you want to write "livelock"?
>>
>
>As explained, rmmod takes and destroys lock, it can happen that
>netlink request/ops hangs on trying to lock when rmmod started deinit.

Yeah, don't take the lock in rmmod, see below.


>
>>If this is livelock prevention, is this something you really see or
>>just an assumption? Seems to me unlikely.
>>
>>Plus, see my note in ice_dpll_init(). If you remove taking the lock from
>>ice_dpll_init() and ice_dpll_deinit(), do you still need this? I don't
>>think so.
>>
>
>I won't remove it from there, as the lock shall serialize the access to ice dpll
>data, so it must be held on init until everything is ready for netlink access,

Before you register the dpll device/pin, no netlink cmd can happen. So
do whatever you want in the init, no lock needed, register at the and
when you are ready. Same on deinit. After you unregister, no call can
happen, no need to lock anything in deinit.

Either I'm missing something really odd, or your locking scheme is very
wrong.

If this locking here is needed, could you please present an example of a
race it solves here?



>or when deinit takes place, until everything is released.
>
>>
>>>+ * tests.
>>>+ *
>>>+ * Return:
>>>+ * 0 - if lock acquired
>>>+ * negative - lock not acquired or dpll was deinitialized
>>>+ */
>>>+static int ice_dpll_cb_lock(struct ice_pf *pf)
>>>+{
>>>+ int i;
>>>+
>>>+ for (i = 0; i < ICE_DPLL_LOCK_TRIES; i++) {
>>>+ if (mutex_trylock(&pf->dplls.lock))
>>>+ return 0;
>>>+ usleep_range(100, 150);
>>>+ if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>
>>How exactly could this happen? I don't think it can. Drop it.
>>
>
>Asynchronously called deinit, and kworker tries to update at the same time.

Could you provide an example please?
Because I see 2 possible paths:
1) dpll op call
2) periodic work

But when ICE_FLAG_DPLL is cleared in ice_dpll_deinit(), no dpll op call
can happen anymore and no periodic work is scheduled.

Is this another "just in case" situation? If yes, please remove.
If no, please provide an example of a race this solves.


>
>>
>>>+ return -EFAULT;
>>>+ }
>>>+
>>>+ return -EBUSY;
>>>+}

[...]


>>>+static void ice_dpll_periodic_work(struct kthread_work *work)
>>>+{
>>>+ struct ice_dplls *d = container_of(work, struct ice_dplls,
>>>work.work);
>>>+ struct ice_pf *pf = container_of(d, struct ice_pf, dplls);
>>>+ struct ice_dpll *de = &pf->dplls.eec;
>>>+ struct ice_dpll *dp = &pf->dplls.pps;
>>>+ int ret = 0;
>>>+
>>>+ if (!test_bit(ICE_FLAG_DPLL, pf->flags))
>>
>>How exactly could this happen? I don't think it can. Drop it.
>>
>
>I will change a bit here, ice_dpll_cb_lock returns an error
>if that flag was already down, so will use it instead.

Yeah, I believe that this simply cannot happen as when the bit is
cleared, no work is scheduled anymore. See above.


>
>>
>>>+ return;
>>>+ ret = ice_dpll_cb_lock(pf);
>>>+ if (ret) {
>>>+ d->lock_err_num++;
>>

[...]


>
>>>+ mutex_lock(&pf->dplls.lock);
>>
>>Related to my question in ice_dpll_init(), why do you need to lock the
>>mutex
>>here?
>
>Because before deinit takes place on driver unload the user can still ask
>for the info from the dpll subsystem or kworker can try to alter the status.

When you unregister dpll and stop the kworker, you can't see anything
like that. No need to take this lock.


>
>>
>>
>>>+ ice_dpll_deinit_pins(pf, cgu);
>>>+ ice_dpll_deinit_info(pf);
>>>+ ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu);
>>>+ ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu);
>>
>>Please reorder to match error path in ice_dpll_init()
>>
>
>Fixed.
>
>>>+ if (cgu)
>>
>>In ice_dpll_init() you call this "cgu_present". Please be consistent in
>>naming.
>>
>
>Fixed.
>
>>
>>>+ ice_dpll_deinit_worker(pf);
>>>+ clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>+ mutex_unlock(&pf->dplls.lock);
>>>+ mutex_destroy(&pf->dplls.lock);
>>>+ }
>>>+}
>>>+
>>>+/**
>>>+ * ice_dpll_init_info_direct_pins - initializes direct pins info
>>>+ * @pf: board private structure
>>>+ * @pin_type: type of pins being initialized
>>>+ *
>>>+ * Init information for directly connected pins, cache them in pf's pins
>>>+ * structures.
>>>+ *
>>>+ * Context: Function initializes and holds pf->dplls.lock mutex.
>>>+ * Return:
>>>+ * * 0 - success
>>>+ * * negative - init failure reason
>>>+ */
>>>+static int
>>>+ice_dpll_init_info_direct_pins(struct ice_pf *pf,
>>>+ enum ice_dpll_pin_type pin_type)
>>>+{
>>>+ struct ice_dpll *de = &pf->dplls.eec, *dp = &pf->dplls.pps;
>>>+ int num_pins, i, ret = -EINVAL;
>>>+ struct ice_hw *hw = &pf->hw;
>>>+ struct ice_dpll_pin *pins;
>>>+ u8 freq_supp_num;
>>>+ bool input;
>>>+
>>>+ switch (pin_type) {
>>>+ case ICE_DPLL_PIN_TYPE_INPUT:
>>>+ pins = pf->dplls.inputs;
>>>+ num_pins = pf->dplls.num_inputs;
>>>+ input = true;
>>>+ break;
>>>+ case ICE_DPLL_PIN_TYPE_OUTPUT:
>>>+ pins = pf->dplls.outputs;
>>>+ num_pins = pf->dplls.num_outputs;
>>>+ input = false;
>>>+ break;
>>>+ default:
>>>+ return ret;
>>>+ }
>>>+
>>>+ for (i = 0; i < num_pins; i++) {
>>>+ pins[i].idx = i;
>>>+ pins[i].prop.board_label = ice_cgu_get_pin_name(hw, i, input);
>>>+ pins[i].prop.type = ice_cgu_get_pin_type(hw, i, input);
>>>+ if (input) {
>>>+ ret = ice_aq_get_cgu_ref_prio(hw, de->dpll_idx, i,
>>>+ &de->input_prio[i]);
>>>+ if (ret)
>>>+ return ret;
>>>+ ret = ice_aq_get_cgu_ref_prio(hw, dp->dpll_idx, i,
>>>+ &dp->input_prio[i]);
>>>+ if (ret)
>>>+ return ret;
>>>+ pins[i].prop.capabilities |=
>>>+ DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE;
>>>+ }
>>>+ pins[i].prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>>+ ret = ice_dpll_pin_state_update(pf, &pins[i], pin_type);
>>>+ if (ret)
>>>+ return ret;
>>>+ pins[i].prop.freq_supported =
>>>+ ice_cgu_get_pin_freq_supp(hw, i, input, &freq_supp_num);
>>>+ pins[i].prop.freq_supported_num = freq_supp_num;
>>>+ pins[i].pf = pf;
>>>+ }
>>>+
>>>+ return ret;
>>>+}
>>>+
>>>+/**
>>>+ * ice_dpll_init_rclk_pin - initializes rclk pin information
>>>+ * @pf: board private structure
>>>+ * @pin_type: type of pins being initialized
>>>+ *
>>>+ * Init information for rclk pin, cache them in pf->dplls.rclk.
>>>+ *
>>>+ * Return:
>>>+ * * 0 - success
>>>+ * * negative - init failure reason
>>>+ */
>>>+static int ice_dpll_init_rclk_pin(struct ice_pf *pf)
>>>+{
>>>+ struct ice_dpll_pin *pin = &pf->dplls.rclk;
>>>+ struct device *dev = ice_pf_to_dev(pf);
>>>+
>>>+ pin->prop.board_label = dev_name(dev);
>>
>>What??? Must be some sort of joke, correct?
>>"board_label" should be an actual writing on a board. For syncE, I don't
>>think it makes sense to fill any label. The connection to the netdev
>>should be enough. That is what I do in mlx5.
>>
>>Please drop this.
>>
>
>No, we want a label, as this is recovered clock, will change it to

Okay, so it is recovered clock, so what? I want a lot of things, that
does not make them meaningful.


>package_label but the name will stay for now, this is much more meaningful
>then i.e. "phy0" or "RCLK".

No, dev_name() here is total non-sense!
The label should contain the actual label as for example a writing on a
front panel, board, etc.

Why exactly do you need this? Why a link from netdev to this dpll pin is
not enough for you.

Please describe exactly what you need the label for. Usecases, examples,
etc.

Jakub, if you read this, this is very nice example of a misuse that even
very precisely defined netlink attribute cannot prevent :/


>
>>
>>
>>>+ pin->prop.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
>>>+ pin->prop.capabilities |= DPLL_PIN_CAPS_STATE_CAN_CHANGE;
>>>+ pin->pf = pf;
>>>+

[...]


>>>+int ice_dpll_init(struct ice_pf *pf)
>>>+{
>>>+ bool cgu_present = ice_is_feature_supported(pf, ICE_F_CGU);
>>>+ struct ice_dplls *d = &pf->dplls;
>>>+ int err = 0;
>>>+
>>>+ mutex_init(&d->lock);
>>>+ mutex_lock(&d->lock);
>>
>>Seeing pattern like this always triggers questions.
>>Why exactly do you need to lock the mutex here?
>>
>
>Could do it few lines below before the dplls are inited,
>but this would make error path confusing.

See above, you don't need to do this if you register dpll and start the
periodic work only after everything is ready. I believe that you
actually have it like this now.


>
>>
>>>+ err = ice_dpll_init_info(pf, cgu_present);
>>>+ if (err)
>>>+ goto err_exit;
>>>+ err = ice_dpll_init_dpll(pf, &pf->dplls.eec, cgu_present,
>>>+ DPLL_TYPE_EEC);
>>>+ if (err)
>>>+ goto deinit_info;
>>>+ err = ice_dpll_init_dpll(pf, &pf->dplls.pps, cgu_present,
>>>+ DPLL_TYPE_PPS);
>>>+ if (err)
>>>+ goto deinit_eec;
>>>+ err = ice_dpll_init_pins(pf, cgu_present);
>>>+ if (err)
>>>+ goto deinit_pps;
>>>+ set_bit(ICE_FLAG_DPLL, pf->flags);
>>>+ if (cgu_present) {
>>>+ err = ice_dpll_init_worker(pf);
>>>+ if (err)
>>>+ goto deinit_pins;
>>>+ }
>>>+ mutex_unlock(&d->lock);
>>>+ dev_info(ice_pf_to_dev(pf), "DPLLs init successful\n");
>>
>>What is this good for? Please avoid polluting dmesg and drop this.
>>
>
>Sure, removed.
>
>>
>>>+
>>>+ return err;
>>>+
>>>+deinit_pins:
>>>+ ice_dpll_deinit_pins(pf, cgu_present);
>>>+deinit_pps:
>>>+ ice_dpll_deinit_dpll(pf, &pf->dplls.pps, cgu_present);
>>>+deinit_eec:
>>>+ ice_dpll_deinit_dpll(pf, &pf->dplls.eec, cgu_present);
>>>+deinit_info:
>>>+ ice_dpll_deinit_info(pf);
>>>+err_exit:
>>>+ clear_bit(ICE_FLAG_DPLL, pf->flags);
>>>+ mutex_unlock(&d->lock);
>>>+ mutex_destroy(&d->lock);
>>>+ dev_warn(ice_pf_to_dev(pf), "DPLLs init failure err:\n");
>>
>>You are missing the err. But why do you need the message?
>>
>
>To give a clue that something went wrong on dpll init.

Yeah, you ignore the err in the caller. That makes sense.
Don't forget to add the "err" :)


>
>>
>>>+
>>>+ return err;


[...]

2023-06-21 13:29:57

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

Mon, Jun 19, 2023 at 10:34:12PM CEST, [email protected] wrote:
>>From: Jiri Pirko <[email protected]>
>>Sent: Saturday, June 10, 2023 6:37 PM
>>
>>Fri, Jun 09, 2023 at 02:18:51PM CEST, [email protected] wrote:
>>
>>[...]
>>
>>
>>>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>>>+ enum dpll_mode *mode,
>>>+ struct netlink_ext_ack *extack)
>>>+{
>>>+ *mode = DPLL_MODE_AUTOMATIC;
>>
>>I don't understand how the automatic mode could work with SyncE. The
>>There is one pin exposed for one netdev. The SyncE daemon should select
>>exacly one pin. How do you achieve that?
>>Is is by setting DPLL_PIN_STATE_SELECTABLE on the pin-netdev you want to
>>select and DPLL_PIN_STATE_DISCONNECTED on the rest?
>>
>>
>>[...]
>
>AUTOMATIC mode autoselects highest priority valid signal.
>As you have pointed out, for SyncE selection, the user must be able to manually
>select a pin state to enable recovery of signal from particular port.
>
>In "ice" case there are 2 pins for network PHY clock signal recovery, and both
>are parent pins (MUX-type). There are also 4 pins assigned to netdevs (one per
>port). Thus passing a signal from PHY to the pin is done through the MUX-pin,
>by selecting proper state on pin-parent pair (where parent pins is highest prio
>pin on dpll).

Could you show me some examples please?


>
>Thank you!
>Arkadiusz

2023-06-21 13:52:19

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 04/10] dpll: netlink: Add DPLL framework base functions

Wed, Jun 21, 2023 at 01:53:24PM CEST, [email protected] wrote:
>Wed, Jun 21, 2023 at 01:18:59PM CEST, [email protected] wrote:
>>Arkadiusz Kubalewski píše v Pá 09. 06. 2023 v 14:18 +0200:
>>> From: Vadim Fedorenko <[email protected]>
>
>[...]
>
>Could you perhaps cut out the text you don't comment? Saves some time
>finding your reply.
>
>
>>> +static int
>>> +dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info
>>> *info)
>>> +{
>>> +       const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>>> +       struct nlattr *tb[DPLL_A_MAX + 1];
>>> +       int ret = 0;
>>> +
>>> +       nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
>>> +                 genlmsg_len(info->genlhdr), NULL, info->extack);
>>> +       if (tb[DPLL_A_MODE]) {
>>Hi,
>>
>>Here should be something like:
>> if (!ops->mode_set)
>> return -EOPNOTSUPP;
>
>Why? All drivers implement that.
>I believe that it's actullaly better that way. For a called setting up
>the same mode it is the dpll in, there should be 0 return by the driver.
>Note that driver holds this value. I'd like to keep this code as it is.

Actually, you are correct Petr, my mistake. Actually, no driver
implements this. Arkadiusz, could you please remove this op and
possibly any other unused op? It will be added when needed.

Thanks!


>
>[...]

2023-06-21 16:54:57

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

>From: Jiri Pirko <[email protected]>
>Sent: Saturday, June 10, 2023 7:38 PM
>
>Fri, Jun 09, 2023 at 02:18:46PM CEST, [email protected] wrote:
>>From: Vadim Fedorenko <[email protected]>
>>
>>DPLL framework is used to represent and configure DPLL devices
>>in systems. Each device that has DPLL and can configure inputs
>>and outputs can use this framework.
>>
>>Implement core framework functions for further interactions
>>with device drivers implementing dpll subsystem, as well as for
>>interactions of DPLL netlink framework part with the subsystem
>>itself.
>>
>>Co-developed-by: Milena Olech <[email protected]>
>>Signed-off-by: Milena Olech <[email protected]>
>>Co-developed-by: Michal Michalik <[email protected]>
>>Signed-off-by: Michal Michalik <[email protected]>
>>Signed-off-by: Vadim Fedorenko <[email protected]>
>>Co-developed-by: Arkadiusz Kubalewski <[email protected]>
>>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>>---
>> drivers/dpll/dpll_core.c | 953 +++++++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_core.h | 104 +++++
>
>Overall, looks very good! I pinpointed couple of nits below, nothing big.
>General question: Why do you put documentation comment to every static
>function? Does not make any sense to me. Even for non-exported functions
>I think it is overkill. Most of them (if not all) give the reader no
>additional information and only make the code a bit harder to read.
>Care to drop them?
>
>
>> 2 files changed, 1057 insertions(+)
>> create mode 100644 drivers/dpll/dpll_core.c
>> create mode 100644 drivers/dpll/dpll_core.h
>>
>>diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
>>new file mode 100644
>>index 000000000000..ee515b7c18be
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.c
>>@@ -0,0 +1,953 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ * dpll_core.c - Generic DPLL Management class support.
>
>What's "management class support"? Does not sound recognizable.
>

Fixed.

>
>>+ *
>>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>>+ * Copyright (c) 2023 Intel Corporation.
>>+ */
>>+
>>+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>+
>>+#include <linux/device.h>
>>+#include <linux/err.h>
>>+#include <linux/slab.h>
>>+#include <linux/string.h>
>>+
>>+#include "dpll_core.h"
>>+
>>+DEFINE_MUTEX(dpll_lock);
>>+
>>+DEFINE_XARRAY_FLAGS(dpll_device_xa, XA_FLAGS_ALLOC);
>>+DEFINE_XARRAY_FLAGS(dpll_pin_xa, XA_FLAGS_ALLOC);
>>+
>>+#define ASSERT_DPLL_REGISTERED(d) \
>>+ WARN_ON_ONCE(!xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>>+#define ASSERT_DPLL_NOT_REGISTERED(d) \
>>+ WARN_ON_ONCE(xa_get_mark(&dpll_device_xa, (d)->id, DPLL_REGISTERED))
>>+
>>+/**
>>+ * dpll_device_get_by_id - find dpll device by it's id
>>+ * @id: id of searched dpll
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return:
>>+ * * dpll_device struct if found
>>+ * * NULL otherwise
>>+ */
>>+struct dpll_device *dpll_device_get_by_id(int id)
>>+{
>>+ if (xa_get_mark(&dpll_device_xa, id, DPLL_REGISTERED))
>>+ return xa_load(&dpll_device_xa, id);
>>+
>>+ return NULL;
>>+}
>>+
>>+/**
>>+ * dpll_pin_registration_find - find a pin registration record
>>+ * @ref: reference between dpll and pin
>>+ * @ops: searched pin ops pointer
>>+ * @priv: searched pin priv pointer
>>+ *
>>+ * Search dpll's registered pins for given ops and priv data.
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return:
>>+ * * NULL - if pin was not found
>>+ * * pointer to `struct dpll_pin_registration` if found
>>+ */
>>+static struct dpll_pin_registration *
>>+dpll_pin_registration_find(struct dpll_pin_ref *ref,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ struct dpll_pin_registration *reg;
>>+
>>+ list_for_each_entry(reg, &ref->registration_list, list) {
>>+ if (reg->ops == ops && reg->priv == priv)
>>+ return reg;
>>+ }
>>+ return NULL;
>>+}
>>+
>>+/**
>>+ * dpll_xa_ref_pin_add - add pin reference to a given xarray
>>+ * @xa_pins: dpll_pin_ref xarray holding pins
>>+ * @pin: pin being added
>>+ * @ops: ops for a pin
>>+ * @priv: pointer to private data of owner
>>+ *
>>+ * Allocate and create reference of a pin and enlist a registration
>>+ * structure storing ops and priv pointers of a caller registant.
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return:
>>+ * * 0 on success
>>+ * * -ENOMEM on failed allocation
>>+ */
>>+static int
>>+dpll_xa_ref_pin_add(struct xarray *xa_pins, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ struct dpll_pin_registration *reg;
>>+ struct dpll_pin_ref *ref;
>>+ bool ref_exists = false;
>>+ unsigned long i;
>>+ int ret;
>>+
>>+ xa_for_each(xa_pins, i, ref) {
>>+ if (ref->pin != pin)
>>+ continue;
>>+ reg = dpll_pin_registration_find(ref, ops, priv);
>>+ if (reg) {
>>+ refcount_inc(&ref->refcount);
>>+ return 0;
>>+ }
>>+ ref_exists = true;
>>+ break;
>>+ }
>>+
>>+ if (!ref_exists) {
>>+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>>+ if (!ref)
>>+ return -ENOMEM;
>>+ ref->pin = pin;
>>+ INIT_LIST_HEAD(&ref->registration_list);
>>+ ret = xa_insert(xa_pins, pin->pin_idx, ref, GFP_KERNEL);
>>+ if (ret) {
>>+ kfree(ref);
>>+ return ret;
>>+ }
>>+ refcount_set(&ref->refcount, 1);
>>+ }
>>+
>>+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>>+ if (!reg) {
>>+ if (!ref_exists)
>>+ kfree(ref);
>>+ return -ENOMEM;
>>+ }
>>+ reg->ops = ops;
>>+ reg->priv = priv;
>>+ if (ref_exists)
>
>If you set refcount to 0 couple of lines above, you can drop the check
>here.
>

Fixed.

>
>>+ refcount_inc(&ref->refcount);
>>+ list_add_tail(&reg->list, &ref->registration_list);
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * dpll_xa_ref_pin_del - remove reference of a pin from xarray
>>+ * @xa_pins: dpll_pin_ref xarray holding pins
>>+ * @pin: pointer to a pin being removed
>>+ * @ops: pointer to ops of pin being removed
>>+ * @priv: pointer to private data of registerer who invoked pin removal
>>+ *
>>+ * Decrement refcount of existing pin reference on given xarray.
>>+ * If all registrations are lifted delete the reference and free its memory.
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return:
>>+ * * 0 on success
>>+ * * -EINVAL if reference to a pin was not found
>>+ */
>>+static int dpll_xa_ref_pin_del(struct xarray *xa_pins, struct dpll_pin
>>*pin,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ struct dpll_pin_registration *reg;
>>+ struct dpll_pin_ref *ref;
>>+ unsigned long i;
>>+
>>+ xa_for_each(xa_pins, i, ref) {
>>+ if (ref->pin != pin)
>>+ continue;
>>+ reg = dpll_pin_registration_find(ref, ops, priv);
>>+ if (WARN_ON(!reg))
>>+ return -EINVAL;
>>+ if (refcount_dec_and_test(&ref->refcount)) {
>>+ list_del(&reg->list);
>>+ kfree(reg);
>>+ xa_erase(xa_pins, i);
>>+ WARN_ON(!list_empty(&ref->registration_list));
>>+ kfree(ref);
>>+ }
>>+ return 0;
>>+ }
>>+
>>+ return -EINVAL;
>>+}
>>+
>>+/**
>>+ * dpll_xa_ref_dpll_add - add dpll reference to a given xarray
>>+ * @xa_dplls: dpll_pin_ref xarray holding dplls
>>+ * @dpll: dpll being added
>>+ * @ops: pin-reference ops for a dpll
>>+ * @priv: pointer to private data of owner
>>+ *
>>+ * Allocate and create reference of a dpll-pin ops or increase refcount
>>+ * on existing dpll reference on given xarray.
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return:
>>+ * * 0 on success
>>+ * * -ENOMEM on failed allocation
>>+ */
>>+static int
>>+dpll_xa_ref_dpll_add(struct xarray *xa_dplls, struct dpll_device *dpll,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ struct dpll_pin_registration *reg;
>>+ struct dpll_pin_ref *ref;
>>+ bool ref_exists = false;
>>+ unsigned long i;
>>+ int ret;
>>+
>>+ xa_for_each(xa_dplls, i, ref) {
>>+ if (ref->dpll != dpll)
>>+ continue;
>>+ reg = dpll_pin_registration_find(ref, ops, priv);
>>+ if (reg) {
>>+ refcount_inc(&ref->refcount);
>>+ return 0;
>>+ }
>>+ ref_exists = true;
>>+ break;
>>+ }
>>+
>>+ if (!ref_exists) {
>>+ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>>+ if (!ref)
>>+ return -ENOMEM;
>>+ ref->dpll = dpll;
>>+ INIT_LIST_HEAD(&ref->registration_list);
>>+ ret = xa_insert(xa_dplls, dpll->device_idx, ref, GFP_KERNEL);
>>+ if (ret) {
>>+ kfree(ref);
>>+ return ret;
>>+ }
>>+ refcount_set(&ref->refcount, 1);
>>+ }
>>+
>>+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>>+ if (!reg) {
>>+ if (!ref_exists)
>>+ kfree(ref);
>>+ return -ENOMEM;
>>+ }
>>+ reg->ops = ops;
>>+ reg->priv = priv;
>>+ if (ref_exists)
>
>If you set refcount to 0 couple of lines above, you can drop the check
>here.
>

Fixed.

>
>>+ refcount_inc(&ref->refcount);
>>+ list_add_tail(&reg->list, &ref->registration_list);
>>+
>>+ return 0;
>>+}
>>+
>>+/**
>>+ * dpll_xa_ref_dpll_del - remove reference of a dpll from xarray
>>+ * @xa_dplls: dpll_pin_ref xarray holding dplls
>>+ * @dpll: pointer to a dpll to remove
>>+ * @ops: pointer to ops of dpll being removed
>>+ * @priv: pointer to private data of registerer who invoked dpll removal
>>+ *
>>+ * Decrement refcount of existing dpll reference on given xarray.
>>+ * If all references are dropped, delete the reference and free its memory.
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ */
>>+static void
>>+dpll_xa_ref_dpll_del(struct xarray *xa_dplls, struct dpll_device *dpll,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ struct dpll_pin_registration *reg;
>>+ struct dpll_pin_ref *ref;
>>+ unsigned long i;
>>+
>>+ xa_for_each(xa_dplls, i, ref) {
>>+ if (ref->dpll != dpll)
>>+ continue;
>>+ reg = dpll_pin_registration_find(ref, ops, priv);
>>+ if (WARN_ON(!reg))
>>+ return;
>>+ if (refcount_dec_and_test(&ref->refcount)) {
>>+ list_del(&reg->list);
>>+ kfree(reg);
>>+ xa_erase(xa_dplls, i);
>>+ WARN_ON(!list_empty(&ref->registration_list));
>>+ kfree(ref);
>>+ }
>>+ return;
>>+ }
>>+}
>>+
>>+/**
>>+ * dpll_xa_ref_dpll_find - find dpll reference on xarray
>>+ * @xa_refs: dpll_pin_ref xarray holding dpll references
>>+ * @dpll: pointer to a dpll being searched
>>+ *
>>+ * Search for dpll-pin ops reference struct of a given dpll on given xarray.
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return:
>>+ * * pin reference struct pointer on success
>>+ * * NULL - reference to a pin was not found
>>+ */
>>+struct dpll_pin_ref *
>>+dpll_xa_ref_dpll_find(struct xarray *xa_refs, const struct dpll_device *dpll)
>>+{
>>+ struct dpll_pin_ref *ref;
>>+ unsigned long i;
>>+
>>+ xa_for_each(xa_refs, i, ref) {
>>+ if (ref->dpll == dpll)
>>+ return ref;
>>+ }
>>+
>>+ return NULL;
>>+}
>>+
>>+/**
>>+ * dpll_xa_ref_dpll_first - find first record of given xarray
>>+ * @xa_refs: xarray
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return: first element on given xaaray
>>+ */
>>+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs)
>>+{
>>+ struct dpll_pin_ref *ref;
>>+ unsigned long i = 0;
>>+
>>+ ref = xa_find(xa_refs, &i, ULONG_MAX, XA_PRESENT);
>>+ WARN_ON(!ref);
>>+ return ref;
>>+}
>>+
>>+/**
>>+ * dpll_device_alloc - allocate the memory for dpll device
>>+ * @clock_id: clock_id of creator
>>+ * @device_idx: id given by dev driver
>>+ * @module: reference to registering module
>>+ *
>>+ * Allocates memory and initialize dpll device, hold its reference on
>>global
>>+ * xarray.
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return:
>>+ * * valid dpll_device struct pointer if succeeded
>>+ * * ERR_PTR(-ENOMEM) - failed memory allocation
>>+ * * ERR_PTR(X) - failed allocation on dpll's xa
>>+ */
>>+static struct dpll_device *
>>+dpll_device_alloc(const u64 clock_id, u32 device_idx, struct module *module)
>>+{
>>+ struct dpll_device *dpll;
>>+ int ret;
>>+
>>+ dpll = kzalloc(sizeof(*dpll), GFP_KERNEL);
>>+ if (!dpll)
>>+ return ERR_PTR(-ENOMEM);
>>+ refcount_set(&dpll->refcount, 1);
>>+ INIT_LIST_HEAD(&dpll->registration_list);
>>+ dpll->device_idx = device_idx;
>>+ dpll->clock_id = clock_id;
>>+ dpll->module = module;
>>+ ret = xa_alloc(&dpll_device_xa, &dpll->id, dpll, xa_limit_16b,
>>+ GFP_KERNEL);
>>+ if (ret) {
>>+ kfree(dpll);
>>+ return ERR_PTR(ret);
>>+ }
>>+ xa_init_flags(&dpll->pin_refs, XA_FLAGS_ALLOC);
>>+
>>+ return dpll;
>>+}
>>+
>>+/**
>>+ * dpll_device_get - find existing or create new dpll device
>>+ * @clock_id: clock_id of creator
>>+ * @device_idx: idx given by device driver
>>+ * @module: reference to registering module
>>+ *
>>+ * Get existing object of a dpll device, unique for given arguments.
>>+ * Create new if doesn't exist yet.
>>+ *
>>+ * Context: Acquires a lock (dpll_lock)
>>+ * Return:
>>+ * * valid dpll_device struct pointer if succeeded
>>+ * * ERR_PTR(-ENOMEM) - failed memory allocation
>>+ * * ERR_PTR(X) - failed allocation on dpll's xa
>>+ */
>>+struct dpll_device *
>>+dpll_device_get(u64 clock_id, u32 device_idx, struct module *module)
>>+{
>>+ struct dpll_device *dpll, *ret = NULL;
>>+ unsigned long index;
>>+
>>+ mutex_lock(&dpll_lock);
>>+ xa_for_each(&dpll_device_xa, index, dpll) {
>>+ if (dpll->clock_id == clock_id &&
>>+ dpll->device_idx == device_idx &&
>>+ dpll->module == module) {
>>+ ret = dpll;
>>+ refcount_inc(&ret->refcount);
>>+ break;
>>+ }
>>+ }
>>+ if (!ret)
>>+ ret = dpll_device_alloc(clock_id, device_idx, module);
>>+ mutex_unlock(&dpll_lock);
>>+
>>+ return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_get);
>>+
>>+/**
>>+ * dpll_device_put - decrease the refcount and free memory if possible
>>+ * @dpll: dpll_device struct pointer
>>+ *
>>+ * Context: Acquires a lock (dpll_lock)
>>+ * Drop reference for a dpll device, if all references are gone, delete
>>+ * dpll device object.
>>+ */
>>+void dpll_device_put(struct dpll_device *dpll)
>>+{
>>+ if (!dpll)
>
>Drop this check and rely on a basic driver sanity. I'm pretty sure I
>asked you to drop checks like this during the last version review.
>

Fixed.

>
>>+ return;
>>+ mutex_lock(&dpll_lock);
>>+ if (refcount_dec_and_test(&dpll->refcount)) {
>>+ ASSERT_DPLL_NOT_REGISTERED(dpll);
>>+ WARN_ON_ONCE(!xa_empty(&dpll->pin_refs));
>>+ xa_destroy(&dpll->pin_refs);
>>+ xa_erase(&dpll_device_xa, dpll->id);
>>+ WARN_ON(!list_empty(&dpll->registration_list));
>>+ kfree(dpll);
>>+ }
>>+ mutex_unlock(&dpll_lock);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_put);
>>+
>>+static struct dpll_device_registration *
>>+dpll_device_registration_find(struct dpll_device *dpll,
>>+ const struct dpll_device_ops *ops, void *priv)
>>+{
>>+ struct dpll_device_registration *reg;
>>+
>>+ list_for_each_entry(reg, &dpll->registration_list, list) {
>>+ if (reg->ops == ops && reg->priv == priv)
>>+ return reg;
>>+ }
>>+ return NULL;
>>+}
>>+
>>+/**
>>+ * dpll_device_register - register the dpll device in the subsystem
>>+ * @dpll: pointer to a dpll
>>+ * @type: type of a dpll
>>+ * @ops: ops for a dpll device
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Make dpll device available for user space.
>>+ *
>>+ * Context: Acquires a lock (dpll_lock)
>>+ * Return:
>>+ * * 0 on success
>>+ * * -EINVAL on failure due to wrong arguments provided
>>+ * * -EEXIST if device was already registered
>>+ */
>>+int dpll_device_register(struct dpll_device *dpll, enum dpll_type type,
>>+ const struct dpll_device_ops *ops, void *priv)
>>+{
>>+ struct dpll_device_registration *reg;
>>+ bool first_registration = false;
>>+
>>+ if (WARN_ON(!ops))
>>+ return -EINVAL;
>>+ if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX))
>>+ return -EINVAL;
>>+
>>+ mutex_lock(&dpll_lock);
>>+ reg = dpll_device_registration_find(dpll, ops, priv);
>>+ if (reg) {
>>+ mutex_unlock(&dpll_lock);
>>+ return -EEXIST;
>>+ }
>>+
>>+ reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>>+ if (!reg) {
>>+ mutex_unlock(&dpll_lock);
>>+ return -EEXIST;
>
>-ENOMEM;
>

Fixed.

>
>>+ }
>>+ reg->ops = ops;
>>+ reg->priv = priv;
>>+ dpll->type = type;
>>+ first_registration = list_empty(&dpll->registration_list);
>>+ list_add_tail(&reg->list, &dpll->registration_list);
>>+ if (!first_registration) {
>>+ mutex_unlock(&dpll_lock);
>>+ return 0;
>>+ }
>>+
>>+ xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>+ mutex_unlock(&dpll_lock);
>>+ dpll_device_create_ntf(dpll);
>>+
>>+ return 0;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_register);
>>+
>>+/**
>>+ * dpll_device_unregister - unregister dpll device
>>+ * @dpll: registered dpll pointer
>>+ * @ops: ops for a dpll device
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Unregister device, make it unavailable for userspace.
>>+ * Note: It does not free the memory
>>+ * Context: Acquires a lock (dpll_lock)
>>+ */
>>+void dpll_device_unregister(struct dpll_device *dpll,
>>+ const struct dpll_device_ops *ops, void *priv)
>>+{
>>+ struct dpll_device_registration *reg;
>>+
>>+ mutex_lock(&dpll_lock);
>>+ ASSERT_DPLL_REGISTERED(dpll);
>>+ dpll_device_delete_ntf(dpll);
>>+ reg = dpll_device_registration_find(dpll, ops, priv);
>>+ if (WARN_ON(!reg)) {
>>+ mutex_unlock(&dpll_lock);
>>+ return;
>>+ }
>>+ list_del(&reg->list);
>>+ kfree(reg);
>>+
>>+ if (!list_empty(&dpll->registration_list)) {
>>+ mutex_unlock(&dpll_lock);
>>+ return;
>>+ }
>>+ xa_clear_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>>+ mutex_unlock(&dpll_lock);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_unregister);
>>+
>>+/**
>>+ * dpll_pin_alloc - allocate the memory for dpll pin
>>+ * @clock_id: clock_id of creator
>>+ * @pin_idx: idx given by dev driver
>>+ * @module: reference to registering module
>>+ * @prop: dpll pin properties
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return:
>>+ * * valid allocated dpll_pin struct pointer if succeeded
>>+ * * ERR_PTR(-ENOMEM) - failed memory allocation
>>+ * * ERR_PTR(-EINVAL) - wrong pin type property value
>>+ */
>>+static struct dpll_pin *
>>+dpll_pin_alloc(u64 clock_id, u32 pin_idx, struct module *module,
>>+ const struct dpll_pin_properties *prop)
>>+{
>>+ struct dpll_pin *pin;
>>+ int ret;
>>+
>>+ pin = kzalloc(sizeof(*pin), GFP_KERNEL);
>>+ if (!pin)
>>+ return ERR_PTR(-ENOMEM);
>>+ pin->pin_idx = pin_idx;
>>+ pin->clock_id = clock_id;
>>+ pin->module = module;
>>+ if (WARN_ON(prop->type < DPLL_PIN_TYPE_MUX ||
>>+ prop->type > DPLL_PIN_TYPE_MAX)) {
>>+ ret = -EINVAL;
>>+ goto err;
>>+ }
>>+ pin->prop = prop;
>>+ refcount_set(&pin->refcount, 1);
>>+ xa_init_flags(&pin->dpll_refs, XA_FLAGS_ALLOC);
>>+ xa_init_flags(&pin->parent_refs, XA_FLAGS_ALLOC);
>>+ ret = xa_alloc(&dpll_pin_xa, &pin->id, pin, xa_limit_16b, GFP_KERNEL);
>>+ if (ret)
>>+ goto err;
>>+ return pin;
>>+err:
>>+ xa_destroy(&pin->dpll_refs);
>>+ xa_destroy(&pin->parent_refs);
>>+ kfree(pin);
>>+ return ERR_PTR(ret);
>>+}
>>+
>>+/**
>>+ * dpll_pin_get - find existing or create new dpll pin
>>+ * @clock_id: clock_id of creator
>>+ * @pin_idx: idx given by dev driver
>>+ * @module: reference to registering module
>>+ * @prop: dpll pin properties
>>+ *
>>+ * Get existing object of a pin (unique for given arguments) or create new
>>+ * if doesn't exist yet.
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return:
>>+ * * valid allocated dpll_pin struct pointer if succeeded
>>+ * * ERR_PTR of an error
>>+ */
>>+struct dpll_pin *
>>+dpll_pin_get(u64 clock_id, u32 pin_idx, struct module *module,
>>+ const struct dpll_pin_properties *prop)
>>+{
>>+ struct dpll_pin *pos, *ret = NULL;
>>+ unsigned long i;
>>+
>>+ xa_for_each(&dpll_pin_xa, i, pos) {
>>+ if (pos->clock_id == clock_id &&
>>+ pos->pin_idx == pin_idx &&
>>+ pos->module == module) {
>>+ ret = pos;
>>+ refcount_inc(&ret->refcount);
>>+ break;
>>+ }
>>+ }
>>+ if (!ret)
>>+ ret = dpll_pin_alloc(clock_id, pin_idx, module, prop);
>>+
>>+ return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_get);
>>+
>>+/**
>>+ * dpll_pin_put - decrease the refcount and free memory if possible
>>+ * @pin: pointer to a pin to be put
>>+ *
>>+ * Drop reference for a pin, if all references are gone, delete pin object.
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ */
>>+void dpll_pin_put(struct dpll_pin *pin)
>>+{
>>+ if (!pin)
>
>Drop this check.
>

Fixed.

>
>>+ return;
>>+ if (refcount_dec_and_test(&pin->refcount)) {
>>+ xa_destroy(&pin->dpll_refs);
>>+ xa_destroy(&pin->parent_refs);
>>+ xa_erase(&dpll_pin_xa, pin->id);
>>+ kfree(pin);
>>+ }
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_put);
>>+
>>+static int
>>+__dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ int ret;
>>+
>>+ if (WARN_ON(!ops))
>>+ return -EINVAL;
>>+
>>+ ret = dpll_xa_ref_pin_add(&dpll->pin_refs, pin, ops, priv);
>>+ if (ret)
>>+ return ret;
>>+ ret = dpll_xa_ref_dpll_add(&pin->dpll_refs, dpll, ops, priv);
>>+ if (ret)
>>+ goto ref_pin_del;
>>+ else
>>+ dpll_pin_create_ntf(pin);
>>+
>>+ return ret;
>>+
>>+ref_pin_del:
>>+ dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
>>+ return ret;
>>+}
>>+
>>+/**
>>+ * dpll_pin_register - register the dpll pin in the subsystem
>>+ * @dpll: pointer to a dpll
>>+ * @pin: pointer to a dpll pin
>>+ * @ops: ops for a dpll pin ops
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Context: Acquires a lock (dpll_lock)
>>+ * Return:
>>+ * * 0 on success
>>+ * * -EINVAL - missing pin ops
>>+ * * -ENOMEM - failed to allocate memory
>>+ */
>>+int
>>+dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ int ret;
>>+
>>+ mutex_lock(&dpll_lock);
>>+ if (WARN_ON(!(dpll->module == pin->module &&
>>+ dpll->clock_id == pin->clock_id)))
>>+ ret = -EFAULT;
>
>In other cases like this you return -EINVAL. I think it is better. One
>way or another, be consistent please.
>

Fixed.

>
>>+ else
>>+ ret = __dpll_pin_register(dpll, pin, ops, priv);
>>+ mutex_unlock(&dpll_lock);
>>+
>>+ return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_register);
>>+
>>+static void
>>+__dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ dpll_xa_ref_pin_del(&dpll->pin_refs, pin, ops, priv);
>>+ dpll_xa_ref_dpll_del(&pin->dpll_refs, dpll, ops, priv);
>>+}
>>+
>>+/**
>>+ * dpll_pin_unregister - unregister dpll pin from dpll device
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ * @ops: ops for a dpll pin
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Note: It does not free the memory
>>+ * Context: Acquires a lock (dpll_lock)
>>+ */
>>+void dpll_pin_unregister(struct dpll_device *dpll, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ if (WARN_ON(xa_empty(&dpll->pin_refs)))
>>+ return;
>>+
>>+ mutex_lock(&dpll_lock);
>>+ __dpll_pin_unregister(dpll, pin, ops, priv);
>>+ mutex_unlock(&dpll_lock);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_unregister);
>>+
>>+/**
>>+ * dpll_pin_on_pin_register - register a pin with a parent pin
>>+ * @parent: pointer to a parent pin
>>+ * @pin: pointer to a pin
>>+ * @ops: ops for a dpll pin
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Register a pin with a parent pin, create references between them and
>>+ * between newly registered pin and dplls connected with a parent pin.
>>+ *
>>+ * Context: Acquires a lock (dpll_lock)
>>+ * Return:
>>+ * * 0 on success
>>+ * * -EINVAL missing pin or parent
>>+ * * -ENOMEM failed allocation
>>+ * * -EPERM if parent is not allowed
>>+ */
>>+int dpll_pin_on_pin_register(struct dpll_pin *parent, struct dpll_pin *pin,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ struct dpll_pin_ref *ref;
>>+ unsigned long i, stop;
>>+ int ret;
>>+
>>+ if (WARN_ON(parent->prop->type != DPLL_PIN_TYPE_MUX))
>>+ return -EINVAL;
>>+ ret = dpll_xa_ref_pin_add(&pin->parent_refs, parent, ops, priv);
>>+ if (ret)
>>+ goto unlock;
>>+ refcount_inc(&pin->refcount);
>>+ xa_for_each(&parent->dpll_refs, i, ref) {
>>+ mutex_lock(&dpll_lock);
>>+ ret = __dpll_pin_register(ref->dpll, pin, ops, priv);
>>+ mutex_unlock(&dpll_lock);
>>+ if (ret) {
>>+ stop = i;
>>+ goto dpll_unregister;
>>+ }
>>+ dpll_pin_create_ntf(pin);
>>+ }
>>+
>>+ return ret;
>>+
>>+dpll_unregister:
>>+ xa_for_each(&parent->dpll_refs, i, ref) {
>>+ if (i < stop) {
>>+ mutex_lock(&dpll_lock);
>>+ __dpll_pin_unregister(ref->dpll, pin, ops, priv);
>>+ mutex_unlock(&dpll_lock);
>>+ }
>>+ }
>>+ refcount_dec(&pin->refcount);
>>+ dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
>>+unlock:
>>+ return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_register);
>>+
>>+/**
>>+ * dpll_pin_on_pin_unregister - unregister dpll pin from a parent pin
>>+ * @parent: pointer to a parent pin
>>+ * @pin: pointer to a pin
>>+ * @ops: ops for a dpll pin
>>+ * @priv: pointer to private information of owner
>>+ *
>>+ * Context: Acquires a lock (dpll_lock)
>>+ * Note: It does not free the memory
>>+ */
>>+void dpll_pin_on_pin_unregister(struct dpll_pin *parent, struct dpll_pin
>>*pin,
>>+ const struct dpll_pin_ops *ops, void *priv)
>>+{
>>+ struct dpll_pin_ref *ref;
>>+ unsigned long i;
>>+
>>+ mutex_lock(&dpll_lock);
>>+ dpll_pin_delete_ntf(pin);
>>+ dpll_xa_ref_pin_del(&pin->parent_refs, parent, ops, priv);
>>+ refcount_dec(&pin->refcount);
>>+ xa_for_each(&pin->dpll_refs, i, ref) {
>>+ __dpll_pin_unregister(ref->dpll, pin, ops, priv);
>>+ }
>
>Drop "{}"s for one line statement. Didn't checkpack warn you?
>

Fixed.

>
>>+ mutex_unlock(&dpll_lock);
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_on_pin_unregister);
>>+
>>+
>>+/**
>>+ * dpll_device_registration_first - get first registration of dpll device
>>+ * @dpll: pointer to a dpll
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return: pointer to the first registration of a dpll
>>+ */
>>+static struct dpll_device_registration *
>>+dpll_device_registration_first(struct dpll_device *dpll)
>>+{
>>+ struct dpll_device_registration *reg;
>>+
>>+ reg = list_first_entry_or_null((struct list_head *) &dpll-
>>registration_list,
>>+ struct dpll_device_registration, list);
>>+ WARN_ON(!reg);
>>+ return reg;
>>+}
>>+
>>+/**
>>+ * dpll_priv - get the dpll device private owner data
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return: pointer to the first registration priv data
>>+ */
>>+void *dpll_priv(struct dpll_device *dpll)
>>+{
>>+ struct dpll_device_registration *reg;
>>+
>>+ reg = dpll_device_registration_first(dpll);
>>+ return reg->priv;
>>+}
>>+
>>+/**
>>+ * dpll_device_ops - get the dpll device ops pointer
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return: pointer to the first registration ops of the dpll
>>+ */
>>+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll)
>>+{
>>+ struct dpll_device_registration *reg;
>>+
>>+ reg = dpll_device_registration_first(dpll);
>>+ return reg->ops;
>>+}
>>+
>>+/**
>>+ * dpll_pin_registration_first - get first registration of dpll pin ref
>>+ * @ref: pointer to a pin ref struct
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return: pointer to the first registration of a dpll_pin_ref
>>+ */
>>+static struct dpll_pin_registration *
>>+dpll_pin_registration_first(struct dpll_pin_ref *ref)
>>+{
>>+ struct dpll_pin_registration *reg;
>>+
>>+ reg = list_first_entry_or_null(&ref->registration_list,
>>+ struct dpll_pin_registration, list);
>>+ WARN_ON(!reg);
>>+ return reg;
>>+}
>>+
>>+/**
>>+ * dpll_pin_on_dpll_priv - get the dpll device private owner data
>>+ * @dpll: registered dpll pointer
>>+ * @pin: pointer to a pin
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return: pointer to the data
>>+ */
>>+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll,
>>+ struct dpll_pin *pin)
>>+{
>>+ struct dpll_pin_registration *reg;
>>+ struct dpll_pin_ref *ref;
>>+
>>+ ref = xa_load(&dpll->pin_refs, pin->pin_idx);
>>+ if (!ref)
>>+ return NULL;
>>+ reg = dpll_pin_registration_first(ref);
>>+ return reg->priv;
>>+}
>>+
>>+/**
>>+ * dpll_pin_on_pin_priv - get the dpll pin private owner data
>>+ * @parent: pointer to a parent pin
>>+ * @pin: pointer to a pin
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return: pointer to the data
>>+ */
>>+void *dpll_pin_on_pin_priv(struct dpll_pin *parent,
>>+ struct dpll_pin *pin)
>>+{
>>+ struct dpll_pin_registration *reg;
>>+ struct dpll_pin_ref *ref;
>>+
>>+ ref = xa_load(&pin->parent_refs, parent->pin_idx);
>>+ if (!ref)
>>+ return NULL;
>>+ reg = dpll_pin_registration_first(ref);
>>+ return reg->priv;
>>+}
>>+
>>+/**
>>+ * dpll_pin_ops - get the pin ops pointer
>>+ * @ref: dpll pin ref
>>+ *
>>+ * Context: shall be called under a lock (dpll_lock)
>>+ * Return: pointer to the first ops registered with the pin
>>+ */
>>+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref)
>>+{
>>+ struct dpll_pin_registration *reg;
>>+
>>+ reg = dpll_pin_registration_first(ref);
>>+ return reg->ops;
>>+}
>>+
>>+/**
>>+ * dpll_init - initialize dpll subsystem
>>+ *
>>+ * Return:
>>+ * 0 - success
>>+ * negative - netlink init error
>>+ */
>>+static int __init dpll_init(void)
>>+{
>>+ int ret;
>>+
>>+ ret = dpll_netlink_init();
>>+ if (ret)
>>+ goto error;
>>+
>>+ return 0;
>>+
>>+error:
>>+ mutex_destroy(&dpll_lock);
>>+ return ret;
>>+}
>>+subsys_initcall(dpll_init);
>>diff --git a/drivers/dpll/dpll_core.h b/drivers/dpll/dpll_core.h
>>new file mode 100644
>>index 000000000000..ef95e272937c
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_core.h
>>@@ -0,0 +1,104 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>>+ * Copyright (c) 2023 Intel and affiliates
>>+ */
>>+
>>+#ifndef __DPLL_CORE_H__
>>+#define __DPLL_CORE_H__
>>+
>>+#include <linux/dpll.h>
>>+#include <linux/list.h>
>>+#include <linux/refcount.h>
>>+#include "dpll_netlink.h"
>>+
>>+#define DPLL_REGISTERED XA_MARK_1
>>+
>>+struct dpll_device_registration {
>>+ struct list_head list;
>>+ const struct dpll_device_ops *ops;
>>+ void *priv;
>
>You don't need this struct in the header. Move to .c
>

Fixed.

>
>>+};
>>+
>>+/**
>>+ * struct dpll_device - stores DPLL device internal data
>>+ * @id: unique id number for each device given by kernel
>
>Who's kernel? Say "dpll subsystem" instead.
>

Fixed.

>
>>+ * @device_idx: id given by dev driver
>>+ * @clock_id: unique identifier (clock_id) of a dpll
>>+ * @module: module of creator
>>+ * @type: type of a dpll
>>+ * @pin_refs: stores pins registered within a dpll
>>+ * @mode_supported_mask: mask of supported modes
>>+ * @refcount: refcount
>>+ * @registration_list: list of registered ops and priv data of dpll
>>owners
>>+ **/
>>+struct dpll_device {
>>+ u32 id;
>>+ u32 device_idx;
>>+ u64 clock_id;
>>+ struct module *module;
>>+ enum dpll_type type;
>>+ struct xarray pin_refs;
>>+ unsigned long mode_supported_mask;
>>+ refcount_t refcount;
>>+ struct list_head registration_list;
>>+};
>>+
>>+/**
>>+ * struct dpll_pin - structure for a dpll pin
>>+ * @id: unique id number for pin given by kernel
>
>Who's kernel? Say "dpll subsystem" instead.
>

Fixed.

>
>>+ * @pin_idx: index of a pin given by dev driver
>>+ * @clock_id: clock_id of creator
>>+ * @module: module of creator
>>+ * @dpll_refs: hold referencees to dplls pin was registered with
>>+ * @parent_refs: hold references to parent pins pin was registered with
>>+ * @prop: pointer to pin properties given by registerer
>>+ * @rclk_dev_name: holds name of device when pin can recover clock from it
>>+ * @refcount: refcount
>>+ **/
>>+struct dpll_pin {
>>+ u32 id;
>>+ u32 pin_idx;
>>+ u64 clock_id;
>>+ struct module *module;
>>+ struct xarray dpll_refs;
>>+ struct xarray parent_refs;
>>+ const struct dpll_pin_properties *prop;
>>+ char *rclk_dev_name;
>>+ refcount_t refcount;
>>+};
>>+
>>+struct dpll_pin_registration {
>>+ struct list_head list;
>>+ const struct dpll_pin_ops *ops;
>>+ void *priv;
>>+};
>
>You don't need this struct in the header. Move to .c
>

Fixed.

Thank you,
Arkadiusz

>
>>+
>>+/**
>>+ * struct dpll_pin_ref - structure for referencing either dpll or pins
>>+ * @dpll: pointer to a dpll
>>+ * @pin: pointer to a pin
>>+ * @registration_list: list of ops and priv data registered with the
>>ref
>>+ * @refcount: refcount
>>+ **/
>>+struct dpll_pin_ref {
>>+ union {
>>+ struct dpll_device *dpll;
>>+ struct dpll_pin *pin;
>>+ };
>>+ struct list_head registration_list;
>>+ refcount_t refcount;
>>+};
>>+
>>+void *dpll_priv(struct dpll_device *dpll);
>>+void *dpll_pin_on_dpll_priv(struct dpll_device *dpll, struct dpll_pin *pin);
>>+void *dpll_pin_on_pin_priv(struct dpll_pin *parent, struct dpll_pin *pin);
>>+
>>+const struct dpll_device_ops *dpll_device_ops(struct dpll_device *dpll);
>>+struct dpll_device *dpll_device_get_by_id(int id);
>>+const struct dpll_pin_ops *dpll_pin_ops(struct dpll_pin_ref *ref);
>>+struct dpll_pin_ref *dpll_xa_ref_dpll_first(struct xarray *xa_refs);
>>+extern struct xarray dpll_device_xa;
>>+extern struct xarray dpll_pin_xa;
>>+extern struct mutex dpll_lock;
>>+#endif
>>--
>>2.37.3
>>

2023-06-21 19:13:50

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

>From: Jiri Pirko <[email protected]>
>Sent: Saturday, June 10, 2023 7:38 PM
>
>Fri, Jun 09, 2023 at 02:18:46PM CEST, [email protected] wrote:
>>From: Vadim Fedorenko <[email protected]>
>>
>>DPLL framework is used to represent and configure DPLL devices
>>in systems. Each device that has DPLL and can configure inputs
>>and outputs can use this framework.
>>
>>Implement core framework functions for further interactions
>>with device drivers implementing dpll subsystem, as well as for
>>interactions of DPLL netlink framework part with the subsystem
>>itself.
>>
>>Co-developed-by: Milena Olech <[email protected]>
>>Signed-off-by: Milena Olech <[email protected]>
>>Co-developed-by: Michal Michalik <[email protected]>
>>Signed-off-by: Michal Michalik <[email protected]>
>>Signed-off-by: Vadim Fedorenko <[email protected]>
>>Co-developed-by: Arkadiusz Kubalewski <[email protected]>
>>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>>---
>> drivers/dpll/dpll_core.c | 953 +++++++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_core.h | 104 +++++
>
>Overall, looks very good! I pinpointed couple of nits below, nothing big.
>General question: Why do you put documentation comment to every static
>function? Does not make any sense to me. Even for non-exported functions
>I think it is overkill. Most of them (if not all) give the reader no
>additional information and only make the code a bit harder to read.
>Care to drop them?
>

I forgot to respond here.. I would rather leave it, but if the others think
the same way, we could remove them.

Thank you!
Arkadiusz

[...]


2023-06-21 20:58:29

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

>From: Paolo Abeni <[email protected]>
>Sent: Monday, June 12, 2023 9:25 AM
>
>On Sun, 2023-06-11 at 11:36 +0200, Jiri Pirko wrote:
>> Fri, Jun 09, 2023 at 02:18:46PM CEST, [email protected]
>wrote:
>> > From: Vadim Fedorenko <[email protected]>
>>
>> [...]
>>
>> > +int dpll_device_register(struct dpll_device *dpll, enum dpll_type
>type,
>> > + const struct dpll_device_ops *ops, void *priv)
>> > +{
>> > + struct dpll_device_registration *reg;
>> > + bool first_registration = false;
>> > +
>> > + if (WARN_ON(!ops))
>> > + return -EINVAL;
>> > + if (WARN_ON(type < DPLL_TYPE_PPS || type > DPLL_TYPE_MAX))
>> > + return -EINVAL;
>> > +
>> > + mutex_lock(&dpll_lock);
>> > + reg = dpll_device_registration_find(dpll, ops, priv);
>> > + if (reg) {
>> > + mutex_unlock(&dpll_lock);
>> > + return -EEXIST;
>> > + }
>> > +
>> > + reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>> > + if (!reg) {
>> > + mutex_unlock(&dpll_lock);
>> > + return -EEXIST;
>> > + }
>> > + reg->ops = ops;
>> > + reg->priv = priv;
>> > + dpll->type = type;
>> > + first_registration = list_empty(&dpll->registration_list);
>> > + list_add_tail(&reg->list, &dpll->registration_list);
>> > + if (!first_registration) {
>> > + mutex_unlock(&dpll_lock);
>> > + return 0;
>> > + }
>> > +
>> > + xa_set_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED);
>> > + mutex_unlock(&dpll_lock);
>> > + dpll_device_create_ntf(dpll);
>>
>> This function is introduced in the next patch. Breaks bissection. Make
>> sure you can compile the code after every patch applied.
>
>WRT, I think the easiest way to solve the above is adding the function
>call in the next patch.
>
>Cheers,
>
>Paolo

Sure, will try to fix as suggested, but this will be much easier to do on
final version of patches before sending.

Thank you,
Arkadiusz

2023-06-21 21:45:58

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

>From: Jakub Kicinski <[email protected]>
>Sent: Tuesday, June 13, 2023 1:45 AM
>
>On Fri, 9 Jun 2023 14:18:46 +0200 Arkadiusz Kubalewski wrote:
>> + xa_for_each(xa_pins, i, ref) {
>> + if (ref->pin != pin)
>> + continue;
>> + reg = dpll_pin_registration_find(ref, ops, priv);
>> + if (reg) {
>> + refcount_inc(&ref->refcount);
>> + return 0;
>> + }
>> + ref_exists = true;
>> + break;
>> + }
>> +
>> + if (!ref_exists) {
>> + ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>> + if (!ref)
>> + return -ENOMEM;
>> + ref->pin = pin;
>> + INIT_LIST_HEAD(&ref->registration_list);
>> + ret = xa_insert(xa_pins, pin->pin_idx, ref, GFP_KERNEL);
>> + if (ret) {
>> + kfree(ref);
>> + return ret;
>> + }
>> + refcount_set(&ref->refcount, 1);
>> + }
>> +
>> + reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>
>Why do we have two structures - ref and reg?
>

Thank to Jiri and reg struct we solved a pin/dpll association
with multiple device drivers..
I.e. for pin:

struct dpll_pin_registration {
struct list_head list;
const struct dpll_pin_ops *ops;
void *priv;
};

struct dpll_pin_ref {
union {
struct dpll_device *dpll;
struct dpll_pin *pin;
};
struct list_head registration_list;
refcount_t refcount;
};

struct dpll_pin {
u32 id;
u32 pin_idx;
u64 clock_id;
struct module *module;
struct xarray dpll_refs;
struct xarray parent_refs;
const struct dpll_pin_properties *prop;
char *rclk_dev_name;
refcount_t refcount;
};

Basically, a pin or a device can be registered from multiple drivers,
where each driver has own priv and ops.
A single dpll_pin has references to dplls or pins (dpll_refs/parent_refs)
it is connected with, and thanks to registration list single reference can
have multiple drivers being attached with a particular dpll/pin.

The same scheme is for a dpll_device struct and associated pins.


>> + if (!reg) {
>> + if (!ref_exists)
>> + kfree(ref);
>
>ref has already been inserted into xa_pins
>

True, seems like a bug, will fix it.

Thank you,
Arkadiusz

>> + return -ENOMEM;

2023-06-22 07:16:19

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

Wed, Jun 21, 2023 at 11:17:26PM CEST, [email protected] wrote:
>>From: Jakub Kicinski <[email protected]>
>>Sent: Tuesday, June 13, 2023 1:45 AM
>>
>>On Fri, 9 Jun 2023 14:18:46 +0200 Arkadiusz Kubalewski wrote:
>>> + xa_for_each(xa_pins, i, ref) {
>>> + if (ref->pin != pin)
>>> + continue;
>>> + reg = dpll_pin_registration_find(ref, ops, priv);
>>> + if (reg) {
>>> + refcount_inc(&ref->refcount);
>>> + return 0;
>>> + }
>>> + ref_exists = true;
>>> + break;
>>> + }
>>> +
>>> + if (!ref_exists) {
>>> + ref = kzalloc(sizeof(*ref), GFP_KERNEL);
>>> + if (!ref)
>>> + return -ENOMEM;
>>> + ref->pin = pin;
>>> + INIT_LIST_HEAD(&ref->registration_list);
>>> + ret = xa_insert(xa_pins, pin->pin_idx, ref, GFP_KERNEL);
>>> + if (ret) {
>>> + kfree(ref);
>>> + return ret;
>>> + }
>>> + refcount_set(&ref->refcount, 1);
>>> + }
>>> +
>>> + reg = kzalloc(sizeof(*reg), GFP_KERNEL);
>>
>>Why do we have two structures - ref and reg?
>>
>
>Thank to Jiri and reg struct we solved a pin/dpll association
>with multiple device drivers..

Multiple instances of the same driver.


>I.e. for pin:
>
>struct dpll_pin_registration {
> struct list_head list;
> const struct dpll_pin_ops *ops;
> void *priv;
>};
>
>struct dpll_pin_ref {
> union {
> struct dpll_device *dpll;
> struct dpll_pin *pin;
> };
> struct list_head registration_list;
> refcount_t refcount;
>};
>
>struct dpll_pin {
> u32 id;
> u32 pin_idx;
> u64 clock_id;
> struct module *module;
> struct xarray dpll_refs;
> struct xarray parent_refs;
> const struct dpll_pin_properties *prop;
> char *rclk_dev_name;
> refcount_t refcount;
>};
>
>Basically, a pin or a device can be registered from multiple drivers,

Again, multiple instances of the same driver.


>where each driver has own priv and ops.

Each instance/device.


>A single dpll_pin has references to dplls or pins (dpll_refs/parent_refs)
>it is connected with, and thanks to registration list single reference can
>have multiple drivers being attached with a particular dpll/pin.

Multiple instances/devices.


In case of mlx5, the same dpll device and same dpll pin could be shared
among two PFs but also among multiple VFs and SFs. They all share the
same clock, same dpll device.


>
>The same scheme is for a dpll_device struct and associated pins.
>
>
>>> + if (!reg) {
>>> + if (!ref_exists)
>>> + kfree(ref);
>>
>>ref has already been inserted into xa_pins
>>
>
>True, seems like a bug, will fix it.
>
>Thank you,
>Arkadiusz
>
>>> + return -ENOMEM;

2023-06-22 07:38:33

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 03/10] dpll: core: Add DPLL framework base functions

Wed, Jun 21, 2023 at 08:55:35PM CEST, [email protected] wrote:
>>From: Jiri Pirko <[email protected]>
>>Sent: Saturday, June 10, 2023 7:38 PM
>>
>>Fri, Jun 09, 2023 at 02:18:46PM CEST, [email protected] wrote:
>>>From: Vadim Fedorenko <[email protected]>
>>>
>>>DPLL framework is used to represent and configure DPLL devices
>>>in systems. Each device that has DPLL and can configure inputs
>>>and outputs can use this framework.
>>>
>>>Implement core framework functions for further interactions
>>>with device drivers implementing dpll subsystem, as well as for
>>>interactions of DPLL netlink framework part with the subsystem
>>>itself.
>>>
>>>Co-developed-by: Milena Olech <[email protected]>
>>>Signed-off-by: Milena Olech <[email protected]>
>>>Co-developed-by: Michal Michalik <[email protected]>
>>>Signed-off-by: Michal Michalik <[email protected]>
>>>Signed-off-by: Vadim Fedorenko <[email protected]>
>>>Co-developed-by: Arkadiusz Kubalewski <[email protected]>
>>>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>>>---
>>> drivers/dpll/dpll_core.c | 953 +++++++++++++++++++++++++++++++++++++++
>>> drivers/dpll/dpll_core.h | 104 +++++
>>
>>Overall, looks very good! I pinpointed couple of nits below, nothing big.
>>General question: Why do you put documentation comment to every static
>>function? Does not make any sense to me. Even for non-exported functions
>>I think it is overkill. Most of them (if not all) give the reader no
>>additional information and only make the code a bit harder to read.
>>Care to drop them?
>>
>
>I forgot to respond here.. I would rather leave it, but if the others think
>the same way, we could remove them.

Could you explain what is the benefit of leaving them? What are they
good for. From what I see, they are obvious and only add blank LOC.

2023-06-22 08:10:09

by Vitaly Grinberg

[permalink] [raw]
Subject: Re: [RFC PATCH v8 00/10] Create common DPLL configuration API

Hi,
Could it be possible to add PPS DPLL phase offset to the netlink API?
We are relying on it in the E810-based grandmaster implementation.
Thanks,
Vitaly


2023-06-23 01:04:22

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 04/10] dpll: netlink: Add DPLL framework base functions

>From: Jiri Pirko <[email protected]>
>Sent: Sunday, June 11, 2023 1:42 PM
>
>Fri, Jun 09, 2023 at 02:18:47PM CEST, [email protected] wrote:
>>From: Vadim Fedorenko <[email protected]>
>
>Arkadiusz, I think it would be appropriate to change the authorship
>of this and other patches to you. I believe that you did vast majority
>of the lines by now. Vadim, would you mind?
>

Well, Vadim started it, then we did changes. I am just fine with
"Co-developed".

>
>>
>>DPLL framework is used to represent and configure DPLL devices
>>in systems. Each device that has DPLL and can configure inputs
>>and outputs can use this framework.
>>
>>Implement dpll netlink framework functions for enablement of dpll
>>subsytem netlink family.
>>
>>Co-developed-by: Milena Olech <[email protected]>
>>Signed-off-by: Milena Olech <[email protected]>
>>Co-developed-by: Michal Michalik <[email protected]>
>>Signed-off-by: Michal Michalik <[email protected]>
>>Signed-off-by: Vadim Fedorenko <[email protected]>
>>Co-developed-by: Arkadiusz Kubalewski <[email protected]>
>>Signed-off-by: Arkadiusz Kubalewski <[email protected]>
>>---
>> drivers/dpll/dpll_netlink.c | 1183 +++++++++++++++++++++++++++++++++++
>> drivers/dpll/dpll_netlink.h | 44 ++
>
>Overall, this looks very good. I did take couple of comments below.
>Thanks for you work!
>

Thanks for your review! :)

>
>> 2 files changed, 1227 insertions(+)
>> create mode 100644 drivers/dpll/dpll_netlink.c
>> create mode 100644 drivers/dpll/dpll_netlink.h
>>
>>diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
>>new file mode 100644
>>index 000000000000..44d9699c9e6c
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.c
>>@@ -0,0 +1,1183 @@
>>+// SPDX-License-Identifier: GPL-2.0
>>+/*
>>+ * Generic netlink for DPLL management framework
>>+ *
>>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>>+ * Copyright (c) 2023 Intel and affiliates
>>+ *
>>+ */
>>+#include <linux/module.h>
>>+#include <linux/kernel.h>
>>+#include <net/genetlink.h>
>>+#include "dpll_core.h"
>>+#include "dpll_nl.h"
>>+#include <uapi/linux/dpll.h>
>>+
>>+static int __dpll_pin_change_ntf(struct dpll_pin *pin);
>
>Could you try to reshuffle the code to avoid forward declarations?
>

Fixed.

>
>>+
>>+struct dpll_dump_ctx {
>>+ unsigned long idx;
>>+};
>>+
>>+static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback
>>*cb)
>>+{
>>+ return (struct dpll_dump_ctx *)cb->ctx;
>>+}
>>+
>>+static int
>>+dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
>
>It is odd to see this helper here and the dpll_msg_add_pin_handle() not.
>Introduce dpll_msg_add_pin_handle() here right away and only export it
>later on in "netdev: expose DPLL pin handle for netdevice".
>

Will do.

>
>>+{
>>+ if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
>>+ return -EMSGSIZE;
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>>+ enum dpll_mode mode;
>>+
>>+ if (WARN_ON(!ops->mode_get))
>>+ return -EOPNOTSUPP;
>>+ if (ops->mode_get(dpll, dpll_priv(dpll), &mode, extack))
>>+ return -EFAULT;
>
>I'm pretty sure I commented this before. But again, please get the
>value the driver op returned and return it.
>

Fixed.

>
>>+ if (nla_put_u8(msg, DPLL_A_MODE, mode))
>>+ return -EMSGSIZE;
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>>+ enum dpll_lock_status status;
>>+
>>+ if (WARN_ON(!ops->lock_status_get))
>>+ return -EOPNOTSUPP;
>>+ if (ops->lock_status_get(dpll, dpll_priv(dpll), &status, extack))
>>+ return -EFAULT;
>
>please get the value the driver op returned and return it.
>

Fixed.

>
>>+ if (nla_put_u8(msg, DPLL_A_LOCK_STATUS, status))
>>+ return -EMSGSIZE;
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>>+ s32 temp;
>>+
>>+ if (!ops->temp_get)
>>+ return -EOPNOTSUPP;
>>+ if (ops->temp_get(dpll, dpll_priv(dpll), &temp, extack))
>>+ return -EFAULT;
>
>please get the value the driver op returned and return it.
>

Fixed.

>
>>+ if (nla_put_s32(msg, DPLL_A_TEMP, temp))
>>+ return -EMSGSIZE;
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_prio(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;
>>+ u32 prio;
>>+
>>+ if (!ops->prio_get)
>>+ return -EOPNOTSUPP;
>>+ if (ops->prio_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>>+ dpll_priv(dpll), &prio, extack))
>>+ return -EFAULT;
>
>please get the value the driver op returned and return it.
>

Fixed.

>
>>+ if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
>>+ return -EMSGSIZE;
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_on_dpll_state(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;
>>+ enum dpll_pin_state state;
>>+
>>+ if (!ops->state_on_dpll_get)
>>+ return -EOPNOTSUPP;
>>+ if (ops->state_on_dpll_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
>>dpll,
>>+ dpll_priv(dpll), &state, extack))
>>+ return -EFAULT;
>
>please get the value the driver op returned and return it.
>

Fixed.

>
>>+ if (nla_put_u8(msg, DPLL_A_PIN_STATE, state))
>>+ return -EMSGSIZE;
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_direction(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;
>>+ enum dpll_pin_direction direction;
>>+
>>+ if (!ops->direction_get)
>>+ return -EOPNOTSUPP;
>>+ if (ops->direction_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>>+ dpll_priv(dpll), &direction, extack))
>>+ return -EFAULT;
>
>please get the value the driver op returned and return it.
>

Fixed.

>
>>+ if (nla_put_u8(msg, DPLL_A_PIN_DIRECTION, direction))
>>+ 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,
>>+ bool dump_freq_supported)
>>+{
>>+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>>+ struct dpll_device *dpll = ref->dpll;
>>+ struct nlattr *nest;
>>+ u64 freq;
>>+ int fs;
>>+
>>+ if (!ops->frequency_get)
>>+ return -EOPNOTSUPP;
>
>Return 0 and avoid the check of -EOPNOTSUPP in the caller.
>

Fixed.

>
>>+ if (ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>>+ dpll_priv(dpll), &freq, extack))
>>+ return -EFAULT;
>
>please get the value the driver op returned and return it.
>

Fixed.

>
>>+ if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq, 0))
>>+ return -EMSGSIZE;
>>+ if (!dump_freq_supported)
>>+ return 0;
>>+ for (fs = 0; fs < pin->prop->freq_supported_num; fs++) {
>>+ nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);
>>+ if (!nest)
>>+ return -EMSGSIZE;
>>+ freq = pin->prop->freq_supported[fs].min;
>>+ if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),
>>+ &freq, 0)) {
>>+ nla_nest_cancel(msg, nest);
>>+ return -EMSGSIZE;
>>+ }
>>+ freq = pin->prop->freq_supported[fs].max;
>>+ if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),
>>+ &freq, 0)) {
>>+ nla_nest_cancel(msg, nest);
>>+ return -EMSGSIZE;
>>+ }
>>+ nla_nest_end(msg, nest);
>>+ }
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
>>+ struct dpll_pin_ref *dpll_ref,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ enum dpll_pin_state state;
>>+ struct dpll_pin_ref *ref;
>>+ struct dpll_pin *ppin;
>>+ struct nlattr *nest;
>>+ unsigned long index;
>>+ int ret;
>>+
>>+ xa_for_each(&pin->parent_refs, index, ref) {
>>+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
>>+ void *parent_priv;
>>+
>>+ ppin = ref->pin;
>>+ parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, ppin);
>>+ if (WARN_ON(!ops->state_on_pin_get))
>
>Wait, so you WARN during user comment on something that driver didn't
>fill up? Plese move the check and WARN to the registration function.
>

Fixed.

>
>>+ return -EFAULT;
>>+ ret = ops->state_on_pin_get(pin,
>>+ dpll_pin_on_pin_priv(ppin, pin),
>>+ ppin, parent_priv, &state, extack);
>>+ if (ret)
>>+ return -EFAULT;
>
>Return ret please.
>

Fixed.

>
>>+ nest = nla_nest_start(msg, DPLL_A_PIN_PARENT);
>>+ if (!nest)
>>+ return -EMSGSIZE;
>>+ if (nla_put_u32(msg, DPLL_A_PIN_ID, ppin->id)) {
>>+ ret = -EMSGSIZE;
>>+ goto nest_cancel;
>>+ }
>>+ if (nla_put_u8(msg, DPLL_A_PIN_STATE, state)) {
>>+ ret = -EMSGSIZE;
>>+ goto nest_cancel;
>>+ }
>>+ nla_nest_end(msg, nest);
>>+ }
>>+
>>+ return 0;
>>+
>>+nest_cancel:
>>+ nla_nest_cancel(msg, nest);
>>+ return ret;
>>+}
>>+
>>+static int
>>+dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ struct dpll_pin_ref *ref;
>>+ struct nlattr *attr;
>>+ unsigned long index;
>>+ int ret;
>>+
>>+ xa_for_each(&pin->dpll_refs, index, ref) {
>>+ attr = nla_nest_start(msg, DPLL_A_PIN_PARENT);
>>+ if (!attr)
>>+ return -EMSGSIZE;
>>+ ret = dpll_msg_add_dev_handle(msg, ref->dpll);
>>+ if (ret)
>>+ goto nest_cancel;
>>+ ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
>>+ if (ret && ret != -EOPNOTSUPP)
>>+ goto nest_cancel;
>>+ ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
>>+ if (ret && ret != -EOPNOTSUPP)
>>+ goto nest_cancel;
>>+ ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
>>+ if (ret)
>>+ goto nest_cancel;
>>+ nla_nest_end(msg, attr);
>>+ }
>>+
>>+ return 0;
>>+
>>+nest_cancel:
>>+ nla_nest_end(msg, attr);
>>+ return ret;
>>+}
>>+
>>+static int
>>+dpll_cmd_pin_fill_details(struct sk_buff *msg, struct dpll_pin *pin,
>
>"details"? Sound odd. I don't think that "DPLL_A_PIN_ID" is a detail
>for example. Why don't you inline this in the __dpll_cmd_pin_dump_one()
>function below?
>

Fixed.

>
>>+ struct dpll_pin_ref *ref, struct netlink_ext_ack
>>*extack)
>>+{
>>+ const struct dpll_pin_properties *prop = pin->prop;
>>+ int ret;
>>+
>>+ if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
>>+ return -EMSGSIZE;
>>+ if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(pin-
>>module)))
>>+ return -EMSGSIZE;
>>+ if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(pin->clock_id),
>>+ &pin->clock_id, 0))
>>+ return -EMSGSIZE;
>>+ if (prop->board_label &&
>>+ nla_put_string(msg, DPLL_A_PIN_BOARD_LABEL, prop->board_label))
>>+ return -EMSGSIZE;
>>+ if (prop->panel_label &&
>>+ nla_put_string(msg, DPLL_A_PIN_PANEL_LABEL, prop->panel_label))
>>+ return -EMSGSIZE;
>>+ if (prop->package_label &&
>>+ nla_put_string(msg, DPLL_A_PIN_PACKAGE_LABEL,
>>+ prop->package_label))
>>+ return -EMSGSIZE;
>>+ if (nla_put_u8(msg, DPLL_A_PIN_TYPE, prop->type))
>>+ return -EMSGSIZE;
>>+ if (nla_put_u32(msg, DPLL_A_PIN_DPLL_CAPS, prop->capabilities))
>>+ return -EMSGSIZE;
>>+ ret = dpll_msg_add_pin_freq(msg, pin, ref, extack, true);
>>+ if (ret && ret != -EOPNOTSUPP)
>>+ return ret;
>>+ return 0;
>>+}
>>+
>>+static int
>>+__dpll_cmd_pin_dump_one(struct sk_buff *msg, struct dpll_pin *pin,
>>+ struct netlink_ext_ack *extack)
>
>To be consistent with dpll_device_get_one(), call this function
>dpll_pin_get_one() please.
>

Fixed.

>
>>+{
>>+ struct dpll_pin_ref *ref;
>>+ int ret;
>>+
>>+ ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
>>+ if (!ref)
>>+ return -EFAULT;
>
>-EINVAL. But it should never happen anyway. Perhaps better to avoid the
>check entirely.
>

To me, it feels pretty strange to look for it and then use it without
validating, as the function designed to return NULL.
Will try to fix it and make it consistent.

>
>>+ ret = dpll_cmd_pin_fill_details(msg, pin, ref, extack);
>>+ if (ret)
>>+ return ret;
>>+ ret = dpll_msg_add_pin_parents(msg, pin, ref, extack);
>>+ if (ret)
>>+ return ret;
>>+ if (!xa_empty(&pin->dpll_refs)) {
>
>Drop this check, not needed.
>

Fixed.

>
>>+ ret = dpll_msg_add_pin_dplls(msg, pin, extack);
>>+ if (ret)
>>+ return ret;
>>+ }
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ enum dpll_mode mode;
>>+ int ret;
>>+
>>+ ret = dpll_msg_add_dev_handle(msg, dpll);
>>+ if (ret)
>>+ return ret;
>>+ if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(dpll-
>>module)))
>>+ return -EMSGSIZE;
>>+ if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
>>+ &dpll->clock_id, 0))
>>+ return -EMSGSIZE;
>>+ ret = dpll_msg_add_temp(msg, dpll, extack);
>>+ if (ret && ret != -EOPNOTSUPP)
>>+ return ret;
>>+ ret = dpll_msg_add_lock_status(msg, dpll, extack);
>>+ if (ret)
>>+ return ret;
>>+ ret = dpll_msg_add_mode(msg, dpll, extack);
>>+ if (ret)
>>+ return ret;
>>+ for (mode = DPLL_MODE_MANUAL; mode <= DPLL_MODE_MAX; mode++)
>>+ if (test_bit(mode, &dpll->mode_supported_mask))
>>+ if (nla_put_s32(msg, DPLL_A_MODE_SUPPORTED, mode))
>>+ return -EMSGSIZE;
>>+ if (nla_put_u8(msg, DPLL_A_TYPE, dpll->type))
>>+ return -EMSGSIZE;
>>+
>>+ return ret;
>>+}
>>+
>>+static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
>>+{
>>+ int fs;
>>+
>>+ for (fs = 0; fs < pin->prop->freq_supported_num; fs++)
>>+ if (freq >= pin->prop->freq_supported[fs].min &&
>
>Avoid double space here ^^
>

Fixed.

>
>>+ freq <= pin->prop->freq_supported[fs].max)
>
>Avoid double space here ^^
>

Fixed.

>
>>+ return true;
>>+ return false;
>>+}
>>+
>>+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;
>>+ unsigned long i;
>>+ int ret;
>>+
>>+ if (!dpll_pin_is_freq_supported(pin, freq))
>
>Fill a proper extack telling the user what's wrong please.
>Could you please check the rest of the cmd attr checks and make sure
>the extack is always filled with meaningful message?
>

Fixed.

>
>>+ 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->frequency_set)
>>+ return -EOPNOTSUPP;
>>+ ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>>+ dpll, dpll_priv(dpll), freq, extack);
>>+ if (ret)
>>+ return -EFAULT;
>
>return "ret"
>

Fixed.

>
>>+ __dpll_pin_change_ntf(pin);
>>+ }
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
>>+ enum dpll_pin_state state,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ struct dpll_pin_ref *parent_ref;
>>+ const struct dpll_pin_ops *ops;
>>+ struct dpll_pin_ref *dpll_ref;
>>+ struct dpll_pin *parent;
>>+ unsigned long i;
>>+
>>+ if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities))
>>+ return -EOPNOTSUPP;
>>+ parent = xa_load(&dpll_pin_xa, parent_idx);
>>+ if (!parent)
>>+ return -EINVAL;
>>+ parent_ref = xa_load(&pin->parent_refs, parent->pin_idx);
>>+ if (!parent_ref)
>>+ return -EINVAL;
>>+ xa_for_each(&parent->dpll_refs, i, dpll_ref) {
>>+ ops = dpll_pin_ops(parent_ref);
>>+ if (!ops->state_on_pin_set)
>>+ return -EOPNOTSUPP;
>>+ if (ops->state_on_pin_set(pin,
>>+ dpll_pin_on_pin_priv(parent, pin),
>>+ parent,
>>+ dpll_pin_on_dpll_priv(dpll_ref->dpll,
>>+ parent),
>>+ state, extack))
>>+ return -EFAULT;
>
>please get the value the driver op returned and return it.
>

Fixed.

>
>>+ }
>>+ __dpll_pin_change_ntf(pin);
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
>>+ enum dpll_pin_state state,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ const struct dpll_pin_ops *ops;
>>+ struct dpll_pin_ref *ref;
>>+
>>+ if (!(DPLL_PIN_CAPS_STATE_CAN_CHANGE & pin->prop->capabilities))
>>+ return -EOPNOTSUPP;
>>+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+ if (!ref)
>>+ return -EFAULT;
>
>-EINVAL. But looks like this should never happen. Perhaps just
>WARN_ON(!ref) and don't check-return.
>

Fixed.

>
>>+ ops = dpll_pin_ops(ref);
>>+ if (!ops->state_on_dpll_set)
>>+ return -EOPNOTSUPP;
>>+ if (ops->state_on_dpll_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>>dpll,
>>+ dpll_priv(dpll), state, extack))
>>+ return -EINVAL;
>>+ __dpll_pin_change_ntf(pin);
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
>>+ u32 prio, struct netlink_ext_ack *extack)
>>+{
>>+ const struct dpll_pin_ops *ops;
>>+ struct dpll_pin_ref *ref;
>>+
>>+ if (!(DPLL_PIN_CAPS_PRIORITY_CAN_CHANGE & pin->prop->capabilities))
>>+ return -EOPNOTSUPP;
>>+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+ if (!ref)
>>+ return -EFAULT;
>
>Same here.
>

Fixed.

>
>>+ ops = dpll_pin_ops(ref);
>>+ if (!ops->prio_set)
>>+ return -EOPNOTSUPP;
>>+ if (ops->prio_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
>>+ dpll_priv(dpll), prio, extack))
>>+ return -EINVAL;
>>+ __dpll_pin_change_ntf(pin);
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
>>+ enum dpll_pin_direction direction,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ const struct dpll_pin_ops *ops;
>>+ struct dpll_pin_ref *ref;
>>+
>>+ if (!(DPLL_PIN_CAPS_DIRECTION_CAN_CHANGE & pin->prop->capabilities))
>>+ return -EOPNOTSUPP;
>>+
>>+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+ if (!ref)
>>+ return -EFAULT;
>
>Same here. This calls for a helper :)
>

Fixed.

>
>>+ ops = dpll_pin_ops(ref);
>>+ if (!ops->direction_set)
>>+ return -EOPNOTSUPP;
>>+ if (ops->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
>>+ dpll, dpll_priv(dpll), direction,
>>+ extack))
>>+ return -EFAULT;
>
>please get the value the driver op returned and return it.
>

Fixed.

>
>>+ __dpll_pin_change_ntf(pin);
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_parent_set(struct dpll_pin *pin, struct nlattr *parent_nest,
>>+ struct netlink_ext_ack *extack)
>>+{
>>+ struct nlattr *tb[DPLL_A_MAX + 1];
>>+ enum dpll_pin_direction direction;
>>+ u32 ppin_idx, pdpll_idx, prio;
>>+ enum dpll_pin_state state;
>>+ struct dpll_pin_ref *ref;
>>+ struct dpll_device *dpll;
>>+ int ret;
>>+
>>+ nla_parse_nested(tb, DPLL_A_MAX, parent_nest,
>>+ NULL, extack);
>>+ if ((tb[DPLL_A_ID] && tb[DPLL_A_PIN_ID]) ||
>>+ !(tb[DPLL_A_ID] || tb[DPLL_A_PIN_ID])) {
>>+ NL_SET_ERR_MSG(extack, "one parent id expected");
>>+ return -EINVAL;
>>+ }
>>+ if (tb[DPLL_A_ID]) {
>>+ pdpll_idx = nla_get_u32(tb[DPLL_A_ID]);
>>+ dpll = xa_load(&dpll_device_xa, pdpll_idx);
>>+ if (!dpll)
>>+ return -EINVAL;
>>+ ref = xa_load(&pin->dpll_refs, dpll->device_idx);
>>+ if (!ref)
>>+ return -EINVAL;
>>+ if (tb[DPLL_A_PIN_STATE]) {
>>+ state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
>>+ ret = dpll_pin_state_set(dpll, pin, state, extack);
>>+ if (ret)
>>+ return ret;
>>+ }
>>+ if (tb[DPLL_A_PIN_PRIO]) {
>>+ prio = nla_get_u8(tb[DPLL_A_PIN_PRIO]);
>>+ ret = dpll_pin_prio_set(dpll, pin, prio, extack);
>>+ if (ret)
>>+ return ret;
>>+ }
>>+ if (tb[DPLL_A_PIN_DIRECTION]) {
>>+ direction = nla_get_u8(tb[DPLL_A_PIN_DIRECTION]);
>>+ ret = dpll_pin_direction_set(pin, dpll, direction,
>>+ extack);
>>+ if (ret)
>>+ return ret;
>>+ }
>>+ } else if (tb[DPLL_A_PIN_ID]) {
>>+ ppin_idx = nla_get_u32(tb[DPLL_A_PIN_ID]);
>>+ state = nla_get_u8(tb[DPLL_A_PIN_STATE]);
>>+ ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);
>>+ if (ret)
>>+ return ret;
>>+ }
>>+
>>+ return 0;
>>+}
>>+
>>+static int
>>+dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
>>+{
>>+ int rem, ret = -EINVAL;
>>+ struct nlattr *a;
>>+
>>+ nla_for_each_attr(a, genlmsg_data(info->genlhdr),
>>+ genlmsg_len(info->genlhdr), rem) {
>>+ switch (nla_type(a)) {
>>+ case DPLL_A_PIN_FREQUENCY:
>>+ ret = dpll_pin_freq_set(pin, a, info->extack);
>>+ if (ret)
>>+ return ret;
>>+ break;
>>+ case DPLL_A_PIN_PARENT:
>>+ ret = dpll_pin_parent_set(pin, a, info->extack);
>>+ if (ret)
>>+ return ret;
>>+ break;
>>+ case DPLL_A_PIN_ID:
>>+ case DPLL_A_ID:
>>+ break;
>>+ default:
>>+ NL_SET_ERR_MSG_FMT(info->extack,
>>+ "unsupported attribute (%d)",
>>+ nla_type(a));
>>+ return -EINVAL;
>>+ }
>>+ }
>>+
>>+ return 0;
>>+}
>>+
>>+static struct dpll_pin *
>>+dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,
>>+ enum dpll_pin_type type, struct nlattr *board_label,
>>+ struct nlattr *panel_label, struct nlattr *package_label)
>>+{
>>+ bool board_match, panel_match, package_match;
>>+ struct dpll_pin *pin_match = NULL, *pin;
>>+ const struct dpll_pin_properties *prop;
>>+ bool cid_match, mod_match, type_match;
>>+ unsigned long i;
>>+
>>+ xa_for_each(&dpll_pin_xa, i, pin) {
>>+ if (xa_empty(&pin->dpll_refs))
>
>This filters out unregistered, right? Could you please introduce a
>"REGISTERED" mark and iterate only over list of registered? Similar to
>what you have for device.
>

Yes it does. Will do.

>
>>+ continue;
>>+ prop = pin->prop;
>>+ cid_match = clock_id ? pin->clock_id == clock_id : true;
>>+ mod_match = mod_name_attr && module_name(pin->module) ?
>>+ !nla_strcmp(mod_name_attr,
>>+ module_name(pin->module)) : true;
>>+ type_match = type ? prop->type == type : true;
>>+ board_match = board_label && prop->board_label ?
>>+ !nla_strcmp(board_label, prop->board_label) : true;
>>+ panel_match = panel_label && prop->panel_label ?
>>+ !nla_strcmp(panel_label, prop->panel_label) : true;
>>+ package_match = package_label && prop->package_label ?
>>+ !nla_strcmp(package_label,
>>+ prop->package_label) : true;
>>+ if (cid_match && mod_match && type_match && board_match &&
>>+ panel_match && package_match) {
>>+ if (pin_match)
>
>Double match, rigth? Fillup the extack telling the user what happened.
>

Fixed.

>
>>+ return NULL;
>>+ pin_match = pin;
>>+ };
>>+ }
>>+
>>+ return pin_match;
>>+}
>>+
>>+static int
>>+dpll_pin_find_from_nlattr(struct genl_info *info, struct sk_buff *skb)
>>+{
>>+ struct nlattr *attr, *mod_name_attr = NULL, *board_label_attr = NULL,
>>+ *panel_label_attr = NULL, *package_label_attr = NULL;
>>+ struct dpll_pin *pin = NULL;
>>+ enum dpll_pin_type type = 0;
>>+ u64 clock_id = 0;
>>+ int rem = 0;
>>+
>>+ nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
>>+ genlmsg_len(info->genlhdr), rem) {
>>+ switch (nla_type(attr)) {
>>+ case DPLL_A_CLOCK_ID:
>>+ if (clock_id)
>>+ return -EINVAL;
>
>Extack
>

Fixed.

>
>>+ clock_id = nla_get_u64(attr);
>>+ break;
>>+ case DPLL_A_MODULE_NAME:
>>+ if (mod_name_attr)
>>+ return -EINVAL;
>
>Extack

Fixed.

>
>
>>+ mod_name_attr = attr;
>>+ break;
>>+ case DPLL_A_PIN_TYPE:
>>+ if (type)
>>+ return -EINVAL;
>
>Extack
>

Fixed.

>
>>+ type = nla_get_u8(attr);
>>+ break;
>>+ case DPLL_A_PIN_BOARD_LABEL:
>>+ if (board_label_attr)
>>+ return -EINVAL;
>
>Extack
>

Fixed.

>
>>+ board_label_attr = attr;
>>+ break;
>>+ case DPLL_A_PIN_PANEL_LABEL:
>>+ if (panel_label_attr)
>>+ return -EINVAL;
>
>Extack
>

Fixed.

>
>>+ panel_label_attr = attr;
>>+ break;
>>+ case DPLL_A_PIN_PACKAGE_LABEL:
>>+ if (package_label_attr)
>>+ return -EINVAL;
>
>Extack
>
>You can use goto with one "duplicate attribute" message.
>

Fixed.

>
>>+ package_label_attr = attr;
>>+ break;
>>+ default:
>>+ break;
>>+ }
>>+ }
>>+ if (!(clock_id || mod_name_attr || board_label_attr ||
>>+ panel_label_attr || package_label_attr))
>>+ return -EINVAL;
>>+ pin = dpll_pin_find(clock_id, mod_name_attr, type, board_label_attr,
>>+ panel_label_attr, package_label_attr);
>
>Error is either "notfound" of "duplicate match". Have the function
>dpll_pin_find() return ERR_PTR with -ENODEV / -EINVAL and let
>the function dpll_pin_find() also fill-up the proper extack inside.
>

Fixed.

>
>>+ if (!pin)
>>+ return -EINVAL;
>>+ if (nla_put_u32(skb, DPLL_A_PIN_ID, pin->id))
>
>Please move this call to the caller. This function should return ERR_PTR
>or dpll_pin pointer.
>

Fixed.

>
>>+ return -EMSGSIZE;
>>+ return 0;
>>+}
>>+
>>+int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+ struct sk_buff *msg;
>>+ struct nlattr *hdr;
>>+ int ret;
>>+
>>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+ if (!msg)
>>+ return -ENOMEM;
>>+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
>>+ DPLL_CMD_PIN_ID_GET);
>>+ if (!hdr)
>>+ return -EMSGSIZE;
>>+
>>+ ret = dpll_pin_find_from_nlattr(info, msg);
>>+ if (ret) {
>>+ nlmsg_free(msg);
>>+ return ret;
>>+ }
>>+ genlmsg_end(msg, hdr);
>
>
>This does not seem to be working:
>$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml
>--do device-id-get --json '{"module-name": "mlx5_dpll"}'
>{'id': 0}
>$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml
>--do pin-id-get --json '{"module-name": "mlx5_dpll"}'
>Traceback (most recent call last):
> File "/mnt/share156/jiri/net-next/./tools/net/ynl/cli.py", line 52, in
><module>
> main()
> File "/mnt/share156/jiri/net-next/./tools/net/ynl/cli.py", line 40, in
>main
> reply = ynl.do(args.do, attrs)
> File "/mnt/share156/jiri/net-next/tools/net/ynl/lib/ynl.py", line 596, in
>do
> return self._op(method, vals)
> File "/mnt/share156/jiri/net-next/tools/net/ynl/lib/ynl.py", line 567, in
>_op
> raise NlError(nl_msg)
>lib.ynl.NlError: Netlink error: Invalid argument
>nl_len = 36 (20) nl_flags = 0x100 nl_type = 2
> error: -22
>$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml
>--do device-id-get --json '{"clock-id": "630763432553410540"}'
>{'id': 0}
>$ sudo ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/dpll.yaml
>--do pin-id-get --json '{"clock-id": "630763432553410540"}'
>Traceback (most recent call last):
> File "/mnt/share156/jiri/net-next/./tools/net/ynl/cli.py", line 52, in
><module>
> main()
> File "/mnt/share156/jiri/net-next/./tools/net/ynl/cli.py", line 40, in
>main
> reply = ynl.do(args.do, attrs)
> File "/mnt/share156/jiri/net-next/tools/net/ynl/lib/ynl.py", line 596, in
>do
> return self._op(method, vals)
> File "/mnt/share156/jiri/net-next/tools/net/ynl/lib/ynl.py", line 567, in
>_op
> raise NlError(nl_msg)
>lib.ynl.NlError: Netlink error: Invalid argument
>nl_len = 36 (20) nl_flags = 0x100 nl_type = 2
> error: -22
>

Wasn't that multiple matches of a pins with the same clock_id?
If you had multiple matches it seems it was fine.

Now, with the changes to find behavior this won't be an error but the reply
with extack, please try on new patchset.

>
>
>>+
>>+ return genlmsg_reply(msg, info);
>>+}
>>+
>>+int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+ struct dpll_pin *pin = info->user_ptr[0];
>>+ struct sk_buff *msg;
>>+ struct nlattr *hdr;
>>+ int ret;
>>+
>>+ if (!pin)
>>+ return -ENODEV;
>>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+ if (!msg)
>>+ return -ENOMEM;
>>+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
>>+ DPLL_CMD_PIN_GET);
>>+ if (!hdr)
>>+ return -EMSGSIZE;
>>+ ret = __dpll_cmd_pin_dump_one(msg, pin, info->extack);
>>+ if (ret) {
>>+ nlmsg_free(msg);
>>+ return ret;
>>+ }
>>+ genlmsg_end(msg, hdr);
>>+
>>+ return genlmsg_reply(msg, info);
>>+}
>>+
>>+int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback
>*cb)
>>+{
>>+ struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>>+ struct dpll_pin *pin;
>>+ struct nlattr *hdr;
>>+ unsigned long i;
>>+ int ret = 0;
>>+
>>+ xa_for_each_start(&dpll_pin_xa, i, pin, ctx->idx) {
>>+ if (xa_empty(&pin->dpll_refs))
>
>Same here, also use REGISTERED mark and iterate over them.
>

As said below, we need new macro for it.

>
>>+ continue;
>>+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
>>+ cb->nlh->nlmsg_seq,
>>+ &dpll_nl_family, NLM_F_MULTI,
>>+ DPLL_CMD_PIN_GET);
>>+ if (!hdr) {
>>+ ret = -EMSGSIZE;
>>+ break;
>>+ }
>>+ ret = __dpll_cmd_pin_dump_one(skb, pin, cb->extack);
>>+ if (ret) {
>>+ genlmsg_cancel(skb, hdr);
>>+ break;
>>+ }
>>+ genlmsg_end(skb, hdr);
>>+ }
>>+ if (ret == -EMSGSIZE) {
>>+ ctx->idx = i;
>>+ return skb->len;
>>+ }
>>+ return ret;
>>+}
>>+
>>+int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+ struct dpll_pin *pin = info->user_ptr[0];
>>+
>>+ return dpll_pin_set_from_nlattr(pin, info);
>>+}
>>+
>>+static struct dpll_device *
>>+dpll_device_find(u64 clock_id, struct nlattr *mod_name_attr,
>>+ enum dpll_type type)
>>+{
>>+ struct dpll_device *dpll_match = NULL, *dpll;
>>+ bool cid_match, mod_match, type_match;
>>+ unsigned long i;
>>+
>>+ xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
>>+ cid_match = clock_id ? dpll->clock_id == clock_id : true;
>>+ mod_match = mod_name_attr && module_name(dpll->module) ?
>>+ !nla_strcmp(mod_name_attr,
>>+ module_name(dpll->module)) : true;
>>+ type_match = type ? dpll->type == type : true;
>>+ if (cid_match && mod_match && type_match) {
>>+ if (dpll_match)
>
>Double match, rigth? Fillup the extack telling the user what happened.
>

Fixed.

>
>>+ return NULL;
>>+ dpll_match = dpll;
>>+ }
>>+ }
>>+
>>+ return dpll_match;
>>+}
>>+
>>+static int
>>+dpll_device_find_from_nlattr(struct genl_info *info, struct sk_buff *skb)
>>+{
>>+ struct nlattr *attr, *mod_name_attr = NULL;
>>+ struct dpll_device *dpll = NULL;
>>+ enum dpll_type type = 0;
>>+ u64 clock_id = 0;
>>+ int rem = 0;
>>+
>>+ nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
>>+ genlmsg_len(info->genlhdr), rem) {
>>+ switch (nla_type(attr)) {
>>+ case DPLL_A_CLOCK_ID:
>>+ if (clock_id)
>>+ return -EINVAL;
>
>Extack
>

Fixed.

>
>>+ clock_id = nla_get_u64(attr);
>>+ break;
>>+ case DPLL_A_MODULE_NAME:
>>+ if (mod_name_attr)
>>+ return -EINVAL;
>
>Extack
>

Fixed.

>
>>+ mod_name_attr = attr;
>>+ break;
>>+ case DPLL_A_TYPE:
>>+ if (type)
>>+ return -EINVAL;
>
>Extack
>
>You can use goto with one "duplicate attribute" message.
>

Fixed.

>
>>+ type = nla_get_u8(attr);
>>+ break;
>>+ default:
>>+ break;
>>+ }
>>+ }
>>+
>>+ if (!clock_id && !mod_name_attr && !type)
>>+ return -EINVAL;
>>+ dpll = dpll_device_find(clock_id, mod_name_attr, type);
>
>Error is either "notfound" of "duplicate match". Have the function
>dpll_device_find() return ERR_PTR with -ENODEV / -EINVAL and let
>the function dpll_device_find() also fill-up the proper extack inside.
>

Fixed.

>
>>+ if (!dpll)
>>+ return -EINVAL;
>>+
>>+ return dpll_msg_add_dev_handle(skb, dpll);
>
>Please move this call to the caller. This function should return ERR_PTR
>or dpll_device pointer.
>

Fixed.

>
>>+}
>>+
>>+static int
>>+dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
>
>Nit: Please move this function above dpll_device_find() to maintain the
>same functions ordering as there is for similar pin functions above.
>

Fixed.

>
>>+{
>>+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>>+ struct nlattr *tb[DPLL_A_MAX + 1];
>>+ int ret = 0;
>
>Drop pointless init.
>

Fixed.

>
>>+
>>+ nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
>>+ genlmsg_len(info->genlhdr), NULL, info->extack);
>>+ if (tb[DPLL_A_MODE]) {
>>+ ret = ops->mode_set(dpll, dpll_priv(dpll),
>>+ nla_get_u8(tb[DPLL_A_MODE]), info->extack);
>>+ if (ret)
>>+ return ret;
>>+ }
>>+
>>+ return 0;
>>+}
>>+
>>+int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info
>>*info)
>>+{
>>+ struct sk_buff *msg;
>>+ struct nlattr *hdr;
>>+ int ret;
>>+
>>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+ if (!msg)
>>+ return -ENOMEM;
>>+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
>>+ DPLL_CMD_DEVICE_ID_GET);
>>+ if (!hdr)
>>+ return -EMSGSIZE;
>>+
>>+ ret = dpll_device_find_from_nlattr(info, msg);
>>+ if (ret) {
>>+ nlmsg_free(msg);
>>+ return ret;
>>+ }
>>+ genlmsg_end(msg, hdr);
>>+
>>+ return genlmsg_reply(msg, info);
>>+}
>>+
>>+int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+ struct dpll_device *dpll = info->user_ptr[0];
>>+ struct sk_buff *msg;
>>+ struct nlattr *hdr;
>>+ int ret;
>>+
>>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+ if (!msg)
>>+ return -ENOMEM;
>>+ hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
>>+ DPLL_CMD_DEVICE_GET);
>>+ if (!hdr)
>>+ return -EMSGSIZE;
>>+
>>+ ret = dpll_device_get_one(dpll, msg, info->extack);
>>+ if (ret) {
>>+ nlmsg_free(msg);
>>+ return ret;
>>+ }
>>+ genlmsg_end(msg, hdr);
>>+
>>+ return genlmsg_reply(msg, info);
>>+}
>>+
>>+int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info)
>>+{
>>+ struct dpll_device *dpll = info->user_ptr[0];
>>+
>>+ return dpll_set_from_nlattr(dpll, info);
>>+}
>>+
>>+int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct
>>netlink_callback *cb)
>>+{
>>+ struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
>>+ struct dpll_device *dpll;
>>+ struct nlattr *hdr;
>>+ unsigned long i;
>>+ int ret = 0;
>>+
>>+ xa_for_each_start(&dpll_device_xa, i, dpll, ctx->idx) {
>>+ if (!xa_get_mark(&dpll_device_xa, i, DPLL_REGISTERED))
>
>Hmm, did you consider adding xa_for_each_marked_start?
>

Sure, can add some helper macro here, altough we probably need to add it to
xarray lib.

>
>>+ continue;
>>+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
>>+ cb->nlh->nlmsg_seq, &dpll_nl_family,
>>+ NLM_F_MULTI, DPLL_CMD_DEVICE_GET);
>>+ if (!hdr) {
>>+ ret = -EMSGSIZE;
>>+ break;
>>+ }
>>+ ret = dpll_device_get_one(dpll, skb, cb->extack);
>>+ if (ret) {
>>+ genlmsg_cancel(skb, hdr);
>>+ break;
>>+ }
>>+ genlmsg_end(skb, hdr);
>>+ }
>>+ if (ret == -EMSGSIZE) {
>>+ ctx->idx = i;
>>+ return skb->len;
>>+ }
>>+ return ret;
>>+}
>>+
>>+int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>>+ struct genl_info *info)
>>+{
>>+ struct dpll_device *dpll_id = NULL;
>>+ u32 id;
>>+
>>+ if (!info->attrs[DPLL_A_ID])
>>+ return -EINVAL;
>>+
>>+ mutex_lock(&dpll_lock);
>>+ id = nla_get_u32(info->attrs[DPLL_A_ID]);
>>+
>>+ dpll_id = dpll_device_get_by_id(id);
>>+ if (!dpll_id)
>>+ goto unlock;
>>+ info->user_ptr[0] = dpll_id;
>>+ return 0;
>>+unlock:
>>+ mutex_unlock(&dpll_lock);
>>+ return -ENODEV;
>>+}
>>+
>>+void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff
>>*skb,
>>+ struct genl_info *info)
>>+{
>>+ mutex_unlock(&dpll_lock);
>>+}
>>+
>>+int
>>+dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>>+ struct genl_info *info)
>>+{
>>+ mutex_lock(&dpll_lock);
>>+
>>+ return 0;
>>+}
>>+
>>+void
>>+dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
>>+ struct genl_info *info)
>>+{
>>+ mutex_unlock(&dpll_lock);
>>+}
>>+
>>+int dpll_lock_dumpit(struct netlink_callback *cb)
>>+{
>>+ mutex_lock(&dpll_lock);
>>+
>>+ return 0;
>>+}
>>+
>>+int dpll_unlock_dumpit(struct netlink_callback *cb)
>>+{
>>+ mutex_unlock(&dpll_lock);
>>+
>>+ return 0;
>>+}
>>+
>>+int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff
>>*skb,
>>+ struct genl_info *info)
>>+{
>>+ int ret;
>>+
>>+ mutex_lock(&dpll_lock);
>>+ if (!info->attrs[DPLL_A_PIN_ID]) {
>
>Use GENL_REQ_ATTR_CHECK(info, DPLL_A_PIN_ID);
>If fills-up the extack info about missing attr giving the user info
>about what went wrong.
>

Fixed.

>
>>+ ret = -EINVAL;
>>+ goto unlock_dev;
>>+ }
>>+ info->user_ptr[0] = xa_load(&dpll_pin_xa,
>>+ nla_get_u32(info->attrs[DPLL_A_PIN_ID]));
>>+ if (!info->user_ptr[0]) {
>
>Fill-up the extack message please.
>

Fixed.

>
>>+ ret = -ENODEV;
>>+ goto unlock_dev;
>>+ }
>>+
>>+ return 0;
>>+
>>+unlock_dev:
>>+ mutex_unlock(&dpll_lock);
>>+ return ret;
>>+}
>>+
>>+void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff
>>*skb,
>>+ struct genl_info *info)
>>+{
>>+ mutex_unlock(&dpll_lock);
>>+}
>>+
>>+static int
>>+dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)
>>+{
>>+ struct sk_buff *msg;
>>+ int ret = -EMSGSIZE;
>
>Drop the pointless init.
>

Fixed.

>
>>+ void *hdr;
>>+
>>+ if (!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED))
>
>WARN_ON? The driver is buggy when he calls this.
>

Fixed.

>
>>+ return -ENODEV;
>>+
>>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+ if (!msg)
>>+ return -ENOMEM;
>>+ hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
>>+ if (!hdr)
>>+ goto out_free_msg;
>
>"err_free_msg" so that is clear is an error path.
>

Fixed.

>
>>+ ret = dpll_device_get_one(dpll, msg, NULL);
>>+ if (ret)
>>+ goto out_cancel_msg;
>
>Same here, "err_cancel_msg"
>

Fixed.

>
>>+ genlmsg_end(msg, hdr);
>>+ genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
>>+
>>+ return 0;
>>+
>>+out_cancel_msg:
>>+ genlmsg_cancel(msg, hdr);
>>+out_free_msg:
>>+ nlmsg_free(msg);
>>+
>>+ return ret;
>>+}
>>+
>>+int dpll_device_create_ntf(struct dpll_device *dpll)
>>+{
>>+ return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
>>+}
>>+
>>+int dpll_device_delete_ntf(struct dpll_device *dpll)
>>+{
>>+ return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
>>+}
>>+
>
>This is an exported function, documentation commentary perhaps?
>I mean, you sometimes have it for static functions, here you don't. Very
>odd.
>
>Let's have that for all exported functions please.
>

Fixed.

>
>>+int dpll_device_change_ntf(struct dpll_device *dpll)
>>+{
>>+ int ret = -EINVAL;
>>+
>>+ if (WARN_ON(!dpll))
>>+ return ret;
>
>Rely on basic driver sanity and drop this check. don't forget to remove
>the ret initialization.
>

Fixed.

>
>>+
>>+ mutex_lock(&dpll_lock);
>>+ ret = dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
>>+ mutex_unlock(&dpll_lock);
>>+
>>+ return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_device_change_ntf);
>>+
>>+static int
>>+dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
>>+{
>>+ struct dpll_pin *pin_verify;
>>+ struct sk_buff *msg;
>>+ int ret = -EMSGSIZE;
>
>Drop the pointless init.
>

Fixed.

>
>>+ void *hdr;
>>+
>>+ pin_verify = xa_load(&dpll_pin_xa, pin->id);
>>+ if (pin != pin_verify)
>
>I don't follow. What is the purpose for this check? Once you have
>REGISTERED mark for pin, you can check it here and be consistent with
>dpll_device_event_send()
>

Fixed.

>
>>+ return -ENODEV;
>>+
>>+ msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>>+ if (!msg)
>>+ return -ENOMEM;
>>+
>>+ hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
>>+ if (!hdr)
>>+ goto out_free_msg;
>
>"err_free_msg" so that is clear is an error path.
>

Fixed.

>
>>+ ret = __dpll_cmd_pin_dump_one(msg, pin, NULL);
>>+ if (ret)
>>+ goto out_cancel_msg;
>
>Same here, "err_cancel_msg"
>

Fixed.

>
>>+ genlmsg_end(msg, hdr);
>>+ genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
>>+
>>+ return 0;
>>+
>>+out_cancel_msg:
>>+ genlmsg_cancel(msg, hdr);
>>+out_free_msg:
>>+ nlmsg_free(msg);
>>+
>>+ return ret;
>>+}
>>+
>>+int dpll_pin_create_ntf(struct dpll_pin *pin)
>>+{
>>+ return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
>>+}
>>+
>>+int dpll_pin_delete_ntf(struct dpll_pin *pin)
>>+{
>>+ return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
>>+}
>>+
>>+static int __dpll_pin_change_ntf(struct dpll_pin *pin)
>>+{
>>+ return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
>>+}
>>+
>>+int dpll_pin_change_ntf(struct dpll_pin *pin)
>>+{
>>+ int ret = -EINVAL;
>>+
>>+ if (WARN_ON(!pin))
>>+ return ret;
>
>Remove this check and expect basic sanity from driver. Also, don't
>forget to drop the "ret" initialization.
>

Fixed.

>
>>+
>>+ mutex_lock(&dpll_lock);
>>+ ret = __dpll_pin_change_ntf(pin);
>>+ mutex_unlock(&dpll_lock);
>>+
>>+ return ret;
>>+}
>>+EXPORT_SYMBOL_GPL(dpll_pin_change_ntf);
>>+
>>+int __init dpll_netlink_init(void)
>>+{
>>+ return genl_register_family(&dpll_nl_family);
>>+}
>>+
>>+void dpll_netlink_finish(void)
>>+{
>>+ genl_unregister_family(&dpll_nl_family);
>>+}
>>+
>>+void __exit dpll_netlink_fini(void)
>>+{
>>+ dpll_netlink_finish();
>>+}
>>diff --git a/drivers/dpll/dpll_netlink.h b/drivers/dpll/dpll_netlink.h
>>new file mode 100644
>>index 000000000000..b5f9bfc88c9e
>>--- /dev/null
>>+++ b/drivers/dpll/dpll_netlink.h
>>@@ -0,0 +1,44 @@
>>+/* SPDX-License-Identifier: GPL-2.0 */
>>+/*
>>+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
>>+ * Copyright (c) 2023 Intel and affiliates
>>+ */
>>+
>>+/**
>>+ * dpll_device_create_ntf - notify that the device has been created
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Context: caller shall hold dpll_xa_lock.
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_device_create_ntf(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_device_delete_ntf - notify that the device has been deleted
>>+ * @dpll: registered dpll pointer
>>+ *
>>+ * Context: caller shall hold dpll_xa_lock.
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>
>Again, I'm going to repeat myself. Please have this kdoc comments once,
>in the .c file. Header should not contain this.
>

Fixed.

Thank you!
Arkadiusz

>
>
>>+int dpll_device_delete_ntf(struct dpll_device *dpll);
>>+
>>+/**
>>+ * dpll_pin_create_ntf - notify that the pin has been created
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Context: caller shall hold dpll_xa_lock.
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_pin_create_ntf(struct dpll_pin *pin);
>>+
>>+/**
>>+ * dpll_pin_delete_ntf - notify that the pin has been deleted
>>+ * @pin: registered pin pointer
>>+ *
>>+ * Context: caller shall hold dpll_xa_lock.
>>+ * Return: 0 if succeeds, error code otherwise.
>>+ */
>>+int dpll_pin_delete_ntf(struct dpll_pin *pin);
>>+
>>+int __init dpll_netlink_init(void);
>>+void dpll_netlink_finish(void);
>>--
>>2.37.3
>>

2023-06-23 01:07:58

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 04/10] dpll: netlink: Add DPLL framework base functions

>From: Petr Oros <[email protected]>
>Sent: Wednesday, June 21, 2023 1:19 PM

[...]

>> +
>> +static int
>> +dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info
>> *info)
>> +{
>> +       const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>> +       struct nlattr *tb[DPLL_A_MAX + 1];
>> +       int ret = 0;
>> +
>> +       nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
>> +                 genlmsg_len(info->genlhdr), NULL, info->extack);
>> +       if (tb[DPLL_A_MODE]) {
>Hi,
>
>Here should be something like:
> if (!ops->mode_set)
> return -EOPNOTSUPP;
>
>Regards,
>Petr

Sure, fixed.

Thank you!
Arkadiusz

[...]

2023-06-23 01:09:12

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 04/10] dpll: netlink: Add DPLL framework base functions

>From: Jiri Pirko <[email protected]>
>Sent: Wednesday, June 21, 2023 3:08 PM
>
>Wed, Jun 21, 2023 at 01:53:24PM CEST, [email protected] wrote:
>>Wed, Jun 21, 2023 at 01:18:59PM CEST, [email protected] wrote:
>>>Arkadiusz Kubalewski píše v Pá 09. 06. 2023 v 14:18 +0200:
>>>> From: Vadim Fedorenko <[email protected]>
>>
>>[...]
>>
>>Could you perhaps cut out the text you don't comment? Saves some time
>>finding your reply.
>>
>>
>>>> +static int
>>>> +dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info
>>>> *info)
>>>> +{
>>>> +       const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>>>> +       struct nlattr *tb[DPLL_A_MAX + 1];
>>>> +       int ret = 0;
>>>> +
>>>> +       nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
>>>> +                 genlmsg_len(info->genlhdr), NULL, info->extack);
>>>> +       if (tb[DPLL_A_MODE]) {
>>>Hi,
>>>
>>>Here should be something like:
>>> if (!ops->mode_set)
>>> return -EOPNOTSUPP;
>>
>>Why? All drivers implement that.
>>I believe that it's actullaly better that way. For a called setting up
>>the same mode it is the dpll in, there should be 0 return by the driver.
>>Note that driver holds this value. I'd like to keep this code as it is.
>
>Actually, you are correct Petr, my mistake. Actually, no driver
>implements this. Arkadiusz, could you please remove this op and
>possibly any other unused op? It will be added when needed.
>
>Thanks!
>

Sorry, didn't have time for such change, added only check as suggested by
Petr.
If you think this is a big issue, we could change it for next version.

Thank you!
Arkadiusz

>
>>
>>[...]

2023-06-23 08:13:21

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 04/10] dpll: netlink: Add DPLL framework base functions

Fri, Jun 23, 2023 at 02:56:24AM CEST, [email protected] wrote:
>>From: Jiri Pirko <[email protected]>
>>Sent: Wednesday, June 21, 2023 3:08 PM
>>
>>Wed, Jun 21, 2023 at 01:53:24PM CEST, [email protected] wrote:
>>>Wed, Jun 21, 2023 at 01:18:59PM CEST, [email protected] wrote:
>>>>Arkadiusz Kubalewski píše v Pá 09. 06. 2023 v 14:18 +0200:
>>>>> From: Vadim Fedorenko <[email protected]>
>>>
>>>[...]
>>>
>>>Could you perhaps cut out the text you don't comment? Saves some time
>>>finding your reply.
>>>
>>>
>>>>> +static int
>>>>> +dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info
>>>>> *info)
>>>>> +{
>>>>> +       const struct dpll_device_ops *ops = dpll_device_ops(dpll);
>>>>> +       struct nlattr *tb[DPLL_A_MAX + 1];
>>>>> +       int ret = 0;
>>>>> +
>>>>> +       nla_parse(tb, DPLL_A_MAX, genlmsg_data(info->genlhdr),
>>>>> +                 genlmsg_len(info->genlhdr), NULL, info->extack);
>>>>> +       if (tb[DPLL_A_MODE]) {
>>>>Hi,
>>>>
>>>>Here should be something like:
>>>> if (!ops->mode_set)
>>>> return -EOPNOTSUPP;
>>>
>>>Why? All drivers implement that.
>>>I believe that it's actullaly better that way. For a called setting up
>>>the same mode it is the dpll in, there should be 0 return by the driver.
>>>Note that driver holds this value. I'd like to keep this code as it is.
>>
>>Actually, you are correct Petr, my mistake. Actually, no driver
>>implements this. Arkadiusz, could you please remove this op and
>>possibly any other unused op? It will be added when needed.
>>
>>Thanks!
>>
>
>Sorry, didn't have time for such change, added only check as suggested by
>Petr.
>If you think this is a big issue, we could change it for next version.

It's odd to carry on ops which are unused. I would prefer that to be
removed now and only introduced when they are actually needed.


>
>Thank you!
>Arkadiusz
>
>>
>>>
>>>[...]

2023-06-29 06:39:45

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

Wed, Jun 21, 2023 at 02:29:59PM CEST, [email protected] wrote:
>Mon, Jun 19, 2023 at 10:34:12PM CEST, [email protected] wrote:
>>>From: Jiri Pirko <[email protected]>
>>>Sent: Saturday, June 10, 2023 6:37 PM
>>>
>>>Fri, Jun 09, 2023 at 02:18:51PM CEST, [email protected] wrote:
>>>
>>>[...]
>>>
>>>
>>>>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>>>>+ enum dpll_mode *mode,
>>>>+ struct netlink_ext_ack *extack)
>>>>+{
>>>>+ *mode = DPLL_MODE_AUTOMATIC;
>>>
>>>I don't understand how the automatic mode could work with SyncE. The
>>>There is one pin exposed for one netdev. The SyncE daemon should select
>>>exacly one pin. How do you achieve that?
>>>Is is by setting DPLL_PIN_STATE_SELECTABLE on the pin-netdev you want to
>>>select and DPLL_PIN_STATE_DISCONNECTED on the rest?
>>>
>>>
>>>[...]
>>
>>AUTOMATIC mode autoselects highest priority valid signal.
>>As you have pointed out, for SyncE selection, the user must be able to manually
>>select a pin state to enable recovery of signal from particular port.
>>
>>In "ice" case there are 2 pins for network PHY clock signal recovery, and both
>>are parent pins (MUX-type). There are also 4 pins assigned to netdevs (one per
>>port). Thus passing a signal from PHY to the pin is done through the MUX-pin,
>>by selecting proper state on pin-parent pair (where parent pins is highest prio
>>pin on dpll).
>
>Could you show me some examples please?

Arkadiusz, could you please reply to this?
Thanks!

>
>
>>
>>Thank you!
>>Arkadiusz

2023-06-29 08:51:36

by Vitaly Grinberg

[permalink] [raw]
Subject: Re: [RFC PATCH v8 00/10] Create common DPLL configuration API

Hi Jiri,
We are pushing for it to be implemented in Intel Ice driver.
Thanks,
Vitaly

On Thu, Jun 29, 2023 at 11:22 AM Jiri Pirko <[email protected]> wrote:
>
> Thu, Jun 22, 2023 at 09:44:19AM CEST, [email protected] wrote:
> >Hi,
> >Could it be possible to add PPS DPLL phase offset to the netlink API? We
> >are relying on it in the E810-based grandmaster implementation.
>
> In which driver you need to implement this?
>
>
> >Thanks,
> >Vitaly
>


2023-06-29 08:54:42

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 00/10] Create common DPLL configuration API

Thu, Jun 22, 2023 at 09:44:19AM CEST, [email protected] wrote:
>Hi,
>Could it be possible to add PPS DPLL phase offset to the netlink API? We
>are relying on it in the E810-based grandmaster implementation.

In which driver you need to implement this?


>Thanks,
>Vitaly

2023-06-29 10:18:37

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 00/10] Create common DPLL configuration API

Thu, Jun 29, 2023 at 10:26:28AM CEST, [email protected] wrote:
>Hi Jiri,
>We are pushing for it to be implemented in Intel Ice driver.

It does not have to be part of the initial submission. It can be
extended later on. Also, not possible to define the UAPI without actual
driver implementation.


>Thanks,
>Vitaly
>
>On Thu, Jun 29, 2023 at 11:22 AM Jiri Pirko <[email protected]> wrote:
>>
>> Thu, Jun 22, 2023 at 09:44:19AM CEST, [email protected] wrote:
>> >Hi,
>> >Could it be possible to add PPS DPLL phase offset to the netlink API? We
>> >are relying on it in the E810-based grandmaster implementation.
>>
>> In which driver you need to implement this?
>>
>>
>> >Thanks,
>> >Vitaly
>>
>

2023-07-03 12:54:20

by Kubalewski, Arkadiusz

[permalink] [raw]
Subject: RE: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

>From: Jiri Pirko <[email protected]>
>Sent: Thursday, June 29, 2023 8:14 AM
>
>Wed, Jun 21, 2023 at 02:29:59PM CEST, [email protected] wrote:
>>Mon, Jun 19, 2023 at 10:34:12PM CEST, [email protected]
>wrote:
>>>>From: Jiri Pirko <[email protected]>
>>>>Sent: Saturday, June 10, 2023 6:37 PM
>>>>
>>>>Fri, Jun 09, 2023 at 02:18:51PM CEST, [email protected]
>>>>wrote:
>>>>
>>>>[...]
>>>>
>>>>
>>>>>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>>>>>+ enum dpll_mode *mode,
>>>>>+ struct netlink_ext_ack *extack)
>>>>>+{
>>>>>+ *mode = DPLL_MODE_AUTOMATIC;
>>>>
>>>>I don't understand how the automatic mode could work with SyncE. The
>>>>There is one pin exposed for one netdev. The SyncE daemon should select
>>>>exacly one pin. How do you achieve that?
>>>>Is is by setting DPLL_PIN_STATE_SELECTABLE on the pin-netdev you want to
>>>>select and DPLL_PIN_STATE_DISCONNECTED on the rest?
>>>>
>>>>
>>>>[...]
>>>
>>>AUTOMATIC mode autoselects highest priority valid signal.
>>>As you have pointed out, for SyncE selection, the user must be able to
>>>manually
>>>select a pin state to enable recovery of signal from particular port.
>>>
>>>In "ice" case there are 2 pins for network PHY clock signal recovery, and
>>>both
>>>are parent pins (MUX-type). There are also 4 pins assigned to netdevs
>>>(one per
>>>port). Thus passing a signal from PHY to the pin is done through the MUX-
>>>pin,
>>>by selecting proper state on pin-parent pair (where parent pins is highest
>>>prio
>>>pin on dpll).
>>
>>Could you show me some examples please?
>
>Arkadiusz, could you please reply to this?
>Thanks!
>

Sure, sorry for the delays, let's try that.

'ice' use case:
Enabling a PHY clock recovery for DPLL_MODE_AUTOMATIC dpll (ID#0) with PHY
recovered clock signals (PIN_ID#13) being muxed using MUX-type pin (PIN_ID#2)

1. Set MUX-type pin to state selectable and highest priority on a dpll device
(or make sure it is already configured):
CMD_PIN_SET:
PIN_ID 2
PIN_PARENT_DEVICE (nest)
ID 0
PIN_STATE SELECTABLE
PIN_PRIO 0
(assume all the other pins have prio >=1)

2. Set connected state on a pin-parent_pin tuple where parent is a pin from #1
CMD_PIN_SET:
PIN_ID 13
PIN_PARENT_PIN (nest)
PIN_ID 2
PIN_STATE CONNECTED

Thank you!
Arkadiusz

>>
>>
>>>
>>>Thank you!
>>>Arkadiusz

2023-07-10 08:37:54

by Jiri Pirko

[permalink] [raw]
Subject: Re: [RFC PATCH v8 08/10] ice: implement dpll interface to control cgu

Mon, Jul 03, 2023 at 02:37:18PM CEST, [email protected] wrote:
>>From: Jiri Pirko <[email protected]>
>>Sent: Thursday, June 29, 2023 8:14 AM
>>
>>Wed, Jun 21, 2023 at 02:29:59PM CEST, [email protected] wrote:
>>>Mon, Jun 19, 2023 at 10:34:12PM CEST, [email protected]
>>wrote:
>>>>>From: Jiri Pirko <[email protected]>
>>>>>Sent: Saturday, June 10, 2023 6:37 PM
>>>>>
>>>>>Fri, Jun 09, 2023 at 02:18:51PM CEST, [email protected]
>>>>>wrote:
>>>>>
>>>>>[...]
>>>>>
>>>>>
>>>>>>+static int ice_dpll_mode_get(const struct dpll_device *dpll, void *priv,
>>>>>>+ enum dpll_mode *mode,
>>>>>>+ struct netlink_ext_ack *extack)
>>>>>>+{
>>>>>>+ *mode = DPLL_MODE_AUTOMATIC;
>>>>>
>>>>>I don't understand how the automatic mode could work with SyncE. The
>>>>>There is one pin exposed for one netdev. The SyncE daemon should select
>>>>>exacly one pin. How do you achieve that?
>>>>>Is is by setting DPLL_PIN_STATE_SELECTABLE on the pin-netdev you want to
>>>>>select and DPLL_PIN_STATE_DISCONNECTED on the rest?
>>>>>
>>>>>
>>>>>[...]
>>>>
>>>>AUTOMATIC mode autoselects highest priority valid signal.
>>>>As you have pointed out, for SyncE selection, the user must be able to
>>>>manually
>>>>select a pin state to enable recovery of signal from particular port.
>>>>
>>>>In "ice" case there are 2 pins for network PHY clock signal recovery, and
>>>>both
>>>>are parent pins (MUX-type). There are also 4 pins assigned to netdevs
>>>>(one per
>>>>port). Thus passing a signal from PHY to the pin is done through the MUX-
>>>>pin,
>>>>by selecting proper state on pin-parent pair (where parent pins is highest
>>>>prio
>>>>pin on dpll).
>>>
>>>Could you show me some examples please?
>>
>>Arkadiusz, could you please reply to this?
>>Thanks!
>>
>
>Sure, sorry for the delays, let's try that.
>
>'ice' use case:
>Enabling a PHY clock recovery for DPLL_MODE_AUTOMATIC dpll (ID#0) with PHY
>recovered clock signals (PIN_ID#13) being muxed using MUX-type pin (PIN_ID#2)
>
>1. Set MUX-type pin to state selectable and highest priority on a dpll device
>(or make sure it is already configured):
>CMD_PIN_SET:
> PIN_ID 2
> PIN_PARENT_DEVICE (nest)
> ID 0
> PIN_STATE SELECTABLE
> PIN_PRIO 0
>(assume all the other pins have prio >=1)
>
>2. Set connected state on a pin-parent_pin tuple where parent is a pin from #1
>CMD_PIN_SET:
> PIN_ID 13
> PIN_PARENT_PIN (nest)
> PIN_ID 2
> PIN_STATE CONNECTED

How does this look from the perspective of a SyncE flow. Let's say you
have eth0 and eth1, both is connected with a DPLL pin. Could you show
how you select eth0 and then eth1?



>
>Thank you!
>Arkadiusz
>
>>>
>>>
>>>>
>>>>Thank you!
>>>>Arkadiusz